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 | 2458 |
1 files changed, 2458 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..551f6584f1e --- /dev/null +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs @@ -0,0 +1,2458 @@ +// 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: +// - Item text editing +// - Column resizing/reodering +// - 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 ColumnHeader clicked_column; + private ListViewItem clicked_item; + private ListViewItem last_clicked_item; + private ColumnHeaderCollection columns; + private ColumnHeader resize_column; + private bool column_resize_active = false; + private bool ctrl_pressed; + private bool shift_pressed; + 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 + 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; + internal Rectangle client_area; // ClientRectangle - scrollbars + private int keysearch_tickcnt; + private string keysearch_text; + static private readonly int keysearch_keydelay = 1000; + + // 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; + + 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; + + public event EventHandler SelectedIndexChanged; + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public new event EventHandler TextChanged; + #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; + + // we are mostly scrollable + 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.DoubleClick += new EventHandler(ListView_DoubleClick); + base.KeyDown += new KeyEventHandler(ListView_KeyDown); + base.KeyUp += new KeyEventHandler(ListView_KeyUp); + base.MouseDown += new MouseEventHandler(ListView_MouseDown); + base.MouseHover += new EventHandler(ListView_MouseHover); + base.MouseUp += new MouseEventHandler(ListView_MouseUp); + base.MouseMove += new MouseEventHandler(ListView_MouseMove); + 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 (this.multiselect && + (this.ctrl_pressed || this.shift_pressed)) + 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 { + if (this.allow_column_reorder != value) { + allow_column_reorder = value; + // column reorder does not matter in Details view + if (this.view != View.Details) + this.Redraw (true); + } + } + } + + [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; + if (BackgroundImageChanged != null) + BackgroundImageChanged (this, new EventArgs ()); + } + } + + [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 { 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 (value != ColumnHeaderStyle.Clickable && value != ColumnHeaderStyle.Nonclickable && + value != ColumnHeaderStyle.None) { + throw new InvalidEnumArgumentException (string.Format + ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value)); + } + + if (header_style != value) { + header_style = value; + // header style matters only in Details view + if (this.view == View.Details) + this.Redraw (false); + } + } + } + + [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); + + if (TextChanged != null) + TextChanged (this, new EventArgs ()); + } + } + + [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 CalculateScrollBars () + { + 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; + } + + 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; + client_area.Height -= h_scroll.Height; + } + + // vertical scrollbar + 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; + client_area.Width -= v_scroll.Width; + } + } + + Size LargeIconItemSize { + get { + int w = Math.Max (text_size.Width, 2 + CheckBoxSize.Width + LargeImageList.ImageSize.Width); + int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, LargeImageList.ImageSize.Height); + return new Size (w, h); + } + } + + Size SmallIconItemSize { + get { + int w = text_size.Width + 2 + CheckBoxSize.Width + SmallImageList.ImageSize.Width; + int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, SmallImageList.ImageSize.Height)); + return new Size (w, h); + } + } + + void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing) + { + if (items.Count == 0) + return; + + Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize; + + int rows, cols; + + if (left_aligned) { + rows = (int) Math.Floor ((double)client_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)client_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; + 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 (); + if (left_aligned) { + if (++row == rows) { + row = 0; + col++; + } + } else { + if (++col == cols) { + col = 0; + row++; + } + } + } + } + + // Sets the location of every item on + // the ListView as per the view + private void CalculateListView (ListViewAlignment align) + { + int current_pos_x = 0; // our x-position marker + int current_pos_y = 0; // our y-position marker + + CalcTextSize (); + + switch (view) { + + case View.Details: + // ColumnHeaders are not drawn if headerstyle is none + int ht = 0; + + if (columns.Count > 0) { + foreach (ColumnHeader col in columns) { + col.X = current_pos_x; + col.Y = current_pos_y; + col.CalcColumnHeader (); + current_pos_x += col.Wd; + } + + if (header_style != ColumnHeaderStyle.None) + ht = columns [0].Ht; + layout_wd = current_pos_x; + } + // set the position marker for placing items + // vertically down + current_pos_y = ht + 2; + + if (items.Count > 0) { + foreach (ListViewItem item in items) { + item.Layout (); + item.Location = new Point (0, current_pos_y); + current_pos_y += item.Bounds.Height + 2; + } + layout_ht = current_pos_y; + + // some space for bottom gridline + if (grid_lines) + layout_ht += 2; + } + 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 i = FocusedItem.Index; + 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 == FocusedItem.Index) + break; + } + return true; + } + + + // Event Handlers + private void ListView_DoubleClick (object sender, EventArgs e) + { + if (this.activation == ItemActivation.Standard + && this.ItemActivate != null) + this.ItemActivate (this, e); + } + + private void ListView_KeyDown (object sender, KeyEventArgs ke) + { + int index = -1; + if (ke.Handled || Items.Count == 0) + return; + + ke.Handled = true; + + switch (ke.KeyCode) { + + case Keys.ControlKey: + ctrl_pressed = true; + break; + + case Keys.Down: + if (focused_item != null && focused_item.Index + 1 < Items.Count) { + index = focused_item.Index + 1; + } + break; + + case Keys.End: + index = Items.Count - 1; + break; + + case Keys.Home: + index = 0; + break; + + case Keys.Left: + index = -1; + if (focused_item != null) + index = focused_item.Index; + else + break; + + if (index > 0) + index -= 1; + + break; + + case Keys.Right: + if (focused_item != null) + index = focused_item.Index + 1; + else + index = 1; + + if (index == items.Count) + index = -1; + + break; + + case Keys.ShiftKey: + shift_pressed = true; + break; + + case Keys.Up: + if (focused_item != null) + index = focused_item.Index; + else + break; + + if (index > 0) + index--; + + if (index < 0) { + index = -1; + } + break; + + default: + if (KeySearchString (ke)) { + ke.Handled = true; + } else { + ke.Handled = false; + } + return; + } + + if (index != -1) { + items [index].Selected = true; + SetFocusedItem (items [index]); + EnsureVisible (index); + } + } + + private void ListView_KeyUp (object sender, KeyEventArgs ke) + { + if (!ke.Handled) { + if (ke.KeyCode == Keys.ControlKey) + this.ctrl_pressed = false; + + if (ke.KeyCode == Keys.ShiftKey) + this.shift_pressed = false; + ke.Handled = true; + } + } + + private void ListView_MouseDown (object sender, MouseEventArgs me) + { + if (items.Count == 0) + return; + + Point hit = Point.Empty; + if (this.HeaderStyle != ColumnHeaderStyle.None) { + // take horizontal scrolling into account + hit = new Point (me.X + h_marker, me.Y); + + // hit test on columns + if (this.view == View.Details && this.columns.Count > 0) { + if (resize_column != null) { + column_resize_active = true; + Capture = true; + return; + } + + foreach (ColumnHeader col in this.columns) { + if (col.Rect.Contains (hit)) { + this.clicked_column = col; + this.Capture = true; + break; + } + } + + if (this.clicked_column != null) { + this.clicked_column.pressed = true; + Rectangle bounds = clicked_column.Rect; + bounds.X -= h_marker; + Invalidate (bounds); + return; + } + } + } + + // hit test on items + // we need to take scrolling into account + hit = new Point (me.X, me.Y); + foreach (ListViewItem item in this.items) { + if (item.CheckRectReal.Contains (hit)) { + CheckState curr_state = item.Checked ? + CheckState.Checked : CheckState.Unchecked; + if (item.Checked) + item.Checked = false; + else + item.Checked = true; + + CheckState new_state = item.Checked ? + CheckState.Checked : CheckState.Unchecked; + + // Raise the ItemCheck event + ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, + curr_state, + new_state); + this.OnItemCheck (ice); + break; + } + + if (this.view == View.Details && + this.FullRowSelect == false) { + if (item.GetBounds (ItemBoundsPortion.Label).Contains (hit)) { + this.clicked_item = item; + break; + } + } + else { + if (item.Bounds.Contains (hit)) { + this.clicked_item = item; + break; + } + } + } + + SetFocusedItem (clicked_item); + + if (clicked_item != null) { + bool changed = !clicked_item.Selected; + clicked_item.Selected = true;; + + // Only Raise the event if the selected item has changed + if (changed) + 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 && this.clicked_item != null) + OnDoubleClick (EventArgs.Empty); + else if (me.Clicks == 1 && clicked_item != null) + OnClick (EventArgs.Empty); + } else if (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) + SelectedItems.Clear (); + SelectedIndices.list.Clear (); + OnSelectedIndexChanged (EventArgs.Empty); + } + } + + private void ListView_MouseHover (object sender, EventArgs e) + { + // handle the hover events only when the mouse + // is not captured. + if (this.hover_selection == false || this.Capture) + return; + + // hit test for the items + Point hit = this.PointToClient (Control.MousePosition); + ListViewItem item = this.GetItemAt (hit.X, hit.Y); + + if (item != null) { + item.Selected = true; + // Raise the event + this.OnSelectedIndexChanged (new EventArgs ()); + } + } + + private void ListView_MouseMove (object sender, MouseEventArgs me) + { + if (View != View.Details || Columns.Count < 2) + return; + + // Column header is always at the top. It can + // scroll only horizontally. So, we have to take + // only horizontal scrolling into account + Point hit = new Point (me.X + h_marker, me.Y); + + if (column_resize_active) { + resize_column.Width = hit.X - resize_column.X; + if (resize_column.Width < 0) + resize_column.Width = 0; + return; + } + + resize_column = null; + + for (int i = 0; i < Columns.Count; i++) { + Rectangle zone = Columns [i].Rect; + zone.X = zone.Right - 5; + zone.Width = 10; + if (zone.Contains (hit)) { + resize_column = Columns [i]; + break; + } + } + + if (resize_column == null) + Cursor = Cursors.Default; + else + Cursor = Cursors.VSplit; + + // non-null clicked_col means mouse down has happened + // on a column + // FIXME: this seems to be drag related + if (this.clicked_column != null) { + if (this.clicked_column.pressed == false && + this.clicked_column.Rect.Contains (hit)) { + this.clicked_column.pressed = true; + this.Redraw (false); + } + else if (this.clicked_column.pressed && + ! this.clicked_column.Rect.Contains (hit)) { + this.clicked_column.pressed = false; + this.Redraw (false); + } + } + } + + private void ListView_MouseUp (object sender, MouseEventArgs me) + { + this.Capture = false; + if (items.Count == 0) + return; + + if (column_resize_active) { + Capture = false; + column_resize_active = false; + resize_column = null; + Cursor = Cursors.Default; + return; + } + + Point hit = new Point (me.X, me.Y); + + if (this.clicked_column != null) { + if (this.clicked_column.pressed) { + this.clicked_column.pressed = false; + Rectangle bounds = clicked_column.Rect; + bounds.X -= h_marker; + Invalidate (bounds); + + // Raise the ColumnClick event + this.OnColumnClick (new ColumnClickEventArgs + (this.clicked_column.Index)); + } + } + + // Raise the ItemActivate event + Rectangle rect = Rectangle.Empty; + if (this.clicked_item != null) { + if (this.view == View.Details && !this.full_row_select) + rect = this.clicked_item.GetBounds (ItemBoundsPortion.Label); + else + rect = this.clicked_item.Bounds; + + // We handle double click in a separate handler + if (this.activation != ItemActivation.Standard && + rect.Contains (hit)) { + if (this.activation == ItemActivation.OneClick) + this.ItemActivate (this, EventArgs.Empty); + + // ItemActivate is raised on the second click on the same item + else if (this.activation == ItemActivation.TwoClick) { + if (this.last_clicked_item == this.clicked_item) { + this.ItemActivate (this, EventArgs.Empty); + this.last_clicked_item = null; + } + else + this.last_clicked_item = this.clicked_item; + } + } + } + + this.clicked_column = null; + this.clicked_item = null; + } + + private void ListView_Paint (object sender, PaintEventArgs pe) + { + if (Width <= 0 || Height <= 0 || !Visible || updating) + return; + + CalculateScrollBars (); + + ThemeEngine.Current.DrawListView (pe.Graphics, + pe.ClipRectangle, this); + + // Raise the Paint event + if (Paint != null) + Paint (this, pe); + } + + 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; + Rectangle area = client_area; + + h_marker = h_scroll.Value; + XplatUI.ScrollWindow (Handle, area, 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 = client_area; + + if (View == View.Details && header_style != ColumnHeaderStyle.None && Columns.Count > 0) { + area.Y += Columns[0].Ht; + area.Height -= Columns[0].Ht; + } + + v_marker = v_scroll.Value; + XplatUI.ScrollWindow (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 (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 = new Rectangle (0, 0, client_area.Width, client_area.Height); + 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 + 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 () + { + for (int i = 0; i < list.Count; i++) + ((ListViewItem) list [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 + } +} |