/* 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 tags) : this(tags, new List()) { } public NewTagDialog(List tags, List indeterminateTags) { InitializeComponent(); LoadTags(tags, indeterminateTags); } public List GetSelectedTags() { return GetTags(CheckState.Checked); } public List GetIndeterminateTags() { return GetTags(CheckState.Indeterminate); } private List GetTags(CheckState checkState) { Program.AssertOnEventThread(); List items = new List(); foreach (var item in ExtractList()) { if (item.Checked == checkState) { items.Add(item.Text); } } return items; } private List ExtractList() { var rows = new List(); foreach (TagsDataGridViewRow item in tagsDataGrid.Rows) { rows.Add(item); } return rows; } private void DisplayList(List 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 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 tags, List indeterminateTags) { Program.AssertOnEventThread(); var rows = new List(); 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 { 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 { private static readonly IList Priority = new List { 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); } } } }