xenadmin/XenAdmin/Dialogs/NewTagDialog.cs
Aaron Robson ca1113fbcc CA-287643: Replaced list view with data grid view to resolve column sizing issue and for consistency with the rest of the product. (#2226)
CA-287643: Replaced list view with data grid view to resolve column sizing issue and for consistency with the rest of the product.

Signed-off-by: Aaron Robson <aaron.robson@citrix.com>
2018-10-02 10:18:34 +01:00

328 lines
11 KiB
C#

/* Copyright (c) Citrix Systems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using XenAdmin.Model;
using XenCenterLib;
namespace XenAdmin.Dialogs
{
public partial class NewTagDialog : XenDialogBase
{
public NewTagDialog(List<string> tags)
: this(tags, new List<string>())
{
}
public NewTagDialog(List<string> tags, List<string> indeterminateTags)
{
InitializeComponent();
LoadTags(tags, indeterminateTags);
}
public List<string> GetSelectedTags()
{
return GetTags(CheckState.Checked);
}
public List<string> GetIndeterminateTags()
{
return GetTags(CheckState.Indeterminate);
}
private List<string> GetTags(CheckState checkState)
{
Program.AssertOnEventThread();
List<string> items = new List<string>();
foreach (var item in ExtractList())
{
if (item.Checked == checkState)
{
items.Add(item.Text);
}
}
return items;
}
private List<TagsDataGridViewRow> ExtractList()
{
var rows = new List<TagsDataGridViewRow>();
foreach (TagsDataGridViewRow item in tagsDataGrid.Rows)
{
rows.Add(item);
}
return rows;
}
private void DisplayList(List<TagsDataGridViewRow> rows)
{
rows.Sort();
try
{
tagsDataGrid.SuspendLayout();
tagsDataGrid.Rows.Clear();
foreach (var item in rows)
{
tagsDataGrid.Rows.Add(item);
}
}
finally
{
tagsDataGrid.ResumeLayout();
}
}
private TagsDataGridViewRow FindTag(string tag, List<TagsDataGridViewRow> rows)
{
// Used to use tagsListView.Items.ContainsKey(tag), but that uses the Name
// instead of the Text, and is also not case sensitive, which caused a bug.
foreach (var item in rows)
{
if (item.Text == tag)
return item;
}
return null;
}
private void AddTag()
{
var rows = ExtractList();
string text = this.textBox1.Text.Trim();
var item = FindTag(text, rows);
if (item == null)
{
item = new TagsDataGridViewRow { Checked = CheckState.Checked, Text = text };
rows.Add(item);
}
else
{
item.Checked = CheckState.Checked;
}
this.textBox1.Text = "";
addButton.Enabled = false;
DisplayList(rows);
}
private void LoadTags(List<string> tags, List<string> indeterminateTags)
{
Program.AssertOnEventThread();
var rows = new List<TagsDataGridViewRow>();
foreach (string tag in Tags.GetAllTags())
{
var checkState = CheckState.Unchecked;
if (tags.Contains(tag))
{
checkState = CheckState.Checked;
}
else if (indeterminateTags.Contains(tag))
{
checkState = CheckState.Indeterminate;
}
var item = new TagsDataGridViewRow { Checked = checkState, Text = tag };
rows.Add(item);
}
foreach (string tag in tags) // We need to include these too, because they may have been recently added and not yet got into GetAllTags()
{
if (FindTag(tag, rows) == null)
{
var item = new TagsDataGridViewRow { Checked = CheckState.Checked, Text = tag};
rows.Add(item);
}
}
DisplayList(rows);
}
private void ToggleItems(System.Collections.IList items)
{
var allChecked = true;
foreach (TagsDataGridViewRow item in items)
{
if (item.Checked != CheckState.Checked)
{
allChecked = false;
break;
}
}
foreach (TagsDataGridViewRow item in items)
item.Checked = allChecked ? CheckState.Unchecked : CheckState.Checked;
}
#region Event handlers
private void NewTagDialog_Activated(object sender, EventArgs e)
{
textBox1.Focus();
}
private void addButton_Click(object sender, EventArgs e)
{
AddTag();
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if ((e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Return) && addButton.Enabled)
{
e.Handled = true;
AddTag();
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
addButton.Enabled = textBox1.Text.Trim() != string.Empty;
}
private void textBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
e.IsInputKey = e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return;
}
private void tagsDataGrid_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Space)
{
ToggleItems(tagsDataGrid.SelectedRows);
e.Handled = true;
}
}
private void tagsDataGrid_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex != ColumnChecked.Index || e.RowIndex < 0 || e.RowIndex >= tagsDataGrid.RowCount)
return;
var row = tagsDataGrid.Rows[e.RowIndex] as TagsDataGridViewRow;
if (row != null)
row.Toggle();
}
private void tagsDataGrid_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
if (e.RowIndex1 < 0 || e.RowIndex1 >= tagsDataGrid.RowCount || e.RowIndex2 < 0 || e.RowIndex2 >= tagsDataGrid.RowCount)
return;
var row1 = tagsDataGrid.Rows[e.RowIndex1] as TagsDataGridViewRow;
var row2 = tagsDataGrid.Rows[e.RowIndex2] as TagsDataGridViewRow;
if (row1 != null && row2 != null)
{
e.SortResult = row1.CompareTo(row2);
e.Handled = true;
}
}
#endregion
public class TagsDataGridViewRow : DataGridViewRow, IComparable<TagsDataGridViewRow>
{
private readonly DataGridViewCheckBoxCell _cellCheckState;
private readonly DataGridViewTextBoxCell _cellTag;
public TagsDataGridViewRow()
{
_cellCheckState = new DataGridViewCheckBoxCell { Value = CheckState.Unchecked, ThreeState = true};
_cellTag = new DataGridViewTextBoxCell();
Cells.AddRange(_cellCheckState, _cellTag);
}
public void Toggle()
{
Checked = Checked == CheckState.Checked ? CheckState.Unchecked : CheckState.Checked;
}
public CheckState Checked
{
get
{
var value = _cellCheckState.Value;
if (value == null)
return CheckState.Unchecked;
if (value is CheckState)
return (CheckState)value;
if (value is bool)
return (bool)value ? CheckState.Checked : CheckState.Unchecked;
return CheckState.Indeterminate;
}
set { _cellCheckState.Value = value; }
}
public string Text
{
get { return _cellTag.Value.ToString(); }
set { _cellTag.Value = value; }
}
public int CompareTo(TagsDataGridViewRow other)
{
if (other == null)
throw new ArgumentNullException(string.Format("Compared {0} must not be null.", GetType().Name));
var checkStateComparer = new SortCheckedStateForTagsHelper();
var output = checkStateComparer.Compare(Checked, other.Checked);
if (output != 0)
return output;
return StringUtility.NaturalCompare(Text, other.Text);
}
}
private class SortCheckedStateForTagsHelper : IComparer<CheckState>
{
private static readonly IList<CheckState> Priority = new List<CheckState>
{
CheckState.Checked,
CheckState.Indeterminate,
CheckState.Unchecked
};
public int Compare(CheckState a, CheckState b)
{
var priorityA = Priority.IndexOf(a);
var priorityB = Priority.IndexOf(b);
return priorityA.CompareTo(priorityB);
}
}
}
}