2023-01-24 15:29:31 +01:00
|
|
|
|
/* Copyright (c) Cloud Software Group, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
* 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;
|
2018-10-02 11:18:34 +02:00
|
|
|
|
using XenCenterLib;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
InitializeComponent();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
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>();
|
2018-10-02 11:18:34 +02:00
|
|
|
|
foreach (var item in ExtractList())
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (item.Checked == checkState)
|
|
|
|
|
{
|
|
|
|
|
items.Add(item.Text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private List<TagsDataGridViewRow> ExtractList()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var rows = new List<TagsDataGridViewRow>();
|
|
|
|
|
foreach (TagsDataGridViewRow item in tagsDataGrid.Rows)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
rows.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
return rows;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void DisplayList(List<TagsDataGridViewRow> rows)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
rows.Sort();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
try
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
tagsDataGrid.SuspendLayout();
|
|
|
|
|
tagsDataGrid.Rows.Clear();
|
|
|
|
|
foreach (var item in rows)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
tagsDataGrid.Rows.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
finally
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
tagsDataGrid.ResumeLayout();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private TagsDataGridViewRow FindTag(string tag, List<TagsDataGridViewRow> rows)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// 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.
|
2018-10-02 11:18:34 +02:00
|
|
|
|
foreach (var item in rows)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (item.Text == tag)
|
2018-10-02 11:18:34 +02:00
|
|
|
|
return item;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
return null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddTag()
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var rows = ExtractList();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
string text = this.textBox1.Text.Trim();
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var item = FindTag(text, rows);
|
|
|
|
|
if (item == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
item = new TagsDataGridViewRow { Checked = CheckState.Checked, Text = text };
|
|
|
|
|
rows.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
item.Checked = CheckState.Checked;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
this.textBox1.Text = "";
|
|
|
|
|
addButton.Enabled = false;
|
2018-10-02 11:18:34 +02:00
|
|
|
|
DisplayList(rows);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void LoadTags(List<string> tags, List<string> indeterminateTags)
|
|
|
|
|
{
|
|
|
|
|
Program.AssertOnEventThread();
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var rows = new List<TagsDataGridViewRow>();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
foreach (string tag in Tags.GetAllTags())
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var checkState = CheckState.Unchecked;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (tags.Contains(tag))
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
checkState = CheckState.Checked;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else if (indeterminateTags.Contains(tag))
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
checkState = CheckState.Indeterminate;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var item = new TagsDataGridViewRow { Checked = checkState, Text = tag };
|
|
|
|
|
rows.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
foreach (string tag in tags) // We need to include these too, because they may have been recently added and not yet got into GetAllTags()
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
if (FindTag(tag, rows) == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var item = new TagsDataGridViewRow { Checked = CheckState.Checked, Text = tag};
|
|
|
|
|
rows.Add(item);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
DisplayList(rows);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void ToggleItems(System.Collections.IList items)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var allChecked = true;
|
|
|
|
|
foreach (TagsDataGridViewRow item in items)
|
|
|
|
|
{
|
|
|
|
|
if (item.Checked != CheckState.Checked)
|
|
|
|
|
{
|
|
|
|
|
allChecked = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
foreach (TagsDataGridViewRow item in items)
|
|
|
|
|
item.Checked = allChecked ? CheckState.Unchecked : CheckState.Checked;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
#region Event handlers
|
|
|
|
|
|
|
|
|
|
private void NewTagDialog_Activated(object sender, EventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
textBox1.Focus();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
AddTag();
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
if ((e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Return) && addButton.Enabled)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
AddTag();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void textBox1_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
addButton.Enabled = textBox1.Text.Trim() != string.Empty;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void textBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
e.IsInputKey = e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void tagsDataGrid_KeyPress(object sender, KeyPressEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
if (e.KeyChar == (char)Keys.Space)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
ToggleItems(tagsDataGrid.SelectedRows);
|
|
|
|
|
e.Handled = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void tagsDataGrid_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
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();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private void tagsDataGrid_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
if (e.RowIndex1 < 0 || e.RowIndex1 >= tagsDataGrid.RowCount || e.RowIndex2 < 0 || e.RowIndex2 >= tagsDataGrid.RowCount)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var row1 = tagsDataGrid.Rows[e.RowIndex1] as TagsDataGridViewRow;
|
|
|
|
|
var row2 = tagsDataGrid.Rows[e.RowIndex2] as TagsDataGridViewRow;
|
|
|
|
|
|
|
|
|
|
if (row1 != null && row2 != null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
e.SortResult = row1.CompareTo(row2);
|
|
|
|
|
e.Handled = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public class TagsDataGridViewRow : DataGridViewRow, IComparable<TagsDataGridViewRow>
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
private readonly DataGridViewCheckBoxCell _cellCheckState;
|
|
|
|
|
private readonly DataGridViewTextBoxCell _cellTag;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
public TagsDataGridViewRow()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
_cellCheckState = new DataGridViewCheckBoxCell { Value = CheckState.Unchecked, ThreeState = true};
|
|
|
|
|
_cellTag = new DataGridViewTextBoxCell();
|
|
|
|
|
Cells.AddRange(_cellCheckState, _cellTag);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
public void Toggle()
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
Checked = Checked == CheckState.Checked ? CheckState.Unchecked : CheckState.Checked;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 11:18:34 +02:00
|
|
|
|
public CheckState Checked
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2018-10-02 11:18:34 +02:00
|
|
|
|
var value = _cellCheckState.Value;
|
|
|
|
|
if (value == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return CheckState.Unchecked;
|
2018-10-02 11:18:34 +02:00
|
|
|
|
|
|
|
|
|
if (value is CheckState)
|
|
|
|
|
return (CheckState)value;
|
|
|
|
|
|
|
|
|
|
if (value is bool)
|
|
|
|
|
return (bool)value ? CheckState.Checked : CheckState.Unchecked;
|
|
|
|
|
|
|
|
|
|
return CheckState.Indeterminate;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-10-02 11:18:34 +02:00
|
|
|
|
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);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|