diff options
Diffstat (limited to 'mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs')
-rw-r--r-- | mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs | 2712 |
1 files changed, 2712 insertions, 0 deletions
diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs new file mode 100644 index 00000000000..e8b5627c537 --- /dev/null +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs @@ -0,0 +1,2712 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004-2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Ravindra Kumar (rkumar@novell.com) +// Jordi Mas i Hernandez, jordi@ximian.com +// Mike Kestner (mkestner@novell.com) +// +// TODO: +// - Feedback for item activation, change in cursor types as mouse moves. +// - HideSelection +// - LabelEdit +// - Drag and drop + + +// NOT COMPLETE + + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Globalization; + +namespace System.Windows.Forms +{ + [DefaultEvent ("SelectedIndexChanged")] + [DefaultProperty ("Items")] + [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")] + public class ListView : Control + { + private ItemActivation activation = ItemActivation.Standard; + private ListViewAlignment alignment = ListViewAlignment.Top; + private bool allow_column_reorder = false; + private bool auto_arrange = true; + private bool check_boxes = false; + private CheckedIndexCollection checked_indices; + private CheckedListViewItemCollection checked_items; + private ColumnHeaderCollection columns; + internal ListViewItem focused_item; + private bool full_row_select = false; + private bool grid_lines = false; + private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable; + private bool hide_selection = true; + private bool hover_selection = false; + private IComparer item_sorter; + private ListViewItemCollection items; + private bool label_edit = false; + private bool label_wrap = true; + private bool multiselect = true; + private bool scrollable = true; + private SelectedIndexCollection selected_indices; + private SelectedListViewItemCollection selected_items; + private SortOrder sort_order = SortOrder.None; + private ImageList state_image_list; + private bool updating = false; + private View view = View.LargeIcon; + private int layout_wd; // We might draw more than our client area + private int layout_ht; // therefore we need to have these two. + //private TextBox editor; // Used for editing an item text + HeaderControl header_control; + internal ItemControl item_control; + internal ScrollBar h_scroll; // used for scrolling horizontally + internal ScrollBar v_scroll; // used for scrolling vertically + internal int h_marker; // Position markers for scrolling + internal int v_marker; + private int keysearch_tickcnt; + private string keysearch_text; + static private readonly int keysearch_keydelay = 1000; + private int[] reordered_column_indices; + + // internal variables + internal ImageList large_image_list; + internal ImageList small_image_list; + internal Size text_size = Size.Empty; + + #region Events + public event LabelEditEventHandler AfterLabelEdit; + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public new event EventHandler BackgroundImageChanged { + add { base.BackgroundImageChanged += value; } + remove { base.BackgroundImageChanged -= value; } + } + + public event LabelEditEventHandler BeforeLabelEdit; + public event ColumnClickEventHandler ColumnClick; + public event EventHandler ItemActivate; + public event ItemCheckEventHandler ItemCheck; + public event ItemDragEventHandler ItemDrag; + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public new event PaintEventHandler Paint { + add { base.Paint += value; } + remove { base.Paint -= value; } + } + + public event EventHandler SelectedIndexChanged; + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public new event EventHandler TextChanged { + add { base.TextChanged += value; } + remove { base.TextChanged -= value; } + } + + #endregion // Events + + #region Public Constructors + public ListView () + { + background_color = ThemeEngine.Current.ColorWindow; + checked_indices = new CheckedIndexCollection (this); + checked_items = new CheckedListViewItemCollection (this); + columns = new ColumnHeaderCollection (this); + foreground_color = SystemColors.WindowText; + items = new ListViewItemCollection (this); + selected_indices = new SelectedIndexCollection (this); + selected_items = new SelectedListViewItemCollection (this); + + border_style = BorderStyle.Fixed3D; + + header_control = new HeaderControl (this); + header_control.Visible = false; + item_control = new ItemControl (this); + item_control.Visible = true; + + h_scroll = new HScrollBar (); + v_scroll = new VScrollBar (); + h_marker = v_marker = 0; + keysearch_tickcnt = 0; + + // scroll bars are disabled initially + h_scroll.Visible = false; + h_scroll.ValueChanged += new EventHandler(HorizontalScroller); + v_scroll.Visible = false; + v_scroll.ValueChanged += new EventHandler(VerticalScroller); + + // event handlers + base.KeyDown += new KeyEventHandler(ListView_KeyDown); + base.Paint += new PaintEventHandler (ListView_Paint); + SizeChanged += new EventHandler (ListView_SizeChanged); + + this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false); + } + #endregion // Public Constructors + + #region Private Internal Properties + internal Size CheckBoxSize { + get { + if (this.check_boxes) { + if (this.state_image_list != null) + return this.state_image_list.ImageSize; + else + return ThemeEngine.Current.ListViewCheckBoxSize; + } + return Size.Empty; + } + } + + internal bool CanMultiselect { + get { + if (multiselect && (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0) + return true; + else + return false; + } + } + + #endregion // Private Internal Properties + + #region Protected Properties + protected override CreateParams CreateParams { + get { return base.CreateParams; } + } + + protected override Size DefaultSize { + get { return ThemeEngine.Current.ListViewDefaultSize; } + } + #endregion // Protected Properties + + #region Public Instance Properties + [DefaultValue (ItemActivation.Standard)] + public ItemActivation Activation { + get { return activation; } + set { + if (value != ItemActivation.Standard && value != ItemActivation.OneClick && + value != ItemActivation.TwoClick) { + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for Activation", value)); + } + + activation = value; + } + } + + [DefaultValue (ListViewAlignment.Top)] + [Localizable (true)] + public ListViewAlignment Alignment { + get { return alignment; } + set { + if (value != ListViewAlignment.Default && value != ListViewAlignment.Left && + value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) { + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for Alignment", value)); + } + + if (this.alignment != value) { + alignment = value; + // alignment does not matter in Details/List views + if (this.view == View.LargeIcon || + this.View == View.SmallIcon) + this.Redraw (true); + } + } + } + + [DefaultValue (false)] + public bool AllowColumnReorder { + get { return allow_column_reorder; } + set { allow_column_reorder = value; } + } + + [DefaultValue (true)] + public bool AutoArrange { + get { return auto_arrange; } + set { + if (auto_arrange != value) { + auto_arrange = value; + // autoarrange does not matter in Details/List views + if (this.view == View.LargeIcon || this.View == View.SmallIcon) + this.Redraw (true); + } + } + } + + public override Color BackColor { + get { + if (background_color.IsEmpty) + return ThemeEngine.Current.ColorWindow; + else + return background_color; + } + set { background_color = value; } + } + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { return background_image; } + set { + if (value == background_image) + return; + + background_image = value; + OnBackgroundImageChanged (EventArgs.Empty); + } + } + + [DefaultValue (BorderStyle.Fixed3D)] + [DispId (-504)] + public BorderStyle BorderStyle { + get { return InternalBorderStyle; } + set { InternalBorderStyle = value; } + } + + [DefaultValue (false)] + public bool CheckBoxes { + get { return check_boxes; } + set { + if (check_boxes != value) { + check_boxes = value; + this.Redraw (true); + } + } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public CheckedIndexCollection CheckedIndices { + get { return checked_indices; } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public CheckedListViewItemCollection CheckedItems { + get { return checked_items; } + } + + [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + [Localizable (true)] + [MergableProperty (false)] + public ColumnHeaderCollection Columns { + get { return columns; } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public ListViewItem FocusedItem { + get { + if (focused_item == null && Focused && items.Count > 0) + focused_item = items [0]; + return focused_item; + } + } + + public override Color ForeColor { + get { + if (foreground_color.IsEmpty) + return ThemeEngine.Current.ColorWindowText; + else + return foreground_color; + } + set { foreground_color = value; } + } + + [DefaultValue (false)] + public bool FullRowSelect { + get { return full_row_select; } + set { full_row_select = value; } + } + + [DefaultValue (false)] + public bool GridLines { + get { return grid_lines; } + set { + if (grid_lines != value) { + grid_lines = value; + this.Redraw (false); + } + } + } + + [DefaultValue (ColumnHeaderStyle.Clickable)] + public ColumnHeaderStyle HeaderStyle { + get { return header_style; } + set { + if (header_style == value) + return; + + switch (value) { + case ColumnHeaderStyle.Clickable: + case ColumnHeaderStyle.Nonclickable: + case ColumnHeaderStyle.None: + break; + default: + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value)); + } + + header_style = value; + if (view == View.Details) + Redraw (true); + } + } + + [DefaultValue (true)] + public bool HideSelection { + get { return hide_selection; } + set { + if (hide_selection != value) { + hide_selection = value; + this.Redraw (false); + } + } + } + + [DefaultValue (false)] + public bool HoverSelection { + get { return hover_selection; } + set { hover_selection = value; } + } + + [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + [Localizable (true)] + [MergableProperty (false)] + public ListViewItemCollection Items { + get { return items; } + } + + [DefaultValue (false)] + public bool LabelEdit { + get { return label_edit; } + set { label_edit = value; } + } + + [DefaultValue (true)] + [Localizable (true)] + public bool LabelWrap { + get { return label_wrap; } + set { + if (label_wrap != value) { + label_wrap = value; + this.Redraw (true); + } + } + } + + [DefaultValue (null)] + public ImageList LargeImageList { + get { return large_image_list; } + set { + large_image_list = value; + this.Redraw (true); + } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public IComparer ListViewItemSorter { + get { return item_sorter; } + set { item_sorter = value; } + } + + [DefaultValue (true)] + public bool MultiSelect { + get { return multiselect; } + set { multiselect = value; } + } + + [DefaultValue (true)] + public bool Scrollable { + get { return scrollable; } + set { + if (scrollable != value) { + scrollable = value; + this.Redraw (true); + } + } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public SelectedIndexCollection SelectedIndices { + get { return selected_indices; } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public SelectedListViewItemCollection SelectedItems { + get { return selected_items; } + } + + [DefaultValue (null)] + public ImageList SmallImageList { + get { return small_image_list; } + set { + small_image_list = value; + this.Redraw (true); + } + } + + [DefaultValue (SortOrder.None)] + public SortOrder Sorting { + get { return sort_order; } + set { + if (value != SortOrder.Ascending && value != SortOrder.Descending && + value != SortOrder.None) { + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for Sorting", value)); + } + + if (sort_order != value) { + sort_order = value; + this.Redraw (false); + } + } + } + + [DefaultValue (null)] + public ImageList StateImageList { + get { return state_image_list; } + set { + state_image_list = value; + this.Redraw (true); + } + } + + [Bindable (false)] + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public override string Text { + get { return text; } + set { + if (value == text) + return; + + text = value; + this.Redraw (true); + + OnTextChanged (EventArgs.Empty); + } + } + + [Browsable (false)] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public ListViewItem TopItem { + get { + // there is no item + if (this.items.Count == 0) + return null; + // if contents are not scrolled + // it is the first item + else if (h_marker == 0 && v_marker == 0) + return this.items [0]; + // do a hit test for the scrolled position + else { + foreach (ListViewItem item in this.items) { + if (item.Bounds.X >= 0 && item.Bounds.Y >= 0) + return item; + } + return null; + } + } + } + + [DefaultValue (View.LargeIcon)] + public View View { + get { return view; } + set { + if (value != View.Details && value != View.LargeIcon && + value != View.List && value != View.SmallIcon ) { + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for View", value)); + } + + if (view != value) { + h_scroll.Value = v_scroll.Value = 0; + view = value; + Redraw (true); + } + } + } + #endregion // Public Instance Properties + + #region Internal Methods Properties + + internal int FirstVisibleIndex { + get { + // there is no item + if (this.items.Count == 0) + return 0; + + if (h_marker == 0 && v_marker == 0) + return 0; + + foreach (ListViewItem item in this.items) { + if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0) + return item.Index; + } + return 0; + + } + } + + + internal int LastVisibleIndex { + get { + for (int i = FirstVisibleIndex; i < Items.Count; i++) { + if (Items[i].Bounds.Y > ClientRectangle.Bottom) + return i -1; + } + + return Items.Count - 1; + } + } + + internal int TotalWidth { + get { return Math.Max (this.Width, this.layout_wd); } + } + + internal int TotalHeight { + get { return Math.Max (this.Height, this.layout_ht); } + } + + internal void Redraw (bool recalculate) + { + // Avoid calculations when control is being updated + if (this.updating) + return; + + if (recalculate) + CalculateListView (this.alignment); + + Refresh (); + } + + internal Size GetChildColumnSize (int index) + { + Size ret_size = Size.Empty; + ColumnHeader col = this.columns [index]; + + if (col.Width == -2) { // autosize = max(items, columnheader) + Size size = Size.Ceiling (this.DeviceContext.MeasureString + (col.Text, this.Font)); + ret_size = BiggestItem (index); + if (size.Width > ret_size.Width) + ret_size = size; + } + else { // -1 and all the values < -2 are put under one category + ret_size = BiggestItem (index); + // fall back to empty columns' width if no subitem is available for a column + if (ret_size.IsEmpty) { + ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth; + if (col.Text.Length > 0) + ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString + (col.Text, this.Font)).Height; + else + ret_size.Height = this.Font.Height; + } + } + + // adjust the size for icon and checkbox for 0th column + if (index == 0) { + ret_size.Width += (this.CheckBoxSize.Width + 4); + if (this.small_image_list != null) + ret_size.Width += this.small_image_list.ImageSize.Width; + } + return ret_size; + } + + // Returns the size of biggest item text in a column. + private Size BiggestItem (int col) + { + Size temp = Size.Empty; + Size ret_size = Size.Empty; + + // 0th column holds the item text, we check the size of + // the various subitems falling in that column and get + // the biggest one's size. + foreach (ListViewItem item in items) { + if (col >= item.SubItems.Count) + continue; + + temp = Size.Ceiling (this.DeviceContext.MeasureString + (item.SubItems [col].Text, this.Font)); + if (temp.Width > ret_size.Width) + ret_size = temp; + } + + // adjustment for space + if (!ret_size.IsEmpty) + ret_size.Width += 4; + + return ret_size; + } + + // Sets the size of the biggest item text as per the view + private void CalcTextSize () + { + // clear the old value + text_size = Size.Empty; + + if (items.Count == 0) + return; + + text_size = BiggestItem (0); + + if (view == View.LargeIcon && this.label_wrap) { + Size temp = Size.Empty; + if (this.check_boxes) + temp.Width += 2 * this.CheckBoxSize.Width; + if (large_image_list != null) + temp.Width += large_image_list.ImageSize.Width; + if (temp.Width == 0) + temp.Width = 43; + // wrapping is done for two lines only + if (text_size.Width > temp.Width) { + text_size.Width = temp.Width; + text_size.Height *= 2; + } + } + else if (view == View.List) { + // in list view max text shown in determined by the + // control width, even if scolling is enabled. + int max_wd = this.Width - (this.CheckBoxSize.Width - 2); + if (this.small_image_list != null) + max_wd -= this.small_image_list.ImageSize.Width; + + if (text_size.Width > max_wd) + text_size.Width = max_wd; + } + + // we do the default settings, if we have got 0's + if (text_size.Height <= 0) + text_size.Height = this.Font.Height; + if (text_size.Width <= 0) + text_size.Width = this.Width; + + // little adjustment + text_size.Width += 4; + text_size.Height += 2; + } + + private void Scroll (ScrollBar scrollbar, int delta) + { + if (delta == 0 || !scrollbar.Visible) + return; + + int max; + if (scrollbar == h_scroll) + max = h_scroll.Maximum - item_control.Width; + else + max = v_scroll.Maximum - item_control.Height; + + int val = scrollbar.Value + delta; + if (val > max) + val = max; + else if (val < scrollbar.Minimum) + val = scrollbar.Minimum; + scrollbar.Value = val; + } + + private void CalculateScrollBars () + { + Rectangle client_area = ClientRectangle; + + if (!this.scrollable || this.items.Count <= 0) { + h_scroll.Visible = false; + v_scroll.Visible = false; + return; + } + + // making a scroll bar visible might make + // other scroll bar visible + if (layout_wd > client_area.Right) { + h_scroll.Visible = true; + if ((layout_ht + h_scroll.Height) > client_area.Bottom) + v_scroll.Visible = true; + else + v_scroll.Visible = false; + } else if (layout_ht > client_area.Bottom) { + v_scroll.Visible = true; + if ((layout_wd + v_scroll.Width) > client_area.Right) + h_scroll.Visible = true; + else + h_scroll.Visible = false; + } else { + h_scroll.Visible = false; + v_scroll.Visible = false; + } + + item_control.Height = ClientRectangle.Height - header_control.Height; + + if (h_scroll.Visible) { + h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height); + h_scroll.Minimum = 0; + + // if v_scroll is visible, adjust the maximum of the + // h_scroll to account for the width of v_scroll + if (v_scroll.Visible) { + h_scroll.Maximum = layout_wd + v_scroll.Width; + h_scroll.Width = client_area.Width - v_scroll.Width; + } + else { + h_scroll.Maximum = layout_wd; + h_scroll.Width = client_area.Width; + } + + h_scroll.LargeChange = client_area.Width; + h_scroll.SmallChange = Font.Height; + item_control.Height -= h_scroll.Height; + } + + if (header_control.Visible) + header_control.Width = ClientRectangle.Width; + item_control.Width = ClientRectangle.Width; + + if (v_scroll.Visible) { + v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y); + v_scroll.Minimum = 0; + + // if h_scroll is visible, adjust the maximum of the + // v_scroll to account for the height of h_scroll + if (h_scroll.Visible) { + v_scroll.Maximum = layout_ht + h_scroll.Height; + v_scroll.Height = client_area.Height; // - h_scroll.Height already done + } else { + v_scroll.Maximum = layout_ht; + v_scroll.Height = client_area.Height; + } + + v_scroll.LargeChange = client_area.Height; + v_scroll.SmallChange = Font.Height; + if (header_control.Visible) + header_control.Width -= v_scroll.Width; + item_control.Width -= v_scroll.Width; + } + } + + ColumnHeader GetReorderedColumn (int index) + { + if (reordered_column_indices == null) + return Columns [index]; + else + return Columns [reordered_column_indices [index]]; + } + + void ReorderColumn (ColumnHeader col, int index) + { + if (reordered_column_indices == null) { + reordered_column_indices = new int [Columns.Count]; + for (int i = 0; i < Columns.Count; i++) + reordered_column_indices [i] = i; + } + + if (reordered_column_indices [index] == col.Index) + return; + + int[] curr = reordered_column_indices; + int[] result = new int [Columns.Count]; + int curr_idx = 0; + for (int i = 0; i < Columns.Count; i++) { + if (curr_idx < Columns.Count && curr [curr_idx] == col.Index) + curr_idx++; + + if (i == index) + result [i] = col.Index; + else + result [i] = curr [curr_idx++]; + } + + reordered_column_indices = result; + LayoutDetails (); + header_control.Invalidate (); + item_control.Invalidate (); + } + + Size LargeIconItemSize { + get { + int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width; + int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height; + int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w); + int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h); + return new Size (w, h); + } + } + + Size SmallIconItemSize { + get { + int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width; + int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height; + int w = text_size.Width + 2 + CheckBoxSize.Width + image_w; + int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h)); + return new Size (w, h); + } + } + + int rows; + int cols; + ListViewItem[,] item_matrix; + + void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing) + { + header_control.Visible = false; + header_control.Size = Size.Empty; + item_control.Location = Point.Empty; + + if (items.Count == 0) + return; + + Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize; + + Rectangle area = ClientRectangle; + + if (left_aligned) { + rows = (int) Math.Floor ((double)area.Height / (double)(sz.Height + y_spacing)); + if (rows == 0) + rows = 1; + cols = (int) Math.Ceiling ((double)items.Count / (double)rows); + } else { + cols = (int) Math.Floor ((double)area.Width / (double)(sz.Width + x_spacing)); + if (cols == 0) + cols = 1; + rows = (int) Math.Ceiling ((double)items.Count / (double)cols); + } + + layout_ht = rows * (sz.Height + y_spacing) - y_spacing; + layout_wd = cols * (sz.Width + x_spacing) - x_spacing; + item_matrix = new ListViewItem [rows, cols]; + int row = 0; + int col = 0; + foreach (ListViewItem item in items) { + int x = col * (sz.Width + x_spacing); + int y = row * (sz.Height + y_spacing); + item.Location = new Point (x, y); + item.Layout (); + item.row = row; + item.col = col; + item_matrix [row, col] = item; + if (left_aligned) { + if (++row == rows) { + row = 0; + col++; + } + } else { + if (++col == cols) { + col = 0; + row++; + } + } + } + + item_control.Size = new Size (layout_wd, layout_ht); + } + + void LayoutHeader () + { + if (columns.Count == 0 || header_style == ColumnHeaderStyle.None) { + header_control.Visible = false; + header_control.Size = Size.Empty; + return; + } + + int x = 0; + for (int i = 0; i < Columns.Count; i++) { + ColumnHeader col = GetReorderedColumn (i); + col.X = x; + col.Y = 0; + col.CalcColumnHeader (); + x += col.Wd; + } + + header_control.Width = x; + header_control.Height = columns [0].Ht; + header_control.Visible = true; + } + + void LayoutDetails () + { + LayoutHeader (); + + item_control.Location = new Point (0, header_control.Height); + + int y = 0; + if (items.Count > 0) { + foreach (ListViewItem item in items) { + item.Layout (); + item.Location = new Point (0, y); + y += item.Bounds.Height + 2; + } + + // some space for bottom gridline + if (grid_lines) + y += 2; + } + + layout_wd = Math.Max (header_control.Width, item_control.Width); + layout_ht = y + header_control.Height; + } + + private void CalculateListView (ListViewAlignment align) + { + CalcTextSize (); + + switch (view) { + case View.Details: + LayoutDetails (); + break; + + case View.SmallIcon: + LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2); + break; + + case View.LargeIcon: + LayoutIcons (true, alignment == ListViewAlignment.Left, + ThemeEngine.Current.ListViewHorizontalSpacing, + ThemeEngine.Current.ListViewVerticalSpacing); + break; + + case View.List: + LayoutIcons (false, true, 4, 2); + break; + } + + CalculateScrollBars (); + } + + internal void UpdateSelection (ListViewItem item) + { + if (item.Selected) { + + if (!CanMultiselect && SelectedItems.Count > 0) { + SelectedItems.Clear (); + SelectedIndices.list.Clear (); + } + + if (!SelectedItems.Contains (item)) { + SelectedItems.list.Add (item); + SelectedIndices.list.Add (item.Index); + } + } else { + SelectedItems.list.Remove (item); + SelectedIndices.list.Remove (item.Index); + } + } + + private bool KeySearchString (KeyEventArgs ke) + { + int current_tickcnt = Environment.TickCount; + if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) { + keysearch_text = string.Empty; + } + + keysearch_text += (char) ke.KeyData; + keysearch_tickcnt = current_tickcnt; + + int start = FocusedItem == null ? 0 : FocusedItem.Index; + int i = start; + while (true) { + if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text, + CompareOptions.IgnoreCase)) { + SetFocusedItem (Items [i]); + items [i].Selected = true; + EnsureVisible (i); + break; + } + i = (i + 1 < Items.Count) ? i+1 : 0; + + if (i == start) + break; + } + return true; + } + + int GetAdjustedIndex (Keys key) + { + int result = -1; + + if (View == View.Details) { + if (key == Keys.Up) + result = FocusedItem.Index - 1; + else if (key == Keys.Down) { + result = FocusedItem.Index + 1; + if (result == items.Count) + result = -1; + } + return result; + } + + int row = FocusedItem.row; + int col = FocusedItem.col; + + switch (key) { + case Keys.Left: + if (col == 0) + return -1; + return item_matrix [row, col - 1].Index; + + case Keys.Right: + if (col == (cols - 1)) + return -1; + while (item_matrix [row, col + 1] == null) + row--; + return item_matrix [row, col + 1].Index; + + case Keys.Up: + if (row == 0) + return -1; + return item_matrix [row - 1, col].Index; + + case Keys.Down: + if (row == (rows - 1)) + return -1; + while (item_matrix [row + 1, col] == null) + col--; + return item_matrix [row + 1, col].Index; + + default: + return -1; + } + } + + ListViewItem selection_start; + + private void SelectItems (ArrayList sel_items) + { + SelectedItems.Clear (); + SelectedIndices.list.Clear (); + foreach (ListViewItem item in sel_items) + item.Selected = true; + } + + private void UpdateMultiSelection (int index) + { + bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0; + bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0; + ListViewItem item = items [index]; + + if (shift_pressed && selection_start != null) { + ArrayList list = new ArrayList (); + int start = Math.Min (selection_start.Index, index); + int end = Math.Max (selection_start.Index, index); + if (View == View.Details) { + for (int i = start; i <= end; i++) + list.Add (items [i]); + } else { + int left = Math.Min (items [start].col, items [end].col); + int right = Math.Max (items [start].col, items [end].col); + int top = Math.Min (items [start].row, items [end].row); + int bottom = Math.Max (items [start].row, items [end].row); + foreach (ListViewItem curr in items) + if (curr.row >= top && curr.row <= bottom && + curr.col >= left && curr.col <= right) + list.Add (curr); + } + SelectItems (list); + } else if (!ctrl_pressed) { + SelectedItems.Clear (); + SelectedIndices.list.Clear (); + item.Selected = true; + selection_start = item; + } + } + + private void ListView_KeyDown (object sender, KeyEventArgs ke) + { + if (ke.Handled || Items.Count == 0) + return; + + int index = -1; + ke.Handled = true; + + switch (ke.KeyCode) { + + case Keys.End: + index = Items.Count - 1; + break; + + case Keys.Home: + index = 0; + break; + + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + index = GetAdjustedIndex (ke.KeyCode); + break; + + default: + ke.Handled = KeySearchString (ke); + return; + } + + if (index == -1) + return; + + if (MultiSelect) + UpdateMultiSelection (index); + else + items [index].Selected = true; + + SetFocusedItem (items [index]); + EnsureVisible (index); + } + + + internal class ItemControl : Control { + + ListView owner; + ListViewItem clicked_item; + ListViewItem last_clicked_item; + bool hover_processed = false; + + public ItemControl (ListView owner) + { + this.owner = owner; + DoubleClick += new EventHandler(ItemsDoubleClick); + KeyDown += new KeyEventHandler (ItemsKeyDown); + KeyUp += new KeyEventHandler (ItemsKeyUp); + MouseDown += new MouseEventHandler(ItemsMouseDown); + MouseMove += new MouseEventHandler(ItemsMouseMove); + MouseHover += new EventHandler(ItemsMouseHover); + MouseUp += new MouseEventHandler(ItemsMouseUp); + MouseWheel += new MouseEventHandler(ItemsMouseWheel); + Paint += new PaintEventHandler (ItemsPaint); + } + + void ItemsDoubleClick (object sender, EventArgs e) + { + if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null) + owner.ItemActivate (this, e); + } + + void ItemsKeyDown (object sender, KeyEventArgs args) + { + owner.OnKeyDown (args); + } + + void ItemsKeyUp (object sender, KeyEventArgs args) + { + owner.OnKeyUp (args); + } + + private void ItemsMouseDown (object sender, MouseEventArgs me) + { + if (owner.items.Count == 0) + return; + + Point pt = new Point (me.X, me.Y); + foreach (ListViewItem item in owner.items) { + if (item.CheckRectReal.Contains (pt)) { + CheckState curr_state = item.Checked ? CheckState.Checked : CheckState.Unchecked; + item.Checked = !item.Checked; + + CheckState new_state = item.Checked ? CheckState.Checked : CheckState.Unchecked; + + // Raise the ItemCheck event + ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state); + owner.OnItemCheck (ice); + break; + } + + if (owner.View == View.Details && !owner.FullRowSelect) { + if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) { + clicked_item = item; + break; + } + } else { + if (item.Bounds.Contains (pt)) { + clicked_item = item; + break; + } + } + } + + owner.SetFocusedItem (clicked_item); + + if (clicked_item != null) { + bool changed = !clicked_item.Selected; + if (owner.MultiSelect && (XplatUI.State.ModifierKeys & Keys.Control) == 0) + owner.UpdateMultiSelection (clicked_item.Index); + else + clicked_item.Selected = true; + + if (changed) + owner.OnSelectedIndexChanged (EventArgs.Empty); + + // Raise double click if the item was clicked. On MS the + // double click is only raised if you double click an item + if (me.Clicks > 1 && clicked_item != null) + owner.OnDoubleClick (EventArgs.Empty); + else if (me.Clicks == 1 && clicked_item != null) + owner.OnClick (EventArgs.Empty); + } else if (owner.selected_indices.Count > 0) { + // Raise the event if there was at least one item + // selected and the user click on a dead area (unselecting all) + owner.SelectedItems.Clear (); + owner.SelectedIndices.list.Clear (); + owner.OnSelectedIndexChanged (EventArgs.Empty); + } + } + + private void ItemsMouseMove (object sender, MouseEventArgs me) + { + if (owner.HoverSelection && hover_processed) { + + Point pt = PointToClient (Control.MousePosition); + ListViewItem item = owner.GetItemAt (pt.X, pt.Y); + if (item == null || item.Selected) + return; + + hover_processed = false; + XplatUI.ResetMouseHover (Handle); + } + } + + + private void ItemsMouseHover (object sender, EventArgs e) + { + if (Capture || !owner.HoverSelection) + return; + + hover_processed = true; + Point pt = PointToClient (Control.MousePosition); + ListViewItem item = owner.GetItemAt (pt.X, pt.Y); + + if (item == null) + return; + + item.Selected = true; + owner.OnSelectedIndexChanged (new EventArgs ()); + } + + private void ItemsMouseUp (object sender, MouseEventArgs me) + { + Capture = false; + if (owner.Items.Count == 0) + return; + + Point pt = new Point (me.X, me.Y); + + Rectangle rect = Rectangle.Empty; + if (clicked_item != null) { + if (owner.view == View.Details && !owner.full_row_select) + rect = clicked_item.GetBounds (ItemBoundsPortion.Label); + else + rect = clicked_item.Bounds; + + if (rect.Contains (pt)) { + switch (owner.activation) { + case ItemActivation.OneClick: + owner.OnItemActivate (EventArgs.Empty); + break; + + case ItemActivation.TwoClick: + if (last_clicked_item == clicked_item) { + owner.OnItemActivate (EventArgs.Empty); + last_clicked_item = null; + } else + last_clicked_item = clicked_item; + break; + default: + // DoubleClick activation is handled in another handler + break; + } + } + } + + clicked_item = null; + } + + private void ItemsMouseWheel (object sender, MouseEventArgs me) + { + if (owner.Items.Count == 0) + return; + + int lines = me.Delta / 120; + + if (lines == 0) + return; + + switch (owner.View) { + case View.Details: + case View.SmallIcon: + owner.Scroll (owner.v_scroll, -owner.Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines); + break; + case View.LargeIcon: + owner.Scroll (owner.v_scroll, -(owner.Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines); + break; + case View.List: + owner.Scroll (owner.h_scroll, -owner.Items [0].Bounds.Width * lines); + break; + } + } + + private void ItemsPaint (object sender, PaintEventArgs pe) + { + ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner); + } + } + + private void ListView_Paint (object sender, PaintEventArgs pe) + { + if (Width <= 0 || Height <= 0 || !Visible || updating) + return; + + CalculateScrollBars (); + } + + private void ListView_SizeChanged (object sender, EventArgs e) + { + CalculateListView (alignment); + } + + private void SetFocusedItem (ListViewItem item) + { + if (focused_item != null) + focused_item.Focused = false; + + if (item != null) + item.Focused = true; + + focused_item = item; + } + + private void HorizontalScroller (object sender, EventArgs e) + { + // Avoid unnecessary flickering, when button is + // kept pressed at the end + if (h_marker != h_scroll.Value) { + + int pixels = h_marker - h_scroll.Value; + + h_marker = h_scroll.Value; + if (header_control.Visible) + XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false); + + XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false); + } + } + + private void VerticalScroller (object sender, EventArgs e) + { + // Avoid unnecessary flickering, when button is + // kept pressed at the end + if (v_marker != v_scroll.Value) { + int pixels = v_marker - v_scroll.Value; + Rectangle area = item_control.ClientRectangle; + v_marker = v_scroll.Value; + XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false); + } + } + #endregion // Internal Methods Properties + + #region Protected Methods + protected override void CreateHandle () + { + base.CreateHandle (); + } + + protected override void Dispose (bool disposing) + { + if (disposing) { + h_scroll.Dispose (); + v_scroll.Dispose (); + + large_image_list = null; + small_image_list = null; + state_image_list = null; + } + + base.Dispose (disposing); + } + + protected override bool IsInputKey (Keys keyData) + { + switch (keyData) { + case Keys.Up: + case Keys.Down: + case Keys.PageUp: + case Keys.PageDown: + case Keys.Right: + case Keys.Left: + case Keys.End: + case Keys.Home: + return true; + + default: + break; + } + + return base.IsInputKey (keyData); + } + + protected virtual void OnAfterLabelEdit (LabelEditEventArgs e) + { + if (AfterLabelEdit != null) + AfterLabelEdit (this, e); + } + + protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e) + { + if (BeforeLabelEdit != null) + BeforeLabelEdit (this, e); + } + + protected virtual void OnColumnClick (ColumnClickEventArgs e) + { + if (ColumnClick != null) + ColumnClick (this, e); + } + + protected override void OnEnabledChanged (EventArgs e) + { + base.OnEnabledChanged (e); + } + + protected override void OnFontChanged (EventArgs e) + { + base.OnFontChanged (e); + Redraw (true); + } + + protected override void OnHandleCreated (EventArgs e) + { + base.OnHandleCreated (e); + SuspendLayout (); + Controls.AddImplicit (header_control); + Controls.AddImplicit (item_control); + Controls.AddImplicit (this.v_scroll); + Controls.AddImplicit (this.h_scroll); + ResumeLayout (); + } + + protected override void OnHandleDestroyed (EventArgs e) + { + base.OnHandleDestroyed (e); + } + + protected virtual void OnItemActivate (EventArgs e) + { + if (ItemActivate != null) + ItemActivate (this, e); + } + + protected virtual void OnItemCheck (ItemCheckEventArgs ice) + { + if (ItemCheck != null) + ItemCheck (this, ice); + } + + protected virtual void OnItemDrag (ItemDragEventArgs e) + { + if (ItemDrag != null) + ItemDrag (this, e); + } + + protected virtual void OnSelectedIndexChanged (EventArgs e) + { + if (SelectedIndexChanged != null) + SelectedIndexChanged (this, e); + } + + protected override void OnSystemColorsChanged (EventArgs e) + { + base.OnSystemColorsChanged (e); + } + + protected void RealizeProperties () + { + // FIXME: TODO + } + + protected void UpdateExtendedStyles () + { + // FIXME: TODO + } + + protected override void WndProc (ref Message m) + { + base.WndProc (ref m); + } + #endregion // Protected Methods + + #region Public Instance Methods + public void ArrangeIcons () + { + ArrangeIcons (this.alignment); + } + + public void ArrangeIcons (ListViewAlignment alignment) + { + // Icons are arranged only if view is set to LargeIcon or SmallIcon + if (view == View.LargeIcon || view == View.SmallIcon) { + this.CalculateListView (alignment); + // we have done the calculations already + this.Redraw (false); + } + } + + public void BeginUpdate () + { + // flag to avoid painting + updating = true; + } + + public void Clear () + { + columns.Clear (); + items.Clear (); // Redraw (true) called here + } + + public void EndUpdate () + { + // flag to avoid painting + updating = false; + + // probably, now we need a redraw with recalculations + this.Redraw (true); + } + + public void EnsureVisible (int index) + { + if (index < 0 || index >= items.Count || scrollable == false) + return; + + Rectangle view_rect = item_control.ClientRectangle; + Rectangle bounds = items [index].Bounds; + + if (view_rect.Contains (bounds)) + return; + + if (bounds.Left < 0) + h_scroll.Value += bounds.Left; + else if (bounds.Right > view_rect.Right) + h_scroll.Value += (bounds.Right - view_rect.Right); + + if (bounds.Top < 0) + v_scroll.Value += bounds.Top; + else if (bounds.Bottom > view_rect.Bottom) + v_scroll.Value += (bounds.Bottom - view_rect.Bottom); + } + + public ListViewItem GetItemAt (int x, int y) + { + foreach (ListViewItem item in items) { + if (item.Bounds.Contains (x, y)) + return item; + } + return null; + } + + public Rectangle GetItemRect (int index) + { + return GetItemRect (index, ItemBoundsPortion.Entire); + } + + public Rectangle GetItemRect (int index, ItemBoundsPortion portion) + { + if (index < 0 || index >= items.Count) + throw new IndexOutOfRangeException ("Invalid Index"); + + return items [index].GetBounds (portion); + } + + public void Sort () + { + if (sort_order != SortOrder.None) + items.list.Sort (item_sorter); + + if (sort_order == SortOrder.Descending) + items.list.Reverse (); + + this.Redraw (true); + } + + public override string ToString () + { + int count = this.Items.Count; + + if (count == 0) + return string.Format ("System.Windows.Forms.ListView, Items.Count: 0"); + else + return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ()); + } + #endregion // Public Instance Methods + + + #region Subclasses + + class HeaderControl : Control { + + ListView owner; + bool column_resize_active = false; + ColumnHeader resize_column; + ColumnHeader clicked_column; + ColumnHeader drag_column; + int drag_x; + int drag_to_index = -1; + + public HeaderControl (ListView owner) + { + this.owner = owner; + MouseDown += new MouseEventHandler (HeaderMouseDown); + MouseMove += new MouseEventHandler (HeaderMouseMove); + MouseUp += new MouseEventHandler (HeaderMouseUp); + Paint += new PaintEventHandler (HeaderPaint); + } + + private ColumnHeader ColumnAtX (int x) + { + Point pt = new Point (x, 0); + ColumnHeader result = null; + foreach (ColumnHeader col in owner.Columns) { + if (col.Rect.Contains (pt)) { + result = col; + break; + } + } + return result; + } + + private int GetReorderedIndex (ColumnHeader col) + { + if (owner.reordered_column_indices == null) + return col.Index; + else + for (int i = 0; i < owner.Columns.Count; i++) + if (owner.reordered_column_indices [i] == col.Index) + return i; + throw new Exception ("Column index missing from reordered array"); + } + + private void HeaderMouseDown (object sender, MouseEventArgs me) + { + if (resize_column != null) { + column_resize_active = true; + Capture = true; + return; + } + + clicked_column = ColumnAtX (me.X + owner.h_marker); + + if (clicked_column != null) { + Capture = true; + if (owner.AllowColumnReorder) { + drag_x = me.X; + drag_column = (ColumnHeader) clicked_column.Clone (); + drag_column.column_rect = clicked_column.Rect; + drag_to_index = GetReorderedIndex (clicked_column); + } + clicked_column.pressed = true; + Rectangle bounds = clicked_column.Rect; + bounds.X -= owner.h_marker; + Invalidate (bounds); + return; + } + } + + private void HeaderMouseMove (object sender, MouseEventArgs me) + { + Point pt = new Point (me.X + owner.h_marker, me.Y); + + if (column_resize_active) { + resize_column.Width = pt.X - resize_column.X; + if (resize_column.Width < 0) + resize_column.Width = 0; + return; + } + + resize_column = null; + + if (clicked_column != null) { + if (owner.AllowColumnReorder) { + Rectangle r; + + r = drag_column.column_rect; + r.X = clicked_column.Rect.X + me.X - drag_x; + drag_column.column_rect = r; + + int x = me.X + owner.h_marker; + ColumnHeader over = ColumnAtX (x); + if (x < over.X + over.Width / 2) + drag_to_index = GetReorderedIndex (over); + else + drag_to_index = GetReorderedIndex (over) + 1; + Invalidate (); + } else { + ColumnHeader over = ColumnAtX (me.X + owner.h_marker); + bool pressed = clicked_column.pressed; + clicked_column.pressed = over == clicked_column; + if (clicked_column.pressed ^ pressed) { + Rectangle bounds = clicked_column.Rect; + bounds.X -= owner.h_marker; + Invalidate (bounds); + } + } + return; + } + + for (int i = 0; i < owner.Columns.Count; i++) { + Rectangle zone = owner.Columns [i].Rect; + zone.X = zone.Right - 5; + zone.Width = 10; + if (zone.Contains (pt)) { + resize_column = owner.Columns [i]; + break; + } + } + + if (resize_column == null) + Cursor = Cursors.Default; + else + Cursor = Cursors.VSplit; + } + + void HeaderMouseUp (object sender, MouseEventArgs me) + { + Capture = false; + + if (column_resize_active) { + column_resize_active = false; + resize_column = null; + Cursor = Cursors.Default; + return; + } + + if (clicked_column != null && clicked_column.pressed) { + clicked_column.pressed = false; + Rectangle bounds = clicked_column.Rect; + bounds.X -= owner.h_marker; + Invalidate (bounds); + owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index)); + } + + if (drag_column != null && owner.AllowColumnReorder) { + drag_column = null; + if (drag_to_index > GetReorderedIndex (clicked_column)) + drag_to_index--; + if (owner.GetReorderedColumn (drag_to_index) != clicked_column) + owner.ReorderColumn (clicked_column, drag_to_index); + drag_to_index = -1; + Invalidate (); + } + + clicked_column = null; + } + + void HeaderPaint (object sender, PaintEventArgs pe) + { + if (Width <= 0 || Height <= 0 || !Visible || owner.updating) + return; + + Theme theme = ThemeEngine.Current; + theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner); + + if (drag_column == null) + return; + + int target_x; + if (drag_to_index == owner.Columns.Count) + target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker; + else + target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker; + theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x); + } + + } + + public class CheckedIndexCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public CheckedIndexCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return true; } + } + + public int this [int index] { + get { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (int) list [index]; + } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return true; } + } + + object IList.this [int index] { + get { return this [index]; } + set { throw new NotSupportedException ("SetItem operation is not supported."); } + } + #endregion // Public Properties + + #region Public Methods + public bool Contains (int checkedIndex) + { + return list.Contains (checkedIndex); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + void ICollection.CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + int IList.Add (object value) + { + throw new NotSupportedException ("Add operation is not supported."); + } + + void IList.Clear () + { + throw new NotSupportedException ("Clear operation is not supported."); + } + + bool IList.Contains (object checkedIndex) + { + return list.Contains (checkedIndex); + } + + int IList.IndexOf (object checkedIndex) + { + return list.IndexOf (checkedIndex); + } + + void IList.Insert (int index, object value) + { + throw new NotSupportedException ("Insert operation is not supported."); + } + + void IList.Remove (object value) + { + throw new NotSupportedException ("Remove operation is not supported."); + } + + void IList.RemoveAt (int index) + { + throw new NotSupportedException ("RemoveAt operation is not supported."); + } + + public int IndexOf (int checkedIndex) + { + return list.IndexOf (checkedIndex); + } + #endregion // Public Methods + + } // CheckedIndexCollection + + public class CheckedListViewItemCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public CheckedListViewItemCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return true; } + } + + public ListViewItem this [int index] { + get { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (ListViewItem) list [index]; + } + } + + bool ICollection.IsSynchronized { + get { return list.IsSynchronized; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return true; } + } + + object IList.this [int index] { + get { return this [index]; } + set { throw new NotSupportedException ("SetItem operation is not supported."); } + } + #endregion // Public Properties + + #region Public Methods + public bool Contains (ListViewItem item) + { + return list.Contains (item); + } + + public virtual void CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + int IList.Add (object value) + { + throw new NotSupportedException ("Add operation is not supported."); + } + + void IList.Clear () + { + throw new NotSupportedException ("Clear operation is not supported."); + } + + bool IList.Contains (object item) + { + return list.Contains (item); + } + + int IList.IndexOf (object item) + { + return list.IndexOf (item); + } + + void IList.Insert (int index, object value) + { + throw new NotSupportedException ("Insert operation is not supported."); + } + + void IList.Remove (object value) + { + throw new NotSupportedException ("Remove operation is not supported."); + } + + void IList.RemoveAt (int index) + { + throw new NotSupportedException ("RemoveAt operation is not supported."); + } + + public int IndexOf (ListViewItem item) + { + return list.IndexOf (item); + } + #endregion // Public Methods + + } // CheckedListViewItemCollection + + public class ColumnHeaderCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public ColumnHeaderCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return false; } + } + + public virtual ColumnHeader this [int index] { + get { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (ColumnHeader) list [index]; + } + } + + bool ICollection.IsSynchronized { + get { return true; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return list.IsFixedSize; } + } + + object IList.this [int index] { + get { return this [index]; } + set { throw new NotSupportedException ("SetItem operation is not supported."); } + } + #endregion // Public Properties + + #region Public Methods + public virtual int Add (ColumnHeader value) + { + int idx; + value.owner = this.owner; + idx = list.Add (value); + owner.Redraw (true); + return idx; + } + + public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign) + { + ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width); + this.Add (colHeader); + return colHeader; + } + + public virtual void AddRange (ColumnHeader [] values) + { + foreach (ColumnHeader colHeader in values) { + colHeader.owner = this.owner; + Add (colHeader); + } + + owner.Redraw (true); + } + + public virtual void Clear () + { + list.Clear (); + owner.Redraw (true); + } + + public bool Contains (ColumnHeader value) + { + return list.Contains (value); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + void ICollection.CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + int IList.Add (object value) + { + if (! (value is ColumnHeader)) { + throw new ArgumentException ("Not of type ColumnHeader", "value"); + } + + return this.Add ((ColumnHeader) value); + } + + bool IList.Contains (object value) + { + if (! (value is ColumnHeader)) { + throw new ArgumentException ("Not of type ColumnHeader", "value"); + } + + return this.Contains ((ColumnHeader) value); + } + + int IList.IndexOf (object value) + { + if (! (value is ColumnHeader)) { + throw new ArgumentException ("Not of type ColumnHeader", "value"); + } + + return this.IndexOf ((ColumnHeader) value); + } + + void IList.Insert (int index, object value) + { + if (! (value is ColumnHeader)) { + throw new ArgumentException ("Not of type ColumnHeader", "value"); + } + + this.Insert (index, (ColumnHeader) value); + } + + void IList.Remove (object value) + { + if (! (value is ColumnHeader)) { + throw new ArgumentException ("Not of type ColumnHeader", "value"); + } + + this.Remove ((ColumnHeader) value); + } + + public int IndexOf (ColumnHeader value) + { + return list.IndexOf (value); + } + + public void Insert (int index, ColumnHeader value) + { + // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property + // but it's really only greater. + if (index < 0 || index > list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + + value.owner = this.owner; + list.Insert (index, value); + owner.Redraw (true); + } + + public void Insert (int index, string str, int width, HorizontalAlignment textAlign) + { + ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width); + this.Insert (index, colHeader); + } + + public virtual void Remove (ColumnHeader column) + { + // TODO: Update Column internal index ? + list.Remove (column); + owner.Redraw (true); + } + + public virtual void RemoveAt (int index) + { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + + // TODO: Update Column internal index ? + list.RemoveAt (index); + owner.Redraw (true); + } + #endregion // Public Methods + + + } // ColumnHeaderCollection + + public class ListViewItemCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public ListViewItemCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return false; } + } + + public virtual ListViewItem this [int displayIndex] { + get { + if (displayIndex < 0 || displayIndex >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (ListViewItem) list [displayIndex]; + } + + set { + if (displayIndex < 0 || displayIndex >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + + if (list.Contains (value)) + throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value"); + + value.Owner = owner; + list [displayIndex] = value; + + owner.Redraw (true); + } + } + + bool ICollection.IsSynchronized { + get { return true; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return list.IsFixedSize; } + } + + object IList.this [int index] { + get { return this [index]; } + set { + if (value is ListViewItem) + this [index] = (ListViewItem) value; + else + this [index] = new ListViewItem (value.ToString ()); + } + } + #endregion // Public Properties + + #region Public Methods + public virtual ListViewItem Add (ListViewItem value) + { + if (list.Contains (value)) + throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value"); + + value.Owner = owner; + list.Add (value); + + if (owner.Sorting != SortOrder.None) + owner.Sort (); + + owner.Redraw (true); + + return value; + } + + public virtual ListViewItem Add (string text) + { + ListViewItem item = new ListViewItem (text); + return this.Add (item); + } + + public virtual ListViewItem Add (string text, int imageIndex) + { + ListViewItem item = new ListViewItem (text, imageIndex); + return this.Add (item); + } + + public void AddRange (ListViewItem [] values) + { + list.Clear (); + owner.SelectedItems.list.Clear (); + owner.SelectedIndices.list.Clear (); + owner.CheckedItems.list.Clear (); + owner.CheckedIndices.list.Clear (); + + foreach (ListViewItem item in values) { + item.Owner = owner; + list.Add (item); + } + + if (owner.Sorting != SortOrder.None) + owner.Sort (); + + owner.Redraw (true); + } + + public virtual void Clear () + { + owner.SetFocusedItem (null); + owner.h_scroll.Value = owner.v_scroll.Value = 0; + list.Clear (); + owner.SelectedItems.list.Clear (); + owner.SelectedIndices.list.Clear (); + owner.CheckedItems.list.Clear (); + owner.CheckedIndices.list.Clear (); + owner.Redraw (true); + } + + public bool Contains (ListViewItem item) + { + return list.Contains (item); + } + + public virtual void CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + int IList.Add (object item) + { + int result; + ListViewItem li; + + if (item is ListViewItem) { + li = (ListViewItem) item; + if (list.Contains (li)) + throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item"); + } + else + li = new ListViewItem (item.ToString ()); + + li.Owner = owner; + result = list.Add (li); + owner.Redraw (true); + + return result; + } + + bool IList.Contains (object item) + { + return list.Contains (item); + } + + int IList.IndexOf (object item) + { + return list.IndexOf (item); + } + + void IList.Insert (int index, object item) + { + if (item is ListViewItem) + this.Insert (index, (ListViewItem) item); + else + this.Insert (index, item.ToString ()); + } + + void IList.Remove (object item) + { + Remove ((ListViewItem) item); + } + + public int IndexOf (ListViewItem item) + { + return list.IndexOf (item); + } + + public ListViewItem Insert (int index, ListViewItem item) + { + // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property + // but it's really only greater. + if (index < 0 || index > list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + + if (list.Contains (item)) + throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item"); + + item.Owner = owner; + list.Insert (index, item); + owner.Redraw (true); + return item; + } + + public ListViewItem Insert (int index, string text) + { + return this.Insert (index, new ListViewItem (text)); + } + + public ListViewItem Insert (int index, string text, int imageIndex) + { + return this.Insert (index, new ListViewItem (text, imageIndex)); + } + + public virtual void Remove (ListViewItem item) + { + if (!list.Contains (item)) + return; + + owner.SelectedItems.list.Remove (item); + owner.SelectedIndices.list.Remove (item.Index); + owner.CheckedItems.list.Remove (item); + owner.CheckedIndices.list.Remove (item.Index); + list.Remove (item); + owner.Redraw (true); + } + + public virtual void RemoveAt (int index) + { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + + list.RemoveAt (index); + owner.SelectedItems.list.RemoveAt (index); + owner.SelectedIndices.list.RemoveAt (index); + owner.CheckedItems.list.RemoveAt (index); + owner.CheckedIndices.list.RemoveAt (index); + owner.Redraw (false); + } + #endregion // Public Methods + + } // ListViewItemCollection + + public class SelectedIndexCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public SelectedIndexCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return true; } + } + + public int this [int index] { + get { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (int) list [index]; + } + } + + bool ICollection.IsSynchronized { + get { return list.IsSynchronized; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return true; } + } + + object IList.this [int index] { + get { return this [index]; } + set { throw new NotSupportedException ("SetItem operation is not supported."); } + } + #endregion // Public Properties + + #region Public Methods + public bool Contains (int selectedIndex) + { + return list.Contains (selectedIndex); + } + + public virtual void CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + int IList.Add (object value) + { + throw new NotSupportedException ("Add operation is not supported."); + } + + void IList.Clear () + { + throw new NotSupportedException ("Clear operation is not supported."); + } + + bool IList.Contains (object selectedIndex) + { + return list.Contains (selectedIndex); + } + + int IList.IndexOf (object selectedIndex) + { + return list.IndexOf (selectedIndex); + } + + void IList.Insert (int index, object value) + { + throw new NotSupportedException ("Insert operation is not supported."); + } + + void IList.Remove (object value) + { + throw new NotSupportedException ("Remove operation is not supported."); + } + + void IList.RemoveAt (int index) + { + throw new NotSupportedException ("RemoveAt operation is not supported."); + } + + public int IndexOf (int selectedIndex) + { + return list.IndexOf (selectedIndex); + } + #endregion // Public Methods + + } // SelectedIndexCollection + + public class SelectedListViewItemCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + private ListView owner; + + #region Public Constructor + public SelectedListViewItemCollection (ListView owner) + { + list = new ArrayList (); + this.owner = owner; + } + #endregion // Public Constructor + + #region Public Properties + [Browsable (false)] + public virtual int Count { + get { return list.Count; } + } + + public virtual bool IsReadOnly { + get { return true; } + } + + public ListViewItem this [int index] { + get { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException ("Index out of range."); + return (ListViewItem) list [index]; + } + } + + bool ICollection.IsSynchronized { + get { return list.IsSynchronized; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return true; } + } + + object IList.this [int index] { + get { return this [index]; } + set { throw new NotSupportedException ("SetItem operation is not supported."); } + } + #endregion // Public Properties + + #region Public Methods + public virtual void Clear () + { + ArrayList copy = (ArrayList) list.Clone (); + for (int i = 0; i < copy.Count; i++) + ((ListViewItem) copy [i]).Selected = false; + + list.Clear (); + } + + public bool Contains (ListViewItem item) + { + return list.Contains (item); + } + + public virtual void CopyTo (Array dest, int index) + { + list.CopyTo (dest, index); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + int IList.Add (object value) + { + throw new NotSupportedException ("Add operation is not supported."); + } + + bool IList.Contains (object item) + { + return list.Contains (item); + } + + int IList.IndexOf (object item) + { + return list.IndexOf (item); + } + + void IList.Insert (int index, object value) + { + throw new NotSupportedException ("Insert operation is not supported."); + } + + void IList.Remove (object value) + { + throw new NotSupportedException ("Remove operation is not supported."); + } + + void IList.RemoveAt (int index) + { + throw new NotSupportedException ("RemoveAt operation is not supported."); + } + + public int IndexOf (ListViewItem item) + { + return list.IndexOf (item); + } + #endregion // Public Methods + + } // SelectedListViewItemCollection + + #endregion // Subclasses + } +} |