aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Widgets
diff options
context:
space:
mode:
Diffstat (limited to 'source/ShiftUI/Widgets')
-rw-r--r--source/ShiftUI/Widgets/Button.cs200
-rw-r--r--source/ShiftUI/Widgets/CaptionButton.cs37
-rw-r--r--source/ShiftUI/Widgets/CategoryGridEntry.cs67
-rw-r--r--source/ShiftUI/Widgets/CheckBox.cs404
-rw-r--r--source/ShiftUI/Widgets/CheckedListBox.cs655
-rw-r--r--source/ShiftUI/Widgets/CollectionEditor.cs738
-rw-r--r--source/ShiftUI/Widgets/ComboBox.cs2825
-rw-r--r--source/ShiftUI/Widgets/ContainerControl.cs850
-rw-r--r--source/ShiftUI/Widgets/DateTimePicker.cs2018
-rw-r--r--source/ShiftUI/Widgets/FlowLayoutPanel.cs197
-rw-r--r--source/ShiftUI/Widgets/GridEntry.cs860
-rw-r--r--source/ShiftUI/Widgets/GridItemCollection.cs158
-rw-r--r--source/ShiftUI/Widgets/GroupBox.cs344
-rw-r--r--source/ShiftUI/Widgets/HScrollBar.cs52
-rw-r--r--source/ShiftUI/Widgets/IRootGridEntry.cs40
-rw-r--r--source/ShiftUI/Widgets/ImplicitHScrollBar.cs48
-rw-r--r--source/ShiftUI/Widgets/ImplicitVScrollBar.cs48
-rw-r--r--source/ShiftUI/Widgets/Label.cs729
-rw-r--r--source/ShiftUI/Widgets/LabelEditTextBox.cs108
-rw-r--r--source/ShiftUI/Widgets/LinkLabel.cs1120
-rw-r--r--source/ShiftUI/Widgets/ListBox.cs3093
-rw-r--r--source/ShiftUI/Widgets/ListControl.cs509
-rw-r--r--source/ShiftUI/Widgets/ListView.cs6155
-rw-r--r--source/ShiftUI/Widgets/ListViewItem.cs1638
-rw-r--r--source/ShiftUI/Widgets/MonthCalendar.cs2430
-rw-r--r--source/ShiftUI/Widgets/NotifyIcon.cs753
-rw-r--r--source/ShiftUI/Widgets/Panel.cs187
-rw-r--r--source/ShiftUI/Widgets/PictureBox.cs618
-rw-r--r--source/ShiftUI/Widgets/PictureBoxSizeMode.cs38
-rw-r--r--source/ShiftUI/Widgets/ProgressBar.cs533
-rw-r--r--source/ShiftUI/Widgets/PropertiesTab.cs76
-rw-r--r--source/ShiftUI/Widgets/PropertyGrid.cs1626
-rw-r--r--source/ShiftUI/Widgets/PropertyGridCommands.cs47
-rw-r--r--source/ShiftUI/Widgets/PropertyGridTextBox.cs320
-rw-r--r--source/ShiftUI/Widgets/PropertyGridView.cs1101
-rw-r--r--source/ShiftUI/Widgets/RadioButton.cs391
-rw-r--r--source/ShiftUI/Widgets/RichTextBox.cs2089
-rw-r--r--source/ShiftUI/Widgets/RootGridEntry.cs88
-rw-r--r--source/ShiftUI/Widgets/ScrollBar.cs1624
-rw-r--r--source/ShiftUI/Widgets/ScrollBarRenderer.cs326
-rw-r--r--source/ShiftUI/Widgets/ScrollButton.cs39
-rw-r--r--source/ShiftUI/Widgets/SizeGrip.cs293
-rw-r--r--source/ShiftUI/Widgets/SizeGripStyle.cs37
-rw-r--r--source/ShiftUI/Widgets/StatusBar.cs806
-rw-r--r--source/ShiftUI/Widgets/StatusBarPanel.cs272
-rw-r--r--source/ShiftUI/Widgets/StatusStrip.cs313
-rw-r--r--source/ShiftUI/Widgets/TabControl.cs2008
-rw-r--r--source/ShiftUI/Widgets/TabPage.cs399
-rw-r--r--source/ShiftUI/Widgets/TableLayoutPanel.cs788
-rw-r--r--source/ShiftUI/Widgets/TextBox.cs1025
-rw-r--r--source/ShiftUI/Widgets/TextBoxBase.cs2493
-rw-r--r--source/ShiftUI/Widgets/TrackBar.cs907
-rw-r--r--source/ShiftUI/Widgets/TreeNode.cs1087
-rw-r--r--source/ShiftUI/Widgets/TreeView.cs2438
-rw-r--r--source/ShiftUI/Widgets/UserWidget.cs236
-rw-r--r--source/ShiftUI/Widgets/VScrollBar.cs79
56 files changed, 48360 insertions, 0 deletions
diff --git a/source/ShiftUI/Widgets/Button.cs b/source/ShiftUI/Widgets/Button.cs
new file mode 100644
index 0000000..1b38e99
--- /dev/null
+++ b/source/ShiftUI/Widgets/Button.cs
@@ -0,0 +1,200 @@
+//
+// 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.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Text;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI {
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ //[Designer ("ShiftUI.Design.ButtonBaseDesigner, " + Consts.AssemblySystem_Design,
+ //"System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class Button : ButtonBase, IButtonWidget {
+ #region Local variables
+ DialogResult dialog_result;
+ #endregion // Local variables
+
+ #region Public Constructors
+ public Button ()
+ {
+ dialog_result = DialogResult.None;
+ SetStyle (Widgetstyles.StandardDoubleClick, false);
+ }
+ #endregion // Public Constructors
+
+ #region Public Properties
+ [Browsable (true)]
+ [Localizable (true)]
+ [DefaultValue (AutoSizeMode.GrowOnly)]
+ [MWFCategory("Layout")]
+ public AutoSizeMode AutoSizeMode {
+ get { return base.GetAutoSizeMode (); }
+ set { base.SetAutoSizeMode (value); }
+ }
+
+ [DefaultValue (DialogResult.None)]
+ [MWFCategory("Behavior")]
+ public virtual DialogResult DialogResult { // IButtonControl
+ get { return dialog_result; }
+ set { dialog_result = value; }
+ }
+ #endregion // Public Properties
+
+ #region Protected Properties
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams; }
+ }
+ #endregion // Protected Properties
+
+ #region Public Methods
+ public virtual void NotifyDefault (bool value) // IButtonControl
+ {
+ this.IsDefault = value;
+ }
+
+ public void PerformClick () // IButtonControl
+ {
+ if (CanSelect)
+ OnClick (EventArgs.Empty);
+ }
+
+ public override string ToString ()
+ {
+ return base.ToString () + ", Text: " + this.Text;
+ }
+ #endregion // Public Methods
+
+ #region Protected Methods
+ protected override void OnClick (EventArgs e)
+ {
+ if (dialog_result != DialogResult.None) {
+ Form p = FindForm ();
+
+ if (p != null)
+ p.DialogResult = dialog_result;
+ }
+
+ base.OnClick (e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnMouseEnter (EventArgs e)
+ {
+ base.OnMouseEnter (e);
+ }
+
+ protected override void OnMouseLeave (EventArgs e)
+ {
+ base.OnMouseLeave (e);
+ }
+
+ protected override void OnMouseUp (MouseEventArgs mevent)
+ {
+ base.OnMouseUp (mevent);
+ }
+
+ protected override void OnTextChanged (EventArgs e)
+ {
+ base.OnTextChanged (e);
+ }
+
+ protected override bool ProcessMnemonic (char charCode)
+ {
+ if (this.UseMnemonic && IsMnemonic (charCode, Text) == true) {
+ PerformClick ();
+ return true;
+ }
+
+ return base.ProcessMnemonic (charCode);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ base.WndProc (ref m);
+ }
+ #endregion // Protected Methods
+
+ #region Events
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+ #endregion // Events
+
+ #region Internal methods
+ internal override void Draw (PaintEventArgs pevent)
+ {
+ // System style does not use any of the new 2.0 stuff
+ if (this.FlatStyle == FlatStyle.System) {
+ base.Draw (pevent);
+ return;
+ }
+
+ // FIXME: This should be called every time something that can affect it
+ // is changed, not every paint. Can only change so many things at a time.
+
+ // Figure out where our text and image should go
+ Rectangle text_rectangle;
+ Rectangle image_rectangle;
+
+ ThemeEngine.Current.CalculateButtonTextAndImageLayout (pevent.Graphics, this, out text_rectangle, out image_rectangle);
+
+ // Draw our button
+ if (this.FlatStyle == FlatStyle.Standard)
+ ThemeEngine.Current.DrawButton (pevent.Graphics, this, text_rectangle, image_rectangle, pevent.ClipRectangle);
+ else if (this.FlatStyle == FlatStyle.Flat)
+ ThemeEngine.Current.DrawFlatButton (pevent.Graphics, this, text_rectangle, image_rectangle, pevent.ClipRectangle);
+ else if (this.FlatStyle == FlatStyle.Popup)
+ ThemeEngine.Current.DrawPopupButton (pevent.Graphics, this, text_rectangle, image_rectangle, pevent.ClipRectangle);
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ if (this.AutoSize)
+ return ThemeEngine.Current.CalculateButtonAutoSize (this);
+
+ return base.GetPreferredSizeCore (proposedSize);
+ }
+ #endregion // Internal methods
+ }
+}
diff --git a/source/ShiftUI/Widgets/CaptionButton.cs b/source/ShiftUI/Widgets/CaptionButton.cs
new file mode 100644
index 0000000..d643327
--- /dev/null
+++ b/source/ShiftUI/Widgets/CaptionButton.cs
@@ -0,0 +1,37 @@
+// 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 Novell, Inc.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+
+
+// COMPLETE
+
+namespace ShiftUI {
+ public enum CaptionButton {
+ Close = 0,
+ Minimize = 1,
+ Maximize = 2,
+ Restore = 3,
+ Help = 4
+ }
+}
diff --git a/source/ShiftUI/Widgets/CategoryGridEntry.cs b/source/ShiftUI/Widgets/CategoryGridEntry.cs
new file mode 100644
index 0000000..cb13f16
--- /dev/null
+++ b/source/ShiftUI/Widgets/CategoryGridEntry.cs
@@ -0,0 +1,67 @@
+// 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.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+//
+
+using System;
+using System.Drawing;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ /// <summary>
+ /// Summary description for CategoryGridEntry
+ /// </summary>
+ internal class CategoryGridEntry : GridEntry
+ {
+ private string label;
+ public CategoryGridEntry (PropertyGrid owner, string category, GridEntry parent)
+ : base (owner, parent)
+ {
+ label = category;
+ }
+
+ public override GridItemType GridItemType {
+ get { return GridItemType.Category; }
+ }
+
+ public override bool Expandable {
+ get { return GridItems.Count > 0; }
+ }
+
+ public override string Label {
+ get { return label; }
+ }
+
+ public override bool IsReadOnly {
+ get { return true; }
+ }
+
+ public override bool IsEditable {
+ get { return false; }
+ }
+
+ public override bool IsResetable {
+ get { return false; }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/CheckBox.cs b/source/ShiftUI/Widgets/CheckBox.cs
new file mode 100644
index 0000000..6ed09f5
--- /dev/null
+++ b/source/ShiftUI/Widgets/CheckBox.cs
@@ -0,0 +1,404 @@
+// 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.
+//
+// Authors:
+// Dennis Hayes [email protected]
+// Peter Bartok [email protected]
+//
+
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [DefaultProperty("Checked")]
+ [DefaultEvent("CheckedChanged")]
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultBindingProperty ("CheckState")]
+ [ToolboxItem ("ShiftUI.Design.AutoSizeToolboxItem," + Consts.AssemblySystem_Design)]
+ [ToolboxWidget]
+ public class CheckBox : ButtonBase {
+ #region Local Variables
+ internal Appearance appearance;
+ internal bool auto_check;
+ internal ContentAlignment check_alignment;
+ internal CheckState check_state;
+ internal bool three_state;
+ #endregion // Local Variables
+
+ #region CheckBoxAccessibleObject Subclass
+ [ComVisible(true)]
+ public class CheckBoxAccessibleObject : ButtonBaseAccessibleObject {
+ #region CheckBoxAccessibleObject Local Variables
+ private new CheckBox owner;
+ #endregion // CheckBoxAccessibleObject Local Variables
+
+ #region CheckBoxAccessibleObject Constructors
+ public CheckBoxAccessibleObject(Widget owner) : base(owner) {
+ this.owner = (CheckBox)owner;
+ }
+ #endregion // CheckBoxAccessibleObject Constructors
+
+ #region CheckBoxAccessibleObject Properties
+ public override string DefaultAction {
+ get {
+ return "Select";
+ }
+ }
+
+ public override AccessibleRole Role {
+ get {
+ return AccessibleRole.CheckButton;
+ }
+ }
+
+ public override AccessibleStates State {
+ get {
+ AccessibleStates retval;
+
+ retval = AccessibleStates.Default;
+
+ if (owner.check_state == CheckState.Checked) {
+ retval |= AccessibleStates.Checked;
+ }
+
+ if (owner.Focused) {
+ retval |= AccessibleStates.Focused;
+ }
+
+ if (owner.CanFocus) {
+ retval |= AccessibleStates.Focusable;
+ }
+
+ return retval;
+ }
+ }
+ #endregion // CheckBoxAccessibleObject Properties
+
+ #region CheckBoxAccessibleObject Methods
+ public override void DoDefaultAction ()
+ {
+ owner.Checked = !owner.Checked;
+ }
+ #endregion // CheckBoxAccessibleObject Methods
+ }
+ #endregion // CheckBoxAccessibleObject Sub-class
+
+ #region Public Constructors
+ public CheckBox() {
+ appearance = Appearance.Normal;
+ auto_check = true;
+ check_alignment = ContentAlignment.MiddleLeft;
+ TextAlign = ContentAlignment.MiddleLeft;
+ SetStyle(Widgetstyles.StandardDoubleClick, false);
+ SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
+ }
+ #endregion // Public Constructors
+
+ #region Internal Methods
+ internal override void Draw (PaintEventArgs pe) {
+ // FIXME: This should be called every time something that can affect it
+ // is changed, not every paint. Can only change so many things at a time.
+
+ // Figure out where our text and image should go
+ Rectangle glyph_rectangle;
+ Rectangle text_rectangle;
+ Rectangle image_rectangle;
+
+ ThemeEngine.Current.CalculateCheckBoxTextAndImageLayout (this, Point.Empty, out glyph_rectangle, out text_rectangle, out image_rectangle);
+
+ // Draw our button
+ if (FlatStyle != FlatStyle.System)
+ ThemeEngine.Current.DrawCheckBox (pe.Graphics, this, glyph_rectangle, text_rectangle, image_rectangle, pe.ClipRectangle);
+ else
+ ThemeEngine.Current.DrawCheckBox (pe.Graphics, this.ClientRectangle, this);
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ if (this.AutoSize)
+ return ThemeEngine.Current.CalculateCheckBoxAutoSize (this);
+
+ return base.GetPreferredSizeCore (proposedSize);
+ }
+
+ internal override void HaveDoubleClick() {
+ if (DoubleClick != null) DoubleClick(this, EventArgs.Empty);
+ }
+ #endregion // Internal Methods
+
+ #region Public Instance Properties
+ [DefaultValue(Appearance.Normal)]
+ [Localizable(true)]
+ public Appearance Appearance {
+ get {
+ return appearance;
+ }
+
+ set {
+ if (value != appearance) {
+ appearance = value;
+ OnAppearanceChanged (EventArgs.Empty);
+
+ if (Parent != null)
+ Parent.PerformLayout (this, "Appearance");
+ Invalidate();
+ }
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool AutoCheck {
+ get {
+ return auto_check;
+ }
+
+ set {
+ auto_check = value;
+ }
+ }
+
+ [Bindable(true)]
+ [Localizable(true)]
+ [DefaultValue(ContentAlignment.MiddleLeft)]
+ public ContentAlignment CheckAlign {
+ get {
+ return check_alignment;
+ }
+
+ set {
+ if (value != check_alignment) {
+ check_alignment = value;
+ if (Parent != null)
+ Parent.PerformLayout (this, "CheckAlign");
+ Invalidate();
+ }
+ }
+ }
+
+ [Bindable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ [DefaultValue(false)]
+ [SettingsBindable (true)]
+ public bool Checked {
+ get {
+ if (check_state != CheckState.Unchecked) {
+ return true;
+ }
+ return false;
+ }
+
+ set {
+ if (value && (check_state != CheckState.Checked)) {
+ check_state = CheckState.Checked;
+ Invalidate();
+ OnCheckedChanged(EventArgs.Empty);
+ } else if (!value && (check_state != CheckState.Unchecked)) {
+ check_state = CheckState.Unchecked;
+ Invalidate();
+ OnCheckedChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue(CheckState.Unchecked)]
+ [RefreshProperties(RefreshProperties.All)]
+ [Bindable(true)]
+ public CheckState CheckState {
+ get {
+ return check_state;
+ }
+
+ set {
+ if (value != check_state) {
+ bool was_checked = (check_state != CheckState.Unchecked);
+
+ check_state = value;
+
+ if (was_checked != (check_state != CheckState.Unchecked)) {
+ OnCheckedChanged(EventArgs.Empty);
+ }
+
+ OnCheckStateChanged(EventArgs.Empty);
+ Invalidate();
+ }
+ }
+ }
+
+ [DefaultValue(ContentAlignment.MiddleLeft)]
+ [Localizable(true)]
+ public override ContentAlignment TextAlign {
+ get { return base.TextAlign; }
+ set { base.TextAlign = value; }
+ }
+
+
+ [DefaultValue(false)]
+ public bool ThreeState {
+ get {
+ return three_state;
+ }
+
+ set {
+ three_state = value;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return new Size(104, 24);
+ }
+ }
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public override string ToString() {
+ return base.ToString() + ", CheckState: " + (int)check_state;
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override AccessibleObject CreateAccessibilityInstance() {
+ AccessibleObject ao;
+
+ ao = base.CreateAccessibilityInstance ();
+ ao.role = AccessibleRole.CheckButton;
+
+ return ao;
+ }
+
+ protected virtual void OnAppearanceChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [AppearanceChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnCheckedChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [CheckedChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnCheckStateChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [CheckStateChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnClick(EventArgs e) {
+ if (auto_check) {
+ switch(check_state) {
+ case CheckState.Unchecked: {
+ if (three_state) {
+ CheckState = CheckState.Indeterminate;
+ } else {
+ CheckState = CheckState.Checked;
+ }
+ break;
+ }
+
+ case CheckState.Indeterminate: {
+ CheckState = CheckState.Checked;
+ break;
+ }
+
+ case CheckState.Checked: {
+ CheckState = CheckState.Unchecked;
+ break;
+ }
+ }
+ }
+
+ base.OnClick (e);
+ }
+
+ protected override void OnHandleCreated(EventArgs e) {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ base.OnKeyDown (e);
+ }
+
+ protected override void OnMouseUp(MouseEventArgs mevent) {
+ base.OnMouseUp (mevent);
+ }
+
+ protected override bool ProcessMnemonic(char charCode) {
+ if (IsMnemonic(charCode, Text) == true) {
+ Select();
+ OnClick(EventArgs.Empty);
+ return true;
+ }
+
+ return base.ProcessMnemonic(charCode);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object AppearanceChangedEvent = new object ();
+ static object CheckedChangedEvent = new object ();
+ static object CheckStateChangedEvent = new object ();
+
+ public event EventHandler AppearanceChanged {
+ add { Events.AddHandler (AppearanceChangedEvent, value); }
+ remove { Events.RemoveHandler (AppearanceChangedEvent, value); }
+ }
+
+ public event EventHandler CheckedChanged {
+ add { Events.AddHandler (CheckedChangedEvent, value); }
+ remove { Events.RemoveHandler (CheckedChangedEvent, value); }
+ }
+
+ public event EventHandler CheckStateChanged {
+ add { Events.AddHandler (CheckStateChangedEvent, value); }
+ remove { Events.RemoveHandler (CheckStateChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+ #endregion // Events
+
+ #region Events
+ // XXX have a look at this and determine if it
+ // manipulates base.DoubleClick, and see if
+ // HaveDoubleClick can just call OnDoubleClick.
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick;
+ #endregion // Events
+ }
+}
diff --git a/source/ShiftUI/Widgets/CheckedListBox.cs b/source/ShiftUI/Widgets/CheckedListBox.cs
new file mode 100644
index 0000000..5bcdf5d
--- /dev/null
+++ b/source/ShiftUI/Widgets/CheckedListBox.cs
@@ -0,0 +1,655 @@
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Mike Kestner <[email protected]>
+//
+//
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI
+{
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [LookupBindingPropertiesAttribute ()]
+ [ToolboxWidget]
+ public class CheckedListBox : ListBox
+ {
+ private CheckedIndexCollection checked_indices;
+ private CheckedItemCollection checked_items;
+ private Hashtable check_states = new Hashtable ();
+ private bool check_onclick = false;
+ private bool three_dcheckboxes = false;
+
+ public CheckedListBox ()
+ {
+ checked_indices = new CheckedIndexCollection (this);
+ checked_items = new CheckedItemCollection (this);
+ SetStyle (Widgetstyles.ResizeRedraw, true);
+ }
+
+ #region events
+ static object ItemCheckEvent = new object ();
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DataSourceChanged {
+ add { base.DataSourceChanged += value; }
+ remove { base.DataSourceChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DisplayMemberChanged {
+ add { base.DisplayMemberChanged += value; }
+ remove { base.DisplayMemberChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event DrawItemEventHandler DrawItem {
+ add { base.DrawItem += value; }
+ remove { base.DrawItem -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MeasureItemEventHandler MeasureItem {
+ add { base.MeasureItem += value; }
+ remove { base.MeasureItem -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ValueMemberChanged {
+ add { base.ValueMemberChanged += value; }
+ remove { base.ValueMemberChanged -= value; }
+ }
+
+ public event ItemCheckEventHandler ItemCheck {
+ add { Events.AddHandler (ItemCheckEvent, value); }
+ remove { Events.RemoveHandler (ItemCheckEvent, value); }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+ #endregion Events
+
+ #region Public Properties
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public CheckedListBox.CheckedIndexCollection CheckedIndices {
+ get {return checked_indices; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public CheckedListBox.CheckedItemCollection CheckedItems {
+ get {return checked_items; }
+ }
+
+ [DefaultValue (false)]
+ public bool CheckOnClick {
+ get { return check_onclick; }
+ set { check_onclick = value; }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams;}
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new object DataSource {
+ get { return base.DataSource; }
+ // FIXME: docs say you can't use a DataSource with this subclass
+ set { base.DataSource = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new string DisplayMember {
+ get { return base.DisplayMember; }
+ set { base.DisplayMember = value; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override DrawMode DrawMode {
+ get { return DrawMode.Normal; }
+ set { /* Not an exception, but has no effect. */ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override int ItemHeight {
+ get { return base.ItemHeight; }
+ set { /* Not an exception, but has no effect. */ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetstringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public new CheckedListBox.ObjectCollection Items {
+ get { return (CheckedListBox.ObjectCollection) base.Items; }
+ }
+
+ public override SelectionMode SelectionMode {
+ get { return base.SelectionMode; }
+ set {
+ if (!Enum.IsDefined (typeof (SelectionMode), value))
+ throw new InvalidEnumArgumentException ("value", (int) value, typeof (SelectionMode));
+
+ if (value == SelectionMode.MultiSimple || value == SelectionMode.MultiExtended)
+ throw new ArgumentException ("Multi selection not supported on CheckedListBox");
+
+ base.SelectionMode = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool ThreeDCheckBoxes {
+ get { return three_dcheckboxes; }
+ set {
+ if (three_dcheckboxes == value)
+ return;
+
+ three_dcheckboxes = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new string ValueMember {
+ get { return base.ValueMember; }
+ set { base.ValueMember = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+ #endregion Public Properties
+
+ #region Public Methods
+
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return base.CreateAccessibilityInstance ();
+ }
+
+ protected override ListBox.ObjectCollection CreateItemCollection ()
+ {
+ return new ObjectCollection (this);
+ }
+
+ public bool GetItemChecked (int index)
+ {
+ return check_states.Contains (Items [index]);
+ }
+
+ public CheckState GetItemCheckState (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ object o = Items [index];
+ if (check_states.Contains (o))
+ return (CheckState) check_states [o];
+ else
+ return CheckState.Unchecked;
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected override void OnClick (EventArgs e)
+ {
+ base.OnClick (e);
+ }
+
+ protected override void OnDrawItem (DrawItemEventArgs e)
+ {
+ if (check_states.Contains (Items [e.Index])) {
+ DrawItemState state = e.State | DrawItemState.Checked;
+ if (((CheckState) check_states [Items [e.Index]]) == CheckState.Indeterminate)
+ state |= DrawItemState.Inactive;
+ e = new DrawItemEventArgs (e.Graphics, e.Font, e.Bounds, e.Index, state, e.ForeColor, e.BackColor);
+ }
+ ThemeEngine.Current.DrawCheckedListBoxItem (this, e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ protected virtual void OnItemCheck (ItemCheckEventArgs ice)
+ {
+ ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
+ if (eh != null)
+ eh (this, ice);
+ }
+
+ protected override void OnKeyPress (KeyPressEventArgs e)
+ {
+ base.OnKeyPress (e);
+
+ if (e.KeyChar == ' ' && FocusedItem != -1)
+ SetItemChecked (FocusedItem, !GetItemChecked (FocusedItem));
+ }
+
+ protected override void OnMeasureItem (MeasureItemEventArgs e)
+ {
+ base.OnMeasureItem (e);
+ }
+
+ protected override void OnSelectedIndexChanged (EventArgs e)
+ {
+ base.OnSelectedIndexChanged (e);
+ }
+
+ protected override void RefreshItems ()
+ {
+ base.RefreshItems ();
+ }
+
+ public void SetItemChecked (int index, bool value)
+ {
+ SetItemCheckState (index, value ? CheckState.Checked : CheckState.Unchecked);
+ }
+
+ public void SetItemCheckState (int index, CheckState value)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (!Enum.IsDefined (typeof (CheckState), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for CheckState", value));
+
+ CheckState old_value = GetItemCheckState (index);
+
+ if (old_value == value)
+ return;
+
+ ItemCheckEventArgs icea = new ItemCheckEventArgs (index, value, old_value);
+ OnItemCheck (icea);
+
+ switch (icea.NewValue) {
+ case CheckState.Checked:
+ case CheckState.Indeterminate:
+ check_states[Items[index]] = icea.NewValue;
+ break;
+ case CheckState.Unchecked:
+ check_states.Remove (Items[index]);
+ break;
+ default:
+ break;
+ }
+
+ UpdateCollections ();
+
+ InvalidateCheckbox (index);
+ }
+
+ protected override void WmReflectCommand (ref Message m)
+ {
+ base.WmReflectCommand (ref m);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ base.WndProc (ref m);
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ int last_clicked_index = -1;
+
+ internal override void OnItemClick (int index)
+ {
+ if ((CheckOnClick || last_clicked_index == index) && index > -1) {
+ if (GetItemChecked (index))
+ SetItemCheckState (index, CheckState.Unchecked);
+ else
+ SetItemCheckState (index, CheckState.Checked);
+ }
+
+ last_clicked_index = index;
+ base.OnItemClick (index);
+ }
+
+ internal override void CollectionChanged ()
+ {
+ base.CollectionChanged ();
+ UpdateCollections ();
+ }
+
+ private void InvalidateCheckbox (int index)
+ {
+ Rectangle area = GetItemDisplayRectangle (index, TopIndex);
+ area.X += 2;
+ area.Y += (area.Height - 11) / 2;
+ area.Width = 11;
+ area.Height = 11;
+ Invalidate (area);
+ }
+
+ private void UpdateCollections ()
+ {
+ CheckedItems.Refresh ();
+ CheckedIndices.Refresh ();
+ }
+
+ #endregion Private Methods
+
+ public new class ObjectCollection : ListBox.ObjectCollection
+ {
+ private CheckedListBox owner;
+
+ public ObjectCollection (CheckedListBox owner) : base (owner)
+ {
+ this.owner = owner;
+ }
+
+ public int Add (object item, bool isChecked)
+ {
+ return Add (item, isChecked ? CheckState.Checked : CheckState.Unchecked);
+ }
+
+ public int Add (object item, CheckState check)
+ {
+ int idx = Add (item);
+
+ ItemCheckEventArgs icea = new ItemCheckEventArgs (idx, check, CheckState.Unchecked);
+
+ if (check == CheckState.Checked)
+ owner.OnItemCheck (icea);
+
+ if (icea.NewValue != CheckState.Unchecked)
+ owner.check_states[item] = icea.NewValue;
+
+ owner.UpdateCollections ();
+ return idx;
+ }
+ }
+
+ public class CheckedIndexCollection : IList, ICollection, IEnumerable
+ {
+ private CheckedListBox owner;
+ private ArrayList indices = new ArrayList ();
+
+ internal CheckedIndexCollection (CheckedListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return indices.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true;}
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ bool IList.IsFixedSize{
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int this[int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return (int) indices[index];
+ }
+ }
+ #endregion Public Properties
+
+ public bool Contains (int index)
+ {
+ return indices.Contains (index);
+ }
+
+
+ public void CopyTo (Array dest, int index)
+ {
+ indices.CopyTo (dest, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return indices.GetEnumerator ();
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ bool IList.Contains (object index)
+ {
+ return Contains ((int)index);
+ }
+
+ int IList.IndexOf (object index)
+ {
+ return IndexOf ((int) index);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ object IList.this[int index]{
+ get {return indices[index]; }
+ set {throw new NotImplementedException (); }
+ }
+
+ public int IndexOf (int index)
+ {
+ return indices.IndexOf (index);
+ }
+
+ #region Private Methods
+ internal void Refresh ()
+ {
+ indices.Clear ();
+ for (int i = 0; i < owner.Items.Count; i++)
+ if (owner.check_states.Contains (owner.Items [i]))
+ indices.Add (i);
+ }
+ #endregion Private Methods
+
+ }
+
+ public class CheckedItemCollection : IList, ICollection, IEnumerable
+ {
+ private CheckedListBox owner;
+ private ArrayList list = new ArrayList ();
+
+ internal CheckedItemCollection (CheckedListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return list[index];
+ }
+ set {throw new NotSupportedException ();}
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return true; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public bool Contains (object item)
+ {
+ return list.Contains (item);
+ }
+
+ public void CopyTo (Array dest, int index)
+ {
+ list.CopyTo (dest, index);
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public int IndexOf (object item)
+ {
+ return list.IndexOf (item);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+ internal void Refresh ()
+ {
+ list.Clear ();
+ for (int i = 0; i < owner.Items.Count; i++)
+ if (owner.check_states.Contains (owner.Items [i]))
+ list.Add (owner.Items[i]);
+ }
+ #endregion Private Methods
+ }
+ [DefaultValue (false)]
+ public bool UseCompatibleTextRendering {
+ get { return use_compatible_text_rendering; }
+ set { use_compatible_text_rendering = value; }
+ }
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/CollectionEditor.cs b/source/ShiftUI/Widgets/CollectionEditor.cs
new file mode 100644
index 0000000..07bbc51
--- /dev/null
+++ b/source/ShiftUI/Widgets/CollectionEditor.cs
@@ -0,0 +1,738 @@
+//
+// System.ComponentModel.Design.CollectionEditor
+//
+// Authors:
+// Martin Willemoes Hansen ([email protected])
+// Andreas Nahr ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+// (C) 2003 Martin Willemoes Hansen
+// (C) 2007 Andreas Nahr
+// (C) 2007 Ivan N. Zlatev
+// (C) 2008 Novell, Inc
+//
+
+//
+// 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.
+//
+
+
+using System;
+using System.Reflection;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing.Design;
+using ShiftUI;
+using ShiftUI.Design;
+
+namespace System.ComponentModel.Design
+{
+ public class CollectionEditor : UITypeEditor
+ {
+ protected abstract class CollectionForm : Form
+ {
+ private CollectionEditor editor;
+ private object editValue;
+
+ public CollectionForm (CollectionEditor editor)
+ {
+ this.editor = editor;
+ }
+
+ protected Type CollectionItemType
+ {
+ get { return editor.CollectionItemType; }
+ }
+
+ protected Type CollectionType
+ {
+ get { return editor.CollectionType; }
+ }
+
+ protected ITypeDescriptorContext Context
+ {
+ get { return editor.Context; }
+ }
+
+ public object EditValue
+ {
+ get { return editValue; }
+ set
+ {
+ editValue = value;
+ OnEditValueChanged ();
+ }
+ }
+
+ protected object[] Items
+ {
+ get { return editor.GetItems (editValue); }
+ set {
+ if (editValue == null) {
+ object newEmptyCollection = null;
+ try {
+ if (typeof (Array).IsAssignableFrom (CollectionType))
+ newEmptyCollection = Array.CreateInstance (CollectionItemType, 0);
+ else
+ newEmptyCollection = Activator.CreateInstance (CollectionType);
+ } catch {}
+
+ object val = editor.SetItems (newEmptyCollection, value);
+ if (val != newEmptyCollection)
+ EditValue = val;
+ } else {
+ object val = editor.SetItems (editValue, value);
+ if (val != editValue)
+ EditValue = val;
+ }
+ }
+ }
+
+ protected Type[] NewItemTypes
+ {
+ get { return editor.NewItemTypes; }
+ }
+
+ protected bool CanRemoveInstance (object value)
+ {
+ return editor.CanRemoveInstance (value);
+ }
+
+ protected virtual bool CanSelectMultipleInstances ()
+ {
+ return editor.CanSelectMultipleInstances ();
+ }
+
+ protected object CreateInstance (Type itemType)
+ {
+ return editor.CreateInstance (itemType);
+ }
+
+ protected void DestroyInstance (object instance)
+ {
+ editor.DestroyInstance (instance);
+ }
+
+ protected virtual void DisplayError (Exception e)
+ {
+ MessageBox.Show (e.Message, "Error");
+ }
+
+ protected override object GetService (Type serviceType)
+ {
+ return editor.GetService (serviceType);
+ }
+
+ protected abstract void OnEditValueChanged ();
+
+ protected internal virtual DialogResult ShowEditorDialog (IWindowsFormsEditorService edSvc)
+ {
+ return edSvc.ShowDialog (this);
+ }
+ }
+
+ private class ConcreteCollectionForm : CollectionForm
+ {
+ internal class ObjectContainerConverter : TypeConverter
+ {
+ private class ObjectContainerPropertyDescriptor : TypeConverter.SimplePropertyDescriptor
+ {
+ private AttributeCollection attributes;
+
+ public ObjectContainerPropertyDescriptor (Type componentType, Type propertyType)
+ : base (componentType, "Value", propertyType)
+ {
+ CategoryAttribute cat = new CategoryAttribute (propertyType.Name);
+ attributes = new AttributeCollection (new Attribute[] { cat });
+ }
+
+ public override object GetValue (object component)
+ {
+ ObjectContainer container = (ObjectContainer)component;
+ return container.Object;
+ }
+
+ public override void SetValue (object component, object value)
+ {
+ ObjectContainer container = (ObjectContainer)component;
+ container.Object = value;
+ }
+
+ public override AttributeCollection Attributes
+ {
+ get { return attributes; }
+ }
+ }
+
+ public override PropertyDescriptorCollection GetProperties (ITypeDescriptorContext context, object value, Attribute[] attributes)
+ {
+ ObjectContainer container = (ObjectContainer)value;
+ ObjectContainerPropertyDescriptor desc = new ObjectContainerPropertyDescriptor (value.GetType (), container.editor.CollectionItemType);
+ PropertyDescriptor[] properties = new PropertyDescriptor[] { desc };
+ PropertyDescriptorCollection pc = new PropertyDescriptorCollection (properties);
+ return pc;
+ }
+
+ public override bool GetPropertiesSupported (ITypeDescriptorContext context)
+ {
+ return true;
+ }
+ }
+
+ [TypeConverter (typeof (ObjectContainerConverter))]
+ private class ObjectContainer
+ {
+ internal object Object;
+ internal CollectionEditor editor;
+
+ public ObjectContainer (object obj, CollectionEditor editor)
+ {
+ this.Object = obj;
+ this.editor = editor;
+ }
+
+ internal string Name {
+ get { return editor.GetDisplayText (Object); }
+ }
+
+ public override string ToString ()
+ {
+ return Name;
+ }
+ }
+
+ private class UpdateableListbox : ListBox
+ {
+ public void DoRefreshItem (int index)
+ {
+ base.RefreshItem (index);
+ }
+ }
+
+ private CollectionEditor editor;
+
+ private ShiftUI.Label labelMember;
+ private ShiftUI.Label labelProperty;
+ private UpdateableListbox itemsList;
+ private ShiftUI.PropertyGrid itemDisplay;
+ private ShiftUI.Button doClose;
+ private ShiftUI.Button moveUp;
+ private ShiftUI.Button moveDown;
+ private ShiftUI.Button doAdd;
+ private ShiftUI.Button doRemove;
+ private ShiftUI.Button doCancel;
+ private ShiftUI.ComboBox addType;
+
+ public ConcreteCollectionForm (CollectionEditor editor)
+ : base (editor)
+ {
+ this.editor = editor;
+
+ this.labelMember = new ShiftUI.Label ();
+ this.labelProperty = new ShiftUI.Label ();
+ this.itemsList = new UpdateableListbox ();
+ this.itemDisplay = new ShiftUI.PropertyGrid ();
+ this.doClose = new ShiftUI.Button ();
+ this.moveUp = new ShiftUI.Button ();
+ this.moveDown = new ShiftUI.Button ();
+ this.doAdd = new ShiftUI.Button ();
+ this.doRemove = new ShiftUI.Button ();
+ this.doCancel = new ShiftUI.Button ();
+ this.addType = new ShiftUI.ComboBox ();
+ this.SuspendLayout ();
+ //
+ // labelMember
+ //
+ this.labelMember.Location = new System.Drawing.Point (12, 9);
+ this.labelMember.Size = new System.Drawing.Size (55, 13);
+ this.labelMember.Text = "Members:";
+ //
+ // labelProperty
+ //
+ this.labelProperty.Anchor = ((ShiftUI.AnchorStyles)(((ShiftUI.AnchorStyles.Top | ShiftUI.AnchorStyles.Left)
+ | ShiftUI.AnchorStyles.Right)));
+ this.labelProperty.Location = new System.Drawing.Point (172, 9);
+ this.labelProperty.Size = new System.Drawing.Size (347, 13);
+ this.labelProperty.Text = "Properties:";
+ //
+ // itemsList
+ //
+ this.itemsList.Anchor = ((ShiftUI.AnchorStyles)(((ShiftUI.AnchorStyles.Top | ShiftUI.AnchorStyles.Bottom)
+ | ShiftUI.AnchorStyles.Left)));
+ this.itemsList.HorizontalScrollbar = true;
+ this.itemsList.Location = new System.Drawing.Point (12, 25);
+ this.itemsList.SelectionMode = ShiftUI.SelectionMode.MultiExtended;
+ this.itemsList.Size = new System.Drawing.Size (120, 290);
+ this.itemsList.TabIndex = 0;
+ this.itemsList.SelectedIndexChanged += new System.EventHandler (this.itemsList_SelectedIndexChanged);
+ //
+ // itemDisplay
+ //
+ this.itemDisplay.Anchor = ((ShiftUI.AnchorStyles)((((ShiftUI.AnchorStyles.Top | ShiftUI.AnchorStyles.Bottom)
+ | ShiftUI.AnchorStyles.Left)
+ | ShiftUI.AnchorStyles.Right)));
+ this.itemDisplay.HelpVisible = false;
+ this.itemDisplay.Location = new System.Drawing.Point (175, 25);
+ this.itemDisplay.Size = new System.Drawing.Size (344, 314);
+ this.itemDisplay.TabIndex = 6;
+ this.itemDisplay.PropertyValueChanged += new ShiftUI.PropertyValueChangedEventHandler (this.itemDisplay_PropertyValueChanged);
+ //
+ // doClose
+ //
+ this.doClose.Anchor = ((ShiftUI.AnchorStyles)((ShiftUI.AnchorStyles.Bottom | ShiftUI.AnchorStyles.Right)));
+ this.doClose.Location = new System.Drawing.Point (341, 345);
+ this.doClose.Size = new System.Drawing.Size (86, 26);
+ this.doClose.TabIndex = 7;
+ this.doClose.Text = "OK";
+ this.doClose.Click += new System.EventHandler (this.doClose_Click);
+ //
+ // moveUp
+ //
+ this.moveUp.Location = new System.Drawing.Point (138, 25);
+ this.moveUp.Size = new System.Drawing.Size (31, 28);
+ this.moveUp.TabIndex = 4;
+ this.moveUp.Enabled = false;
+ this.moveUp.Text = "Up";
+ this.moveUp.Click += new System.EventHandler (this.moveUp_Click);
+ //
+ // moveDown
+ //
+ this.moveDown.Location = new System.Drawing.Point (138, 59);
+ this.moveDown.Size = new System.Drawing.Size (31, 28);
+ this.moveDown.TabIndex = 5;
+ this.moveDown.Enabled = false;
+ this.moveDown.Text = "Dn";
+ this.moveDown.Click += new System.EventHandler (this.moveDown_Click);
+ //
+ // doAdd
+ //
+ this.doAdd.Anchor = ((ShiftUI.AnchorStyles)((ShiftUI.AnchorStyles.Bottom | ShiftUI.AnchorStyles.Left)));
+ this.doAdd.Location = new System.Drawing.Point (12, 346);
+ this.doAdd.Size = new System.Drawing.Size (59, 25);
+ this.doAdd.TabIndex = 1;
+ this.doAdd.Text = "Add";
+ this.doAdd.Click += new System.EventHandler (this.doAdd_Click);
+ //
+ // doRemove
+ //
+ this.doRemove.Anchor = ((ShiftUI.AnchorStyles)((ShiftUI.AnchorStyles.Bottom | ShiftUI.AnchorStyles.Left)));
+ this.doRemove.Location = new System.Drawing.Point (77, 346);
+ this.doRemove.Size = new System.Drawing.Size (55, 25);
+ this.doRemove.TabIndex = 2;
+ this.doRemove.Text = "Remove";
+ this.doRemove.Click += new System.EventHandler (this.doRemove_Click);
+ //
+ // doCancel
+ //
+ this.doCancel.Anchor = ((ShiftUI.AnchorStyles)((ShiftUI.AnchorStyles.Bottom | ShiftUI.AnchorStyles.Right)));
+ this.doCancel.DialogResult = ShiftUI.DialogResult.Cancel;
+ this.doCancel.Location = new System.Drawing.Point (433, 345);
+ this.doCancel.Size = new System.Drawing.Size (86, 26);
+ this.doCancel.TabIndex = 8;
+ this.doCancel.Text = "Cancel";
+ this.doCancel.Click += new System.EventHandler (this.doCancel_Click);
+ //
+ // addType
+ //
+ this.addType.Anchor = ((ShiftUI.AnchorStyles)((ShiftUI.AnchorStyles.Bottom | ShiftUI.AnchorStyles.Left)));
+ this.addType.DropDownStyle = ShiftUI.ComboBoxStyle.DropDownList;
+ this.addType.Location = new System.Drawing.Point (12, 319);
+ this.addType.Size = new System.Drawing.Size (120, 21);
+ this.addType.TabIndex = 3;
+ //
+ // DesignerForm
+ //
+ this.AcceptButton = this.doClose;
+ this.CancelButton = this.doCancel;
+ this.ClientSize = new System.Drawing.Size (531, 381);
+ this.WidgetBox = false;
+ this.Widgets.Add (this.addType);
+ this.Widgets.Add (this.doCancel);
+ this.Widgets.Add (this.doRemove);
+ this.Widgets.Add (this.doAdd);
+ this.Widgets.Add (this.moveDown);
+ this.Widgets.Add (this.moveUp);
+ this.Widgets.Add (this.doClose);
+ this.Widgets.Add (this.itemDisplay);
+ this.Widgets.Add (this.itemsList);
+ this.Widgets.Add (this.labelProperty);
+ this.Widgets.Add (this.labelMember);
+ this.HelpButton = true;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.MinimumSize = new System.Drawing.Size (400, 300);
+ this.ShowInTaskbar = false;
+ this.StartPosition = ShiftUI.FormStartPosition.CenterScreen;
+ this.ResumeLayout (false);
+
+ if (editor.CollectionType.IsGenericType)
+ this.Text = editor.CollectionItemType.Name + " Collection Editor";
+ else
+ this.Text = editor.CollectionType.Name + " Collection Editor";
+ foreach (Type type in editor.NewItemTypes)
+ addType.Items.Add (type.Name);
+ if (addType.Items.Count > 0)
+ addType.SelectedIndex = 0;
+ }
+
+ private void UpdateItems ()
+ {
+ object[] items = editor.GetItems (EditValue);
+ if (items != null) {
+ itemsList.BeginUpdate ();
+ itemsList.Items.Clear ();
+ foreach (object o in items)
+ this.itemsList.Items.Add (new ObjectContainer (o, editor));
+ if (itemsList.Items.Count > 0)
+ itemsList.SelectedIndex = 0;
+ itemsList.EndUpdate ();
+ }
+ }
+
+ private void doClose_Click (object sender, EventArgs e)
+ {
+ SetEditValue ();
+ this.Close ();
+ }
+
+ private void SetEditValue ()
+ {
+ object[] items = new object[itemsList.Items.Count];
+ for (int i = 0; i < itemsList.Items.Count; i++)
+ items[i] = ((ObjectContainer)itemsList.Items[i]).Object;
+ this.Items = items;
+ }
+
+ private void doCancel_Click (object sender, EventArgs e)
+ {
+ editor.CancelChanges ();
+ this.Close ();
+ }
+
+ private void itemsList_SelectedIndexChanged (object sender, EventArgs e)
+ {
+ if (itemsList.SelectedIndex == -1) {
+ itemDisplay.SelectedObject = null;
+ return;
+ }
+
+ if (itemsList.SelectedIndex <= 0 || itemsList.SelectedItems.Count > 1)
+ moveUp.Enabled = false;
+ else
+ moveUp.Enabled = true;
+ if (itemsList.SelectedIndex > itemsList.Items.Count - 2 || itemsList.SelectedItems.Count > 1)
+ moveDown.Enabled = false;
+ else
+ moveDown.Enabled = true;
+
+ if (itemsList.SelectedItems.Count == 1)
+ {
+ ObjectContainer o = (ObjectContainer)itemsList.SelectedItem;
+ if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object)
+ itemDisplay.SelectedObject = o;
+ else
+ itemDisplay.SelectedObject = o.Object;
+ }
+ else
+ {
+ object[] items = new object[itemsList.SelectedItems.Count];
+ for (int i = 0; i < itemsList.SelectedItems.Count; i++)
+ {
+ ObjectContainer o = (ObjectContainer)itemsList.SelectedItem;
+ if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object)
+ items[i] = ((ObjectContainer)itemsList.SelectedItems[i]);
+ else
+ items[i] = ((ObjectContainer)itemsList.SelectedItems[i]).Object;
+ }
+ itemDisplay.SelectedObjects = items;
+ }
+ labelProperty.Text = ((ObjectContainer)itemsList.SelectedItem).Name + " properties:";
+ }
+
+ private void itemDisplay_PropertyValueChanged (object sender, EventArgs e)
+ {
+ int[] selected = new int[itemsList.SelectedItems.Count];
+ for (int i = 0; i < itemsList.SelectedItems.Count; i++)
+ selected[i] = itemsList.Items.IndexOf (itemsList.SelectedItems[i]);
+
+ // The list might be repopulated if a new instance of the collection edited
+ // is created during the update. This happen for example for Arrays.
+ SetEditValue ();
+
+ // Restore current selection in case the list gets repopulated.
+ // Refresh the item after that to reflect possible value change.
+ //
+ itemsList.BeginUpdate ();
+ itemsList.ClearSelected ();
+ foreach (int index in selected) {
+ itemsList.DoRefreshItem (index);
+ itemsList.SetSelected (index, true);
+ }
+ itemsList.SelectedIndex = selected[0];
+ itemsList.EndUpdate ();
+ }
+
+ private void moveUp_Click (object sender, EventArgs e)
+ {
+ if (itemsList.SelectedIndex <= 0)
+ return;
+
+ object selected = itemsList.SelectedItem;
+ int index = itemsList.SelectedIndex;
+ itemsList.Items.RemoveAt (index);
+ itemsList.Items.Insert (index - 1, selected);
+ itemsList.SelectedIndex = index - 1;
+ }
+
+ private void moveDown_Click (object sender, EventArgs e)
+ {
+ if (itemsList.SelectedIndex > itemsList.Items.Count - 2)
+ return;
+
+ object selected = itemsList.SelectedItem;
+ int index = itemsList.SelectedIndex;
+ itemsList.Items.RemoveAt (index);
+ itemsList.Items.Insert (index + 1, selected);
+ itemsList.SelectedIndex = index + 1;
+ }
+
+ private void doAdd_Click (object sender, EventArgs e)
+ {
+ object o;
+ try {
+ o = editor.CreateInstance (editor.NewItemTypes[addType.SelectedIndex]);
+ } catch (Exception ex) {
+ DisplayError (ex);
+ return;
+ }
+ itemsList.Items.Add (new ObjectContainer (o, editor));
+ itemsList.SelectedIndex = -1;
+ itemsList.SelectedIndex = itemsList.Items.Count - 1;
+ }
+
+ private void doRemove_Click (object sender, EventArgs e)
+ {
+ if (itemsList.SelectedIndex != -1) {
+ int[] selected = new int[itemsList.SelectedItems.Count];
+ for (int i=0; i < itemsList.SelectedItems.Count; i++)
+ selected[i] = itemsList.Items.IndexOf (itemsList.SelectedItems[i]);
+
+ for (int i = selected.Length - 1; i >= 0; i--)
+ itemsList.Items.RemoveAt (selected[i]);
+
+ itemsList.SelectedIndex = Math.Min (selected[0], itemsList.Items.Count-1);
+ }
+ }
+
+ // OnEditValueChanged is called only if the EditValue has changed,
+ // which is only in the case when a new instance of the collection is
+ // required, e.g for arrays.
+ //
+ protected override void OnEditValueChanged ()
+ {
+ UpdateItems ();
+ }
+ }
+
+ private Type type;
+ private Type collectionItemType;
+ private Type[] newItemTypes;
+ private ITypeDescriptorContext context;
+ private IServiceProvider provider;
+ private IWindowsFormsEditorService editorService;
+
+ public CollectionEditor (Type type)
+ {
+ this.type = type;
+ this.collectionItemType = CreateCollectionItemType ();
+ this.newItemTypes = CreateNewItemTypes ();
+ }
+
+ protected Type CollectionItemType
+ {
+ get { return collectionItemType; }
+ }
+
+ protected Type CollectionType
+ {
+ get { return type; }
+ }
+
+ protected ITypeDescriptorContext Context
+ {
+ get { return context; }
+ }
+
+ protected virtual string HelpTopic
+ {
+ get { return "CollectionEditor"; }
+ }
+
+ protected Type[] NewItemTypes
+ {
+ get { return newItemTypes; }
+ }
+
+ protected virtual void CancelChanges ()
+ {
+ }
+
+ protected virtual bool CanRemoveInstance (object value)
+ {
+ return true;
+ }
+
+ protected virtual bool CanSelectMultipleInstances ()
+ {
+ return true;
+ }
+
+ protected virtual CollectionEditor.CollectionForm CreateCollectionForm ()
+ {
+ return new ConcreteCollectionForm (this);
+ }
+
+ protected virtual Type CreateCollectionItemType ()
+ {
+ PropertyInfo[] properties = type.GetProperties ();
+ foreach (PropertyInfo property in properties)
+ if (property.Name == "Item")
+ return property.PropertyType;
+ return typeof (object);
+ }
+
+ protected virtual object CreateInstance (Type itemType)
+ {
+ object instance = null;
+ if (typeof (IComponent).IsAssignableFrom (itemType)) {
+ IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
+ if (host != null)
+ instance = host.CreateComponent (itemType);
+ }
+
+ if (instance == null) {
+ instance = TypeDescriptor.CreateInstance (provider, itemType, null, null);
+ }
+ return instance;
+ }
+
+ protected virtual Type[] CreateNewItemTypes ()
+ {
+ return new Type[] { collectionItemType };
+ }
+
+ protected virtual void DestroyInstance (object instance)
+ {
+ IComponent component = instance as IComponent;
+ if (component != null) {
+ IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
+ if (host != null)
+ host.DestroyComponent (component);
+ }
+ }
+
+ public override object EditValue (ITypeDescriptorContext context, IServiceProvider provider, object value)
+ {
+ this.context = context;
+ this.provider = provider;
+
+ if (context != null && provider != null)
+ {
+ editorService = (IWindowsFormsEditorService)provider.GetService (typeof (IWindowsFormsEditorService));
+ if (editorService != null)
+ {
+ CollectionForm editorForm = CreateCollectionForm ();
+ editorForm.EditValue = value;
+ editorForm.ShowEditorDialog (editorService);
+ return editorForm.EditValue;
+ }
+ }
+ return base.EditValue (context, provider, value);
+ }
+
+ protected virtual string GetDisplayText (object value)
+ {
+ if (value == null)
+ return string.Empty;
+
+ PropertyInfo nameProperty = value.GetType ().GetProperty ("Name");
+ if (nameProperty != null)
+ {
+ string data = (nameProperty.GetValue (value, null)) as string;
+ if (data != null)
+ if (data.Length != 0)
+ return data;
+ }
+
+ if (Type.GetTypeCode (value.GetType ()) == TypeCode.Object)
+ return value.GetType ().Name;
+ else
+ return value.ToString ();
+ }
+
+ public override UITypeEditorEditStyle GetEditStyle (ITypeDescriptorContext context)
+ {
+ return UITypeEditorEditStyle.Modal;
+ }
+
+ protected virtual object[] GetItems (object editValue)
+ {
+ if (editValue == null)
+ return new object[0];
+ ICollection collection = editValue as ICollection;
+ if (collection == null)
+ return new object[0];
+
+ object[] result = new object[collection.Count];
+ collection.CopyTo (result, 0);
+ return result;
+ }
+
+ protected virtual IList GetObjectsFromInstance (object instance)
+ {
+ ArrayList list = new ArrayList ();
+ list.Add (instance);
+ return list;
+ }
+
+ protected object GetService (Type serviceType)
+ {
+ return context.GetService (serviceType);
+ }
+
+ protected virtual object SetItems (object editValue, object[] value)
+ {
+ IList list = (IList) editValue;
+ if (list == null)
+ return null;
+
+ list.Clear ();
+ foreach (object o in value)
+ list.Add (o);
+
+ return list;
+ }
+
+ protected virtual void ShowHelp ()
+ {
+ //TODO: Fixme No help provider.
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/ComboBox.cs b/source/ShiftUI/Widgets/ComboBox.cs
new file mode 100644
index 0000000..88bcbec
--- /dev/null
+++ b/source/ShiftUI/Widgets/ComboBox.cs
@@ -0,0 +1,2825 @@
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Mike Kestner <[email protected]>
+// Daniel Nauck (dna(at)mono-project(dot)de)
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.ComponentModel.Design.Serialization;
+using System.Drawing;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace ShiftUI
+{
+ [DefaultProperty("Items")]
+ [DefaultEvent("SelectedIndexChanged")]
+ //[Designer ("ShiftUI.Design.ComboBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultBindingProperty ("Text")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible(true)]
+ [ToolboxWidget]
+ public class ComboBox : ListWidget
+ {
+ private DrawMode draw_mode = DrawMode.Normal;
+ private ComboBoxStyle dropdown_style;
+ private int dropdown_width = -1;
+ private int selected_index = -1;
+ private ObjectCollection items;
+ private bool suspend_ctrlupdate;
+ private int maxdrop_items = 8;
+ private bool integral_height = true;
+ private bool sorted;
+ private int max_length;
+ private ComboListBox listbox_ctrl;
+ private ComboTextBox textbox_ctrl;
+ private bool process_textchanged_event = true;
+ private bool process_texchanged_autoscroll = true;
+ private bool item_height_specified;
+ private int item_height;
+ private int requested_height = -1;
+ private Hashtable item_heights;
+ private bool show_dropdown_button;
+ private ButtonState button_state = ButtonState.Normal;
+ private bool dropped_down;
+ private Rectangle text_area;
+ private Rectangle button_area;
+ private Rectangle listbox_area;
+ private const int button_width = 16;
+ bool drop_down_button_entered;
+ private AutoCompleteStringCollection auto_complete_custom_source = null;
+ private AutoCompleteMode auto_complete_mode = AutoCompleteMode.None;
+ private AutoCompleteSource auto_complete_source = AutoCompleteSource.None;
+ private FlatStyle flat_style;
+ private int drop_down_height;
+ const int default_drop_down_height = 106;
+
+ [ComVisible(true)]
+ public class ChildAccessibleObject : AccessibleObject {
+
+ public ChildAccessibleObject (ComboBox owner, IntPtr handle)
+ : base (owner)
+ {
+ }
+
+ public override string Name {
+ get {
+ return base.Name;
+ }
+ }
+ }
+
+ public ComboBox ()
+ {
+ items = new ObjectCollection (this);
+ DropDownStyle = ComboBoxStyle.DropDown;
+ item_height = FontHeight + 2;
+ background_color = ThemeEngine.Current.ColorControl;
+ border_style = BorderStyle.None;
+
+ drop_down_height = default_drop_down_height;
+ flat_style = FlatStyle.Standard;
+
+ /* Events */
+ MouseDown += new MouseEventHandler (OnMouseDownCB);
+ MouseUp += new MouseEventHandler (OnMouseUpCB);
+ MouseMove += new MouseEventHandler (OnMouseMoveCB);
+ MouseWheel += new MouseEventHandler (OnMouseWheelCB);
+ MouseEnter += new EventHandler (OnMouseEnter);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ KeyDown +=new KeyEventHandler(OnKeyDownCB);
+ }
+
+ #region events
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged
+ {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick
+ {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ static object DrawItemEvent = new object ();
+ static object DropDownEvent = new object ();
+ static object DropDownStyleChangedEvent = new object ();
+ static object MeasureItemEvent = new object ();
+ static object SelectedIndexChangedEvent = new object ();
+ static object SelectionChangeCommittedEvent = new object ();
+ static object DropDownClosedEvent = new object ();
+ static object TextUpdateEvent = new object ();
+
+ public event DrawItemEventHandler DrawItem {
+ add { Events.AddHandler (DrawItemEvent, value); }
+ remove { Events.RemoveHandler (DrawItemEvent, value); }
+ }
+
+ public event EventHandler DropDown {
+ add { Events.AddHandler (DropDownEvent, value); }
+ remove { Events.RemoveHandler (DropDownEvent, value); }
+ }
+ public event EventHandler DropDownClosed
+ {
+ add { Events.AddHandler (DropDownClosedEvent, value); }
+ remove { Events.RemoveHandler (DropDownClosedEvent, value); }
+ }
+
+ public event EventHandler DropDownStyleChanged {
+ add { Events.AddHandler (DropDownStyleChangedEvent, value); }
+ remove { Events.RemoveHandler (DropDownStyleChangedEvent, value); }
+ }
+
+ public event MeasureItemEventHandler MeasureItem {
+ add { Events.AddHandler (MeasureItemEvent, value); }
+ remove { Events.RemoveHandler (MeasureItemEvent, value); }
+ }
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged
+ {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler SelectedIndexChanged {
+ add { Events.AddHandler (SelectedIndexChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
+ }
+
+ public event EventHandler SelectionChangeCommitted {
+ add { Events.AddHandler (SelectionChangeCommittedEvent, value); }
+ remove { Events.RemoveHandler (SelectionChangeCommittedEvent, value); }
+ }
+ public event EventHandler TextUpdate
+ {
+ add { Events.AddHandler (TextUpdateEvent, value); }
+ remove { Events.RemoveHandler (TextUpdateEvent, value); }
+ }
+
+ #endregion Events
+
+ #region Public Properties
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetStringCollectionEditor, " + Consts.AssemblySystem_Design,
+ //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+ public AutoCompleteStringCollection AutoCompleteCustomSource {
+ get {
+ if(auto_complete_custom_source == null) {
+ auto_complete_custom_source = new AutoCompleteStringCollection ();
+ auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+ }
+ return auto_complete_custom_source;
+ }
+ set {
+ if(auto_complete_custom_source == value)
+ return;
+
+ if(auto_complete_custom_source != null) //remove eventhandler from old collection
+ auto_complete_custom_source.CollectionChanged -= new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+
+ auto_complete_custom_source = value;
+
+ if(auto_complete_custom_source != null)
+ auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+
+ SetTextBoxAutoCompleteData ();
+ }
+ }
+
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [DefaultValue (AutoCompleteMode.None)]
+ public AutoCompleteMode AutoCompleteMode {
+ get { return auto_complete_mode; }
+ set {
+ if(auto_complete_mode == value)
+ return;
+
+ if((value < AutoCompleteMode.None) || (value > AutoCompleteMode.SuggestAppend))
+ throw new InvalidEnumArgumentException (String.Format("Enum argument value '{0}' is not valid for AutoCompleteMode", value));
+
+ auto_complete_mode = value;
+ SetTextBoxAutoCompleteData ();
+ }
+ }
+
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [DefaultValue (AutoCompleteSource.None)]
+ public AutoCompleteSource AutoCompleteSource {
+ get { return auto_complete_source; }
+ set {
+ if(auto_complete_source == value)
+ return;
+
+ if(!Enum.IsDefined (typeof (AutoCompleteSource), value))
+ throw new InvalidEnumArgumentException (String.Format ("Enum argument value '{0}' is not valid for AutoCompleteSource", value));
+
+ auto_complete_source = value;
+ SetTextBoxAutoCompleteData ();
+ }
+ }
+
+ void SetTextBoxAutoCompleteData ()
+ {
+ if (textbox_ctrl == null)
+ return;
+
+ textbox_ctrl.AutoCompleteMode = auto_complete_mode;
+
+ if (auto_complete_source == AutoCompleteSource.ListItems) {
+ textbox_ctrl.AutoCompleteSource = AutoCompleteSource.CustomSource;
+ textbox_ctrl.AutoCompleteCustomSource = null;
+ textbox_ctrl.AutoCompleteInternalSource = this;
+ } else {
+ textbox_ctrl.AutoCompleteSource = auto_complete_source;
+ textbox_ctrl.AutoCompleteCustomSource = auto_complete_custom_source;
+ textbox_ctrl.AutoCompleteInternalSource = null;
+ }
+ }
+ public override Color BackColor {
+ get { return base.BackColor; }
+ set {
+ if (base.BackColor == value)
+ return;
+ base.BackColor = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set {
+ if (base.BackgroundImage == value)
+ return;
+ base.BackgroundImage = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams;}
+ }
+
+ [DefaultValue ((string)null)]
+ [AttributeProvider (typeof (IListSource))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [MWFCategory("Data")]
+ public new object DataSource {
+ get { return base.DataSource; }
+ set { base.DataSource = value; }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (121, 21); }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [DefaultValue (DrawMode.Normal)]
+ [MWFCategory("Behavior")]
+ public DrawMode DrawMode {
+ get { return draw_mode; }
+ set {
+ if (!Enum.IsDefined (typeof (DrawMode), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
+
+ if (draw_mode == value)
+ return;
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights = null;
+ draw_mode = value;
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights = new Hashtable ();
+ Refresh ();
+ }
+ }
+
+ [Browsable (true)]
+ [DefaultValue (106)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [MWFCategory("Behavior")]
+ public int DropDownHeight {
+ get {
+ return drop_down_height;
+ }
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException ("DropDownHeight", "DropDownHeight must be greater than 0.");
+
+ if (value == drop_down_height)
+ return;
+
+ drop_down_height = value;
+ IntegralHeight = false;
+ }
+ }
+
+ [DefaultValue (ComboBoxStyle.DropDown)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [MWFCategory("Appearance")]
+ public ComboBoxStyle DropDownStyle {
+ get { return dropdown_style; }
+ set {
+ if (!Enum.IsDefined (typeof (ComboBoxStyle), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for ComboBoxStyle", value));
+
+ if (dropdown_style == value)
+ return;
+
+ SuspendLayout ();
+
+ if (dropdown_style == ComboBoxStyle.Simple) {
+ if (listbox_ctrl != null) {
+ Widgets.RemoveImplicit (listbox_ctrl);
+ listbox_ctrl.Dispose ();
+ listbox_ctrl = null;
+ }
+ }
+
+ dropdown_style = value;
+
+ if (dropdown_style == ComboBoxStyle.DropDownList && textbox_ctrl != null) {
+ Widgets.RemoveImplicit (textbox_ctrl);
+ textbox_ctrl.Dispose ();
+ textbox_ctrl = null;
+ }
+
+ if (dropdown_style == ComboBoxStyle.Simple) {
+ show_dropdown_button = false;
+
+ CreateComboListBox ();
+ Widgets.AddImplicit (listbox_ctrl);
+ listbox_ctrl.Visible = true;
+
+ // This should give us a 150 default height
+ // for Simple mode if size hasn't been set
+ // (DefaultSize doesn't work for us in this case)
+ if (requested_height == -1)
+ requested_height = 150;
+ } else {
+ show_dropdown_button = true;
+ button_state = ButtonState.Normal;
+ }
+
+ if (dropdown_style != ComboBoxStyle.DropDownList && textbox_ctrl == null) {
+ textbox_ctrl = new ComboTextBox (this);
+ object selected_item = SelectedItem;
+ if (selected_item != null)
+ textbox_ctrl.Text = GetItemText (selected_item);
+ textbox_ctrl.BorderStyle = BorderStyle.None;
+ textbox_ctrl.TextChanged += new EventHandler (OnTextChangedEdit);
+ textbox_ctrl.KeyPress += new KeyPressEventHandler (OnTextKeyPress);
+ textbox_ctrl.Click += new EventHandler (OnTextBoxClick);
+ textbox_ctrl.TopMargin = 1; // since we don't have borders, adjust manually the top
+
+ if (IsHandleCreated == true)
+ Widgets.AddImplicit (textbox_ctrl);
+ SetTextBoxAutoCompleteData ();
+ }
+
+ ResumeLayout ();
+ OnDropDownStyleChanged (EventArgs.Empty);
+
+ LayoutComboBox ();
+ UpdateComboBoxBounds ();
+ Refresh ();
+ }
+ }
+
+ [MWFCategory("Behavior")]
+ public int DropDownWidth {
+ get {
+ if (dropdown_width == -1)
+ return Width;
+
+ return dropdown_width;
+ }
+ set {
+ if (dropdown_width == value)
+ return;
+
+ if (value < 1)
+ throw new ArgumentOutOfRangeException ("DropDownWidth",
+ "The DropDownWidth value is less than one.");
+
+ dropdown_width = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public bool DroppedDown {
+ get {
+ if (dropdown_style == ComboBoxStyle.Simple)
+ return true;
+
+ return dropped_down;
+ }
+ set {
+ if (dropdown_style == ComboBoxStyle.Simple || dropped_down == value)
+ return;
+
+ if (value)
+ DropDownListBox ();
+ else
+ listbox_ctrl.HideWindow ();
+ }
+ }
+
+ [DefaultValue (FlatStyle.Standard)]
+ [Localizable (true)]
+ [MWFCategory("Appearance")]
+ public FlatStyle FlatStyle {
+ get { return flat_style; }
+ set {
+ if (!Enum.IsDefined (typeof (FlatStyle), value))
+ throw new InvalidEnumArgumentException ("FlatStyle", (int) value, typeof (FlatStyle));
+
+ flat_style = value;
+ LayoutComboBox ();
+ Invalidate ();
+ }
+ }
+
+ public override bool Focused {
+ get { return base.Focused; }
+ }
+
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set {
+ if (base.ForeColor == value)
+ return;
+ base.ForeColor = value;
+ Refresh ();
+ }
+ }
+
+ [DefaultValue (true)]
+ [Localizable (true)]
+ [MWFCategory("Behavior")]
+ public bool IntegralHeight {
+ get { return integral_height; }
+ set {
+ if (integral_height == value)
+ return;
+ integral_height = value;
+ UpdateComboBoxBounds ();
+ Refresh ();
+ }
+ }
+
+ [Localizable (true)]
+ [MWFCategory("Behavior")]
+ public int ItemHeight {
+ get {
+ if (item_height == -1) {
+ SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
+ item_height = (int) sz.Height;
+ }
+ return item_height;
+ }
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException ("ItemHeight",
+ "The item height value is less than one.");
+
+ item_height_specified = true;
+ item_height = value;
+ if (IntegralHeight)
+ UpdateComboBoxBounds ();
+ LayoutComboBox ();
+ Refresh ();
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [MergableProperty (false)]
+ [MWFCategory("Data")]
+ public ComboBox.ObjectCollection Items {
+ get { return items; }
+ }
+
+ [DefaultValue (8)]
+ [Localizable (true)]
+ [MWFCategory("Behavior")]
+ public int MaxDropDownItems {
+ get { return maxdrop_items; }
+ set {
+ if (maxdrop_items == value)
+ return;
+ maxdrop_items = value;
+ }
+ }
+
+ public override Size MaximumSize {
+ get { return base.MaximumSize; }
+ set {
+ base.MaximumSize = new Size (value.Width, 0);
+ }
+ }
+
+ [DefaultValue (0)]
+ [Localizable (true)]
+ [MWFCategory("Behavior")]
+ public int MaxLength {
+ get { return max_length; }
+ set {
+ if (max_length == value)
+ return;
+
+ max_length = value;
+
+ if (dropdown_style != ComboBoxStyle.DropDownList) {
+ if (value < 0) {
+ value = 0;
+ }
+ textbox_ctrl.MaxLength = value;
+ }
+ }
+ }
+
+ public override Size MinimumSize {
+ get { return base.MinimumSize; }
+ set {
+ base.MinimumSize = new Size (value.Width, 0);
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Browsable (false)]
+ public int PreferredHeight {
+ get { return Font.Height + 8; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override int SelectedIndex {
+ get { return selected_index; }
+ set {
+ SetSelectedIndex (value, false);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Bindable(true)]
+ public object SelectedItem {
+ get { return selected_index == -1 ? null : Items [selected_index]; }
+ set {
+ object item = selected_index == -1 ? null : Items [selected_index];
+ if (item == value)
+ return;
+
+ if (value == null)
+ SelectedIndex = -1;
+ else
+ SelectedIndex = Items.IndexOf (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public string SelectedText {
+ get {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return string.Empty;
+
+ string retval = textbox_ctrl.SelectedText;
+
+ return retval;
+ }
+ set {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return;
+ textbox_ctrl.SelectedText = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int SelectionLength {
+ get {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return 0;
+
+ int result = textbox_ctrl.SelectionLength;
+ return result == -1 ? 0 : result;
+ }
+ set {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return;
+ if (textbox_ctrl.SelectionLength == value)
+ return;
+ textbox_ctrl.SelectionLength = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int SelectionStart {
+ get {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return 0;
+ return textbox_ctrl.SelectionStart;
+ }
+ set {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return;
+ if (textbox_ctrl.SelectionStart == value)
+ return;
+ textbox_ctrl.SelectionStart = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ [MWFCategory("Behavior")]
+ public bool Sorted {
+ get { return sorted; }
+ set {
+ if (sorted == value)
+ return;
+ sorted = value;
+ SelectedIndex = -1;
+ if (sorted) {
+ Items.Sort ();
+ LayoutComboBox ();
+ }
+ }
+ }
+
+ [Bindable (true)]
+ [Localizable (true)]
+ public override string Text {
+ get {
+ if (dropdown_style != ComboBoxStyle.DropDownList) {
+ if (textbox_ctrl != null) {
+ return textbox_ctrl.Text;
+ }
+ }
+
+ if (SelectedItem != null)
+ return GetItemText (SelectedItem);
+
+ return base.Text;
+ }
+ set {
+ if (value == null) {
+ if (SelectedIndex == -1) {
+ if (dropdown_style != ComboBoxStyle.DropDownList)
+ SetWidgetText (string.Empty, false);
+ } else {
+ SelectedIndex = -1;
+ }
+ return;
+ }
+
+ // don't set the index if value exactly matches text of selected item
+ if (SelectedItem == null || string.Compare (value, GetItemText (SelectedItem), false, CultureInfo.CurrentCulture) != 0)
+ {
+ // find exact match using case-sensitive comparison, and if does
+ // not result in any match then use case-insensitive comparison
+ int index = FindStringExact (value, -1, false);
+ if (index == -1) {
+ index = FindStringExact (value, -1, true);
+ }
+ if (index != -1) {
+ SelectedIndex = index;
+ return;
+ }
+ }
+
+ // set directly the passed value
+ if (dropdown_style != ComboBoxStyle.DropDownList)
+ textbox_ctrl.Text = value;
+ }
+ }
+
+ #endregion Public Properties
+
+ #region Internal Properties
+ internal Rectangle ButtonArea {
+ get { return button_area; }
+ }
+
+ internal Rectangle TextArea {
+ get { return text_area; }
+ }
+ #endregion
+
+ #region UIA Framework Properties
+
+ internal TextBox UIATextBox {
+ get { return textbox_ctrl; }
+ }
+
+ internal ComboListBox UIAComboListBox {
+ get { return listbox_ctrl; }
+ }
+
+ #endregion UIA Framework Properties
+
+ #region Public Methods
+ [Obsolete ("This method has been deprecated")]
+ protected virtual void AddItemsCore (object[] value)
+ {
+
+ }
+
+ public void BeginUpdate ()
+ {
+ suspend_ctrlupdate = true;
+ }
+
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return base.CreateAccessibilityInstance ();
+ }
+
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ if (listbox_ctrl != null) {
+ listbox_ctrl.Dispose ();
+ Widgets.RemoveImplicit (listbox_ctrl);
+ listbox_ctrl = null;
+ }
+
+ if (textbox_ctrl != null) {
+ Widgets.RemoveImplicit (textbox_ctrl);
+ textbox_ctrl.Dispose ();
+ textbox_ctrl = null;
+ }
+ }
+
+ base.Dispose (disposing);
+ }
+
+ public void EndUpdate ()
+ {
+ suspend_ctrlupdate = false;
+ UpdatedItems ();
+ Refresh ();
+ }
+
+ public int FindString (string s)
+ {
+ return FindString (s, -1);
+ }
+
+ public int FindString (string s, int startIndex)
+ {
+ if (s == null || Items.Count == 0)
+ return -1;
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("startIndex");
+
+ int i = startIndex;
+ if (i == (Items.Count - 1))
+ i = -1;
+ do {
+ i++;
+ if (string.Compare (s, 0, GetItemText (Items [i]), 0, s.Length, true) == 0)
+ return i;
+ if (i == (Items.Count - 1))
+ i = -1;
+ } while (i != startIndex);
+
+ return -1;
+ }
+
+ public int FindStringExact (string s)
+ {
+ return FindStringExact (s, -1);
+ }
+
+ public int FindStringExact (string s, int startIndex)
+ {
+ return FindStringExact (s, startIndex, true);
+ }
+
+ private int FindStringExact (string s, int startIndex, bool ignoreCase)
+ {
+ if (s == null || Items.Count == 0)
+ return -1;
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("startIndex");
+
+ int i = startIndex;
+ if (i == (Items.Count - 1))
+ i = -1;
+ do {
+ i++;
+ if (string.Compare (s, GetItemText (Items [i]), ignoreCase, CultureInfo.CurrentCulture) == 0)
+ return i;
+ if (i == (Items.Count - 1))
+ i = -1;
+ } while (i != startIndex);
+
+ return -1;
+ }
+
+ public int GetItemHeight (int index)
+ {
+ if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated) {
+
+ if (index < 0 || index >= Items.Count )
+ throw new ArgumentOutOfRangeException ("The item height value is less than zero");
+
+ object item = Items [index];
+ if (item_heights.Contains (item))
+ return (int) item_heights [item];
+
+ MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
+ OnMeasureItem (args);
+ item_heights [item] = args.ItemHeight;
+ return args.ItemHeight;
+ }
+
+ return ItemHeight;
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ switch (keyData & ~Keys.Modifiers) {
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.PageUp:
+ case Keys.PageDown:
+ case Keys.Home:
+ case Keys.End:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+
+ if (textbox_ctrl != null)
+ textbox_ctrl.BackColor = BackColor;
+ }
+
+ protected override void OnDataSourceChanged (EventArgs e)
+ {
+ base.OnDataSourceChanged (e);
+ BindDataItems ();
+
+ /**
+ ** This 'Debugger.IsAttached' hack is here because of
+ ** Xamarin Bug #2234, which noted that when changing
+ ** the DataSource, in Windows exceptions are eaten
+ ** when SelectedIndexChanged is fired. However, when
+ ** the debugger is running (i.e. in MonoDevelop), we
+ ** want to be alerted of exceptions.
+ **/
+
+ if (Debugger.IsAttached) {
+ SetSelectedIndex ();
+ } else {
+ try {
+ SetSelectedIndex ();
+ } catch {
+ //ignore exceptions here per
+ //bug 2234
+ }
+ }
+ }
+
+ private void SetSelectedIndex ()
+ {
+ if (DataSource == null || DataManager == null) {
+ SelectedIndex = -1;
+ }
+ else {
+ SelectedIndex = DataManager.Position;
+ }
+ }
+
+ protected override void OnDisplayMemberChanged (EventArgs e)
+ {
+ base.OnDisplayMemberChanged (e);
+
+ if (DataManager == null)
+ return;
+
+ SelectedIndex = DataManager.Position;
+
+ if (selected_index != -1 && DropDownStyle != ComboBoxStyle.DropDownList)
+ SetWidgetText (GetItemText (Items [selected_index]), true);
+
+ if (!IsHandleCreated)
+ return;
+
+ Invalidate ();
+ }
+
+ protected virtual void OnDrawItem (DrawItemEventArgs e)
+ {
+ DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ internal void HandleDrawItem (DrawItemEventArgs e)
+ {
+ // Only raise OnDrawItem if we are in an OwnerDraw mode
+ switch (DrawMode) {
+ case DrawMode.OwnerDrawFixed:
+ case DrawMode.OwnerDrawVariable:
+ OnDrawItem (e);
+ break;
+ default:
+ ThemeEngine.Current.DrawComboBoxItem (this, e);
+ break;
+ }
+ }
+
+ protected virtual void OnDropDown (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [DropDownEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnDropDownClosed (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [DropDownClosedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnDropDownStyleChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [DropDownStyleChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+
+ if (textbox_ctrl != null)
+ textbox_ctrl.Font = Font;
+
+ if (!item_height_specified)
+ item_height = Font.Height + 2;
+
+ if (IntegralHeight)
+ UpdateComboBoxBounds ();
+
+ LayoutComboBox ();
+ }
+
+ protected override void OnForeColorChanged (EventArgs e)
+ {
+ base.OnForeColorChanged (e);
+ if (textbox_ctrl != null)
+ textbox_ctrl.ForeColor = ForeColor;
+ }
+
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected override void OnGotFocus (EventArgs e)
+ {
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ // We draw DDL styles manually, so they require a
+ // refresh to have their selection drawn
+ Invalidate ();
+ }
+
+ if (textbox_ctrl != null) {
+ textbox_ctrl.SetSelectable (false);
+ textbox_ctrl.ShowSelection = Enabled;
+ textbox_ctrl.ActivateCaret (true);
+ textbox_ctrl.SelectAll ();
+ }
+
+ base.OnGotFocus (e);
+ }
+
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected override void OnLostFocus (EventArgs e)
+ {
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ // We draw DDL styles manually, so they require a
+ // refresh to have their selection drawn
+ Invalidate ();
+ }
+
+ if (listbox_ctrl != null && dropped_down) {
+ listbox_ctrl.HideWindow ();
+ }
+
+ if (textbox_ctrl != null) {
+ textbox_ctrl.SetSelectable (true);
+ textbox_ctrl.ActivateCaret (false);
+ textbox_ctrl.ShowSelection = false;
+ textbox_ctrl.SelectionLength = 0;
+ textbox_ctrl.HideAutoCompleteList ();
+ }
+
+ base.OnLostFocus (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ SetBoundsInternal (Left, Top, Width, PreferredHeight, BoundsSpecified.None);
+
+ if (textbox_ctrl != null)
+ Widgets.AddImplicit (textbox_ctrl);
+
+ LayoutComboBox ();
+ UpdateComboBoxBounds ();
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected override void OnKeyPress (KeyPressEventArgs e)
+ {
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ int index = FindStringCaseInsensitive (e.KeyChar.ToString (), SelectedIndex + 1);
+ if (index != -1) {
+ SelectedIndex = index;
+ if (DroppedDown) { //Scroll into view
+ if (SelectedIndex >= listbox_ctrl.LastVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.LastVisibleItem () + 1);
+ // Or, selecting an item earlier in the list.
+ if (SelectedIndex < listbox_ctrl.FirstVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.FirstVisibleItem ());
+ }
+ }
+ }
+
+ base.OnKeyPress (e);
+ }
+
+ protected virtual void OnMeasureItem (MeasureItemEventArgs e)
+ {
+ MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnParentBackColorChanged (EventArgs e)
+ {
+ base.OnParentBackColorChanged (e);
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ LayoutComboBox ();
+ if (listbox_ctrl != null)
+ listbox_ctrl.CalcListBoxArea ();
+ }
+
+ protected override void OnSelectedIndexChanged (EventArgs e)
+ {
+ base.OnSelectedIndexChanged (e);
+
+ EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnSelectedItemChanged (EventArgs e)
+ {
+ }
+
+ protected override void OnSelectedValueChanged (EventArgs e)
+ {
+ base.OnSelectedValueChanged (e);
+ }
+
+ protected virtual void OnSelectionChangeCommitted (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [SelectionChangeCommittedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void RefreshItem (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights.Remove (Items [index]);
+ }
+
+ protected override void RefreshItems ()
+ {
+ for (int i = 0; i < Items.Count; i++) {
+ RefreshItem (i);
+ }
+
+ LayoutComboBox ();
+ Refresh ();
+
+ if (selected_index != -1 && DropDownStyle != ComboBoxStyle.DropDownList)
+ SetWidgetText (GetItemText (Items [selected_index]), false);
+ }
+
+ public override void ResetText ()
+ {
+ Text = String.Empty;
+ }
+
+ protected override bool ProcessKeyEventArgs (ref Message m)
+ {
+ return base.ProcessKeyEventArgs (ref m);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ base.OnKeyDown (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnValidating (CancelEventArgs e)
+ {
+ base.OnValidating (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnTextChanged (EventArgs e)
+ {
+ base.OnTextChanged (e);
+ }
+
+ protected virtual void OnTextUpdate (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [TextUpdateEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+ protected override void OnMouseLeave (EventArgs e)
+ {
+ if (flat_style == FlatStyle.Popup)
+ Invalidate ();
+ base.OnMouseLeave (e);
+ }
+
+ protected override void OnMouseEnter (EventArgs e)
+ {
+ if (flat_style == FlatStyle.Popup)
+ Invalidate ();
+ base.OnMouseEnter (e);
+ }
+
+ protected override void ScaleWidget (SizeF factor, BoundsSpecified specified)
+ {
+ base.ScaleWidget (factor, specified);
+ }
+
+ public void Select (int start, int length)
+ {
+ if (start < 0)
+ throw new ArgumentException ("Start cannot be less than zero");
+
+ if (length < 0)
+ throw new ArgumentException ("length cannot be less than zero");
+
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return;
+
+ textbox_ctrl.Select (start, length);
+ }
+
+ public void SelectAll ()
+ {
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ return;
+
+ if (textbox_ctrl != null) {
+ textbox_ctrl.ShowSelection = true;
+ textbox_ctrl.SelectAll ();
+ }
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ bool vertically_anchored = (Anchor & AnchorStyles.Top) != 0 && (Anchor & AnchorStyles.Bottom) != 0;
+ bool vertically_docked = Dock == DockStyle.Left || Dock == DockStyle.Right || Dock == DockStyle.Fill;
+
+ if ((specified & BoundsSpecified.Height) != 0 ||
+ (specified == BoundsSpecified.None && (vertically_anchored || vertically_docked))) {
+
+ requested_height = height;
+ height = SnapHeight (height);
+ }
+
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+
+ protected override void SetItemCore (int index, object value)
+ {
+ if (index < 0 || index >= Items.Count)
+ return;
+
+ Items[index] = value;
+ }
+
+ protected override void SetItemsCore (IList value)
+ {
+ BeginUpdate ();
+ try {
+ Items.Clear ();
+ Items.AddRange (value);
+ } finally {
+ EndUpdate ();
+ }
+ }
+
+ public override string ToString ()
+ {
+ return base.ToString () + ", Items.Count:" + Items.Count;
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg) m.Msg) {
+ case Msg.WM_KEYUP:
+ case Msg.WM_KEYDOWN:
+ Keys keys = (Keys) m.WParam.ToInt32 ();
+ // Don't pass the message to base if auto complete is being used and available.
+ if (textbox_ctrl != null && textbox_ctrl.CanNavigateAutoCompleteList) {
+ XplatUI.SendMessage (textbox_ctrl.Handle, (Msg) m.Msg, m.WParam, m.LParam);
+ return;
+ }
+ if (keys == Keys.Up || keys == Keys.Down)
+ break;
+ goto case Msg.WM_CHAR;
+ case Msg.WM_CHAR:
+ // Call our own handler first and send the message to the TextBox if still needed
+ if (!ProcessKeyMessage (ref m) && textbox_ctrl != null)
+ XplatUI.SendMessage (textbox_ctrl.Handle, (Msg) m.Msg, m.WParam, m.LParam);
+ return;
+ case Msg.WM_MOUSELEAVE:
+ Point location = PointToClient (Widget.MousePosition);
+ if (ClientRectangle.Contains (location))
+ return;
+ break;
+ default:
+ break;
+ }
+ base.WndProc (ref m);
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+ void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) {
+ if(auto_complete_source == AutoCompleteSource.CustomSource) {
+ //FIXME: handle add, remove and refresh events in AutoComplete algorithm.
+ }
+ }
+
+ internal override bool InternalCapture {
+ get { return Capture; }
+ set {}
+ }
+
+ void LayoutComboBox ()
+ {
+ int border = ThemeEngine.Current.Border3DSize.Width;
+
+ text_area = ClientRectangle;
+ text_area.Height = PreferredHeight;
+
+ listbox_area = ClientRectangle;
+ listbox_area.Y = text_area.Bottom + 3;
+ listbox_area.Height -= (text_area.Height + 2);
+
+ Rectangle prev_button_area = button_area;
+
+ if (DropDownStyle == ComboBoxStyle.Simple)
+ button_area = Rectangle.Empty;
+ else {
+ button_area = text_area;
+ button_area.X = text_area.Right - button_width - border;
+ button_area.Y = text_area.Y + border;
+ button_area.Width = button_width;
+ button_area.Height = text_area.Height - 2 * border;
+ if (flat_style == FlatStyle.Popup || flat_style == FlatStyle.Flat) {
+ button_area.Inflate (1, 1);
+ button_area.X += 2;
+ button_area.Width -= 2;
+ }
+ }
+
+ if (button_area != prev_button_area) {
+ prev_button_area.Y -= border;
+ prev_button_area.Width += border;
+ prev_button_area.Height += 2 * border;
+ Invalidate (prev_button_area);
+ Invalidate (button_area);
+ }
+
+ if (textbox_ctrl != null) {
+ int text_border = border + 1;
+ textbox_ctrl.Location = new Point (text_area.X + text_border, text_area.Y + text_border);
+ textbox_ctrl.Width = text_area.Width - button_area.Width - text_border * 2;
+ textbox_ctrl.Height = text_area.Height - text_border * 2;
+ }
+
+ if (listbox_ctrl != null && dropdown_style == ComboBoxStyle.Simple) {
+ listbox_ctrl.Location = listbox_area.Location;
+ listbox_ctrl.CalcListBoxArea ();
+ }
+ }
+
+ private void CreateComboListBox ()
+ {
+ listbox_ctrl = new ComboListBox (this);
+ listbox_ctrl.HighlightedIndex = SelectedIndex;
+ }
+
+ internal void Draw (Rectangle clip, Graphics dc)
+ {
+ Theme theme = ThemeEngine.Current;
+ FlatStyle style = FlatStyle.Standard;
+ bool is_flat = false;
+
+ style = this.FlatStyle;
+ is_flat = style == FlatStyle.Flat || style == FlatStyle.Popup;
+
+ theme.ComboBoxDrawBackground (this, dc, clip, style);
+
+ int border = theme.Border3DSize.Width;
+
+ // No edit Widget, we paint the edit ourselves
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ DrawItemState state = DrawItemState.None;
+ Color back_color = BackColor;
+ Color fore_color = ForeColor;
+ Rectangle item_rect = text_area;
+ item_rect.X += border;
+ item_rect.Y += border;
+ item_rect.Width -= (button_area.Width + 2 * border);
+ item_rect.Height -= 2 * border;
+
+ if (Focused) {
+ state = DrawItemState.Selected;
+ state |= DrawItemState.Focus;
+ back_color = SystemColors.Highlight;
+ fore_color = SystemColors.HighlightText;
+ }
+
+ state |= DrawItemState.ComboBoxEdit;
+ HandleDrawItem (new DrawItemEventArgs (dc, Font, item_rect, SelectedIndex, state, fore_color, back_color));
+ }
+
+ if (show_dropdown_button) {
+ ButtonState current_state;
+ if (is_enabled)
+ current_state = button_state;
+ else
+ current_state = ButtonState.Inactive;
+
+ if (is_flat || theme.ComboBoxNormalDropDownButtonHasTransparentBackground (this, current_state))
+ dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), button_area);
+
+ if (is_flat) {
+ theme.DrawFlatStyleComboButton (dc, button_area, current_state);
+ } else {
+ theme.ComboBoxDrawNormalDropDownButton (this, dc, clip, button_area, current_state);
+ }
+ }
+ }
+
+ internal bool DropDownButtonEntered {
+ get { return drop_down_button_entered; }
+ private set {
+ if (drop_down_button_entered == value)
+ return;
+ drop_down_button_entered = value;
+ if (ThemeEngine.Current.ComboBoxDropDownButtonHasHotElementStyle (this))
+ Invalidate (button_area);
+ }
+ }
+
+ internal void DropDownListBox ()
+ {
+ DropDownButtonEntered = false;
+
+ if (DropDownStyle == ComboBoxStyle.Simple)
+ return;
+
+ if (listbox_ctrl == null)
+ CreateComboListBox ();
+
+ listbox_ctrl.Location = PointToScreen (new Point (text_area.X, text_area.Y + text_area.Height));
+
+ FindMatchOrSetIndex(SelectedIndex);
+
+ if (textbox_ctrl != null)
+ textbox_ctrl.HideAutoCompleteList ();
+
+ if (listbox_ctrl.ShowWindow ())
+ dropped_down = true;
+
+ button_state = ButtonState.Pushed;
+ if (dropdown_style == ComboBoxStyle.DropDownList)
+ Invalidate (text_area);
+ }
+
+ internal void DropDownListBoxFinished ()
+ {
+ if (DropDownStyle == ComboBoxStyle.Simple)
+ return;
+
+ FindMatchOrSetIndex (SelectedIndex);
+ button_state = ButtonState.Normal;
+ Invalidate (button_area);
+ dropped_down = false;
+ OnDropDownClosed (EventArgs.Empty);
+ /*
+ * Apples X11 looses override-redirect when doing a Unmap/Map on a previously mapped window
+ * this causes the popup to appear under the main form. This is horrible but necessary
+ */
+
+ // If the user opens a new form in an event, it will close our dropdown,
+ // so we need a null check here
+ if (listbox_ctrl != null) {
+ listbox_ctrl.Dispose ();
+ listbox_ctrl = null;
+ }
+ // The auto complete list could have been shown after the listbox,
+ // so make sure it's hidden.
+ if (textbox_ctrl != null)
+ textbox_ctrl.HideAutoCompleteList ();
+ }
+
+ private int FindStringCaseInsensitive (string search)
+ {
+ if (search.Length == 0) {
+ return -1;
+ }
+
+ for (int i = 0; i < Items.Count; i++)
+ {
+ if (String.Compare (GetItemText (Items[i]), 0, search, 0, search.Length, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+
+ // Search in the list for the substring, starting the search at the list
+ // position specified, the search wraps thus covering all the list.
+ internal int FindStringCaseInsensitive (string search, int start_index)
+ {
+ if (search.Length == 0) {
+ return -1;
+ }
+ // Accept from first item to after last item. i.e. all cases of (SelectedIndex+1).
+ if (start_index < 0 || start_index > Items.Count)
+ throw new ArgumentOutOfRangeException("start_index");
+
+ for (int i = 0; i < Items.Count; i++) {
+ int index = (i + start_index) % Items.Count;
+ if (String.Compare (GetItemText (Items [index]), 0, search, 0, search.Length, true) == 0)
+ return index;
+ }
+
+ return -1;
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+
+ internal void RestoreContextMenu ()
+ {
+ textbox_ctrl.RestoreContextMenu ();
+ }
+
+ private void OnKeyDownCB(object sender, KeyEventArgs e)
+ {
+ if (Items.Count == 0)
+ return;
+
+ // for keyboard navigation, we have to do our own scroll, since
+ // the default behaviour for the SelectedIndex property is a little different,
+ // setting the selected index in the top always
+
+ int offset;
+ switch (e.KeyCode)
+ {
+ case Keys.Up:
+ FindMatchOrSetIndex(Math.Max(SelectedIndex - 1, 0));
+
+ if (DroppedDown)
+ if (SelectedIndex < listbox_ctrl.FirstVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.FirstVisibleItem ());
+ break;
+
+ case Keys.Down:
+ if ((e.Modifiers & Keys.Alt) == Keys.Alt)
+ DropDownListBox ();
+ else
+ FindMatchOrSetIndex(Math.Min(SelectedIndex + 1, Items.Count - 1));
+
+ if (DroppedDown)
+ if (SelectedIndex >= listbox_ctrl.LastVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.LastVisibleItem () + 1);
+ break;
+
+ case Keys.PageUp:
+ offset = listbox_ctrl == null ? MaxDropDownItems - 1 : listbox_ctrl.page_size - 1;
+ if (offset < 1)
+ offset = 1;
+
+ SetSelectedIndex (Math.Max (SelectedIndex - offset, 0), true);
+
+ if (DroppedDown)
+ if (SelectedIndex < listbox_ctrl.FirstVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.FirstVisibleItem ());
+ break;
+
+ case Keys.PageDown:
+ if (SelectedIndex == -1) {
+ SelectedIndex = 0;
+ if (dropdown_style != ComboBoxStyle.Simple)
+ return;
+ }
+
+ offset = listbox_ctrl == null ? MaxDropDownItems - 1 : listbox_ctrl.page_size - 1;
+ if (offset < 1)
+ offset = 1;
+
+ SetSelectedIndex (Math.Min (SelectedIndex + offset, Items.Count - 1), true);
+
+ if (DroppedDown)
+ if (SelectedIndex >= listbox_ctrl.LastVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.LastVisibleItem () + 1);
+ break;
+
+ case Keys.Enter:
+ case Keys.Escape:
+ if (listbox_ctrl != null && listbox_ctrl.Visible)
+ DropDownListBoxFinished ();
+ break;
+
+ case Keys.Home:
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ SelectedIndex = 0;
+
+ if (DroppedDown)
+ if (SelectedIndex < listbox_ctrl.FirstVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.FirstVisibleItem ());
+ }
+
+ break;
+ case Keys.End:
+ if (dropdown_style == ComboBoxStyle.DropDownList) {
+ SetSelectedIndex (Items.Count - 1, true);
+
+ if (DroppedDown)
+ if (SelectedIndex >= listbox_ctrl.LastVisibleItem ())
+ listbox_ctrl.Scroll (SelectedIndex - listbox_ctrl.LastVisibleItem () + 1);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SetSelectedIndex (int value, bool supressAutoScroll)
+ {
+ if (selected_index == value)
+ return;
+
+ if (value <= -2 || value >= Items.Count)
+ throw new ArgumentOutOfRangeException ("SelectedIndex");
+
+ selected_index = value;
+
+ if (dropdown_style != ComboBoxStyle.DropDownList) {
+ if (value == -1)
+ SetWidgetText (string.Empty, false, supressAutoScroll);
+ else
+ SetWidgetText (GetItemText (Items [value]), false, supressAutoScroll);
+ }
+
+ if (DropDownStyle == ComboBoxStyle.DropDownList)
+ Invalidate ();
+
+ if (listbox_ctrl != null)
+ listbox_ctrl.HighlightedIndex = value;
+
+ OnSelectedValueChanged (EventArgs.Empty);
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedItemChanged (EventArgs.Empty);
+ }
+
+ // If no item is currently selected, and an item is found matching the text
+ // in the textbox, then selected that item. Otherwise the item at the given
+ // index is selected.
+ private void FindMatchOrSetIndex(int index)
+ {
+ int match = -1;
+ if (SelectedIndex == -1 && Text.Length != 0)
+ match = FindStringCaseInsensitive(Text);
+ if (match != -1)
+ SetSelectedIndex (match, true);
+ else
+ SetSelectedIndex (index, true);
+ }
+
+ void OnMouseDownCB (object sender, MouseEventArgs e)
+ {
+ Rectangle area;
+ if (DropDownStyle == ComboBoxStyle.DropDownList)
+ area = ClientRectangle;
+ else
+ area = button_area;
+
+ if (area.Contains (e.X, e.Y)) {
+ if (Items.Count > 0)
+ DropDownListBox ();
+ else {
+ button_state = ButtonState.Pushed;
+ OnDropDown (EventArgs.Empty);
+ }
+
+ Invalidate (button_area);
+ Update ();
+ }
+ Capture = true;
+ }
+
+ void OnMouseEnter (object sender, EventArgs e)
+ {
+ if (ThemeEngine.Current.CombBoxBackgroundHasHotElementStyle (this))
+ Invalidate ();
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ if (ThemeEngine.Current.CombBoxBackgroundHasHotElementStyle (this)) {
+ drop_down_button_entered = false;
+ Invalidate ();
+ } else {
+ if (show_dropdown_button)
+ DropDownButtonEntered = false;
+ }
+ }
+
+ void OnMouseMoveCB (object sender, MouseEventArgs e)
+ {
+ if (show_dropdown_button && !dropped_down)
+ DropDownButtonEntered = button_area.Contains (e.Location);
+
+ if (DropDownStyle == ComboBoxStyle.Simple)
+ return;
+
+ if (listbox_ctrl != null && listbox_ctrl.Visible) {
+ Point location = listbox_ctrl.PointToClient (Widget.MousePosition);
+ if (listbox_ctrl.ClientRectangle.Contains (location))
+ listbox_ctrl.Capture = true;
+ }
+ }
+
+ void OnMouseUpCB (object sender, MouseEventArgs e)
+ {
+ Capture = false;
+
+ button_state = ButtonState.Normal;
+ Invalidate (button_area);
+
+ OnClick (EventArgs.Empty);
+
+ if (dropped_down)
+ listbox_ctrl.Capture = true;
+ }
+
+ private void OnMouseWheelCB (object sender, MouseEventArgs me)
+ {
+ if (Items.Count == 0)
+ return;
+
+ if (listbox_ctrl != null && listbox_ctrl.Visible) {
+ int lines = me.Delta / 120 * SystemInformation.MouseWheelScrollLines;
+ listbox_ctrl.Scroll (-lines);
+ } else {
+ int lines = me.Delta / 120;
+ int index = SelectedIndex - lines;
+ if (index < 0)
+ index = 0;
+ else if (index >= Items.Count)
+ index = Items.Count - 1;
+ SelectedIndex = index;
+ }
+ }
+
+ MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
+ {
+ Point loc = PointToClient (Widget.MousePosition);
+ return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ if (suspend_ctrlupdate)
+ return;
+
+ Draw (ClientRectangle, pevent.Graphics);
+ }
+
+ private void OnTextBoxClick (object sender, EventArgs e)
+ {
+ OnClick (e);
+ }
+
+ private void OnTextChangedEdit (object sender, EventArgs e)
+ {
+ if (process_textchanged_event == false)
+ return;
+
+ int item = FindStringCaseInsensitive (textbox_ctrl.Text);
+
+ if (item == -1) {
+ // Setting base.Text below will raise this event
+ // if we found something
+ OnTextChanged (EventArgs.Empty);
+ return;
+ }
+
+ if (listbox_ctrl != null) {
+ // Set as top item
+ if (process_texchanged_autoscroll)
+ listbox_ctrl.EnsureTop (item);
+ }
+
+ base.Text = textbox_ctrl.Text;
+ }
+
+ private void OnTextKeyPress (object sender, KeyPressEventArgs e)
+ {
+ selected_index = -1;
+ if (listbox_ctrl != null)
+ listbox_ctrl.HighlightedIndex = -1;
+ }
+
+ internal void SetWidgetText (string s, bool suppressTextChanged)
+ {
+ SetWidgetText (s, suppressTextChanged, false);
+ }
+
+ internal void SetWidgetText (string s, bool suppressTextChanged, bool supressAutoScroll)
+ {
+ if (suppressTextChanged)
+ process_textchanged_event = false;
+ if (supressAutoScroll)
+ process_texchanged_autoscroll = false;
+
+ textbox_ctrl.Text = s;
+ textbox_ctrl.SelectAll ();
+ process_textchanged_event = true;
+ process_texchanged_autoscroll = true;
+ }
+
+ void UpdateComboBoxBounds ()
+ {
+ if (requested_height == -1)
+ return;
+
+ // Save the requested height since set bounds can destroy it
+ int save_height = requested_height;
+ SetBounds (bounds.X, bounds.Y, bounds.Width, SnapHeight (requested_height),
+ BoundsSpecified.Height);
+ requested_height = save_height;
+ }
+
+ int SnapHeight (int height)
+ {
+ if (DropDownStyle == ComboBoxStyle.Simple && height > PreferredHeight) {
+ if (IntegralHeight) {
+ int border = ThemeEngine.Current.Border3DSize.Height;
+ int lb_height = (height - PreferredHeight - 2) - border * 2;
+ if (lb_height > ItemHeight) {
+ int partial = (lb_height) % ItemHeight;
+ height -= partial;
+ } else if (lb_height < ItemHeight)
+ height = PreferredHeight;
+ }
+ } else
+ height = PreferredHeight;
+
+ return height;
+ }
+
+ private void UpdatedItems ()
+ {
+ if (listbox_ctrl != null) {
+ listbox_ctrl.UpdateLastVisibleItem ();
+ listbox_ctrl.CalcListBoxArea ();
+ listbox_ctrl.Refresh ();
+ }
+ }
+
+ #endregion Private Methods
+
+ [ListBindableAttribute (false)]
+ public class ObjectCollection : IList, ICollection, IEnumerable
+ {
+
+ private ComboBox owner;
+ internal ArrayList object_items = new ArrayList ();
+
+ public ComboBox Owner
+ {
+ get { return owner; }
+ }
+
+ #region UIA Framework Events
+
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+
+ #endregion UIA Framework Events
+
+ public ObjectCollection (ComboBox owner)
+ {
+ this.owner = owner;
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return object_items.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public virtual object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ return object_items[index];
+ }
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
+
+ object_items[index] = value;
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ if (owner.listbox_ctrl != null)
+ owner.listbox_ctrl.InvalidateItem (index);
+ if (index == owner.SelectedIndex) {
+ if (owner.textbox_ctrl == null)
+ owner.Refresh ();
+ else {
+ owner.textbox_ctrl.Text = value.ToString ();
+ owner.textbox_ctrl.SelectAll ();
+ }
+ }
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return false; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public int Add (object item)
+ {
+ int idx;
+
+ idx = AddItem (item, false);
+ owner.UpdatedItems ();
+ return idx;
+ }
+
+ public void AddRange (object[] items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (object mi in items)
+ AddItem (mi, true);
+
+ if (owner.sorted)
+ Sort ();
+
+ owner.UpdatedItems ();
+ }
+
+ public void Clear ()
+ {
+ owner.selected_index = -1;
+ object_items.Clear ();
+ owner.UpdatedItems ();
+ owner.Refresh ();
+
+ //UIA Framework event: Items list cleared
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
+ }
+
+ public bool Contains (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.Contains (value);
+ }
+
+ public void CopyTo (object [] destination, int arrayIndex)
+ {
+ object_items.CopyTo (destination, arrayIndex);
+ }
+
+ void ICollection.CopyTo (Array destination, int index)
+ {
+ object_items.CopyTo (destination, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return object_items.GetEnumerator ();
+ }
+
+ int IList.Add (object item)
+ {
+ return Add (item);
+ }
+
+ public int IndexOf (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.IndexOf (value);
+ }
+
+ public void Insert (int index, object item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException ("index");
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ owner.BeginUpdate ();
+
+ if (owner.Sorted)
+ AddItem (item, false);
+ else {
+ object_items.Insert (index, item);
+ //UIA Framework event: Item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+ }
+
+ owner.EndUpdate (); // Calls UpdatedItems
+ }
+
+ public void Remove (object value)
+ {
+ if (value == null)
+ return;
+ int index = IndexOf (value);
+ if (index >= 0)
+ RemoveAt (index);
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (index < owner.SelectedIndex)
+ --owner.SelectedIndex;
+ else if (index == owner.SelectedIndex)
+ owner.SelectedIndex = -1;
+
+ object removed = object_items [index];
+
+ object_items.RemoveAt (index);
+ owner.UpdatedItems ();
+
+ //UIA Framework event: Item removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
+ }
+ #endregion Public Methods
+
+ #region Private Methods
+ private int AddItem (object item, bool suspend)
+ {
+ // suspend means do not sort as we put new items in, we will do a
+ // big sort at the end
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ if (owner.Sorted && !suspend) {
+ int index = 0;
+ foreach (object o in object_items) {
+ if (String.Compare (item.ToString (), o.ToString ()) < 0) {
+ object_items.Insert (index, item);
+
+ // If we added the new item before the selectedindex
+ // bump the selectedindex by one, behavior differs if
+ // Handle has not been created.
+ if (index <= owner.selected_index && owner.IsHandleCreated)
+ owner.selected_index++;
+
+ //UIA Framework event: Item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+
+ return index;
+ }
+ index++;
+ }
+ }
+ object_items.Add (item);
+
+ //UIA Framework event: Item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+
+ return object_items.Count - 1;
+ }
+
+ internal void AddRange (IList items)
+ {
+ foreach (object mi in items)
+ AddItem (mi, false);
+
+ if (owner.sorted)
+ Sort ();
+
+ owner.UpdatedItems ();
+ }
+
+ internal void Sort ()
+ {
+ // If the objects the user put here don't have their own comparer,
+ // use one that compares based on the object's ToString
+ if (object_items.Count > 0 && object_items[0] is IComparer)
+ object_items.Sort ();
+ else
+ object_items.Sort (new ObjectComparer (owner));
+ }
+
+ private class ObjectComparer : IComparer
+ {
+ private ListWidget owner;
+
+ public ObjectComparer (ListWidget owner)
+ {
+ this.owner = owner;
+ }
+
+ #region IComparer Members
+ public int Compare (object x, object y)
+ {
+ return string.Compare (owner.GetItemText (x), owner.GetItemText (y));
+ }
+ #endregion
+ }
+ #endregion Private Methods
+ }
+
+ internal class ComboTextBox : TextBox {
+
+ private ComboBox owner;
+
+ public ComboTextBox (ComboBox owner)
+ {
+ this.owner = owner;
+ ShowSelection = false;
+ owner.EnabledChanged += OwnerEnabledChangedHandler;
+ owner.LostFocus += OwnerLostFocusHandler;
+ }
+
+ void OwnerEnabledChangedHandler (object o, EventArgs args)
+ {
+ ShowSelection = owner.Focused && owner.Enabled;
+ }
+
+ void OwnerLostFocusHandler (object o, EventArgs args)
+ {
+ if (IsAutoCompleteAvailable)
+ owner.Text = Text;
+ }
+
+ protected override void OnKeyDown (KeyEventArgs args)
+ {
+ if (args.KeyCode == Keys.Enter && IsAutoCompleteAvailable)
+ owner.Text = Text;
+
+ base.OnKeyDown (args);
+ }
+
+ internal override void OnAutoCompleteValueSelected (EventArgs args)
+ {
+ base.OnAutoCompleteValueSelected (args);
+ owner.Text = Text;
+ }
+
+ internal void SetSelectable (bool selectable)
+ {
+ SetStyle (Widgetstyles.Selectable, selectable);
+ }
+
+ internal void ActivateCaret (bool active)
+ {
+ if (active)
+ document.CaretHasFocus ();
+ else
+ document.CaretLostFocus ();
+ }
+
+ internal override void OnTextUpdate ()
+ {
+ base.OnTextUpdate ();
+ owner.OnTextUpdate (EventArgs.Empty);
+ }
+
+ protected override void OnGotFocus (EventArgs e)
+ {
+ owner.Select (false, true);
+ }
+
+ protected override void OnLostFocus (EventArgs e)
+ {
+ owner.Select (false, true);
+ }
+
+ // We have to pass these events to our owner - MouseMove is not, however.
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ base.OnMouseDown (e);
+ owner.OnMouseDown (owner.TranslateMouseEventArgs (e));
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e)
+ {
+ base.OnMouseUp (e);
+ owner.OnMouseUp (owner.TranslateMouseEventArgs (e));
+ }
+
+ protected override void OnMouseClick (MouseEventArgs e)
+ {
+ base.OnMouseClick (e);
+ owner.OnMouseClick (owner.TranslateMouseEventArgs (e));
+ }
+
+ protected override void OnMouseDoubleClick (MouseEventArgs e)
+ {
+ base.OnMouseDoubleClick (e);
+ owner.OnMouseDoubleClick (owner.TranslateMouseEventArgs (e));
+ }
+
+ public override bool Focused {
+ get {
+ return owner.Focused;
+ }
+ }
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing ) {
+ // Prevents corruption of combobox text by disposed object
+ owner.EnabledChanged -= OwnerEnabledChangedHandler;
+ owner.LostFocus -= OwnerLostFocusHandler;
+ }
+ base.Dispose(disposing);
+ }
+
+ internal override bool ActivateOnShow { get { return false; } }
+ }
+
+ internal class ComboListBox : Widget
+ {
+ private ComboBox owner;
+ private VScrollBarLB vscrollbar_ctrl;
+ private int top_item; /* First item that we show the in the current page */
+ private int last_item; /* Last visible item */
+ internal int page_size; /* Number of listbox items per page */
+ private Rectangle textarea_drawable; /* Rectangle of the drawable text area */
+
+ internal enum ItemNavigation
+ {
+ First,
+ Last,
+ Next,
+ Previous,
+ NextPage,
+ PreviousPage,
+ }
+
+ #region UIA Framework: Properties
+
+ internal int UIATopItem {
+ get { return top_item; }
+ }
+
+ internal int UIALastItem {
+ get { return last_item; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return vscrollbar_ctrl; }
+ }
+
+ #endregion
+
+ class VScrollBarLB : VScrollBar
+ {
+ public VScrollBarLB ()
+ {
+ }
+
+ internal override bool InternalCapture {
+ get { return Capture; }
+ set { }
+ }
+
+ public void FireMouseDown (MouseEventArgs e)
+ {
+ if (!Visible)
+ return;
+
+ e = TranslateEvent (e);
+ OnMouseDown (e);
+ }
+
+ public void FireMouseUp (MouseEventArgs e)
+ {
+ if (!Visible)
+ return;
+
+ e = TranslateEvent (e);
+ OnMouseUp (e);
+ }
+
+ public void FireMouseMove (MouseEventArgs e)
+ {
+ if (!Visible)
+ return;
+
+ e = TranslateEvent (e);
+ OnMouseMove (e);
+ }
+
+ MouseEventArgs TranslateEvent (MouseEventArgs e)
+ {
+ Point loc = PointToClient (Widget.MousePosition);
+ return new MouseEventArgs (e.Button, e.Clicks, loc.X, loc.Y, e.Delta);
+ }
+ }
+
+ public ComboListBox (ComboBox owner)
+ {
+ this.owner = owner;
+ top_item = 0;
+ last_item = 0;
+ page_size = 0;
+
+ MouseWheel += new MouseEventHandler (OnMouseWheelCLB);
+
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.AllPaintingInWmPaint, true);
+ SetStyle (Widgetstyles.ResizeRedraw | Widgetstyles.Opaque, true);
+
+ this.is_visible = false;
+
+ if (owner.DropDownStyle == ComboBoxStyle.Simple)
+ InternalBorderStyle = BorderStyle.Fixed3D;
+ else
+ InternalBorderStyle = BorderStyle.FixedSingle;
+ }
+
+ protected override CreateParams CreateParams
+ {
+ get {
+ CreateParams cp = base.CreateParams;
+ if (owner == null || owner.DropDownStyle == ComboBoxStyle.Simple)
+ return cp;
+
+ cp.Style ^= (int)WindowStyles.WS_CHILD;
+ cp.Style ^= (int)WindowStyles.WS_VISIBLE;
+ cp.Style |= (int)WindowStyles.WS_POPUP;
+ cp.ExStyle |= (int) WindowExStyles.WS_EX_TOOLWINDOW | (int) WindowExStyles.WS_EX_TOPMOST;
+ return cp;
+ }
+ }
+
+ internal override bool InternalCapture {
+ get {
+ return Capture;
+ }
+
+ set {
+ }
+ }
+
+ internal override bool ActivateOnShow { get { return false; } }
+ #region Private Methods
+
+ // Calcs the listbox area
+ internal void CalcListBoxArea ()
+ {
+ int width, height;
+ bool show_scrollbar;
+
+ if (owner.DropDownStyle == ComboBoxStyle.Simple) {
+ Rectangle area = owner.listbox_area;
+ width = area.Width;
+ height = area.Height;
+ show_scrollbar = owner.Items.Count * owner.ItemHeight > height;
+
+ // No calculation needed
+ if (height <= 0 || width <= 0)
+ return;
+
+ }
+ else { // DropDown or DropDownList
+
+ width = owner.DropDownWidth;
+ int visible_items_count = (owner.Items.Count <= owner.MaxDropDownItems) ? owner.Items.Count : owner.MaxDropDownItems;
+
+ if (owner.DrawMode == DrawMode.OwnerDrawVariable) {
+ height = 0;
+ for (int i = 0; i < visible_items_count; i++) {
+ height += owner.GetItemHeight (i);
+ }
+
+ show_scrollbar = owner.Items.Count > owner.MaxDropDownItems;
+
+ } else {
+ if (owner.DropDownHeight == default_drop_down_height) { // ignore DropDownHeight
+ height = owner.ItemHeight * visible_items_count;
+ show_scrollbar = owner.Items.Count > owner.MaxDropDownItems;
+ } else {
+ // ignore visible items count, and use manual height instead
+ height = owner.DropDownHeight;
+ show_scrollbar = (owner.Items.Count * owner.ItemHeight) > height;
+ }
+ }
+ }
+
+ page_size = Math.Max (height / owner.ItemHeight, 1);
+
+ ComboBoxStyle dropdown_style = owner.DropDownStyle;
+ if (!show_scrollbar) {
+
+ if (vscrollbar_ctrl != null)
+ vscrollbar_ctrl.Visible = false;
+ if (dropdown_style != ComboBoxStyle.Simple)
+ height = owner.ItemHeight * owner.items.Count;
+ } else {
+ /* Need vertical scrollbar */
+ if (vscrollbar_ctrl == null) {
+ vscrollbar_ctrl = new VScrollBarLB ();
+ vscrollbar_ctrl.Minimum = 0;
+ vscrollbar_ctrl.SmallChange = 1;
+ vscrollbar_ctrl.LargeChange = 1;
+ vscrollbar_ctrl.Maximum = 0;
+ vscrollbar_ctrl.ValueChanged += new EventHandler (VerticalScrollEvent);
+ Widgets.AddImplicit (vscrollbar_ctrl);
+ }
+
+ vscrollbar_ctrl.Dock = DockStyle.Right;
+
+ vscrollbar_ctrl.Maximum = owner.Items.Count - 1;
+ int large = page_size;
+ if (large < 1)
+ large = 1;
+ vscrollbar_ctrl.LargeChange = large;
+ vscrollbar_ctrl.Visible = true;
+
+ int hli = HighlightedIndex;
+ if (hli > 0) {
+ hli = Math.Min (hli, vscrollbar_ctrl.Maximum);
+ vscrollbar_ctrl.Value = hli;
+ }
+ }
+
+ var borderWidth = Hwnd.GetBorderWidth (CreateParams);
+ var borderAdjustment = dropdown_style == ComboBoxStyle.Simple ? new Size (0, 0) :
+ new Size (borderWidth.top + borderWidth.bottom, borderWidth.left + borderWidth.right);
+ Size = new Size (width, height + borderAdjustment.Height);
+ textarea_drawable = new Rectangle (ClientRectangle.Location,
+ new Size (width - borderAdjustment.Width, height));
+
+ if (vscrollbar_ctrl != null && show_scrollbar)
+ textarea_drawable.Width -= vscrollbar_ctrl.Width;
+
+ last_item = LastVisibleItem ();
+ }
+
+ private void Draw (Rectangle clip, Graphics dc)
+ {
+ dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (owner.BackColor), clip);
+
+ if (owner.Items.Count > 0) {
+
+ for (int i = top_item; i <= last_item; i++) {
+ Rectangle item_rect = GetItemDisplayRectangle (i, top_item);
+
+ if (!clip.IntersectsWith (item_rect))
+ continue;
+
+ DrawItemState state = DrawItemState.None;
+ Color back_color = owner.BackColor;
+ Color fore_color = owner.ForeColor;
+
+ if (i == HighlightedIndex) {
+ state |= DrawItemState.Selected;
+ back_color = SystemColors.Highlight;
+ fore_color = SystemColors.HighlightText;
+
+ if (owner.DropDownStyle == ComboBoxStyle.DropDownList) {
+ state |= DrawItemState.Focus;
+ }
+ }
+
+ owner.HandleDrawItem (new DrawItemEventArgs (dc, owner.Font, item_rect,
+ i, state, fore_color, back_color));
+ }
+ }
+ }
+
+ int highlighted_index = -1;
+
+ public int HighlightedIndex {
+ get { return highlighted_index; }
+ set {
+ if (highlighted_index == value)
+ return;
+
+ if (highlighted_index != -1 && highlighted_index < this.owner.Items.Count)
+ Invalidate (GetItemDisplayRectangle (highlighted_index, top_item));
+ highlighted_index = value;
+ if (highlighted_index != -1)
+ Invalidate (GetItemDisplayRectangle (highlighted_index, top_item));
+ }
+ }
+
+ private Rectangle GetItemDisplayRectangle (int index, int top_index)
+ {
+ if (index < 0 || index >= owner.Items.Count)
+ throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
+
+ Rectangle item_rect = new Rectangle ();
+ int height = owner.GetItemHeight (index);
+
+ item_rect.X = 0;
+ item_rect.Width = textarea_drawable.Width;
+ if (owner.DrawMode == DrawMode.OwnerDrawVariable) {
+ item_rect.Y = 0;
+ for (int i = top_index; i < index; i++)
+ item_rect.Y += owner.GetItemHeight (i);
+ } else
+ item_rect.Y = height * (index - top_index);
+
+ item_rect.Height = height;
+ return item_rect;
+ }
+
+ public void HideWindow ()
+ {
+ if (owner.DropDownStyle == ComboBoxStyle.Simple)
+ return;
+
+ Capture = false;
+ Hide ();
+ owner.DropDownListBoxFinished ();
+ }
+
+ private int IndexFromPointDisplayRectangle (int x, int y)
+ {
+ for (int i = top_item; i <= last_item; i++) {
+ if (GetItemDisplayRectangle (i, top_item).Contains (x, y) == true)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public void InvalidateItem (int index)
+ {
+ if (Visible)
+ Invalidate (GetItemDisplayRectangle (index, top_item));
+ }
+
+ public int LastVisibleItem ()
+ {
+ Rectangle item_rect;
+ int top_y = textarea_drawable.Y + textarea_drawable.Height;
+ int i = 0;
+
+ for (i = top_item; i < owner.Items.Count; i++) {
+ item_rect = GetItemDisplayRectangle (i, top_item);
+ if (item_rect.Y + item_rect.Height > top_y) {
+ return i;
+ }
+ }
+ return i - 1;
+ }
+
+ public void SetTopItem (int item)
+ {
+ if (top_item == item)
+ return;
+ top_item = item;
+ UpdateLastVisibleItem ();
+ Invalidate ();
+ }
+
+ public int FirstVisibleItem ()
+ {
+ return top_item;
+ }
+
+ public void EnsureTop (int item)
+ {
+ if (owner.Items.Count == 0)
+ return;
+ if (vscrollbar_ctrl == null || !vscrollbar_ctrl.Visible)
+ return;
+
+ int max = vscrollbar_ctrl.Maximum - page_size + 1;
+ if (item > max)
+ item = max;
+ else if (item < vscrollbar_ctrl.Minimum)
+ item = vscrollbar_ctrl.Minimum;
+
+ vscrollbar_ctrl.Value = item;
+ }
+
+ bool scrollbar_grabbed = false;
+
+ bool InScrollBar {
+ get {
+ if (vscrollbar_ctrl == null || !vscrollbar_ctrl.is_visible)
+ return false;
+
+ return vscrollbar_ctrl.Bounds.Contains (PointToClient (Widget.MousePosition));
+ }
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ if (InScrollBar) {
+ vscrollbar_ctrl.FireMouseDown (e);
+ scrollbar_grabbed = true;
+ }
+ }
+
+ protected override void OnMouseMove (MouseEventArgs e)
+ {
+ if (owner.DropDownStyle == ComboBoxStyle.Simple)
+ return;
+
+ if (scrollbar_grabbed || (!Capture && InScrollBar)) {
+ vscrollbar_ctrl.FireMouseMove (e);
+ return;
+ }
+
+ Point pt = PointToClient (Widget.MousePosition);
+ int index = IndexFromPointDisplayRectangle (pt.X, pt.Y);
+
+ if (index != -1)
+ HighlightedIndex = index;
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e)
+ {
+ int index = IndexFromPointDisplayRectangle (e.X, e.Y);
+
+ if (scrollbar_grabbed) {
+ vscrollbar_ctrl.FireMouseUp (e);
+ scrollbar_grabbed = false;
+ if (index != -1)
+ HighlightedIndex = index;
+ return;
+ }
+
+ if (index == -1) {
+ HideWindow ();
+ return;
+ }
+
+ bool is_change = owner.SelectedIndex != index;
+
+ owner.SetSelectedIndex (index, true);
+ owner.OnSelectionChangeCommitted (new EventArgs ());
+
+ // If the user selected the already selected item, SelectedIndex
+ // won't fire these events, but .Net does, so we do it here
+ if (!is_change) {
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ }
+
+ HideWindow ();
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ Draw (pevent.ClipRectangle,pevent.Graphics);
+ }
+
+ public bool ShowWindow ()
+ {
+ if (owner.DropDownStyle == ComboBoxStyle.Simple && owner.Items.Count == 0)
+ return false;
+
+ HighlightedIndex = owner.SelectedIndex;
+
+ CalcListBoxArea ();
+ // If the listbox would extend below the screen, move it above the textbox.
+ Rectangle scrn_rect = Screen.FromControl (owner).Bounds;
+ if (this.Location.Y + this.Height >= scrn_rect.Bottom)
+ this.Location = new Point (this.Location.X, this.Location.Y - (this.Height + owner.TextArea.Height));
+ Show ();
+
+ Refresh ();
+ owner.OnDropDown (EventArgs.Empty);
+ return true;
+ }
+
+ public void UpdateLastVisibleItem ()
+ {
+ last_item = LastVisibleItem ();
+ }
+
+ public void Scroll (int delta)
+ {
+ if (delta == 0 || vscrollbar_ctrl == null || !vscrollbar_ctrl.Visible)
+ return;
+
+ int max = vscrollbar_ctrl.Maximum - page_size + 1;
+
+ int val = vscrollbar_ctrl.Value + delta;
+ if (val > max)
+ val = max;
+ else if (val < vscrollbar_ctrl.Minimum)
+ val = vscrollbar_ctrl.Minimum;
+ vscrollbar_ctrl.Value = val;
+ }
+
+ private void OnMouseWheelCLB (object sender, MouseEventArgs me)
+ {
+ if (owner.Items.Count == 0)
+ return;
+
+ int lines = me.Delta / 120 * SystemInformation.MouseWheelScrollLines;
+ Scroll (-lines);
+ }
+
+ // Value Changed
+ private void VerticalScrollEvent (object sender, EventArgs e)
+ {
+ if (top_item == vscrollbar_ctrl.Value)
+ return;
+
+ top_item = vscrollbar_ctrl.Value;
+ UpdateLastVisibleItem ();
+ Invalidate ();
+ }
+
+ protected override void WndProc(ref Message m) {
+ if (m.Msg == (int)Msg.WM_SETFOCUS) {
+ owner.Select (false, true);
+ }
+ base.WndProc (ref m);
+ }
+
+ #endregion Private Methods
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/ContainerControl.cs b/source/ShiftUI/Widgets/ContainerControl.cs
new file mode 100644
index 0000000..5d02f46
--- /dev/null
+++ b/source/ShiftUI/Widgets/ContainerControl.cs
@@ -0,0 +1,850 @@
+// 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 Novell, Inc.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+//
+
+
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI {
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ public class ContainerWidget : ScrollableWidget, IContainerWidget {
+ private Widget active_Widget;
+ private Widget unvalidated_Widget;
+ private ArrayList pending_validation_chain;
+
+ // This is an internal hack that allows some container Widgets
+ // to not auto select their child when they are activated
+ internal bool auto_select_child = true;
+ private SizeF auto_scale_dimensions;
+ private AutoScaleMode auto_scale_mode;
+ private bool auto_scale_mode_set;
+ private bool auto_scale_pending;
+ private bool is_auto_scaling;
+
+ internal bool validation_failed; //track whether validation was cancelled by a validating Widget
+
+ #region Public Constructors
+ public ContainerWidget() {
+ active_Widget = null;
+ unvalidated_Widget = null;
+ WidgetRemoved += new WidgetEventHandler(OnWidgetRemoved);
+ auto_scale_dimensions = SizeF.Empty;
+ auto_scale_mode = AutoScaleMode.Inherit;
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ [Browsable (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Widget ActiveWidget {
+ get {
+ return active_Widget;
+ }
+
+ set {
+ if (value==null || (active_Widget == value && active_Widget.Focused)) {
+ return;
+ }
+
+ if (!Contains(value)) {
+ throw new ArgumentException("Cannot activate invisible or disabled Widget.");
+ }
+
+ // Fire the enter and leave events if possible
+ Form form = FindForm ();
+ Widget active = GetMostDeeplyNestedActiveWidget (form == null ? this : form);
+ Widget common_ancestor = GetCommonContainer (active, value);
+ ArrayList chain = new ArrayList ();
+ ArrayList validation_chain = new ArrayList ();
+ Widget walk = active;
+
+ // we split this up into three steps:
+ // 1. walk up the tree (if we need to) to our root, firing leave events.
+ // 2. validate.
+ // 3. walk down the tree (if we need to), firing enter events.
+
+ // "our root" is either the common ancestor of the current active
+ // Widget and the new active Widget, or the current active Widget,
+ // or the new active Widget. That is, it's either one of these three
+ // configurations:
+
+ // (1) root (2) new (3) current
+ // / \ / \ / \
+ // ... ... ... ... ... ...
+ // / \ / \
+ // current new current new
+
+
+ // note (3) doesn't require any upward walking, and no leave events are generated.
+ // (2) doesn't require any downward walking, and no enter events are generated.
+
+ // as we walk up the tree, we generate a list of all Widgets which cause
+ // validation. After firing the leave events, we invoke (in order starting from
+ // the most deeply nested) their Validating event. If one sets CancelEventArgs.Cancel
+ // to true, we ignore the Widget the user wanted to set ActiveWidget to, and use
+ // the Validating Widget instead.
+
+ bool fire_enter = true;
+ Widget root = common_ancestor;
+
+ active_Widget = value;
+
+ // Generate the leave messages
+ while (walk != common_ancestor && walk != null) {
+ if (walk == value) {
+ root = value;
+ fire_enter = false;
+ break;
+ }
+ walk.FireLeave ();
+ /* clear our idea of the active Widget as we go back up */
+ if (walk is ContainerWidget)
+ ((ContainerWidget)walk).active_Widget = null;
+
+ if (walk.CausesValidation)
+ validation_chain.Add (walk);
+
+ walk = walk.Parent;
+ }
+
+ // Validation can be postponed due to all the Widgets
+ // in the enter chain not causing validation. If we don't have any
+ // enter chain, it means that the selected Widget is a child and then
+ // we need to validate the Widgets anyway
+ bool postpone_validation;
+ Widget topmost_under_root = null; // topmost under root, in the *enter* chain
+ if (value == root)
+ postpone_validation = false;
+ else {
+ postpone_validation = true;
+ walk = value;
+ while (walk != root && walk != null) {
+ if (walk.CausesValidation)
+ postpone_validation = false;
+
+ topmost_under_root = walk;
+ walk = walk.Parent;
+ }
+ }
+
+ Widget failed_validation_Widget = PerformValidation (form == null ? this : form, postpone_validation,
+ validation_chain, topmost_under_root);
+ if (failed_validation_Widget != null) {
+ active_Widget = value = failed_validation_Widget;
+ fire_enter = true;
+ }
+
+ if (fire_enter) {
+ walk = value;
+ while (walk != root && walk != null) {
+ chain.Add (walk);
+ walk = walk.Parent;
+ }
+
+ if (root != null && walk == root && !(root is ContainerWidget))
+ chain.Add (walk);
+
+ for (int i = chain.Count - 1; i >= 0; i--) {
+ walk = (Widget) chain [i];
+ walk.FireEnter ();
+ }
+ }
+
+ walk = this;
+ Widget ctl = this;
+ while (walk != null) {
+ if (walk.Parent is ContainerWidget) {
+ ((ContainerWidget) walk.Parent).active_Widget = ctl;
+ ctl = walk.Parent;
+ }
+ walk = walk.Parent;
+ }
+
+ if (this is Form)
+ CheckAcceptButton();
+
+ // Scroll Widget into view
+ ScrollWidgetIntoView(active_Widget);
+
+
+ walk = this;
+ ctl = this;
+ while (walk != null) {
+ if (walk.Parent is ContainerWidget) {
+ ctl = walk.Parent;
+ }
+ walk = walk.Parent;
+ }
+
+ // Let the Widget know it's selected
+ if (ctl.InternalContainsFocus)
+ SendWidgetFocus (active_Widget);
+ }
+ }
+
+ // Return the Widget where validation failed, and null otherwise
+ // @topmost_under_root is the Widget under the root in the enter chain, if any
+ //
+ // The process of validation happens as described:
+ //
+ // 1. Iterate over the nodes in the enter chain (walk down), looking for any node
+ // causing validation. If we can't find any, don't validate the current validation chain, but postpone it,
+ // saving it in the top_container.pending_validation_chain field, since we need to keep track of it later.
+ // If we have a previous pending_validation_chain, add the new nodes, making sure they are not repeated
+ // (this is computed in ActiveWidget and we receive if as the postpone_validation parameter).
+ //
+ // 2. If we found at least one node causing validation in the enter chain, try to validate the elements
+ // in pending_validation_chain, if any. Then continue with the ones receives as parameters.
+ //
+ // 3. Return null if all the validation performed successfully, and return the Widget where the validation
+ // failed otherwise.
+ //
+ private Widget PerformValidation (ContainerWidget top_container, bool postpone_validation, ArrayList validation_chain,
+ Widget topmost_under_root)
+ {
+ validation_failed = false;
+
+ if (postpone_validation) {
+ AddValidationChain (top_container, validation_chain);
+ return null;
+ }
+
+ // if not null, pending chain has always one element or more
+ if (top_container.pending_validation_chain != null) {
+ // if the topmost node in the enter chain is exactly the topmost
+ // int the validation chain, remove it, as .net does
+ int last_idx = top_container.pending_validation_chain.Count - 1;
+ if (topmost_under_root == top_container.pending_validation_chain [last_idx])
+ top_container.pending_validation_chain.RemoveAt (last_idx);
+
+ AddValidationChain (top_container, validation_chain);
+ validation_chain = top_container.pending_validation_chain;
+ top_container.pending_validation_chain = null;
+ }
+
+ for (int i = 0; i < validation_chain.Count; i ++) {
+ if (!ValidateWidget ((Widget)validation_chain[i])) {
+ validation_failed = true;
+ return (Widget)validation_chain[i];
+ }
+ }
+
+ return null;
+ }
+
+ // Add the elements in validation_chain to the pending validation chain stored in top_container
+ private void AddValidationChain (ContainerWidget top_container, ArrayList validation_chain)
+ {
+ if (validation_chain.Count == 0)
+ return;
+
+ if (top_container.pending_validation_chain == null || top_container.pending_validation_chain.Count == 0) {
+ top_container.pending_validation_chain = validation_chain;
+ return;
+ }
+
+ foreach (Widget c in validation_chain)
+ if (!top_container.pending_validation_chain.Contains (c))
+ top_container.pending_validation_chain.Add (c);
+ }
+
+ private bool ValidateWidget (Widget c)
+ {
+ CancelEventArgs e = new CancelEventArgs ();
+
+ c.FireValidating (e);
+
+ if (e.Cancel)
+ return false;
+
+ c.FireValidated ();
+ return true;
+ }
+
+ private Widget GetMostDeeplyNestedActiveWidget (ContainerWidget container)
+ {
+ Widget active = container.ActiveWidget;
+ while (active is ContainerWidget) {
+ if (((ContainerWidget)active).ActiveWidget == null)
+ break;
+ active = ((ContainerWidget)active).ActiveWidget;
+ }
+ return active;
+ }
+
+ // Just in a separate method to make debugging a little easier,
+ // should eventually be rolled into ActiveWidget setter
+ private Widget GetCommonContainer (Widget active_Widget, Widget value)
+ {
+ Widget new_container = null;
+ Widget prev_container = active_Widget;
+
+ while (prev_container != null) {
+ new_container = value.Parent;
+ while (new_container != null) {
+ if (new_container == prev_container)
+ return new_container;
+ new_container = new_container.Parent;
+ }
+
+ prev_container = prev_container.Parent;
+ }
+
+ return null;
+ }
+
+ internal void SendWidgetFocus (Widget c)
+ {
+ if (c != null && c.IsHandleCreated) {
+ XplatUI.SetFocus (c.window.Handle);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ [Localizable (true)]
+ public SizeF AutoScaleDimensions {
+ get {
+ return auto_scale_dimensions;
+ }
+
+ set {
+ if (auto_scale_dimensions != value) {
+ auto_scale_dimensions = value;
+
+ PerformAutoScale ();
+ }
+ }
+ }
+
+ protected SizeF AutoScaleFactor {
+ get {
+ if (auto_scale_dimensions.IsEmpty)
+ return new SizeF (1f, 1f);
+
+ return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
+ CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
+ }
+ }
+
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public AutoScaleMode AutoScaleMode {
+ get {
+ return auto_scale_mode;
+ }
+ set {
+ if (this is Form)
+ (this as Form).AutoScale = false;
+
+ if (auto_scale_mode != value) {
+ auto_scale_mode = value;
+
+ if (auto_scale_mode_set)
+ auto_scale_dimensions = SizeF.Empty;
+
+ auto_scale_mode_set = true;
+
+ PerformAutoScale ();
+ }
+ }
+ }
+
+ [Browsable (false)]
+ public override BindingContext BindingContext {
+ get {
+ if (base.BindingContext == null) {
+ base.BindingContext = new BindingContext();
+ }
+ return base.BindingContext;
+ }
+
+ set {
+ base.BindingContext = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public SizeF CurrentAutoScaleDimensions {
+ get {
+ switch(auto_scale_mode) {
+ case AutoScaleMode.Dpi:
+ return TextRenderer.GetDpi ();
+
+ case AutoScaleMode.Font:
+ Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
+ int width = (int)Math.Round ((float)s.Width / 62f);
+
+ return new SizeF (width, s.Height);
+ }
+
+ return auto_scale_dimensions;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Form ParentForm {
+ get {
+ Widget parent;
+
+ parent = this.Parent;
+
+ while (parent != null) {
+ if (parent is Form) {
+ return (Form)parent;
+ }
+ parent = parent.Parent;
+ }
+
+ return null;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Methods
+ protected override bool CanEnableIme {
+ get { return false; }
+ }
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+ #endregion // Public Instance Methods
+
+ #region Public Instance Methods
+ internal void PerformAutoScale (bool called_by_scale)
+ {
+ if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
+ return;
+
+ if ((layout_suspended > 0) && !called_by_scale) {
+ auto_scale_pending = true;
+ return;
+ }
+ // Set this first so we don't get called again from
+ // PerformDelayedAutoScale after ResumeLayout
+ auto_scale_pending = false;
+
+ SizeF factor = AutoScaleFactor;
+ if (AutoScaleMode == AutoScaleMode.Inherit) {
+ ContainerWidget cc = FindContainer (this.Parent);
+ if (cc != null)
+ factor = cc.AutoScaleFactor;
+ }
+ if (factor != new SizeF (1F, 1F)) {
+ is_auto_scaling = true;
+ SuspendLayout ();
+ Scale (factor);
+ ResumeLayout (false);
+ is_auto_scaling = false;
+ }
+
+ auto_scale_dimensions = CurrentAutoScaleDimensions;
+ }
+
+ public void PerformAutoScale ()
+ {
+ PerformAutoScale (false);
+ }
+
+ internal void PerformDelayedAutoScale ()
+ {
+ if (auto_scale_pending)
+ PerformAutoScale ();
+ }
+
+ internal bool IsAutoScaling {
+ get { return is_auto_scaling; }
+ }
+
+ [MonoTODO ("Stub, not implemented")]
+ static bool ValidateWarned;
+ public bool Validate() {
+ //throw new NotImplementedException();
+ if (!ValidateWarned) {
+ Console.WriteLine("ContainerWidget.Validate is not yet implemented");
+ ValidateWarned = true;
+ }
+ return true;
+ }
+
+ public bool Validate (bool checkAutoValidate)
+ {
+ if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate)
+ return Validate ();
+
+ return true;
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public virtual bool ValidateChildren ()
+ {
+ return ValidateChildren (ValidationConstraints.Selectable);
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public virtual bool ValidateChildren (ValidationConstraints validationConstraints)
+ {
+ bool recurse = !((validationConstraints & ValidationConstraints.ImmediateChildren) == ValidationConstraints.ImmediateChildren);
+
+ foreach (Widget Widget in Widgets)
+ if (!ValidateNestedWidgets (Widget, validationConstraints, recurse))
+ return false;
+
+ return true;
+ }
+
+ bool IContainerWidget.ActivateWidget(Widget Widget) {
+ return Select(Widget);
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void AdjustFormScrollbars(bool displayScrollbars) {
+ base.AdjustFormScrollbars(displayScrollbars);
+ }
+
+ protected override void Dispose(bool disposing) {
+ base.Dispose(disposing);
+ }
+
+ // LAMESPEC This used to be documented, but it's not in code
+ // and no longer listed in MSDN2
+ // //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ // protected override void OnWidgetRemoved(WidgetEventArgs e) {
+ private void OnWidgetRemoved(object sender, WidgetEventArgs e) {
+ if (e.Widget == this.unvalidated_Widget) {
+ this.unvalidated_Widget = null;
+ }
+
+ if (e.Widget == this.active_Widget) {
+ this.unvalidated_Widget = null;
+ }
+
+ // base.OnWidgetRemoved(e);
+ }
+
+ protected override void OnCreateWidget() {
+ base.OnCreateWidget();
+ // MS seems to call this here, it gets the whole databinding process started
+ OnBindingContextChanged (EventArgs.Empty);
+ }
+
+ protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
+ {
+ if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
+ return true;
+
+ return base.ProcessCmdKey (ref msg, keyData);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override bool ProcessDialogChar(char charCode) {
+ if (GetTopLevel()) {
+ if (ProcessMnemonic(charCode)) {
+ return true;
+ }
+ }
+ return base.ProcessDialogChar(charCode);
+ }
+
+ protected override bool ProcessDialogKey(Keys keyData) {
+ Keys key;
+ bool forward;
+
+ key = keyData & Keys.KeyCode;
+ forward = true;
+
+ switch (key) {
+ case Keys.Tab: {
+ if ((keyData & (Keys.Alt | Keys.Widget)) == Keys.None) {
+ if (ProcessTabKey ((Widget.ModifierKeys & Keys.Shift) == 0)) {
+ return true;
+ }
+ }
+ break;
+ }
+
+ case Keys.Left: {
+ forward = false;
+ goto case Keys.Down;
+ }
+
+ case Keys.Up: {
+ forward = false;
+ goto case Keys.Down;
+ }
+
+ case Keys.Right: {
+ goto case Keys.Down;
+ }
+ case Keys.Down: {
+ if (SelectNextWidget(active_Widget, forward, false, false, true)) {
+ return true;
+ }
+ break;
+ }
+
+
+ }
+ return base.ProcessDialogKey(keyData);
+ }
+
+ protected override bool ProcessMnemonic(char charCode) {
+ bool wrapped;
+ Widget c;
+
+ wrapped = false;
+ c = active_Widget;
+
+ do {
+ c = GetNextWidget(c, true);
+ if (c != null) {
+ // This is stupid. I want to be able to call c.ProcessMnemonic directly
+ if (c.ProcessWidgetMnemonic(charCode)) {
+ return(true);
+ }
+ continue;
+ } else {
+ if (wrapped) {
+ break;
+ }
+ wrapped = true;
+ }
+ } while (c != active_Widget);
+
+ return false;
+ }
+
+ protected virtual bool ProcessTabKey(bool forward) {
+ return SelectNextWidget(active_Widget, forward, true, true, false);
+ }
+
+ protected override void Select(bool directed, bool forward)
+ {
+ if (Parent != null) {
+ IContainerWidget parent = Parent.GetContainerWidget ();
+ if (parent != null) {
+ parent.ActiveWidget = this;
+ }
+ }
+
+ if (directed && auto_select_child) {
+ SelectNextWidget (null, forward, true, true, false);
+ }
+ }
+
+ protected virtual void UpdateDefaultButton() {
+ // MS Internal
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void WndProc(ref Message m) {
+ switch ((Msg) m.Msg) {
+
+ case Msg.WM_SETFOCUS:
+ if (active_Widget != null)
+ Select (active_Widget);
+ else
+ base.WndProc (ref m);
+ break;
+
+ default:
+ base.WndProc(ref m);
+ break;
+ }
+ }
+ #endregion // Protected Instance Methods
+
+ #region Internal Methods
+ internal void ChildWidgetRemoved (Widget Widget)
+ {
+ ContainerWidget top_container = FindForm ();
+ if (top_container == null)
+ top_container = this;
+
+ // Remove Widgets -as well as any sub Widget- that was in the pending validation chain
+ ArrayList pending_validation_chain = top_container.pending_validation_chain;
+ if (pending_validation_chain != null) {
+ RemoveChildrenFromValidation (pending_validation_chain, Widget);
+
+ if (pending_validation_chain.Count == 0)
+ top_container.pending_validation_chain = null;
+ }
+
+ if (Widget == active_Widget || Widget.Contains (active_Widget)) {
+ SelectNextWidget (this, true, true, true, true);
+ if (Widget == active_Widget || Widget.Contains (active_Widget)) {
+ active_Widget = null;
+ }
+ }
+ }
+
+ // Check that this Widget (or any child) is included in the pending validation chain
+ bool RemoveChildrenFromValidation (ArrayList validation_chain, Widget c)
+ {
+ if (RemoveFromValidationChain (validation_chain, c))
+ return true;
+
+ foreach (Widget child in c.Widgets)
+ if (RemoveChildrenFromValidation (validation_chain, child))
+ return true;
+
+ return false;
+ }
+
+ // Remove the top most Widget in the pending validation chain, as well as any children there,
+ // taking advantage of the fact that the chain is in reverse order of the Widget's hierarchy
+ bool RemoveFromValidationChain (ArrayList validation_chain, Widget c)
+ {
+ int idx = validation_chain.IndexOf (c);
+ if (idx > -1) {
+ pending_validation_chain.RemoveAt (idx--);
+ return true;
+ }
+
+ return false;
+ }
+
+ internal virtual void CheckAcceptButton()
+ {
+ // do nothing here, only called if it is a Form
+ }
+
+ private bool ValidateNestedWidgets (Widget c, ValidationConstraints constraints, bool recurse)
+ {
+ bool validate_result = true;
+
+ if (!c.CausesValidation)
+ validate_result = true;
+ else if (!ValidateThisWidget (c, constraints))
+ validate_result = true;
+ else if (!ValidateWidget (c))
+ validate_result = false;
+
+ if (recurse)
+ foreach (Widget Widget in c.Widgets)
+ if (!ValidateNestedWidgets (Widget, constraints, recurse))
+ return false;
+
+ return validate_result;
+ }
+
+ private bool ValidateThisWidget (Widget c, ValidationConstraints constraints)
+ {
+ if (constraints == ValidationConstraints.None)
+ return true;
+
+ if ((constraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled)
+ return false;
+
+ if ((constraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle (Widgetstyles.Selectable))
+ return false;
+
+ if ((constraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop)
+ return false;
+
+ if ((constraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible)
+ return false;
+
+ return true;
+ }
+ #endregion // Internal Methods
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+
+ if (AutoScaleMode == AutoScaleMode.Font)
+ PerformAutoScale ();
+ }
+
+ protected override void OnLayout (LayoutEventArgs e)
+ {
+ base.OnLayout (e);
+ }
+
+ AutoValidate auto_validate = AutoValidate.Inherit;
+
+ [Browsable (false)]
+ [AmbientValue (AutoValidate.Inherit)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public virtual AutoValidate AutoValidate {
+ get {
+ return auto_validate;
+ }
+
+ [MonoTODO("Currently does nothing with the setting")]
+ set {
+ if (auto_validate != value){
+ auto_validate = value;
+ OnAutoValidateChanged (new EventArgs ());
+ }
+ }
+ }
+
+ internal bool ShouldSerializeAutoValidate ()
+ {
+ return this.AutoValidate != AutoValidate.Inherit;
+ }
+
+ static object OnValidateChanged = new object ();
+
+ protected virtual void OnAutoValidateChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler AutoValidateChanged {
+ add { Events.AddHandler (OnValidateChanged, value); }
+ remove { Events.RemoveHandler (OnValidateChanged, value); }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/DateTimePicker.cs b/source/ShiftUI/Widgets/DateTimePicker.cs
new file mode 100644
index 0000000..183fd77
--- /dev/null
+++ b/source/ShiftUI/Widgets/DateTimePicker.cs
@@ -0,0 +1,2018 @@
+// 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.
+//
+// Authors:
+// John BouAntoun [email protected]
+// Rolf Bjarne Kvinge [email protected]
+//
+// TODO:
+// - wire in all events from monthcalendar
+
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace ShiftUI {
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultBindingProperty ("Value")]
+ [ComVisible (true)]
+ [DefaultEvent("ValueChanged")]
+ [DefaultProperty("Value")]
+ //[Designer("ShiftUI.Design.DateTimePickerDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class DateTimePicker : Widget {
+
+ #region Public variables
+
+ // this class has to have the specified hour, minute and second, as it says in msdn
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public static readonly DateTime MaxDateTime = new DateTime (9998, 12, 31, 0, 0, 0);
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
+
+ internal const int check_box_size = 13;
+ internal const int check_box_space = 4;
+
+ #endregion // Public variables
+
+ #region Local variables
+
+ protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
+ protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveCaption;
+ protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorActiveCaptionText;
+ protected static readonly Color DefaultTrailingForeColor = SystemColors.GrayText;
+
+ internal MonthCalendar month_calendar;
+ bool is_checked;
+ string custom_format;
+ LeftRightAlignment drop_down_align;
+ DateTimePickerFormat format;
+ DateTime max_date;
+ DateTime min_date;
+ bool show_check_box;
+ bool show_up_down;
+ DateTime date_value;
+ bool right_to_left_layout;
+
+ // variables used for drawing and such
+ internal const int up_down_width = check_box_size;
+ internal bool is_drop_down_visible;
+ internal bool is_up_pressed;
+ internal bool is_down_pressed;
+ internal Timer updown_timer;
+ internal const int initial_timer_delay = 500;
+ internal const int subsequent_timer_delay = 100;
+ internal bool is_checkbox_selected;
+
+ // variables for determining how to format the string
+ internal PartData[] part_data;
+ internal int editing_part_index = -1;
+ internal int editing_number = -1;
+ internal string editing_text;
+
+ bool drop_down_button_entered;
+ #endregion // Local variables
+
+ #region DateTimePickerAccessibleObject Subclass
+ [ComVisible(true)]
+ public class DateTimePickerAccessibleObject : WidgetAccessibleObject {
+ #region DateTimePickerAccessibleObject Local Variables
+ private new DateTimePicker owner;
+ #endregion // DateTimePickerAccessibleObject Local Variables
+
+ #region DateTimePickerAccessibleObject Constructors
+ public DateTimePickerAccessibleObject(DateTimePicker owner) : base(owner) {
+ this.owner = owner;
+ }
+ #endregion // DateTimePickerAccessibleObject Constructors
+
+ #region DateTimePickerAccessibleObject Properties
+ public override string KeyboardShortcut {
+ get {
+ return base.KeyboardShortcut;
+ }
+ }
+
+ public override AccessibleRole Role {
+ get {
+ return base.Role;
+ }
+ }
+
+ public override AccessibleStates State {
+ get {
+ AccessibleStates retval;
+
+ retval = AccessibleStates.Default;
+
+ if (owner.Checked) {
+ retval |= AccessibleStates.Checked;
+ }
+
+ return retval;
+ }
+ }
+
+ public override string Value {
+ get {
+ return owner.Text;
+ }
+ }
+ #endregion // DateTimePickerAccessibleObject Properties
+ }
+ #endregion // DateTimePickerAccessibleObject Sub-class
+
+ #region public constructors
+
+ // only public constructor
+ public DateTimePicker () {
+
+ // initialise the month calendar
+ month_calendar = new MonthCalendar (this);
+ month_calendar.CalendarDimensions = new Size (1, 1);
+ month_calendar.MaxSelectionCount = 1;
+ month_calendar.ForeColor = Widget.DefaultForeColor;
+ month_calendar.BackColor = DefaultMonthBackColor;
+ month_calendar.TitleBackColor = DefaultTitleBackColor;
+ month_calendar.TitleForeColor = DefaultTitleForeColor;
+ month_calendar.TrailingForeColor = DefaultTrailingForeColor;
+ month_calendar.Visible = false;
+ // initialize the timer
+ updown_timer = new Timer();
+ updown_timer.Interval = initial_timer_delay;
+
+
+ // initialise other variables
+ is_checked = true;
+ custom_format = null;
+ drop_down_align = LeftRightAlignment.Left;
+ format = DateTimePickerFormat.Long;
+ max_date = MaxDateTime;
+ min_date = MinDateTime;
+ show_check_box = false;
+ show_up_down = false;
+ date_value = DateTime.Now;
+
+ is_drop_down_visible = false;
+ BackColor = SystemColors.Window;
+ ForeColor = SystemColors.WindowText;
+
+ month_calendar.DateChanged += new DateRangeEventHandler (MonthCalendarDateChangedHandler);
+ month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
+ month_calendar.LostFocus += new EventHandler (MonthCalendarLostFocusHandler);
+ updown_timer.Tick += new EventHandler (UpDownTimerTick);
+ KeyPress += new KeyPressEventHandler (KeyPressHandler);
+ KeyDown += new KeyEventHandler (KeyDownHandler);
+ GotFocus += new EventHandler (GotFocusHandler);
+ LostFocus += new EventHandler (LostFocusHandler);
+ MouseDown += new MouseEventHandler (MouseDownHandler);
+ MouseUp += new MouseEventHandler (MouseUpHandler);
+ MouseEnter += new EventHandler (OnMouseEnter);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ MouseMove += new MouseEventHandler (OnMouseMove);
+ Paint += new PaintEventHandler (PaintHandler);
+ Resize += new EventHandler (ResizeHandler);
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.StandardClick, false);
+ SetStyle (Widgetstyles.FixedHeight, true);
+ SetStyle (Widgetstyles.Selectable, true);
+
+ CalculateFormats ();
+ }
+
+ #endregion
+
+ #region public properties
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color BackColor {
+ set {
+ base.BackColor = value;
+ }
+ get {
+ return base.BackColor;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ set {
+ base.BackgroundImage = value;
+ }
+ get {
+ return base.BackgroundImage;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get{
+ return base.BackgroundImageLayout;
+ }
+ set {
+ base.BackgroundImageLayout = value;
+ }
+ }
+
+ [AmbientValue(null)]
+ [Localizable(true)]
+ public Font CalendarFont {
+ set {
+ month_calendar.Font = value;
+ }
+ get {
+ return month_calendar.Font;
+ }
+ }
+
+ public Color CalendarForeColor {
+ set {
+ month_calendar.ForeColor = value;
+ }
+ get {
+ return month_calendar.ForeColor;
+ }
+ }
+
+ public Color CalendarMonthBackground {
+ set {
+ month_calendar.BackColor = value;
+ }
+ get {
+ return month_calendar.BackColor;
+ }
+ }
+
+ public Color CalendarTitleBackColor {
+ set {
+ month_calendar.TitleBackColor = value;
+ }
+ get {
+ return month_calendar.TitleBackColor;
+ }
+ }
+
+ public Color CalendarTitleForeColor {
+ set {
+ month_calendar.TitleForeColor = value;
+ }
+ get {
+ return month_calendar.TitleForeColor;
+ }
+ }
+
+ public Color CalendarTrailingForeColor {
+ set {
+ month_calendar.TrailingForeColor = value;
+ }
+ get {
+ return month_calendar.TrailingForeColor;
+ }
+ }
+
+ // when checked the value is grayed out
+ [Bindable(true)]
+ [DefaultValue(true)]
+ public bool Checked {
+ set {
+ if (is_checked != value) {
+ is_checked = value;
+ // invalidate the value inside this control
+ if (ShowCheckBox) {
+ for (int i = 0; i < part_data.Length; i++)
+ part_data [i].Selected = false;
+ Invalidate (date_area_rect);
+ OnUIAChecked ();
+ OnUIASelectionChanged ();
+ }
+ }
+ }
+ get {
+ return is_checked;
+ }
+ }
+
+ // the custom format string to format this control with
+ [Localizable (true)]
+ [DefaultValue(null)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public string CustomFormat {
+ set {
+ if (custom_format != value) {
+ custom_format = value;
+ if (this.Format == DateTimePickerFormat.Custom) {
+ CalculateFormats ();
+ }
+ }
+ }
+ get {
+ return custom_format;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get {
+ return base.DoubleBuffered;
+ }
+ set {
+ base.DoubleBuffered = value;
+ }
+ }
+
+ // which side the drop down is to be aligned on
+ [DefaultValue(LeftRightAlignment.Left)]
+ [Localizable(true)]
+ public LeftRightAlignment DropDownAlign {
+ set {
+ if (drop_down_align != value) {
+ drop_down_align = value;
+ }
+ }
+ get {
+ return drop_down_align;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ set {
+ base.ForeColor = value;
+ }
+ get {
+ return base.ForeColor;
+ }
+ }
+
+ // the format of the date time picker text, default is long
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public DateTimePickerFormat Format {
+ set {
+ if (format != value) {
+ format = value;
+ RecreateHandle (); // MS recreates the handle on every format change.
+ CalculateFormats ();
+ this.OnFormatChanged (EventArgs.Empty);
+ // invalidate the value inside this control
+ this.Invalidate (date_area_rect);
+ }
+ }
+ get {
+ return format;
+ }
+ }
+
+ public DateTime MaxDate {
+ set {
+ if (value < min_date) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "'{0}' is not a valid value for 'MaxDate'. 'MaxDate' "
+ + "must be greater than or equal to MinDate.",
+ value.ToString ("G"));
+ throw new ArgumentOutOfRangeException ("MaxDate", msg);
+ }
+ if (value > MaxDateTime) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "DateTimePicker does not support dates after {0}.",
+ MaxDateTime.ToString ("G", CultureInfo.CurrentCulture));
+ throw new ArgumentOutOfRangeException ("MaxDate", msg);
+ }
+ if (max_date != value) {
+ max_date = value;
+ if (Value > max_date) {
+ Value = max_date;
+ // invalidate the value inside this control
+ this.Invalidate (date_area_rect);
+ }
+ OnUIAMaximumChanged ();
+ }
+ }
+ get {
+ return max_date;
+ }
+ }
+
+ public static DateTime MaximumDateTime {
+ get {
+ return MaxDateTime;
+ }
+ }
+
+ public DateTime MinDate {
+ set {
+ // If the user tries to set DateTime.MinValue, fix it to
+ // DateTimePicker's minimum.
+ if (value == DateTime.MinValue)
+ value = MinDateTime;
+
+ if (value > MaxDate) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "'{0}' is not a valid value for 'MinDate'. 'MinDate' "
+ + "must be less than MaxDate.",
+ value.ToString ("G"));
+ throw new ArgumentOutOfRangeException ("MinDate", msg);
+ }
+ if (value < MinDateTime) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "DateTimePicker does not support dates before {0}.",
+ MinDateTime.ToString ("G", CultureInfo.CurrentCulture));
+ throw new ArgumentOutOfRangeException ("MinDate", msg);
+ }
+ if (min_date != value) {
+ min_date = value;
+ if (Value < min_date) {
+ Value = min_date;
+ // invalidate the value inside this control
+ this.Invalidate (date_area_rect);
+ }
+ OnUIAMinimumChanged ();
+ }
+ }
+ get {
+ return min_date;
+ }
+ }
+
+ public static DateTime MinimumDateTime {
+ get {
+ return MinDateTime;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Browsable (false)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ // the prefered height to draw this control using current font
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int PreferredHeight {
+ get {
+ // Make it proportional
+ return (int) Math.Ceiling (Font.Height * 1.5);
+ }
+ }
+
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public virtual bool RightToLeftLayout {
+ get {
+ return right_to_left_layout;
+ }
+ set {
+ if (right_to_left_layout != value) {
+ right_to_left_layout = value;
+ OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ // whether or not the check box is shown
+ [DefaultValue(false)]
+ public bool ShowCheckBox {
+ set {
+ if (show_check_box != value) {
+ show_check_box = value;
+ // invalidate the value inside this control
+ this.Invalidate (date_area_rect);
+ OnUIAShowCheckBoxChanged ();
+ }
+ }
+ get {
+ return show_check_box;
+ }
+ }
+
+ // if true show the updown control, else popup the monthcalendar
+ [DefaultValue(false)]
+ public bool ShowUpDown {
+ set {
+ if (show_up_down != value) {
+ show_up_down = value;
+ // need to invalidate the whole control
+ this.Invalidate ();
+ OnUIAShowUpDownChanged ();
+ }
+ }
+ get {
+ return show_up_down;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public override string Text {
+ set {
+ DateTime parsed_value;
+
+ if (value == null || value == string.Empty) {
+ date_value = DateTime.Now;
+ OnValueChanged (EventArgs.Empty);
+ OnTextChanged (EventArgs.Empty);
+ return;
+ }
+
+ if (format == DateTimePickerFormat.Custom) {
+ // TODO: if the format is a custom format we need to do a custom parse here
+ // This implementation will fail if the custom format is set to something that can
+ // be a standard datetime format string
+ // http://msdn2.microsoft.com/en-us/library/az4se3k1.aspx
+ parsed_value = DateTime.ParseExact (value, GetExactFormat (), null);
+ } else {
+ parsed_value = DateTime.ParseExact (value, GetExactFormat (), null);
+ }
+
+ if (date_value != parsed_value) {
+ Value = parsed_value;
+ }
+ }
+ get {
+ if (!IsHandleCreated)
+ return "";
+
+ if (format == DateTimePickerFormat.Custom) {
+ System.Text.StringBuilder result = new System.Text.StringBuilder ();
+ for (int i = 0; i < part_data.Length; i++) {
+ result.Append(part_data[i].GetText(date_value));
+ }
+ return result.ToString ();
+ } else {
+ return Value.ToString (GetExactFormat ());
+ }
+ }
+ }
+
+ [Bindable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ public DateTime Value {
+ set {
+ if (date_value != value) {
+ if (value < MinDate || value > MaxDate)
+ throw new ArgumentOutOfRangeException ("value", "value must be between MinDate and MaxDate");
+
+ date_value = value;
+ this.OnValueChanged (EventArgs.Empty);
+ this.Invalidate (date_area_rect);
+ }
+ }
+ get {
+ return date_value;
+ }
+ }
+
+ #endregion // public properties
+
+ #region public methods
+
+ // just return the text value
+ public override string ToString () {
+ return this.Text;
+ }
+
+ #endregion // public methods
+
+ #region public events
+ static object CloseUpEvent = new object ();
+ static object DropDownEvent = new object ();
+ static object FormatChangedEvent = new object ();
+ static object ValueChangedEvent = new object ();
+ static object RightToLeftLayoutChangedEvent = new object ();
+
+ // raised when the monthcalendar is closed
+ public event EventHandler CloseUp {
+ add { Events.AddHandler (CloseUpEvent, value); }
+ remove { Events.RemoveHandler (CloseUpEvent, value); }
+ }
+
+ // raised when the monthcalendar is opened
+ public event EventHandler DropDown {
+ add { Events.AddHandler (DropDownEvent, value); }
+ remove { Events.RemoveHandler (DropDownEvent, value); }
+ }
+
+ // raised when the format of the value is changed
+ public event EventHandler FormatChanged {
+ add { Events.AddHandler (FormatChangedEvent, value); }
+ remove { Events.RemoveHandler (FormatChangedEvent, value); }
+ }
+
+ // raised when the date Value is changed
+ public event EventHandler ValueChanged {
+ add { Events.AddHandler (ValueChangedEvent, value); }
+ remove { Events.RemoveHandler (ValueChangedEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackColorChanged {
+ add {
+ base.BackColorChanged += value;
+ }
+
+ remove {
+ base.BackColorChanged -= value;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add {
+ base.BackgroundImageChanged += value;
+ }
+
+ remove {
+ base.BackgroundImageChanged -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add
+ {
+ base.BackgroundImageLayoutChanged += value;
+ }
+
+ remove
+ {
+ base.BackgroundImageLayoutChanged -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Click {
+ add {
+ base.Click += value;
+ }
+ remove {
+ base.Click -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add {
+ base.DoubleClick += value;
+ }
+ remove {
+ base.DoubleClick -= value;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add {
+ base.ForeColorChanged += value;
+ }
+
+ remove {
+ base.ForeColorChanged -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseClick {
+ add {
+ base.MouseClick += value;
+ }
+ remove {
+ base.MouseClick -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add {
+ base.MouseDoubleClick += value;
+ }
+ remove {
+ base.MouseDoubleClick -= value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add
+ {
+ base.PaddingChanged += value;
+ }
+ remove
+ {
+ base.PaddingChanged -= value;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add {
+ base.Paint += value;
+ }
+
+ remove {
+ base.Paint -= value;
+ }
+ }
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add {
+ Events.AddHandler (RightToLeftLayoutChangedEvent, value);
+ }
+ remove {
+ Events.RemoveHandler (RightToLeftLayoutChangedEvent, value);
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler TextChanged {
+ add {
+ base.TextChanged += value;
+ }
+
+ remove {
+ base.TextChanged -= value;
+ }
+ }
+ #endregion // public events
+
+ #region protected properties
+
+ // not sure why we're overriding this one
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ // specify the default size for this control
+ protected override Size DefaultSize {
+ get {
+ // todo actually measure this properly
+ return new Size (200, PreferredHeight);
+ }
+ }
+
+ #endregion // protected properties
+
+ #region protected methods
+
+ // not sure why we're overriding this one
+ protected override AccessibleObject CreateAccessibilityInstance () {
+ return base.CreateAccessibilityInstance ();
+ }
+
+ // not sure why we're overriding this one
+ protected override void CreateHandle () {
+ base.CreateHandle ();
+ }
+
+ // not sure why we're overriding this one
+ protected override void DestroyHandle () {
+ base.DestroyHandle ();
+ }
+
+ // find out if this key is an input key for us, depends on which date part is focused
+ protected override bool IsInputKey (Keys keyData) {
+ switch (keyData)
+ {
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.Left:
+ case Keys.Right:
+ return true;
+ }
+ return false;
+ }
+
+ // raises the CloseUp event
+ protected virtual void OnCloseUp (EventArgs eventargs) {
+ EventHandler eh = (EventHandler)(Events [CloseUpEvent]);
+ if (eh != null)
+ eh (this, eventargs);
+ }
+
+ // raise the drop down event
+ protected virtual void OnDropDown (EventArgs eventargs) {
+ EventHandler eh = (EventHandler)(Events [DropDownEvent]);
+ if (eh != null)
+ eh (this, eventargs);
+ }
+
+ protected override void OnFontChanged(EventArgs e) {
+ // FIXME - do we need to update/invalidate/recalc our stuff?
+ month_calendar.Font = Font;
+ Size = new Size (Size.Width, PreferredHeight);
+
+ base.OnFontChanged (e);
+ }
+
+ // raises the format changed event
+ protected virtual void OnFormatChanged (EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [FormatChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated(e);
+ }
+ protected override void OnHandleDestroyed (EventArgs e) {
+ base.OnHandleDestroyed(e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e) {
+ EventHandler eh = (EventHandler) Events [RightToLeftLayoutChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ // not sure why we're overriding this one
+ protected override void OnSystemColorsChanged (EventArgs e) {
+ base.OnSystemColorsChanged (e);
+ }
+
+ // raise the ValueChanged event
+ protected virtual void OnValueChanged (EventArgs eventargs) {
+ EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
+ if (eh != null)
+ eh (this, eventargs);
+ }
+
+ // SetBoundsCore was removed from the 2.0 public API, so
+ // I had to do this hack instead. :/
+ internal override int OverrideHeight (int height)
+ {
+ return DefaultSize.Height;
+ }
+
+ // not sure why we're overriding this
+ protected override void WndProc (ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ #endregion // protected methods
+
+ #region internal / private properties
+
+ // this is the region that the date and the check box is drawn on
+ internal Rectangle date_area_rect {
+ get {
+ return ThemeEngine.Current.DateTimePickerGetDateArea (this);
+ }
+ }
+
+ internal Rectangle CheckBoxRect {
+ get {
+ Rectangle retval = new Rectangle (check_box_space, ClientSize.Height / 2 - check_box_size / 2,
+ check_box_size, check_box_size);
+ return retval;
+ }
+ }
+
+ // the rectangle for the drop down arrow
+ internal Rectangle drop_down_arrow_rect {
+ get {
+ return ThemeEngine.Current.DateTimePickerGetDropDownButtonArea (this);
+ }
+ }
+
+ // the part of the date that is currently hilighted
+ internal Rectangle hilight_date_area {
+ get {
+ // TODO: put hilighted part calculation in here
+ return Rectangle.Empty;
+ }
+ }
+
+ internal bool DropDownButtonEntered {
+ get { return drop_down_button_entered; }
+ }
+
+ #endregion
+
+ #region internal / private methods
+
+ private void ResizeHandler (object sender, EventArgs e)
+ {
+ Invalidate ();
+ }
+
+ private void UpDownTimerTick (object sender, EventArgs e)
+ {
+ if (updown_timer.Interval == initial_timer_delay)
+ updown_timer.Interval = subsequent_timer_delay;
+
+ if (is_down_pressed)
+ IncrementSelectedPart (-1);
+ else if (is_up_pressed)
+ IncrementSelectedPart (1);
+ else
+ updown_timer.Enabled = false;
+ }
+
+ // calculates the maximum width
+ internal Single CalculateMaxWidth(string format, Graphics gr, StringFormat string_format)
+ {
+ SizeF size;
+ float result = 0;
+ string text;
+ Font font = this.Font;
+
+ switch (format)
+ {
+ case "M":
+ case "MM":
+ case "MMM":
+ case "MMMM":
+ for (int i = 1; i <= 12; i++) {
+ text = PartData.GetText (Value.AddMonths (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "d":
+ case "dd":
+ case "ddd":
+ case "dddd":
+ for (int i = 1; i <= 12; i++) {
+ text = PartData.GetText (Value.AddDays (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "h":
+ case "hh":
+ for (int i = 1; i <= 12; i++) {
+ text = PartData.GetText (Value.AddHours (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "H":
+ case "HH":
+ for (int i = 1; i <= 24; i++) {
+ text = PartData.GetText (Value.AddDays (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "m":
+ case "mm":
+ for (int i = 1; i <= 60; i++) {
+ text = PartData.GetText (Value.AddMinutes (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "s":
+ case "ss":
+ for (int i = 1; i <= 60; i++) {
+ text = PartData.GetText (Value.AddSeconds (i), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "t":
+ case "tt":
+ for (int i = 1; i <= 2; i++) {
+ text = PartData.GetText (Value.AddHours (i * 12), format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ }
+ return result;
+ case "y":
+ case "yy":
+ case "yyyy":
+ // Actually all the allowed year values are between MinDateTime and MaxDateTime,
+ // which are 4 digits always
+ text = PartData.GetText (Value, format);
+ size = gr.MeasureString (text, font, int.MaxValue, string_format);
+ result = Math.Max (result, size.Width);
+ return result;
+ default:
+ return gr.MeasureString (format, font, int.MaxValue, string_format).Width;
+ }
+ }
+
+ // returns the format of the date as a string
+ // (i.e. resolves the Format enum values to it's corresponding string format)
+ // Why CurrentCulture and not CurrentUICulture is explained here:
+ // http://blogs.msdn.com/michkap/archive/2007/01/11/1449754.aspx
+ private string GetExactFormat()
+ {
+ switch (this.format) {
+ case DateTimePickerFormat.Long:
+ return Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern;
+ case DateTimePickerFormat.Short:
+ return Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
+ case DateTimePickerFormat.Time:
+ return Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongTimePattern;
+ case DateTimePickerFormat.Custom:
+ return this.custom_format == null ? String.Empty : this.custom_format;
+ default:
+ return Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern;
+ }
+ }
+
+ private void CalculateFormats()
+ {
+ string real_format;
+ System.Text.StringBuilder literal = new System.Text.StringBuilder ();
+ System.Collections.ArrayList formats = new ArrayList ();
+ bool is_literal = false;
+ char lastch = (char) 0;
+ char ch;
+
+ real_format = GetExactFormat ();
+
+ // parse the format string
+ for (int i = 0; i < real_format.Length; i++)
+ {
+ ch = real_format [i];
+
+ if (is_literal && ch != '\'')
+ {
+ literal.Append (ch);
+ continue;
+ }
+
+ switch (ch)
+ {
+ case 't':
+ case 'd':
+ case 'h':
+ case 'H':
+ case 'm':
+ case 'M':
+ case 's':
+ case 'y':
+ case 'g': // Spec says nothing about g, but it seems to be treated like spaces.
+ if (!(lastch == ch || lastch == 0) && literal.Length != 0)
+ {
+ formats.Add (new PartData(literal.ToString (), false, this));
+ literal.Length = 0;
+ }
+ literal.Append (ch);
+ break;
+ case '\'':
+ if (is_literal && i < real_format.Length - 1 && real_format [i + 1] == '\'') {
+ literal.Append (ch);
+ i++;
+ break;
+ }
+ if (literal.Length == 0) {
+ is_literal = !is_literal;
+ break;
+ }
+ formats.Add (new PartData (literal.ToString (), is_literal, this));
+ literal.Length = 0;
+ is_literal = !is_literal;
+ break;
+ default:
+ if (literal.Length != 0)
+ {
+ formats.Add (new PartData(literal.ToString (), false, this));
+ literal.Length = 0;
+ }
+ formats.Add (new PartData (ch.ToString(), true, this));
+ break;
+
+ }
+ lastch = ch;
+ }
+ if (literal.Length >= 0)
+ formats.Add (new PartData (literal.ToString (), is_literal, this));
+
+ part_data = new PartData [formats.Count];
+ formats.CopyTo (part_data);
+ }
+
+ private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
+ {
+ // default bottom left
+ Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
+ // now adjust the alignment
+ if (!align_left) {
+ location.X = parent_control_rect.Right - child_size.Width;
+ }
+
+ Point screen_location = PointToScreen (location);
+ Rectangle working_area = Screen.FromControl(this).WorkingArea;
+ // now adjust if off the right side of the screen
+ if (screen_location.X < working_area.X) {
+ screen_location.X = working_area.X;
+ }
+ // now adjust if it should be displayed above control
+ if (screen_location.Y + child_size.Height > working_area.Bottom) {
+ screen_location.Y -= (parent_control_rect.Height + child_size.Height);
+ }
+
+ // since the parent of the month calendar is the form, adjust accordingly.
+ if (month_calendar.Parent != null) {
+ screen_location = month_calendar.Parent.PointToClient(screen_location);
+ }
+
+ return screen_location;
+ }
+
+ // actually draw this control
+ internal void Draw (Rectangle clip_rect, Graphics dc)
+ {
+ ThemeEngine.Current.DrawDateTimePicker (dc, clip_rect, this);
+ }
+
+ // drop the calendar down
+ internal void DropDownMonthCalendar ()
+ {
+ EndDateEdit (true);
+ // ensure the right date is set for the month_calendar
+ month_calendar.SetDate (this.date_value);
+ // get a rectangle that has the dimensions of the text area,
+ // but the height of the dtp control.
+ Rectangle align_area = this.date_area_rect;
+ align_area.Y = this.ClientRectangle.Y;
+ align_area.Height = this.ClientRectangle.Height;
+
+ // establish the month calendar's location
+ month_calendar.Location = CalculateDropDownLocation (
+ align_area,
+ month_calendar.Size,
+ (this.DropDownAlign == LeftRightAlignment.Left));
+ month_calendar.Show ();
+ month_calendar.Focus ();
+ month_calendar.Capture = true;
+
+ // fire any registered events
+ // XXX should this just call OnDropDown?
+ EventHandler eh = (EventHandler)(Events [DropDownEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ // hide the month calendar
+ internal void HideMonthCalendar ()
+ {
+ this.is_drop_down_visible = false;
+ Invalidate (drop_down_arrow_rect);
+ month_calendar.Capture = false;
+ if (month_calendar.Visible) {
+ month_calendar.Hide ();
+ }
+ Focus ();
+ }
+
+ private int GetSelectedPartIndex()
+ {
+ for (int i = 0; i < part_data.Length; i++)
+ {
+ if (part_data[i].Selected && !part_data[i].is_literal)
+ return i;
+ }
+ return -1;
+ }
+
+ internal void IncrementSelectedPart(int delta)
+ {
+ int selected_index = GetSelectedPartIndex();
+
+ if (selected_index == -1) {
+ return;
+ }
+
+ EndDateEdit (false);
+
+ DateTimePart dt_part = part_data [selected_index].date_time_part;
+ switch (dt_part)
+ {
+ case DateTimePart.Day:
+ if (delta < 0) {
+ if (Value.Day == 1)
+ SetPart(DateTime.DaysInMonth(Value.Year, Value.Month), dt_part);
+ else
+ SetPart(Value.Day + delta, dt_part);
+ } else {
+ if (Value.Day == DateTime.DaysInMonth(Value.Year, Value.Month))
+ SetPart(1, dt_part);
+ else
+ SetPart(Value.Day + delta, dt_part) ;
+ }
+ break;
+ case DateTimePart.DayName:
+ Value = Value.AddDays(delta);
+ break;
+ case DateTimePart.AMPMHour:
+ case DateTimePart.Hour:
+ SetPart(Value.Hour + delta, dt_part);
+ break;
+ case DateTimePart.Minutes:
+ SetPart(Value.Minute + delta, dt_part);
+ break;
+ case DateTimePart.Month:
+ SetPart (Value.Month + delta, dt_part, true);
+ break;
+ case DateTimePart.Seconds:
+ SetPart(Value.Second + delta, dt_part);
+ break;
+ case DateTimePart.AMPMSpecifier:
+ int hour = Value.Hour;
+ hour = hour >= 0 && hour <= 11 ? hour + 12 : hour - 12;
+ SetPart (hour, DateTimePart.Hour);
+ break;
+ case DateTimePart.Year:
+ SetPart(Value.Year + delta, dt_part);
+ break;
+ }
+ }
+
+ internal void SelectPart (int index)
+ {
+ is_checkbox_selected = false;
+ for (int i = 0; i < part_data.Length; i++)
+ {
+ part_data[i].Selected = (i == index);
+ }
+
+ Invalidate ();
+ OnUIASelectionChanged ();
+ }
+
+ internal void SelectNextPart()
+ {
+ int selected_index;
+ if (is_checkbox_selected) {
+ for (int i = 0; i < part_data.Length; i++)
+ {
+ if (!part_data[i].is_literal)
+ {
+ is_checkbox_selected = false;
+ part_data[i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ } else {
+ selected_index = GetSelectedPartIndex();
+ if (selected_index >= 0)
+ part_data [selected_index].Selected = false;
+
+ for (int i = selected_index + 1; i < part_data.Length; i++)
+ {
+ if (!part_data[i].is_literal)
+ {
+ part_data [i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ if (GetSelectedPartIndex() == -1)
+ { // if no part was found before the end, look from the beginning
+ if (ShowCheckBox)
+ {
+ is_checkbox_selected = true;
+ Invalidate();
+ }
+ else
+ {
+ for (int i = 0; i <= selected_index; i++)
+ {
+ if (!part_data[i].is_literal)
+ {
+ part_data[i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ OnUIASelectionChanged ();
+ }
+
+ internal void SelectPreviousPart()
+ {
+ if (is_checkbox_selected)
+ {
+ for (int i = part_data.Length - 1; i >= 0; i--)
+ {
+ if (!part_data[i].is_literal)
+ {
+ is_checkbox_selected = false;
+ part_data[i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ }
+ else
+ {
+ int selected_index = GetSelectedPartIndex();
+
+ if (selected_index >= 0)
+ part_data[selected_index].Selected = false;
+
+ for (int i = selected_index - 1; i >= 0; i--)
+ {
+ if (!part_data[i].is_literal)
+ {
+ part_data[i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ if (GetSelectedPartIndex() == -1)
+ { // if no part was found before the beginning, look from the end
+ if (ShowCheckBox)
+ {
+ is_checkbox_selected = true;
+ Invalidate();
+ }
+ else
+ {
+ for (int i = part_data.Length - 1; i >= selected_index; i--)
+ {
+ if (!part_data[i].is_literal)
+ {
+ part_data[i].Selected = true;
+ Invalidate();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ OnUIASelectionChanged ();
+ }
+
+ // raised by key down events.
+ private void KeyDownHandler(object sender, KeyEventArgs e)
+ {
+ switch (e.KeyCode)
+ {
+ case Keys.Add:
+ case Keys.Up:
+ {
+ if (ShowCheckBox && Checked == false)
+ break;
+ IncrementSelectedPart(1);
+ e.Handled = true;
+ break;
+ }
+ case Keys.Subtract:
+ case Keys.Down:
+ {
+ if (ShowCheckBox && Checked == false)
+ break;
+ IncrementSelectedPart(-1);
+ e.Handled = true;
+ break;
+ }
+ case Keys.Left:
+ {// select the next part to the left
+ if (ShowCheckBox && Checked == false)
+ break;
+ SelectPreviousPart();
+ e.Handled = true;
+ break;
+ }
+ case Keys.Right:
+ {// select the next part to the right
+ if (ShowCheckBox && Checked == false)
+ break;
+ SelectNextPart();
+ e.Handled = true;
+ break;
+ }
+ case Keys.F4:
+ if (!e.Alt && !is_drop_down_visible) {
+ DropDownMonthCalendar ();
+ e.Handled = true;
+ }
+
+ break;
+ }
+ }
+
+ // raised by any key down events
+ private void KeyPressHandler (object sender, KeyPressEventArgs e)
+ {
+ switch (e.KeyChar) {
+ case ' ':
+ if (show_check_box && is_checkbox_selected)
+ Checked = !Checked;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ int number = e.KeyChar - (int) '0';
+ int selected_index = GetSelectedPartIndex();
+ if (selected_index == -1)
+ break;
+ if (!part_data[selected_index].is_numeric_format)
+ break;
+
+ DateTimePart dt_part = part_data [selected_index].date_time_part;
+ if (editing_part_index < 0) {
+ editing_part_index = selected_index;
+ editing_number = 0;
+ editing_text = String.Empty;
+ }
+
+ editing_text += number.ToString ();
+ int date_part_max_length = 0;
+
+ switch (dt_part) {
+ case DateTimePart.Day:
+ case DateTimePart.Month:
+ case DateTimePart.Seconds:
+ case DateTimePart.Minutes:
+ case DateTimePart.AMPMHour:
+ case DateTimePart.Hour:
+ date_part_max_length = 2;
+ break;
+ case DateTimePart.Year:
+ date_part_max_length = 4;
+ break;
+ }
+
+ editing_number = editing_number * 10 + number;
+ if (editing_text.Length >= date_part_max_length)
+ EndDateEdit (false);
+
+ Invalidate (date_area_rect);
+ break;
+ default:
+ break;
+ }
+ e.Handled = true;
+ }
+
+ private void EndDateEdit (bool invalidate)
+ {
+ if (editing_part_index == -1)
+ return;
+
+ PartData part = part_data [editing_part_index];
+ if (part.date_time_part == DateTimePart.Year) { // Special case
+ // Infer, like .Net does
+ if (editing_number > 0 && editing_number < 30)
+ editing_number += 2000;
+ else if (editing_number >= 30 && editing_number < 100)
+ editing_number += 1900;
+ }
+
+ SetPart (editing_number, part.date_time_part);
+ editing_part_index = editing_number = -1;
+ editing_text = null;
+
+ if (invalidate)
+ Invalidate (date_area_rect);
+ }
+
+ internal void SetPart (int value, DateTimePart dt_part)
+ {
+ SetPart (value, dt_part, false);
+ }
+
+ // set the specified part of the date to the specified value
+ internal void SetPart (int value, DateTimePart dt_part, bool adjust)
+ {
+ switch (dt_part)
+ {
+ case DateTimePart.Seconds:
+ if (value == -1)
+ value = 59;
+ if (value >= 0 && value <= 59)
+ Value = new DateTime(Value.Year, Value.Month, Value.Day, Value.Hour, Value.Minute, value, Value.Millisecond);
+ break;
+ case DateTimePart.Minutes:
+ if (value == -1)
+ value = 59;
+ if (value >= 0 && value <= 59)
+ Value = new DateTime(Value.Year, Value.Month, Value.Day, Value.Hour, value, Value.Second, Value.Millisecond);
+ break;
+ case DateTimePart.AMPMHour:
+ if (value == -1)
+ value = 23;
+ if (value >= 0 && value <= 23) {
+ int prev_hour = Value.Hour;
+ if ((prev_hour >= 12 && prev_hour <= 23) && value < 12) // Adjust to p.m.
+ value += 12;
+ Value = new DateTime (Value.Year, Value.Month, Value.Day, value, Value.Minute, Value.Second, Value.Millisecond);
+ }
+ break;
+ case DateTimePart.Hour:
+ if (value == -1)
+ value = 23;
+ if (value >= 0 && value <= 23)
+ Value = new DateTime(Value.Year, Value.Month, Value.Day, value, Value.Minute, Value.Second, Value.Millisecond);
+ break;
+ case DateTimePart.Day:
+ int max_days = DateTime.DaysInMonth(Value.Year, Value.Month);
+ if (value >= 1 && value <= 31 && value <= max_days)
+ Value = new DateTime(Value.Year, Value.Month, value, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
+ break;
+ case DateTimePart.Month:
+ DateTime date = Value;
+
+ if (adjust) {
+ if (value == 0) {
+ date = date.AddYears (-1);
+ value = 12;
+ } else if (value == 13) {
+ date = date.AddYears (1);
+ value = 1;
+ }
+ }
+
+ if (value >= 1 && value <= 12) {
+ // if we move from say december to november with days on 31, we must
+ // remap to the maximum number of days
+ int days_in_new_month = DateTime.DaysInMonth (date.Year, value);
+
+ if (date.Day > days_in_new_month)
+ Value = new DateTime (date.Year, value, days_in_new_month, date.Hour, date.Minute, date.Second, date.Millisecond);
+ else
+ Value = new DateTime (date.Year, value, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
+ }
+ break;
+ case DateTimePart.Year:
+ if (value >= min_date.Year && value <= max_date.Year) {
+ // if we move to a leap year, the days in month could throw an exception
+ int days_in_new_month = DateTime.DaysInMonth (value, Value.Month);
+
+ if (Value.Day > days_in_new_month)
+ Value = new DateTime (value, Value.Month, days_in_new_month, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
+ else
+ Value = new DateTime (value, Value.Month, Value.Day, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
+ }
+ break;
+ }
+ }
+
+ private void GotFocusHandler (object sender, EventArgs e)
+ {
+ if (ShowCheckBox) {
+ is_checkbox_selected = true;
+ Invalidate (CheckBoxRect);
+ OnUIASelectionChanged ();
+ }
+ }
+
+ // if we loose focus deselect any selected parts.
+ private void LostFocusHandler (object sender, EventArgs e)
+ {
+ int selected_index = GetSelectedPartIndex ();
+ if (selected_index != -1)
+ {
+ part_data [selected_index].Selected = false;
+ Rectangle invalidate_rect = Rectangle.Ceiling (part_data [selected_index].drawing_rectangle);
+ invalidate_rect.Inflate (2, 2);
+ Invalidate (invalidate_rect);
+ OnUIASelectionChanged ();
+ }
+ else if (is_checkbox_selected)
+ {
+ is_checkbox_selected = false;
+ Invalidate (CheckBoxRect);
+ OnUIASelectionChanged ();
+ }
+ }
+
+ // if month calendar looses focus and the drop down is up, then close it
+ private void MonthCalendarLostFocusHandler(object sender, EventArgs e)
+ {
+ if (is_drop_down_visible && !month_calendar.Focused)
+ {
+ //this.HideMonthCalendar();
+ //This is handled from the monthcalender itself,
+ //it may loose focus, but still has to be visible,
+ //for instance when the context menu is displayed.
+ }
+
+ }
+
+ private void MonthCalendarDateChangedHandler (object sender, DateRangeEventArgs e)
+ {
+ if (month_calendar.Visible)
+ this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
+ }
+
+ // fired when a user clicks on the month calendar to select a date
+ private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
+ {
+ this.HideMonthCalendar ();
+ }
+
+ private void MouseUpHandler(object sender, MouseEventArgs e)
+ {
+ if (ShowUpDown)
+ {
+ if (is_up_pressed || is_down_pressed)
+ {
+ updown_timer.Enabled = false;
+ is_up_pressed = false;
+ is_down_pressed = false;
+ Invalidate (drop_down_arrow_rect);
+ }
+ }
+ }
+
+ // to check if the mouse has come down on this control
+ private void MouseDownHandler (object sender, MouseEventArgs e)
+ {
+ // Only left clicks are handled.
+ if (e.Button != MouseButtons.Left)
+ return;
+
+ if (ShowCheckBox && CheckBoxRect.Contains(e.X, e.Y))
+ {
+ is_checkbox_selected = true;
+ Checked = !Checked;
+ OnUIASelectionChanged ();
+ return;
+ }
+
+ // Deselect the checkbox only if the pointer is not on it
+ // *and* the other parts are enabled (Checked as true)
+ if (Checked) {
+ is_checkbox_selected = false;
+ OnUIASelectionChanged ();
+ }
+
+ if (ShowUpDown && drop_down_arrow_rect.Contains (e.X, e.Y))
+ {
+ if (!(ShowCheckBox && Checked == false))
+ {
+ if (e.Y < this.Height / 2) {
+ is_up_pressed = true;
+ is_down_pressed = false;
+ IncrementSelectedPart (1);
+ } else {
+ is_up_pressed = false;
+ is_down_pressed = true;
+ IncrementSelectedPart (-1);
+ }
+ Invalidate (drop_down_arrow_rect);
+ updown_timer.Interval = initial_timer_delay;
+ updown_timer.Enabled = true;
+ }
+ } else if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
+ DropDownButtonClicked ();
+ } else {
+ // mouse down on this control anywhere else collapses it
+ if (is_drop_down_visible) {
+ HideMonthCalendar ();
+ }
+ if (!(ShowCheckBox && Checked == false))
+ {
+ // go through the parts to see if the click is in any of them
+ bool invalidate_afterwards = false;
+ for (int i = 0; i < part_data.Length; i++) {
+ bool old = part_data [i].Selected;
+
+ if (part_data [i].is_literal)
+ continue;
+
+ if (part_data [i].drawing_rectangle.Contains (e.X, e.Y)) {
+ part_data [i].Selected = true;
+ } else
+ part_data [i].Selected = false;
+
+ if (old != part_data [i].Selected)
+ invalidate_afterwards = true;
+ }
+ if (invalidate_afterwards) {
+ Invalidate ();
+ OnUIASelectionChanged ();
+ }
+ }
+
+ }
+ }
+
+ internal void DropDownButtonClicked ()
+ {
+ if (!is_drop_down_visible) {
+ is_drop_down_visible = true;
+ if (!Checked)
+ Checked = true;
+ Invalidate (drop_down_arrow_rect);
+ DropDownMonthCalendar ();
+ } else {
+ HideMonthCalendar ();
+ }
+ }
+
+ // paint this control now
+ private void PaintHandler (object sender, PaintEventArgs pe) {
+ if (Width <= 0 || Height <= 0 || Visible == false)
+ return;
+
+ Draw (pe.ClipRectangle, pe.Graphics);
+ }
+
+ void OnMouseEnter (object sender, EventArgs e)
+ {
+ if (ThemeEngine.Current.DateTimePickerBorderHasHotElementStyle)
+ Invalidate ();
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ drop_down_button_entered = false;
+ if (ThemeEngine.Current.DateTimePickerBorderHasHotElementStyle)
+ Invalidate ();
+ }
+
+ void OnMouseMove (object sender, MouseEventArgs e)
+ {
+ if (!is_drop_down_visible &&
+ ThemeEngine.Current.DateTimePickerDropDownButtonHasHotElementStyle &&
+ drop_down_button_entered != drop_down_arrow_rect.Contains (e.Location)) {
+ drop_down_button_entered = !drop_down_button_entered;
+ Invalidate (drop_down_arrow_rect);
+ }
+ }
+ #endregion
+
+ #region internal classes
+ internal enum DateTimePart {
+ Seconds,
+ Minutes,
+ AMPMHour,
+ Hour,
+ Day,
+ DayName,
+ Month,
+ Year,
+ AMPMSpecifier,
+ Literal
+ }
+
+ internal class PartData
+ {
+ internal string value;
+ internal bool is_literal;
+ bool is_selected;
+ internal RectangleF drawing_rectangle;
+ internal DateTimePart date_time_part;
+ DateTimePicker owner;
+
+ internal bool is_numeric_format
+ {
+ get
+ {
+ if (is_literal)
+ return false;
+ switch (value) {
+ case "m":
+ case "mm":
+ case "d":
+ case "dd":
+ case "h":
+ case "hh":
+ case "H":
+ case "HH":
+ case "M":
+ case "MM":
+ case "s":
+ case "ss":
+ case "y":
+ case "yy":
+ case "yyyy":
+ return true;
+ case "ddd":
+ case "dddd":
+ return false;
+ default:
+ return false;
+ }
+ }
+ }
+
+ internal PartData(string value, bool is_literal, DateTimePicker owner)
+ {
+ this.value = value;
+ this.is_literal = is_literal;
+ this.owner = owner;
+ date_time_part = GetDateTimePart (value);
+ }
+
+ internal bool Selected {
+ get {
+ return is_selected;
+ }
+ set {
+ if (value == is_selected)
+ return;
+
+ owner.EndDateEdit (false);
+ is_selected = value;
+ }
+ }
+
+ // calculate the string to show for this data
+ internal string GetText(DateTime date)
+ {
+ if (is_literal) {
+ return value;
+ } else {
+ return GetText (date, value);
+ }
+ }
+
+ static DateTimePart GetDateTimePart (string value)
+ {
+ switch (value) {
+ case "s":
+ case "ss":
+ return DateTimePart.Seconds;
+ case "m":
+ case "mm":
+ return DateTimePart.Minutes;
+ case "h":
+ case "hh":
+ return DateTimePart.AMPMHour;
+ case "H":
+ case "HH":
+ return DateTimePart.Hour;
+ case "d":
+ case "dd":
+ return DateTimePart.Day;
+ case "ddd":
+ case "dddd":
+ return DateTimePart.DayName;
+ case "M":
+ case "MM":
+ case "MMMM":
+ return DateTimePart.Month;
+ case "y":
+ case "yy":
+ case "yyy":
+ case "yyyy":
+ return DateTimePart.Year;
+ case "t":
+ case "tt":
+ return DateTimePart.AMPMSpecifier;
+ }
+
+ return DateTimePart.Literal;
+ }
+
+ static internal string GetText(DateTime date, string format)
+ {
+ if (format.StartsWith ("g"))
+ return " ";
+ else if (format.Length == 1)
+ return date.ToString ("%" + format);
+ else if (format == "yyyyy" || format == "yyyyyy" || format == "yyyyyyy" || format == "yyyyyyyy")
+ return date.ToString ("yyyy");
+ else if (format.Length > 1)
+ return date.ToString (format);
+ else
+ return string.Empty;
+ }
+ }
+
+ #endregion
+
+ #region UIA Framework: Methods, Properties and Events
+
+ internal bool UIAIsCheckBoxSelected {
+ get { return is_checkbox_selected; }
+ }
+
+ static object UIAMinimumChangedEvent = new object ();
+ static object UIAMaximumChangedEvent = new object ();
+ static object UIASelectionChangedEvent = new object ();
+ static object UIACheckedEvent = new object ();
+ static object UIAShowCheckBoxChangedEvent = new object ();
+ static object UIAShowUpDownChangedEvent = new object ();
+
+ internal event EventHandler UIAMinimumChanged {
+ add { Events.AddHandler (UIAMinimumChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAMinimumChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAMaximumChanged {
+ add { Events.AddHandler (UIAMinimumChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAMinimumChangedEvent, value); }
+ }
+
+ internal event EventHandler UIASelectionChanged {
+ add { Events.AddHandler (UIASelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (UIASelectionChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAChecked {
+ add { Events.AddHandler (UIACheckedEvent, value); }
+ remove { Events.RemoveHandler (UIACheckedEvent, value); }
+ }
+
+ internal event EventHandler UIAShowCheckBoxChanged {
+ add { Events.AddHandler (UIAShowCheckBoxChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAShowCheckBoxChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAShowUpDownChanged {
+ add { Events.AddHandler (UIAShowUpDownChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAShowUpDownChangedEvent, value); }
+ }
+
+ internal void OnUIAMinimumChanged ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIAMinimumChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIAMaximumChanged ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIAMaximumChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIASelectionChanged ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIASelectionChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIAChecked ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIACheckedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIAShowCheckBoxChanged ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIAShowCheckBoxChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIAShowUpDownChanged ()
+ {
+ EventHandler eh = (EventHandler)(Events [UIAShowUpDownChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/FlowLayoutPanel.cs b/source/ShiftUI/Widgets/FlowLayoutPanel.cs
new file mode 100644
index 0000000..680cc57
--- /dev/null
+++ b/source/ShiftUI/Widgets/FlowLayoutPanel.cs
@@ -0,0 +1,197 @@
+//
+// FlowLayoutPanel.cs
+//
+// 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) 2006 Jonathan Pobst
+//
+// Authors:
+// Jonathan Pobst ([email protected])
+//
+
+using ShiftUI.Layout;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using System;
+
+namespace ShiftUI
+{
+ [ComVisibleAttribute (true)]
+ [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
+ [ProvideProperty ("FlowBreak", typeof (Widget))]
+ [DefaultProperty ("FlowDirection")]
+ [Docking (DockingBehavior.Ask)]
+ //[Designer ("ShiftUI.Design.FlowLayoutPanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class FlowLayoutPanel : Panel, IExtenderProvider
+ {
+ private FlowLayoutSettings settings;
+
+ public FlowLayoutPanel () : base ()
+ {
+ CreateDockPadding ();
+ }
+
+ #region Properties
+ [Localizable (true)]
+ [DefaultValue (FlowDirection.LeftToRight)]
+ public FlowDirection FlowDirection {
+ get { return LayoutSettings.FlowDirection; }
+ set { LayoutSettings.FlowDirection = value; }
+ }
+
+ [LocalizableAttribute (true)]
+ [DefaultValue (true)]
+ public bool WrapContents {
+ get { return LayoutSettings.WrapContents; }
+ set { LayoutSettings.WrapContents = value; }
+ }
+
+ public override LayoutEngine LayoutEngine {
+ get { return this.LayoutSettings.LayoutEngine; }
+ }
+
+ internal FlowLayoutSettings LayoutSettings {
+ get {
+ if (this.settings == null)
+ this.settings = new FlowLayoutSettings (this);
+
+ return this.settings;
+ }
+ }
+ #endregion
+
+ #region Public Methods
+ [DefaultValue (false)]
+ [DisplayName ("FlowBreak")]
+ public bool GetFlowBreak (Widget control)
+ {
+ return LayoutSettings.GetFlowBreak (control);
+ }
+
+ [DisplayName ("FlowBreak")]
+ public void SetFlowBreak (Widget control, bool value)
+ {
+ LayoutSettings.SetFlowBreak (control, value);
+ }
+ #endregion
+
+ #region IExtenderProvider Members
+ bool IExtenderProvider.CanExtend (object obj)
+ {
+ if (obj is Widget)
+ if ((obj as Widget).Parent == this)
+ return true;
+
+ return false;
+ }
+ #endregion
+
+ #region Internal Methods
+ internal override void CalculateCanvasSize (bool canOverride)
+ {
+ if (canOverride)
+ canvas_size = ClientSize;
+ else
+ base.CalculateCanvasSize (canOverride);
+ }
+
+ protected override void OnLayout (LayoutEventArgs levent)
+ {
+ base.OnLayout (levent);
+
+ // base.OnLayout() calls CalculateCanvasSize(true) in which we just set the canvas to
+ // clientsize so we could re-layout everything according to the flow.
+ // This time we want to actually calculate the canvas.
+ CalculateCanvasSize (false);
+ if (AutoSize && (canvas_size.Width > ClientSize.Width || canvas_size.Height > ClientSize.Height)) {
+ ClientSize = canvas_size;
+ }
+ AdjustFormScrollbars (AutoScroll);
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ int width = 0;
+ int height = 0;
+ bool horizontal = FlowDirection == FlowDirection.LeftToRight || FlowDirection == FlowDirection.RightToLeft;
+ if (!WrapContents || (horizontal && proposedSize.Width == 0) || (!horizontal && proposedSize.Height == 0)) {
+ foreach (Widget control in Widgets) {
+ Size control_preferred_size;
+ if (control.AutoSize)
+ control_preferred_size = control.PreferredSize;
+ else
+ control_preferred_size = control.Size;
+ Padding control_margin = control.Margin;
+ if (horizontal) {
+ width += control_preferred_size.Width + control_margin.Horizontal;
+ height = Math.Max (height, control_preferred_size.Height + control_margin.Vertical);
+ } else {
+ height += control_preferred_size.Height + control_margin.Vertical;
+ width = Math.Max (width, control_preferred_size.Width + control_margin.Horizontal);
+ }
+ }
+ } else {
+ int size_in_flow_direction = 0;
+ int size_in_other_direction = 0;
+ int increase;
+ foreach (Widget control in Widgets) {
+ Size control_preferred_size;
+ if (control.AutoSize)
+ control_preferred_size = control.PreferredSize;
+ else
+ control_preferred_size = control.ExplicitBounds.Size;
+ Padding control_margin = control.Margin;
+ if (horizontal) {
+ increase = control_preferred_size.Width + control_margin.Horizontal;
+ if (size_in_flow_direction != 0 && size_in_flow_direction + increase >= proposedSize.Width) {
+ width = Math.Max (width, size_in_flow_direction);
+ size_in_flow_direction = 0;
+ height += size_in_other_direction;
+ size_in_other_direction = 0;
+ }
+ size_in_flow_direction += increase;
+ size_in_other_direction = Math.Max (size_in_other_direction, control_preferred_size.Height + control_margin.Vertical);
+ } else {
+ increase = control_preferred_size.Height + control_margin.Vertical;
+ if (size_in_flow_direction != 0 && size_in_flow_direction + increase >= proposedSize.Height) {
+ height = Math.Max (height, size_in_flow_direction);
+ size_in_flow_direction = 0;
+ width += size_in_other_direction;
+ size_in_other_direction = 0;
+ }
+ size_in_flow_direction += increase;
+ size_in_other_direction = Math.Max (size_in_other_direction, control_preferred_size.Width + control_margin.Horizontal);
+ }
+ }
+ if (horizontal) {
+ width = Math.Max (width, size_in_flow_direction);
+ height += size_in_other_direction;
+ } else {
+ height = Math.Max (height, size_in_flow_direction);
+ width += size_in_other_direction;
+ }
+ }
+ return new Size (width, height);
+ }
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/GridEntry.cs b/source/ShiftUI/Widgets/GridEntry.cs
new file mode 100644
index 0000000..619bb4c
--- /dev/null
+++ b/source/ShiftUI/Widgets/GridEntry.cs
@@ -0,0 +1,860 @@
+// 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-2008 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Design;
+using ShiftUI;
+using ShiftUI.Design;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Globalization;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ internal class GridEntry : GridItem, ITypeDescriptorContext
+ {
+ #region Local Variables
+ private PropertyGrid property_grid;
+ private bool expanded;
+ private GridItemCollection grid_items;
+ private GridItem parent;
+ private PropertyDescriptor[] property_descriptors;
+ private int top;
+ private Rectangle plus_minus_bounds;
+ private GridItemCollection child_griditems_cache;
+ #endregion // Local Variables
+
+ #region Contructors
+ protected GridEntry (PropertyGrid propertyGrid, GridEntry parent)
+ {
+ if (propertyGrid == null)
+ throw new ArgumentNullException ("propertyGrid");
+ property_grid = propertyGrid;
+ plus_minus_bounds = new Rectangle (0,0,0,0);
+ top = -1;
+ grid_items = new GridItemCollection ();
+ expanded = false;
+ this.parent = parent;
+ child_griditems_cache = null;
+ }
+
+ // Cannot use one PropertyDescriptor for all owners, because the
+ // propertydescriptors might have different Invokees. Check
+ // ReflectionPropertyDescriptor.GetInvokee and how it's used.
+ //
+ public GridEntry (PropertyGrid propertyGrid, PropertyDescriptor[] properties,
+ GridEntry parent) : this (propertyGrid, parent)
+ {
+ if (properties == null || properties.Length == 0)
+ throw new ArgumentNullException ("prop_desc");
+ property_descriptors = properties;
+ }
+ #endregion // Constructors
+
+
+ public override bool Expandable {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (converter == null || !converter.GetPropertiesSupported ((ITypeDescriptorContext)this))
+ return false;
+
+ if (GetChildGridItemsCached ().Count > 0)
+ return true;
+
+ return false;
+ }
+ }
+
+ public override bool Expanded {
+ get { return expanded; }
+ set {
+ if (expanded != value) {
+ expanded = value;
+ PopulateChildGridItems ();
+ if (value)
+ property_grid.OnExpandItem (this);
+ else
+ property_grid.OnCollapseItem (this);
+ }
+ }
+ }
+
+ public override GridItemCollection GridItems {
+ get {
+ PopulateChildGridItems ();
+ return grid_items;
+ }
+ }
+
+ public override GridItemType GridItemType {
+ get { return GridItemType.Property; }
+ }
+
+ public override string Label {
+ get {
+ PropertyDescriptor property = this.PropertyDescriptor;
+ if (property != null) {
+ string label = property.DisplayName;
+ ParenthesizePropertyNameAttribute parensAttr =
+ property.Attributes[typeof (ParenthesizePropertyNameAttribute)] as ParenthesizePropertyNameAttribute;
+ if (parensAttr != null && parensAttr.NeedParenthesis)
+ label = "(" + label + ")";
+ return label;
+ }
+ return String.Empty;
+ }
+ }
+
+ public override GridItem Parent {
+ get { return parent; }
+ }
+
+ public GridEntry ParentEntry {
+ get {
+ if (parent != null && parent.GridItemType == GridItemType.Category)
+ return parent.Parent as GridEntry;
+ return parent as GridEntry;
+ }
+ }
+
+ public override PropertyDescriptor PropertyDescriptor {
+ get { return property_descriptors != null ? property_descriptors[0] : null; }
+ }
+
+ public PropertyDescriptor[] PropertyDescriptors {
+ get { return property_descriptors; }
+ }
+
+ public object PropertyOwner {
+ get {
+ object[] owners = PropertyOwners;
+ if (owners != null)
+ return owners[0];
+ return null;
+ }
+ }
+
+ public object[] PropertyOwners {
+ get {
+ if (ParentEntry == null)
+ return null;
+
+ object[] owners = ParentEntry.Values;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ object newOwner = null;
+ for (int i=0; i < owners.Length; i++) {
+ if (owners[i] is ICustomTypeDescriptor) {
+ newOwner = ((ICustomTypeDescriptor)owners[i]).GetPropertyOwner (properties[i]);
+ if (newOwner != null)
+ owners[i] = newOwner;
+ }
+ }
+ return owners;
+ }
+ }
+
+ // true if the value is the same among all properties
+ public bool HasMergedValue {
+ get {
+ if (!IsMerged)
+ return false;
+
+ object[] values = this.Values;
+ for (int i=0; i+1 < values.Length; i++) {
+ if (!Object.Equals (values[i], values[i+1]))
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public virtual bool IsMerged {
+ get { return (PropertyDescriptors != null && PropertyDescriptors.Length > 1); }
+ }
+
+ // If IsMerged this will return all values for all properties in all owners
+ public virtual object[] Values {
+ get {
+ if (PropertyDescriptor == null || this.PropertyOwners == null)
+ return null;
+ if (IsMerged) {
+ object[] owners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ object[] values = new object[owners.Length];
+ for (int i=0; i < owners.Length; i++)
+ values[i] = properties[i].GetValue (owners[i]);
+ return values;
+ } else {
+ return new object[] { this.Value };
+ }
+ }
+ }
+
+ // Returns the first value for the first propertyowner and propertydescriptor
+ //
+ public override object Value {
+ get {
+ if (PropertyDescriptor == null || PropertyOwner == null)
+ return null;
+
+ return PropertyDescriptor.GetValue (PropertyOwner);
+ }
+ set
+ {
+ PropertyDescriptor.SetValue(PropertyOwner, value);
+ }
+ }
+
+ public string ValueText {
+ get {
+ string text = null;
+ try {
+ text = ConvertToString (this.Value);
+ if (text == null)
+ text = String.Empty;
+ } catch {
+ text = String.Empty;
+ }
+ return text;
+ }
+ }
+
+ public override bool Select ()
+ {
+ property_grid.SelectedGridItem = this;
+ return true;
+ }
+
+ #region ITypeDescriptorContext
+ void ITypeDescriptorContext.OnComponentChanged ()
+ {
+ }
+
+ bool ITypeDescriptorContext.OnComponentChanging ()
+ {
+ return false;
+ }
+
+ IContainer ITypeDescriptorContext.Container {
+ get {
+ if (PropertyOwner == null)
+ return null;
+
+ IComponent component = property_grid.SelectedObject as IComponent;
+ if (component != null && component.Site != null)
+ return component.Site.Container;
+ return null;
+ }
+ }
+
+ object ITypeDescriptorContext.Instance {
+ get {
+ if (ParentEntry != null && ParentEntry.PropertyOwner != null)
+ return ParentEntry.PropertyOwner;
+ return PropertyOwner;
+ }
+ }
+
+ PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
+ get {
+ if (ParentEntry != null && ParentEntry.PropertyDescriptor != null)
+ return ParentEntry.PropertyDescriptor;
+ return PropertyDescriptor;
+ }
+ }
+ #endregion
+
+ #region IServiceProvider Members
+
+ object IServiceProvider.GetService (Type serviceType) {
+ IComponent selectedComponent = property_grid.SelectedObject as IComponent;
+ if (selectedComponent != null && selectedComponent.Site != null)
+ return selectedComponent.Site.GetService (serviceType);
+ return null;
+ }
+
+ #endregion
+
+ internal int Top {
+ get { return top; }
+ set {
+ if (top != value)
+ top = value;
+ }
+ }
+
+ internal Rectangle PlusMinusBounds {
+ get { return plus_minus_bounds; }
+ set { plus_minus_bounds = value; }
+ }
+
+ public void SetParent (GridItem parent)
+ {
+ this.parent = parent;
+ }
+
+ public ICollection AcceptedValues {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor != null && converter != null &&
+ converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
+ ArrayList values = new ArrayList ();
+ string stringVal = null;
+ ICollection standardValues = converter.GetStandardValues ((ITypeDescriptorContext)this);
+ if (standardValues != null) {
+ foreach (object value in standardValues) {
+ stringVal = ConvertToString (value);
+ if (stringVal != null)
+ values.Add (stringVal);
+ }
+ }
+ return values.Count > 0 ? values : null;
+ }
+ return null;
+ }
+ }
+
+ private string ConvertToString (object value)
+ {
+ if (value is string)
+ return (string)value;
+
+ if (PropertyDescriptor != null && PropertyDescriptor.Converter != null &&
+ PropertyDescriptor.Converter.CanConvertTo ((ITypeDescriptorContext)this, typeof (string))) {
+ try {
+ return PropertyDescriptor.Converter.ConvertToString ((ITypeDescriptorContext)this, value);
+ } catch {
+ // XXX: Happens too often...
+ // property_grid.ShowError ("Property value of '" + property_descriptor.Name + "' is not convertible to string.");
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public bool HasCustomEditor {
+ get { return EditorStyle != UITypeEditorEditStyle.None; }
+ }
+
+ public UITypeEditorEditStyle EditorStyle {
+ get {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ return editor.GetEditStyle ((ITypeDescriptorContext)this);
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ return UITypeEditorEditStyle.None;
+ }
+ }
+
+ public bool EditorResizeable {
+ get {
+ if (this.EditorStyle == UITypeEditorEditStyle.DropDown) {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null && editor.IsDropDownResizable)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public bool EditValue (IWindowsFormsEditorService service)
+ {
+ if (service == null)
+ throw new ArgumentNullException ("service");
+
+ IServiceContainer parent = ((ITypeDescriptorContext)this).GetService (typeof (IServiceContainer)) as IServiceContainer;
+ ServiceContainer container = null;
+
+ if (parent != null)
+ container = new ServiceContainer (parent);
+ else
+ container = new ServiceContainer ();
+
+ container.AddService (typeof (IWindowsFormsEditorService), service);
+
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ object value = editor.EditValue ((ITypeDescriptorContext)this,
+ container,
+ this.Value);
+ string error = null;
+ return SetValue (value, out error);
+ } catch { //(Exception e) {
+ // property_grid.ShowError (e.Message + Environment.NewLine + e.StackTrace);
+ }
+ }
+ return false;
+ }
+
+ private UITypeEditor GetEditor ()
+ {
+ if (PropertyDescriptor != null) {
+ try { // can happen, because we are missing some editors
+ if (PropertyDescriptor != null)
+ return (UITypeEditor) PropertyDescriptor.GetEditor (typeof (UITypeEditor));
+ } catch {
+ // property_grid.ShowError ("Unable to load UITypeEditor for property '" + PropertyDescriptor.Name + "'.");
+ }
+ }
+ return null;
+ }
+
+ private TypeConverter GetConverter ()
+ {
+ if (PropertyDescriptor != null)
+ return PropertyDescriptor.Converter;
+ return null;
+ }
+
+ public bool ToggleValue ()
+ {
+ if (IsReadOnly || (IsMerged && !HasMergedValue))
+ return false;
+
+ bool success = false;
+ string error = null;
+ object value = this.Value;
+ if (PropertyDescriptor.PropertyType == typeof(bool))
+ success = SetValue (!(bool)value, out error);
+ else {
+ TypeConverter converter = GetConverter ();
+ if (converter != null &&
+ converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
+ TypeConverter.StandardValuesCollection values =
+ (TypeConverter.StandardValuesCollection) converter.GetStandardValues ((ITypeDescriptorContext)this);
+ if (values != null) {
+ for (int i = 0; i < values.Count; i++) {
+ if (value != null && value.Equals (values[i])){
+ if (i < values.Count-1)
+ success = SetValue (values[i+1], out error);
+ else
+ success = SetValue (values[0], out error);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!success && error != null)
+ property_grid.ShowError (error);
+ return success;
+ }
+
+ public bool SetValue (object value, out string error)
+ {
+ error = null;
+ if (this.IsReadOnly)
+ return false;
+
+ if (SetValueCore (value, out error)) {
+ InvalidateChildGridItemsCache ();
+ property_grid.OnPropertyValueChangedInternal (this, this.Value);
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual bool SetValueCore (object value, out string error)
+ {
+ error = null;
+
+ TypeConverter converter = GetConverter ();
+ Type valueType = value != null ? value.GetType () : null;
+ // if the new value is not of the same type try to convert it
+ if (valueType != null && this.PropertyDescriptor.PropertyType != null &&
+ !this.PropertyDescriptor.PropertyType.IsAssignableFrom (valueType)) {
+ bool conversionError = false;
+ try {
+ if (converter != null &&
+ converter.CanConvertFrom ((ITypeDescriptorContext)this, valueType))
+ value = converter.ConvertFrom ((ITypeDescriptorContext)this,
+ CultureInfo.CurrentCulture, value);
+ else
+ conversionError = true;
+ } catch (Exception e) {
+ error = e.Message;
+ conversionError = true;
+ }
+ if (conversionError) {
+ string valueText = ConvertToString (value);
+ string errorShortDescription = null;
+ if (valueText != null) {
+ errorShortDescription = "Property value '" + valueText + "' of '" +
+ PropertyDescriptor.Name + "' is not convertible to type '" +
+ this.PropertyDescriptor.PropertyType.Name + "'";
+
+ } else {
+ errorShortDescription = "Property value of '" +
+ PropertyDescriptor.Name + "' is not convertible to type '" +
+ this.PropertyDescriptor.PropertyType.Name + "'";
+ }
+ error = errorShortDescription + Environment.NewLine + Environment.NewLine + error;
+ return false;
+ }
+ }
+
+ bool changed = false;
+ bool current_changed = false;
+ object[] propertyOwners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ for (int i=0; i < propertyOwners.Length; i++) {
+ object currentVal = properties[i].GetValue (propertyOwners[i]);
+ current_changed = false;
+ if (!Object.Equals (currentVal, value)) {
+ if (this.ShouldCreateParentInstance) {
+ Hashtable updatedParentProperties = new Hashtable ();
+ PropertyDescriptorCollection parentProperties = TypeDescriptor.GetProperties (propertyOwners[i]);
+ foreach (PropertyDescriptor property in parentProperties) {
+ if (property.Name == properties[i].Name)
+ updatedParentProperties[property.Name] = value;
+ else
+ updatedParentProperties[property.Name] = property.GetValue (propertyOwners[i]);
+ }
+ object updatedParentValue = this.ParentEntry.PropertyDescriptor.Converter.CreateInstance (
+ (ITypeDescriptorContext)this, updatedParentProperties);
+ if (updatedParentValue != null)
+ current_changed = this.ParentEntry.SetValueCore (updatedParentValue, out error);
+ } else {
+ try {
+ properties[i].SetValue (propertyOwners[i], value);
+ } catch {
+ // MS seems to swallow this
+ //
+ // string valueText = ConvertToString (value);
+ // if (valueText != null)
+ // error = "Property value '" + valueText + "' of '" + properties[i].Name + "' is invalid.";
+ // else
+ // error = "Property value of '" + properties[i].Name + "' is invalid.";
+ return false;
+ }
+
+ if (IsValueType (this.ParentEntry))
+ current_changed = ParentEntry.SetValueCore (propertyOwners[i], out error);
+ else
+ current_changed = Object.Equals (properties[i].GetValue (propertyOwners[i]), value);
+ }
+ }
+ if (current_changed)
+ changed = true;
+ }
+ return changed;
+ }
+
+ private bool IsValueType (GridEntry item)
+ {
+ if (item != null && item.PropertyDescriptor != null &&
+ (item.PropertyDescriptor.PropertyType.IsValueType ||
+ item.PropertyDescriptor.PropertyType.IsPrimitive))
+ return true;
+ return false;
+ }
+
+ public bool ResetValue ()
+ {
+ if (IsResetable) {
+ object[] owners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ for (int i=0; i < owners.Length; i++) {
+ properties[i].ResetValue (owners[i]);
+ if (IsValueType (this.ParentEntry)) {
+ string error = null;
+ if (!ParentEntry.SetValueCore (owners[i], out error) && error != null)
+ property_grid.ShowError (error);
+ }
+ }
+ property_grid.OnPropertyValueChangedInternal (this, this.Value);
+ return true;
+ }
+ return false;
+ }
+
+ public bool HasDefaultValue {
+ get {
+ if (PropertyDescriptor != null)
+ return !PropertyDescriptor.ShouldSerializeValue (PropertyOwner);
+ return false;
+ }
+ }
+
+ // Determines if the current value can be reset
+ //
+ public virtual bool IsResetable {
+ get { return (!IsReadOnly && PropertyDescriptor.CanResetValue (PropertyOwner)); }
+
+ }
+
+ // If false the entry can be modified only by the means of a predefined values
+ // and not such inputed by the user.
+ //
+ public virtual bool IsEditable {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor == null)
+ return false;
+ else if (PropertyDescriptor.PropertyType.IsArray)
+ return false;
+ else if (PropertyDescriptor.IsReadOnly && !this.ShouldCreateParentInstance)
+ return false;
+ else if (converter == null ||
+ !converter.CanConvertFrom ((ITypeDescriptorContext)this, typeof (string)))
+ return false;
+ else if (converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
+ converter.GetStandardValuesExclusive ((ITypeDescriptorContext)this))
+ return false;
+ else
+ return true;
+ }
+ }
+
+ // If true the the entry cannot be modified at all
+ //
+ public virtual bool IsReadOnly {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor == null || PropertyOwner == null)
+ return true;
+ else if (PropertyDescriptor.IsReadOnly &&
+ (EditorStyle != UITypeEditorEditStyle.Modal || PropertyDescriptor.PropertyType.IsValueType) &&
+ !this.ShouldCreateParentInstance)
+ return true;
+ else if (PropertyDescriptor.IsReadOnly &&
+ TypeDescriptor.GetAttributes (PropertyDescriptor.PropertyType)
+ [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes))
+ return true;
+ else if (ShouldCreateParentInstance && ParentEntry.IsReadOnly)
+ return true;
+ else if (!HasCustomEditor && converter == null)
+ return true;
+ else if (converter != null &&
+ !converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
+ !converter.CanConvertFrom ((ITypeDescriptorContext)this,
+ typeof (string)) &&
+ !HasCustomEditor) {
+ return true;
+ } else if (PropertyDescriptor.PropertyType.IsArray && !HasCustomEditor)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ public bool IsPassword {
+ get {
+ if (PropertyDescriptor != null)
+ return PropertyDescriptor.Attributes.Contains (PasswordPropertyTextAttribute.Yes);
+ return false;
+ }
+ }
+ // This is a way to set readonly properties (e.g properties without a setter).
+ // The way it works is that if CreateInstance is supported by the parent's converter
+ // it gets passed a list of properties and their values which it uses to create an
+ // instance (e.g by passing them to the ctor of that object type).
+ //
+ // This is used for e.g Font
+ //
+ public virtual bool ShouldCreateParentInstance {
+ get {
+ if (this.ParentEntry != null && ParentEntry.PropertyDescriptor != null) {
+ TypeConverter parentConverter = ParentEntry.GetConverter ();
+ if (parentConverter != null && parentConverter.GetCreateInstanceSupported ((ITypeDescriptorContext)this))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public virtual bool PaintValueSupported {
+ get {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ return editor.GetPaintValueSupported ();
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ return false;
+ }
+ }
+
+ public virtual void PaintValue (Graphics gfx, Rectangle rect)
+ {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ editor.PaintValue (this.Value, gfx, rect);
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ }
+
+#region Population
+ protected void PopulateChildGridItems ()
+ {
+ grid_items = GetChildGridItemsCached ();
+ }
+
+ private void InvalidateChildGridItemsCache ()
+ {
+ if (child_griditems_cache != null) {
+ child_griditems_cache = null;
+ PopulateChildGridItems ();
+ }
+ }
+
+ private GridItemCollection GetChildGridItemsCached ()
+ {
+ if (child_griditems_cache == null) {
+ child_griditems_cache = GetChildGridItems ();
+ // foreach (GridEntry item in child_griditems_cache)
+ // PrintDebugInfo (item);
+ }
+
+ return child_griditems_cache;
+ }
+
+ // private static void PrintDebugInfo (GridEntry item)
+ // {
+ // if (item.PropertyDescriptor != null) {
+ // Console.WriteLine ("=== [" + item.PropertyDescriptor.Name + "] ===");
+ // try {
+ // TypeConverter converter = item.GetConverter ();
+ // Console.WriteLine ("IsReadOnly: " + item.IsReadOnly);
+ // Console.WriteLine ("IsEditable: " + item.IsEditable);
+ // Console.WriteLine ("PropertyDescriptor.IsReadOnly: " + item.PropertyDescriptor.IsReadOnly);
+ // if (item.ParentEntry != null)
+ // Console.WriteLine ("ParentEntry.IsReadOnly: " + item.ParentEntry.IsReadOnly);
+ // Console.WriteLine ("ImmutableObjectAttribute.Yes: " + TypeDescriptor.GetAttributes (item.PropertyDescriptor.PropertyType)
+ // [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes));
+ // UITypeEditor editor = item.GetEditor ();
+ // Console.WriteLine ("Editor: " + (editor == null ? "none" : editor.GetType ().Name));
+ // if (editor != null)
+ // Console.WriteLine ("Editor.EditorStyle: " + editor.GetEditStyle ((ITypeDescriptorContext)item));
+ // Console.WriteLine ("Converter: " + (converter == null ? "none" : converter.GetType ().Name));
+ // if (converter != null) {
+ // Console.WriteLine ("Converter.GetStandardValuesSupported: " + converter.GetStandardValuesSupported ((ITypeDescriptorContext)item).ToString ());
+ // Console.WriteLine ("Converter.GetStandardValuesExclusive: " + converter.GetStandardValuesExclusive ((ITypeDescriptorContext)item).ToString ());
+ // Console.WriteLine ("ShouldCreateParentInstance: " + item.ShouldCreateParentInstance);
+ // Console.WriteLine ("CanConvertFrom (string): " + converter.CanConvertFrom ((ITypeDescriptorContext)item, typeof (string)));
+ // }
+ // Console.WriteLine ("IsArray: " + item.PropertyDescriptor.PropertyType.IsArray.ToString ());
+ // } catch { /* Some converters and editor throw NotImplementedExceptions */ }
+ // }
+ // }
+
+ private GridItemCollection GetChildGridItems ()
+ {
+ object[] propertyOwners = this.Values;
+ string[] propertyNames = GetMergedPropertyNames (propertyOwners);
+ GridItemCollection items = new GridItemCollection ();
+
+ foreach (string propertyName in propertyNames) {
+ PropertyDescriptor[] properties = new PropertyDescriptor[propertyOwners.Length];
+ for (int i=0; i < propertyOwners.Length; i++)
+ properties[i] = GetPropertyDescriptor (propertyOwners[i], propertyName);
+ items.Add (new GridEntry (property_grid, properties, this));
+ }
+
+ return items;
+ }
+
+ private bool IsPropertyMergeable (PropertyDescriptor property)
+ {
+ if (property == null)
+ return false;
+
+ MergablePropertyAttribute attrib = property.Attributes [typeof (MergablePropertyAttribute)] as MergablePropertyAttribute;
+ if (attrib != null && !attrib.AllowMerge)
+ return false;
+
+ return true;
+ }
+
+ private string[] GetMergedPropertyNames (object [] objects)
+ {
+ if (objects == null || objects.Length == 0)
+ return new string[0];
+
+ ArrayList intersection = new ArrayList ();
+ for (int i = 0; i < objects.Length; i ++) {
+ if (objects [i] == null)
+ continue;
+
+ PropertyDescriptorCollection properties = GetProperties (objects[i], property_grid.BrowsableAttributes);
+ ArrayList new_intersection = new ArrayList ();
+
+ foreach (PropertyDescriptor currentProperty in (i == 0 ? (ICollection)properties : (ICollection)intersection)) {
+ PropertyDescriptor matchingProperty = (i == 0 ? currentProperty : properties [currentProperty.Name]);
+ if (objects.Length > 1 && !IsPropertyMergeable (matchingProperty))
+ continue;
+ if (matchingProperty.PropertyType == currentProperty.PropertyType)
+ new_intersection.Add (matchingProperty);
+ }
+
+ intersection = new_intersection;
+ }
+
+ string[] propertyNames = new string [intersection.Count];
+ for (int i=0; i < intersection.Count; i++)
+ propertyNames[i] = ((PropertyDescriptor)intersection[i]).Name;
+
+ return propertyNames;
+ }
+
+ private PropertyDescriptor GetPropertyDescriptor (object propertyOwner, string propertyName)
+ {
+ if (propertyOwner == null || propertyName == null)
+ return null;
+
+ PropertyDescriptorCollection properties = GetProperties (propertyOwner, property_grid.BrowsableAttributes);
+ if (properties != null)
+ return properties[propertyName];
+ return null;
+ }
+
+ private PropertyDescriptorCollection GetProperties (object propertyOwner, AttributeCollection attributes)
+ {
+ if (propertyOwner == null || property_grid.SelectedTab == null)
+ return new PropertyDescriptorCollection (null);
+
+ Attribute[] atts = new Attribute[attributes.Count];
+ attributes.CopyTo (atts, 0);
+ return property_grid.SelectedTab.GetProperties ((ITypeDescriptorContext)this, propertyOwner, atts);
+ }
+#endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/GridItemCollection.cs b/source/ShiftUI/Widgets/GridItemCollection.cs
new file mode 100644
index 0000000..22eedc8
--- /dev/null
+++ b/source/ShiftUI/Widgets/GridItemCollection.cs
@@ -0,0 +1,158 @@
+// 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.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+//
+
+// COMPLETE
+
+using System;
+using System.Collections;
+using ShiftUI.PropertyGridInternal;
+
+namespace ShiftUI
+{
+ public class GridItemCollection : IEnumerable, ICollection
+ {
+ #region Local Variables
+ private System.Collections.SortedList list;
+ #endregion // Local Variables
+
+ #region Public Static Fields
+ public static GridItemCollection Empty = new GridItemCollection();
+ #endregion // Public Static Fields
+
+ #region Constructors
+ internal GridItemCollection()
+ {
+ list = new SortedList();
+ }
+ #endregion // Constructors
+
+ #region Internal Properties and Methods
+ internal void Add (GridItem grid_item)
+ {
+ string key = grid_item.Label;
+ while (list.ContainsKey (key))
+ key += "_";
+ list.Add (key, grid_item);
+ }
+
+ internal void AddRange (GridItemCollection items)
+ {
+ foreach (GridItem item in items)
+ Add (item);
+ }
+
+ internal int IndexOf (GridItem grid_item)
+ {
+ return list.IndexOfValue (grid_item);
+ }
+ #endregion // Internal Properties and Methods
+
+ #region Public Instance Properties
+ public int Count {
+ get {
+ return list.Count;
+ }
+ }
+
+ public GridItem this [int index] {
+ get {
+ if (index>=list.Count) {
+ throw new ArgumentOutOfRangeException("index");
+ }
+ return (GridItem)list.GetByIndex(index);
+ }
+ }
+
+ public GridItem this [string label] {
+ get {
+ return (GridItem)list[label];
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region IEnumerable Members
+ public IEnumerator GetEnumerator()
+ {
+ return new GridItemEnumerator (this);
+ }
+ #endregion
+
+ #region Enumerator Class
+ internal class GridItemEnumerator : IEnumerator{
+ int nIndex;
+ GridItemCollection collection;
+
+ public GridItemEnumerator(GridItemCollection coll)
+ {
+ collection = coll;
+ nIndex = -1;
+ }
+
+ public bool MoveNext ()
+ {
+ nIndex++;
+ return (nIndex < collection.Count);
+ }
+
+ public void Reset ()
+ {
+ nIndex = -1;
+ }
+
+ object System.Collections.IEnumerator.Current {
+ get {
+ return collection [nIndex];
+ }
+ }
+ }
+ #endregion
+
+ #region ICollection Members
+
+ bool ICollection.IsSynchronized {
+ get {
+ return list.IsSynchronized;
+ }
+ }
+
+ void ICollection.CopyTo(Array dest, int index)
+ {
+ list.CopyTo (dest, index);
+ }
+
+ object ICollection.SyncRoot {
+ get {
+ return list.SyncRoot;
+ }
+ }
+
+ #endregion
+
+ internal void Clear ()
+ {
+ list.Clear ();
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/GroupBox.cs b/source/ShiftUI/Widgets/GroupBox.cs
new file mode 100644
index 0000000..27b8384
--- /dev/null
+++ b/source/ShiftUI/Widgets/GroupBox.cs
@@ -0,0 +1,344 @@
+//
+// ShiftUI.GroupBox.cs
+//
+// 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.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+//
+// TODO:
+//
+// Copyright (C) Novell Inc., 2004-2005
+//
+//
+
+using System.Drawing;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultProperty("Text")]
+ [DefaultEvent("Enter")]
+ //[Designer ("ShiftUI.Design.GroupBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class GroupBox : Widget
+ {
+ private FlatStyle flat_style;
+ private Rectangle display_rectangle = new Rectangle ();
+
+ #region Events
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler AutoSizeChanged {
+ add { base.AutoSizeChanged += value; }
+ remove { base.AutoSizeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseDown {
+ add { base.MouseDown += value; }
+ remove { base.MouseDown -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler MouseEnter {
+ add { base.MouseEnter += value; }
+ remove { base.MouseEnter -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler MouseLeave {
+ add { base.MouseLeave += value; }
+ remove { base.MouseLeave -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseMove {
+ add { base.MouseMove += value; }
+ remove { base.MouseMove -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseUp {
+ add { base.MouseUp += value; }
+ remove { base.MouseUp -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+ #endregion Events
+
+ public GroupBox ()
+ {
+ TabStop = false;
+ flat_style = FlatStyle.Standard;
+
+ SetStyle(Widgetstyles.ContainerWidget | Widgetstyles.ResizeRedraw | Widgetstyles.SupportsTransparentBackColor, true);
+ SetStyle(Widgetstyles.Selectable, false);
+ }
+
+ #region Public Properties
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public override bool AllowDrop {
+ get { return base.AllowDrop; }
+ set { base.AllowDrop = value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ public override bool AutoSize {
+ get { return base.AutoSize; }
+ set { base.AutoSize = value; }
+ }
+
+ [Browsable (true)]
+ [DefaultValue (AutoSizeMode.GrowOnly)]
+ [Localizable (true)]
+ public AutoSizeMode AutoSizeMode {
+ get { return base.GetAutoSizeMode (); }
+ set { base.SetAutoSizeMode (value); }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams; }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.GroupBoxDefaultSize;}
+ }
+
+ public override Rectangle DisplayRectangle {
+ get {
+ display_rectangle.X = Padding.Left;
+ display_rectangle.Y = Font.Height + Padding.Top;
+ display_rectangle.Width = Width - Padding.Horizontal;
+ display_rectangle.Height = Height - Font.Height - Padding.Vertical;
+ return display_rectangle;
+ }
+ }
+
+ [DefaultValue(FlatStyle.Standard)]
+ public FlatStyle FlatStyle {
+ get { return flat_style; }
+ set {
+ if (!Enum.IsDefined (typeof (FlatStyle), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for FlatStyle", value));
+
+ if (flat_style == value)
+ return;
+
+ flat_style = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [Localizable(true)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (base.Text == value)
+ return;
+
+ base.Text = value;
+ Refresh ();
+ }
+ }
+
+ #endregion //Public Properties
+
+ #region Public Methods
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return new GroupBoxAccessibleObject (this);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ Refresh ();
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ ThemeEngine.Current.DrawGroupBox (e.Graphics, ClientRectangle, this);
+ base.OnPaint(e);
+ }
+
+ protected override bool ProcessMnemonic (char charCode)
+ {
+ if (IsMnemonic(charCode, Text) == true) {
+ // Select item next in line in tab order
+ if (this.Parent != null) {
+ Parent.SelectNextWidget(this, true, false, true, false);
+ }
+ return true;
+ }
+
+ return base.ProcessMnemonic (charCode);
+ }
+
+ protected override void ScaleWidget (SizeF factor, BoundsSpecified specified)
+ {
+ base.ScaleWidget (factor, specified);
+ }
+
+ public override string ToString()
+ {
+ return GetType ().FullName + ", Text: " + Text;
+ }
+
+ protected override void WndProc(ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ #endregion Public Methods
+
+ [DefaultValue (false)]
+ public bool UseCompatibleTextRendering {
+ get {
+ return use_compatible_text_rendering;
+ }
+
+ set {
+ if (use_compatible_text_rendering != value) {
+ use_compatible_text_rendering = value;
+ if (Parent != null)
+ Parent.PerformLayout (this, "UseCompatibleTextRendering");
+ Invalidate ();
+ }
+ }
+ }
+
+ #region Protected Properties
+ protected override Padding DefaultPadding {
+ get { return new Padding (3); }
+ }
+ #endregion
+
+ #region Internal Methods
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ Size retsize = new Size (Padding.Left, Padding.Top);
+
+ foreach (Widget child in Widgets) {
+ if (child.Dock == DockStyle.Fill) {
+ if (child.Bounds.Right > retsize.Width)
+ retsize.Width = child.Bounds.Right;
+ } else if (child.Dock != DockStyle.Top && child.Dock != DockStyle.Bottom && (child.Bounds.Right + child.Margin.Right) > retsize.Width)
+ retsize.Width = child.Bounds.Right + child.Margin.Right;
+
+ if (child.Dock == DockStyle.Fill) {
+ if (child.Bounds.Bottom > retsize.Height)
+ retsize.Height = child.Bounds.Bottom;
+ } else if (child.Dock != DockStyle.Left && child.Dock != DockStyle.Right && (child.Bounds.Bottom + child.Margin.Bottom) > retsize.Height)
+ retsize.Height = child.Bounds.Bottom + child.Margin.Bottom;
+ }
+
+ retsize.Width += Padding.Right;
+ retsize.Height += Padding.Bottom;
+
+ retsize.Height += this.Font.Height;
+
+ return retsize;
+ }
+ #endregion
+
+ #region Private Classes
+ private class GroupBoxAccessibleObject : Widget.WidgetAccessibleObject
+ {
+ public GroupBoxAccessibleObject (Widget owner) : base (owner)
+ {
+ }
+ }
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/HScrollBar.cs b/source/ShiftUI/Widgets/HScrollBar.cs
new file mode 100644
index 0000000..a6d1fe2
--- /dev/null
+++ b/source/ShiftUI/Widgets/HScrollBar.cs
@@ -0,0 +1,52 @@
+//
+// ShiftUI.HScrollBar.cs
+//
+// 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, Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez [email protected]
+//
+
+
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI
+{
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ public class HScrollBar : ScrollBar
+ {
+ public HScrollBar()
+ {
+ vert = false;
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.HScrollBarDefaultSize;}
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams; }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/IRootGridEntry.cs b/source/ShiftUI/Widgets/IRootGridEntry.cs
new file mode 100644
index 0000000..25142bb
--- /dev/null
+++ b/source/ShiftUI/Widgets/IRootGridEntry.cs
@@ -0,0 +1,40 @@
+// 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) 2005 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers [email protected]
+//
+
+
+// COMPLETE
+
+using System;
+namespace ShiftUI.PropertyGridInternal
+{
+ public interface IRootGridEntry
+ {
+ System.ComponentModel.AttributeCollection BrowsableAttributes { get; set; }
+
+ void ShowCategories ( bool showCategories );
+
+ void ResetBrowsableAttributes ();
+ }
+}
diff --git a/source/ShiftUI/Widgets/ImplicitHScrollBar.cs b/source/ShiftUI/Widgets/ImplicitHScrollBar.cs
new file mode 100644
index 0000000..3e2acfa
--- /dev/null
+++ b/source/ShiftUI/Widgets/ImplicitHScrollBar.cs
@@ -0,0 +1,48 @@
+// 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.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+// Partially based on work by:
+// Aleksey Ryabchuk [email protected]
+// Alexandre Pigolkine [email protected]
+// Dennis Hayes [email protected]
+// Jaak Simm [email protected]
+// John Sohn [email protected]
+//
+
+// COMPLETE
+
+using System;
+
+namespace ShiftUI {
+
+ internal class ImplicitHScrollBar : HScrollBar {
+
+ public ImplicitHScrollBar ()
+ {
+ implicit_Widget = true;
+ SetStyle (Widgetstyles.Selectable, false);
+ }
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/ImplicitVScrollBar.cs b/source/ShiftUI/Widgets/ImplicitVScrollBar.cs
new file mode 100644
index 0000000..618cc53
--- /dev/null
+++ b/source/ShiftUI/Widgets/ImplicitVScrollBar.cs
@@ -0,0 +1,48 @@
+// 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.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+// Partially based on work by:
+// Aleksey Ryabchuk [email protected]
+// Alexandre Pigolkine [email protected]
+// Dennis Hayes [email protected]
+// Jaak Simm [email protected]
+// John Sohn [email protected]
+//
+
+// COMPLETE
+
+using System;
+
+namespace ShiftUI {
+
+ internal class ImplicitVScrollBar : VScrollBar {
+
+ public ImplicitVScrollBar ()
+ {
+ implicit_Widget = true;
+ SetStyle (Widgetstyles.Selectable, false);
+ }
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/Label.cs b/source/ShiftUI/Widgets/Label.cs
new file mode 100644
index 0000000..4bd4515
--- /dev/null
+++ b/source/ShiftUI/Widgets/Label.cs
@@ -0,0 +1,729 @@
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Peter Bartok, [email protected]
+//
+//
+
+// COMPLETE
+
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Text;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using ShiftUI.Theming;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultProperty ("Text")]
+ //[Designer ("ShiftUI.Design.LabelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxItem ("ShiftUI.Design.AutoSizeToolboxItem," + Consts.AssemblySystem_Design)]
+ [DefaultBindingProperty ("Text")]
+ [ToolboxWidget]
+ public class Label : Widget
+ {
+ private bool autosize;
+ private bool auto_ellipsis;
+ private Image image;
+ private bool render_transparent;
+ private FlatStyle flat_style;
+ private bool use_mnemonic;
+ private int image_index = -1;
+ private string image_key = string.Empty;
+ private ImageList image_list;
+ internal ContentAlignment image_align;
+ internal StringFormat string_format;
+ internal ContentAlignment text_align;
+ static SizeF req_witdthsize = new SizeF (0,0);
+
+ #region Events
+ static object AutoSizeChangedEvent = new object ();
+ static object TextAlignChangedEvent = new object ();
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler AutoSizeChanged {
+ add { Events.AddHandler (AutoSizeChangedEvent, value); }
+ remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+
+ public event EventHandler TextAlignChanged {
+ add { Events.AddHandler (TextAlignChangedEvent, value); }
+ remove { Events.RemoveHandler (TextAlignChangedEvent, value); }
+ }
+ #endregion
+
+ public Label ()
+ {
+ // Defaults in the Spec
+ autosize = false;
+ TabStop = false;
+ string_format = new StringFormat();
+ string_format.FormatFlags = StringFormatFlags.LineLimit;
+ TextAlign = ContentAlignment.TopLeft;
+ image = null;
+ UseMnemonic = true;
+ image_list = null;
+ image_align = ContentAlignment.MiddleCenter;
+ SetUseMnemonic (UseMnemonic);
+ flat_style = FlatStyle.Standard;
+
+ SetStyle (Widgetstyles.Selectable, false);
+ SetStyle (Widgetstyles.ResizeRedraw |
+ Widgetstyles.UserPaint |
+ Widgetstyles.AllPaintingInWmPaint |
+ Widgetstyles.SupportsTransparentBackColor |
+ Widgetstyles.OptimizedDoubleBuffer
+ , true);
+
+ HandleCreated += new EventHandler (OnHandleCreatedLB);
+ }
+
+ #region Public Properties
+
+ [DefaultValue (false)]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public bool AutoEllipsis {
+ get { return this.auto_ellipsis; }
+ set
+ {
+ if (this.auto_ellipsis != value) {
+ this.auto_ellipsis = value;
+
+ if (this.auto_ellipsis)
+ string_format.Trimming = StringTrimming.EllipsisCharacter;
+ else
+ string_format.Trimming = StringTrimming.Character;
+
+ if (Parent != null)
+ Parent.PerformLayout (this, "AutoEllipsis");
+ this.Invalidate ();
+ }
+ }
+ }
+
+ [Browsable (true)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [DefaultValue(false)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ public override bool AutoSize {
+ get { return autosize; }
+ set {
+ if (autosize == value)
+ return;
+
+ base.SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
+ base.AutoSize = value;
+ autosize = value;
+ CalcAutoSize ();
+ Invalidate ();
+
+ OnAutoSizeChanged (EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set {
+ base.BackgroundImage = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue(BorderStyle.None)]
+ [DispId(-504)]
+ public virtual BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set { InternalBorderStyle = value; }
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams create_params = base.CreateParams;
+
+ if (BorderStyle != BorderStyle.Fixed3D)
+ return create_params;
+
+ create_params.ExStyle &= ~(int) WindowExStyles.WS_EX_CLIENTEDGE;
+ create_params.ExStyle |= (int)WindowExStyles.WS_EX_STATICEDGE;
+
+ return create_params;
+ }
+ }
+
+ protected override ImeMode DefaultImeMode {
+ get { return ImeMode.Disable;}
+ }
+
+ protected override Padding DefaultMargin {
+ get { return new Padding (3, 0, 3, 0); }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeElements.LabelPainter.DefaultSize; }
+ }
+
+ [DefaultValue(FlatStyle.Standard)]
+ public FlatStyle FlatStyle {
+ get { return flat_style; }
+ set {
+ if (!Enum.IsDefined (typeof (FlatStyle), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for FlatStyle", value));
+
+ if (flat_style == value)
+ return;
+
+ flat_style = value;
+ if (Parent != null)
+ Parent.PerformLayout (this, "FlatStyle");
+ Invalidate ();
+ }
+ }
+
+ [Localizable(true)]
+ public Image Image {
+ get {
+ if (this.image != null)
+ return this.image;
+
+ if (this.image_index >= 0)
+ if (this.image_list != null)
+ return this.image_list.Images[this.image_index];
+
+ if (!string.IsNullOrEmpty (this.image_key))
+ if (this.image_list != null)
+ return this.image_list.Images[this.image_key];
+
+ return null;
+ }
+ set {
+ if (this.image != value) {
+ this.image = value;
+ this.image_index = -1;
+ this.image_key = string.Empty;
+ this.image_list = null;
+
+ if (this.AutoSize && this.Parent != null)
+ this.Parent.PerformLayout (this, "Image");
+
+ Invalidate ();
+ }
+ }
+ }
+
+ [DefaultValue(ContentAlignment.MiddleCenter)]
+ [Localizable(true)]
+ public ContentAlignment ImageAlign {
+ get { return image_align; }
+ set {
+ if (!Enum.IsDefined (typeof (ContentAlignment), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for ContentAlignment", value));
+
+ if (image_align == value)
+ return;
+
+ image_align = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue (-1)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable (true)]
+ [TypeConverter (typeof (ImageIndexConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public int ImageIndex {
+ get {
+ if (ImageList == null) {
+ return -1;
+ }
+
+ if (image_index >= image_list.Images.Count) {
+ return image_list.Images.Count - 1;
+ }
+
+ return image_index;
+ }
+ set {
+
+ if (value < -1)
+ throw new ArgumentException ();
+
+ if (this.image_index != value) {
+ this.image_index = value;
+ this.image = null;
+ this.image_key = string.Empty;
+ Invalidate ();
+ }
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue ("")]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ public string ImageKey {
+ get { return this.image_key; }
+ set {
+ if (this.image_key != value) {
+ this.image = null;
+ this.image_index = -1;
+ this.image_key = value;
+ this.Invalidate ();
+ }
+ }
+ }
+
+ [DefaultValue(null)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public ImageList ImageList {
+ get { return image_list;}
+ set {
+ if (image_list == value)
+ return;
+
+ image_list = value;
+
+ if (image_list != null && image_index !=-1)
+ Image = null;
+
+ Invalidate ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new ImeMode ImeMode {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ internal virtual Size InternalGetPreferredSize (Size proposed)
+ {
+ Size size;
+
+ if (Text == string.Empty) {
+ size = new Size (0, Font.Height);
+ } else {
+ size = Size.Ceiling (TextRenderer.MeasureString (Text, Font, req_witdthsize, string_format));
+ size.Width += 3;
+ }
+
+ size.Width += Padding.Horizontal;
+ size.Height += Padding.Vertical;
+
+ if (!use_compatible_text_rendering)
+ return size;
+
+ if (border_style == BorderStyle.None)
+ size.Height += 3;
+ else
+ size.Height += 6;
+
+ return size;
+ }
+
+ public override Size GetPreferredSize (Size proposedSize)
+ {
+ return InternalGetPreferredSize (proposedSize);
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public virtual int PreferredHeight {
+ get { return InternalGetPreferredSize (Size.Empty).Height; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ public virtual int PreferredWidth {
+ get { return InternalGetPreferredSize (Size.Empty).Width; }
+ }
+
+ [Obsolete ("This property has been deprecated. Use BackColor instead.")]
+ protected virtual bool RenderTransparent {
+ get { return render_transparent; }
+ set { render_transparent = value;}
+ }
+
+ [Browsable(false)]
+ [DefaultValue(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [DefaultValue(ContentAlignment.TopLeft)]
+ [Localizable(true)]
+ public virtual ContentAlignment TextAlign {
+ get { return text_align; }
+
+ set {
+ if (!Enum.IsDefined (typeof (ContentAlignment), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for ContentAlignment", value));
+
+ if (text_align != value) {
+ text_align = value;
+ switch (value) {
+ case ContentAlignment.BottomLeft:
+ string_format.LineAlignment = StringAlignment.Far;
+ string_format.Alignment = StringAlignment.Near;
+ break;
+ case ContentAlignment.BottomCenter:
+ string_format.LineAlignment = StringAlignment.Far;
+ string_format.Alignment = StringAlignment.Center;
+ break;
+ case ContentAlignment.BottomRight:
+ string_format.LineAlignment = StringAlignment.Far;
+ string_format.Alignment = StringAlignment.Far;
+ break;
+ case ContentAlignment.TopLeft:
+ string_format.LineAlignment = StringAlignment.Near;
+ string_format.Alignment = StringAlignment.Near;
+ break;
+ case ContentAlignment.TopCenter:
+ string_format.LineAlignment = StringAlignment.Near;
+ string_format.Alignment = StringAlignment.Center;
+ break;
+ case ContentAlignment.TopRight:
+ string_format.LineAlignment = StringAlignment.Near;
+ string_format.Alignment = StringAlignment.Far;
+ break;
+ case ContentAlignment.MiddleLeft:
+ string_format.LineAlignment = StringAlignment.Center;
+ string_format.Alignment = StringAlignment.Near;
+ break;
+ case ContentAlignment.MiddleRight:
+ string_format.LineAlignment = StringAlignment.Center;
+ string_format.Alignment = StringAlignment.Far;
+ break;
+ case ContentAlignment.MiddleCenter:
+ string_format.LineAlignment = StringAlignment.Center;
+ string_format.Alignment = StringAlignment.Center;
+ break;
+ default:
+ break;
+ }
+
+ OnTextAlignChanged (EventArgs.Empty);
+ Invalidate ();
+ }
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool UseMnemonic {
+ get { return use_mnemonic; }
+ set {
+ if (use_mnemonic != value) {
+ use_mnemonic = value;
+ SetUseMnemonic (use_mnemonic);
+ Invalidate ();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ protected Rectangle CalcImageRenderBounds (Image image, Rectangle r, ContentAlignment align)
+ {
+ Rectangle rcImageClip = r;
+ rcImageClip.Inflate (-2,-2);
+
+ int X = r.X;
+ int Y = r.Y;
+
+ if (align == ContentAlignment.TopCenter ||
+ align == ContentAlignment.MiddleCenter ||
+ align == ContentAlignment.BottomCenter) {
+ X += (r.Width - image.Width) / 2;
+ } else if (align == ContentAlignment.TopRight ||
+ align == ContentAlignment.MiddleRight||
+ align == ContentAlignment.BottomRight) {
+ X += (r.Width - image.Width);
+ }
+
+ if( align == ContentAlignment.BottomCenter ||
+ align == ContentAlignment.BottomLeft ||
+ align == ContentAlignment.BottomRight) {
+ Y += r.Height - image.Height;
+ } else if(align == ContentAlignment.MiddleCenter ||
+ align == ContentAlignment.MiddleLeft ||
+ align == ContentAlignment.MiddleRight) {
+ Y += (r.Height - image.Height) / 2;
+ }
+
+ rcImageClip.X = X;
+ rcImageClip.Y = Y;
+ rcImageClip.Width = image.Width;
+ rcImageClip.Height = image.Height;
+
+ return rcImageClip;
+ }
+
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return base.CreateAccessibilityInstance ();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose (disposing);
+
+ if (disposing)
+ string_format.Dispose ();
+ }
+
+ protected internal void DrawImage (Graphics g, Image image, Rectangle r, ContentAlignment align)
+ {
+ if (image == null || g == null)
+ return;
+
+ Rectangle rcImageClip = CalcImageRenderBounds (image, r, align);
+
+ if (Enabled)
+ g.DrawImage (image, rcImageClip.X, rcImageClip.Y, rcImageClip.Width, rcImageClip.Height);
+ else
+ WidgetPaint.DrawImageDisabled (g, image, rcImageClip.X, rcImageClip.Y, BackColor);
+ }
+
+ protected override void OnEnabledChanged (EventArgs e)
+ {
+ base.OnEnabledChanged (e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ if (autosize)
+ CalcAutoSize();
+ Invalidate ();
+ }
+
+ protected override void OnPaddingChanged (EventArgs e)
+ {
+ base.OnPaddingChanged (e);
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ ThemeElements.LabelPainter.Draw (e.Graphics, ClientRectangle, this);
+ base.OnPaint(e);
+ }
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ protected override void OnRightToLeftChanged (EventArgs e)
+ {
+ base.OnRightToLeftChanged (e);
+ }
+
+ protected virtual void OnTextAlignChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [TextAlignChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnTextChanged (EventArgs e)
+ {
+ base.OnTextChanged (e);
+ if (autosize)
+ CalcAutoSize ();
+ Invalidate ();
+ }
+
+ protected override void OnVisibleChanged (EventArgs e)
+ {
+ base.OnVisibleChanged (e);
+ }
+
+ protected override bool ProcessMnemonic (char charCode)
+ {
+ if (IsMnemonic (charCode, Text)) {
+ // Select item next in line in tab order
+ if (this.Parent != null)
+ Parent.SelectNextWidget(this, true, false, false, false);
+ return true;
+ }
+
+ return base.ProcessMnemonic (charCode);
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+
+ public override string ToString()
+ {
+ return base.ToString () + ", Text: " + Text;
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg) m.Msg) {
+ case Msg.WM_DRAWITEM:
+ m.Result = (IntPtr)1;
+ break;
+ default:
+ base.WndProc (ref m);
+ break;
+ }
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ private void CalcAutoSize ()
+ {
+ if (!AutoSize)
+ return;
+
+ Size s = InternalGetPreferredSize (Size.Empty);
+
+ SetBounds (Left, Top, s.Width, s.Height, BoundsSpecified.Size);
+ }
+
+ private void OnHandleCreatedLB (Object o, EventArgs e)
+ {
+ if (autosize)
+ CalcAutoSize ();
+ }
+
+ private void SetUseMnemonic (bool use)
+ {
+ if (use)
+ string_format.HotkeyPrefix = HotkeyPrefix.Show;
+ else
+ string_format.HotkeyPrefix = HotkeyPrefix.None;
+ }
+
+ #endregion Private Methods
+ [DefaultValue (false)]
+ public bool UseCompatibleTextRendering {
+ get { return use_compatible_text_rendering; }
+ set { use_compatible_text_rendering = value; }
+ }
+
+ [SettingsBindable (true)]
+ //[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
+ // typeof (System.Drawing.Design.UITypeEditor))]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ protected override void OnMouseEnter (EventArgs e)
+ {
+ base.OnMouseEnter (e);
+ }
+
+ protected override void OnMouseLeave (EventArgs e)
+ {
+ base.OnMouseLeave (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/LabelEditTextBox.cs b/source/ShiftUI/Widgets/LabelEditTextBox.cs
new file mode 100644
index 0000000..8e03bd0
--- /dev/null
+++ b/source/ShiftUI/Widgets/LabelEditTextBox.cs
@@ -0,0 +1,108 @@
+// 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) 2006 Novell, Inc.
+//
+// Authors:
+// Jackson Harper ([email protected])
+//
+//
+
+// This is an internal class that allows us to use a textbox for label editing
+// in the tree and in listview. The textbox will make itself invisible when
+// the user pressed the enter key
+using System;
+
+namespace ShiftUI {
+
+ internal class LabelEditTextBox : FixedSizeTextBox {
+
+ public LabelEditTextBox () : base (true, true)
+ {
+ }
+
+ protected override bool IsInputKey (Keys key_data)
+ {
+ if ((key_data & Keys.Alt) == 0) {
+ switch (key_data & Keys.KeyCode) {
+ case Keys.Enter:
+ return true;
+ case Keys.Escape:
+ return true;
+ }
+ }
+ return base.IsInputKey (key_data);
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ if (!Visible)
+ return;
+
+ switch (e.KeyCode) {
+ case Keys.Return:
+ Visible = false;
+ Parent.Focus ();
+ e.Handled = true;
+ OnEditingFinished (e);
+ break;
+ case Keys.Escape:
+ Visible = false;
+ Parent.Focus ();
+ e.Handled = true;
+ OnEditingCancelled (e);
+ break;
+ }
+ }
+
+ protected override void OnLostFocus (EventArgs e)
+ {
+ if (Visible) {
+ OnEditingFinished (e);
+ }
+ }
+
+ protected void OnEditingCancelled (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events[EditingCancelledEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected void OnEditingFinished (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ static object EditingCancelledEvent = new object ();
+ public event EventHandler EditingCancelled {
+ add { Events.AddHandler (EditingCancelledEvent, value); }
+ remove { Events.RemoveHandler (EditingCancelledEvent, value); }
+ }
+
+ static object EditingFinishedEvent = new object ();
+ public event EventHandler EditingFinished {
+ add { Events.AddHandler (EditingFinishedEvent, value); }
+ remove { Events.AddHandler (EditingFinishedEvent, value); }
+ }
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/LinkLabel.cs b/source/ShiftUI/Widgets/LinkLabel.cs
new file mode 100644
index 0000000..3654449
--- /dev/null
+++ b/source/ShiftUI/Widgets/LinkLabel.cs
@@ -0,0 +1,1120 @@
+// 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.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Chris Toshok <[email protected]>
+// Everaldo Canuto <[email protected]>
+//
+// Based on work by:
+// Daniel Carrera, [email protected] (stubbed out)
+// Jaak Simm ([email protected]) (stubbed out)
+//
+
+using System.ComponentModel;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Runtime.InteropServices;
+using ShiftUI.Theming;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultEvent("LinkClicked")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxItem ("ShiftUI.Design.AutoSizeToolboxItem," + Consts.AssemblySystem_Design)]
+ [ToolboxWidget]
+ public class LinkLabel : Label, IButtonWidget
+ {
+ /* Encapsulates a piece of text (regular or link)*/
+ internal class Piece
+ {
+ public string text;
+ public int start;
+ public int length;
+ public LinkLabel.Link link; // Empty link indicates regular text
+ public Region region;
+
+ public Piece (int start, int length, string text, Link link)
+ {
+ this.start = start;
+ this.length = length;
+ this.text = text;
+ this.link = link;
+ }
+ }
+
+ private Color active_link_color;
+ private Color disabled_link_color;
+ private Color link_color;
+ private Color visited_color;
+ private LinkArea link_area;
+ private LinkBehavior link_behavior;
+ private LinkCollection link_collection;
+ private ArrayList links = new ArrayList();
+ internal Link[] sorted_links;
+ private bool link_visited;
+ internal Piece[] pieces;
+ private Cursor override_cursor;
+ private DialogResult dialog_result;
+
+ private Link active_link;
+ private Link hovered_link;
+ /* this is an index instead of a Link because we have
+ * to search through sorted links for the new one */
+ private int focused_index;
+
+ #region Events
+ static object LinkClickedEvent = new object ();
+
+ public event LinkLabelLinkClickedEventHandler LinkClicked {
+ add { Events.AddHandler (LinkClickedEvent, value); }
+ remove { Events.RemoveHandler (LinkClickedEvent, value); }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+ #endregion // Events
+
+ public LinkLabel ()
+ {
+ LinkArea = new LinkArea (0, -1);
+ link_behavior = LinkBehavior.SystemDefault;
+ link_visited = false;
+ pieces = null;
+ focused_index = -1;
+
+ string_format.FormatFlags |= StringFormatFlags.NoClip;
+
+ ActiveLinkColor = Color.Red;
+ DisabledLinkColor = ThemeEngine.Current.ColorGrayText;
+ LinkColor = Color.FromArgb (255, 0, 0, 255);
+ VisitedLinkColor = Color.FromArgb (255, 128, 0, 128);
+ SetStyle (Widgetstyles.Selectable, false);
+ SetStyle (Widgetstyles.ResizeRedraw |
+ Widgetstyles.UserPaint |
+ Widgetstyles.AllPaintingInWmPaint |
+ Widgetstyles.SupportsTransparentBackColor |
+ Widgetstyles.Opaque |
+ Widgetstyles.OptimizedDoubleBuffer
+ , true);
+ CreateLinkPieces ();
+ }
+
+ #region Public Properties
+
+ public Color ActiveLinkColor {
+ get { return active_link_color; }
+ set {
+ if (active_link_color == value)
+ return;
+
+ active_link_color = value;
+ Invalidate ();
+ }
+ }
+
+ public Color DisabledLinkColor {
+
+ get { return disabled_link_color; }
+ set {
+ if (disabled_link_color == value)
+ return;
+
+ disabled_link_color = value;
+ Invalidate ();
+ }
+ }
+
+ public Color LinkColor {
+ get { return link_color; }
+ set {
+ if (link_color == value)
+ return;
+
+ link_color = value;
+ Invalidate ();
+ }
+ }
+
+ public Color VisitedLinkColor {
+ get { return visited_color;}
+ set {
+ if (visited_color == value)
+ return;
+
+ visited_color = value;
+ Invalidate ();
+ }
+ }
+
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.LinkAreaEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public LinkArea LinkArea {
+ get { return link_area;}
+ set {
+
+ if (value.Start <0 || value.Length < -1)
+ throw new ArgumentException ();
+
+ Links.Clear ();
+
+ if (!value.IsEmpty) {
+ Links.Add (value.Start, value.Length);
+
+ link_area = value;
+ Invalidate ();
+ }
+ }
+ }
+
+ [DefaultValue (LinkBehavior.SystemDefault)]
+ public LinkBehavior LinkBehavior {
+
+ get { return link_behavior;}
+ set {
+ if (link_behavior == value)
+ return;
+
+ link_behavior = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public LinkLabel.LinkCollection Links {
+ get {
+ if (link_collection == null)
+ link_collection = new LinkCollection (this);
+ return link_collection;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool LinkVisited {
+ get { return link_visited;}
+ set {
+ if (link_visited == value)
+ return;
+
+ link_visited = value;
+ Invalidate ();
+ }
+ }
+
+ protected Cursor OverrideCursor {
+ get {
+ if (override_cursor == null)
+ override_cursor = Cursors.Hand;
+ return override_cursor;
+ }
+ set { override_cursor = value; }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (base.Text == value)
+ return;
+
+ base.Text = value;
+ CreateLinkPieces ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new FlatStyle FlatStyle {
+ get { return base.FlatStyle; }
+ set {
+ if (base.FlatStyle == value)
+ return;
+
+ base.FlatStyle = value;
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set {
+ if (base.Padding == value)
+ return;
+
+ base.Padding = value;
+ CreateLinkPieces ();
+ }
+ }
+
+ #endregion // Public Properties
+
+ DialogResult IButtonWidget.DialogResult {
+ get { return dialog_result; }
+ set { dialog_result = value; }
+ }
+
+
+ void IButtonWidget.NotifyDefault (bool value)
+ {
+ }
+
+ void IButtonWidget.PerformClick ()
+ {
+ }
+
+ #region Public Methods
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return base.CreateAccessibilityInstance();
+ }
+
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ CreateLinkPieces ();
+ }
+
+ protected override void OnAutoSizeChanged (EventArgs e)
+ {
+ base.OnAutoSizeChanged (e);
+ }
+
+ protected override void OnEnabledChanged (EventArgs e)
+ {
+ base.OnEnabledChanged (e);
+ Invalidate ();
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ CreateLinkPieces ();
+ }
+
+ protected override void OnGotFocus (EventArgs e)
+ {
+ base.OnGotFocus (e);
+
+ // And yes it can actually be null..... arghh..
+ if (sorted_links == null)
+ return;
+
+ // Set focus to the first enabled link piece
+ if (focused_index == -1) {
+ if ((Widget.ModifierKeys & Keys.Shift) == 0) {
+ for (int i = 0; i < sorted_links.Length; i ++) {
+ if (sorted_links[i].Enabled) {
+ focused_index = i;
+ break;
+ }
+ }
+ } else {
+ if (focused_index == -1)
+ focused_index = sorted_links.Length;
+
+ for (int n = focused_index - 1; n >= 0; n--) {
+ if (sorted_links[n].Enabled) {
+ sorted_links[n].Focused = true;
+ focused_index = n;
+ return;
+ }
+ }
+ }
+ }
+
+ if (focused_index != -1)
+ sorted_links[focused_index].Focused = true;
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return) {
+ if (focused_index != -1)
+ OnLinkClicked (new LinkLabelLinkClickedEventArgs (sorted_links[focused_index]));
+ }
+
+ base.OnKeyDown(e);
+ }
+
+ protected virtual void OnLinkClicked (LinkLabelLinkClickedEventArgs e)
+ {
+ LinkLabelLinkClickedEventHandler eh = (LinkLabelLinkClickedEventHandler)(Events [LinkClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnLostFocus (EventArgs e)
+ {
+ base.OnLostFocus (e);
+
+ // Clean focus in link pieces
+ if (focused_index != -1)
+ sorted_links[focused_index].Focused = false;
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ if (!Enabled) return;
+
+ base.OnMouseDown (e);
+
+ for (int i = 0; i < sorted_links.Length; i ++) {
+ if (sorted_links[i].Contains (e.X, e.Y) && sorted_links[i].Enabled) {
+ sorted_links[i].Active = true;
+ if (focused_index != -1)
+ sorted_links[focused_index].Focused = false;
+ active_link = sorted_links[i];
+ focused_index = i;
+ sorted_links[focused_index].Focused = true;
+ break;
+ }
+ }
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ if (!Enabled) return;
+ base.OnMouseLeave (e);
+ UpdateHover (null);
+ }
+
+ protected override void OnPaddingChanged (EventArgs e)
+ {
+ base.OnPaddingChanged (e);
+ }
+
+ private void UpdateHover (Link link)
+ {
+ if (link == hovered_link)
+ return;
+
+ if (hovered_link != null)
+ hovered_link.Hovered = false;
+
+ hovered_link = link;
+
+ if (hovered_link != null)
+ hovered_link.Hovered = true;
+
+ Cursor = (hovered_link != null) ? OverrideCursor : Cursors.Default;
+
+ /* XXX this shouldn't be here. the
+ * Link.Invalidate machinery should be enough,
+ * but it seems the piece regions don't
+ * contain the area with the underline. this
+ * can be seen easily when you click on a link
+ * and the focus rectangle shows up (it's too
+ * far up), and also the bottom few pixels of
+ * a linklabel aren't active when it comes to
+ * hovering */
+ Invalidate ();
+ }
+
+ protected override void OnMouseMove (MouseEventArgs e)
+ {
+ UpdateHover (PointInLink (e.X, e.Y));
+ base.OnMouseMove (e);
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e)
+ {
+ if (!Enabled) return;
+
+ base.OnMouseUp (e);
+
+ if (active_link == null)
+ return;
+
+ Link clicked_link = (PointInLink (e.X, e.Y) == active_link) ? active_link : null;
+
+ active_link.Active = false;
+ active_link = null;
+
+ if (clicked_link != null)
+ OnLinkClicked (new LinkLabelLinkClickedEventArgs (clicked_link, e.Button));
+ }
+
+ protected override void OnClick (EventArgs e)
+ {
+ if (active_link != null && this.Capture) {
+ this.Capture = false;
+ }
+ base.OnClick (e);
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ // We need to invoke paintbackground because control is opaque
+ // and can have transparent colors.
+ base.InvokePaintBackground (this, e);
+
+ ThemeElements.LinkLabelPainter.Draw (e.Graphics, e.ClipRectangle, this);
+ // Do not call base.OnPaint since it's the Label class
+ }
+
+ protected override void OnPaintBackground (PaintEventArgs e)
+ {
+ base.OnPaintBackground (e);
+ }
+
+ protected override void OnTextAlignChanged (EventArgs e)
+ {
+ CreateLinkPieces ();
+ base.OnTextAlignChanged (e);
+ }
+
+ protected override void OnTextChanged (EventArgs e)
+ {
+ CreateLinkPieces ();
+ base.OnTextChanged (e);
+ }
+
+ protected Link PointInLink (int x, int y)
+ {
+ for (int i = 0; i < sorted_links.Length; i ++)
+ if (sorted_links[i].Contains (x, y))
+ return sorted_links[i];
+
+ return null;
+ }
+
+ protected override bool ProcessDialogKey (Keys keyData)
+ {
+ if ((keyData & Keys.KeyCode) == Keys.Tab) {
+ Select (true, (keyData & Keys.Shift) == 0);
+ return true;
+ }
+ return base.ProcessDialogKey (keyData);
+ }
+
+ protected override void Select (bool directed, bool forward)
+ {
+ if (directed) {
+ if (focused_index != -1) {
+ sorted_links[focused_index].Focused = false;
+ focused_index = -1;
+ }
+
+ if (forward) {
+ for (int n = focused_index + 1; n < sorted_links.Length; n++) {
+ if (sorted_links[n].Enabled) {
+ sorted_links[n].Focused = true;
+ focused_index = n;
+ base.Select (directed, forward);
+ return;
+ }
+ }
+ } else {
+ if (focused_index == -1)
+ focused_index = sorted_links.Length;
+
+ for (int n = focused_index - 1; n >= 0; n--) {
+ if (sorted_links[n].Enabled) {
+ sorted_links[n].Focused = true;
+ focused_index = n;
+ base.Select (directed, forward);
+ return;
+ }
+ }
+ }
+
+ focused_index = -1;
+
+ if (Parent != null)
+ Parent.SelectNextWidget (this, forward, false, true, true);
+ }
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ base.SetBoundsCore (x, y, width, height, specified);
+ CreateLinkPieces();
+ }
+
+ protected override void WndProc (ref Message msg)
+ {
+ base.WndProc (ref msg);
+ }
+
+ #endregion //Public Methods
+
+ #region Private Methods
+
+ private ArrayList CreatePiecesFromText (int start, int len, Link link)
+ {
+ ArrayList rv = new ArrayList ();
+
+ if (start + len > Text.Length)
+ len = Text.Length - start;
+ if (len < 0)
+ return rv;
+
+ string t = Text.Substring (start, len);
+
+ int ps = 0;
+ for (int i = 0; i < t.Length; i ++) {
+ if (t[i] == '\n') {
+ if (i != 0) {
+ Piece p = new Piece (start + ps, i + 1 - ps, t.Substring (ps, i+1-ps), link);
+ rv.Add (p);
+ }
+ ps = i+1;
+ }
+ }
+ if (ps < t.Length) {
+ Piece p = new Piece (start + ps, t.Length - ps, t.Substring (ps, t.Length-ps), link);
+ rv.Add (p);
+ }
+
+ return rv;
+ }
+
+ private void CreateLinkPieces ()
+ {
+ if (Text.Length == 0) {
+ SetStyle (Widgetstyles.Selectable, false);
+ TabStop = false;
+ link_area.Start = 0;
+ link_area.Length = 0;
+ return;
+ }
+
+ if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1)
+ Links[0].Length = Text.Length;
+
+ SortLinks ();
+
+ // Set the LinkArea values based on first link.
+ if (Links.Count > 0) {
+ link_area.Start = Links[0].Start;
+ link_area.Length = Links[0].Length;
+ } else {
+ link_area.Start = 0;
+ link_area.Length = 0;
+ }
+
+ TabStop = (LinkArea.Length > 0);
+ SetStyle (Widgetstyles.Selectable, TabStop);
+
+ /* don't bother doing the rest if our handle hasn't been created */
+ if (!IsHandleCreated)
+ return;
+
+ ArrayList pieces_list = new ArrayList ();
+
+ int current_end = 0;
+
+ for (int l = 0; l < sorted_links.Length; l ++) {
+ int new_link_start = sorted_links[l].Start;
+
+ if (new_link_start > current_end) {
+ /* create/push a piece
+ * containing the text between
+ * the previous/new link */
+ ArrayList text_pieces = CreatePiecesFromText (current_end, new_link_start - current_end, null);
+ pieces_list.AddRange (text_pieces);
+ }
+
+ /* now create a group of pieces for
+ * the new link (split up by \n's) */
+ ArrayList link_pieces = CreatePiecesFromText (new_link_start, sorted_links[l].Length, sorted_links[l]);
+ pieces_list.AddRange (link_pieces);
+ sorted_links[l].pieces.AddRange (link_pieces);
+
+ current_end = sorted_links[l].Start + sorted_links[l].Length;
+ }
+ if (current_end < Text.Length) {
+ ArrayList text_pieces = CreatePiecesFromText (current_end, Text.Length - current_end, null);
+ pieces_list.AddRange (text_pieces);
+ }
+
+ pieces = new Piece[pieces_list.Count];
+ pieces_list.CopyTo (pieces, 0);
+
+ CharacterRange[] ranges = new CharacterRange[pieces.Length];
+
+ for(int i = 0; i < pieces.Length; i++)
+ ranges[i] = new CharacterRange (pieces[i].start, pieces[i].length);
+
+ string_format.SetMeasurableCharacterRanges (ranges);
+
+ Region[] regions = TextRenderer.MeasureCharacterRanges (Text,
+ ThemeEngine.Current.GetLinkFont (this),
+ PaddingClientRectangle,
+ string_format);
+
+ for (int i = 0; i < pieces.Length; i ++) {
+ pieces[i].region = regions[i];
+ pieces[i].region.Translate (Padding.Left, Padding.Top);
+ }
+
+ Invalidate ();
+ }
+
+ private void SortLinks ()
+ {
+ if (sorted_links != null)
+ return;
+
+ sorted_links = new Link [Links.Count];
+ ((ICollection)Links).CopyTo (sorted_links, 0);
+
+ Array.Sort (sorted_links, new LinkComparer ());
+ }
+
+ /* Check if the links overlap */
+ private void CheckLinks ()
+ {
+ SortLinks ();
+
+ int current_end = 0;
+
+ for (int i = 0; i < sorted_links.Length; i++) {
+ if (sorted_links[i].Start < current_end)
+ throw new InvalidOperationException ("Overlapping link regions.");
+ current_end = sorted_links[i].Start + sorted_links[i].Length;
+ }
+ }
+
+ #endregion // Private Methods
+
+ //
+ // ShiftUI.LinkLabel.Link
+ //
+ [TypeConverter (typeof (LinkConverter))]
+ public class Link
+ {
+ private bool enabled;
+ internal int length;
+ private object linkData;
+ private int start;
+ private bool visited;
+ private LinkLabel owner;
+ private bool hovered;
+ internal ArrayList pieces;
+ private bool focused;
+ private bool active;
+ private string description;
+ private string name;
+ private object tag;
+
+ internal Link (LinkLabel owner)
+ {
+ focused = false;
+ enabled = true;
+ visited = false;
+ length = start = 0;
+ linkData = null;
+ this.owner = owner;
+ pieces = new ArrayList ();
+ name = string.Empty;
+ }
+
+ public Link ()
+ {
+ this.enabled = true;
+ this.name = string.Empty;
+ this.pieces = new ArrayList ();
+ }
+
+ public Link (int start, int length) : this ()
+ {
+ this.start = start;
+ this.length = length;
+ }
+
+ public Link (int start, int length, Object linkData) : this (start, length)
+ {
+ this.linkData = linkData;
+ }
+
+ #region Public Properties
+ public string Description {
+ get { return this.description; }
+ set { this.description = value; }
+ }
+
+ [DefaultValue ("")]
+ public string Name {
+ get { return this.name; }
+ set { this.name = value; }
+ }
+
+ [Bindable (true)]
+ [Localizable (false)]
+ [DefaultValue (null)]
+ [TypeConverter (typeof (StringConverter))]
+ public Object Tag {
+ get { return this.tag; }
+ set { this.tag = value; }
+ }
+
+ [DefaultValue (true)]
+ public bool Enabled {
+ get { return enabled; }
+ set {
+ if (enabled != value)
+ Invalidate ();
+
+ enabled = value;
+ }
+ }
+
+ public int Length {
+ get {
+ if (length == -1) {
+ return owner.Text.Length;
+ }
+
+ return length;
+ }
+ set {
+ if (length == value)
+ return;
+
+ length = value;
+
+ owner.CreateLinkPieces ();
+ }
+ }
+
+ [DefaultValue (null)]
+ public object LinkData {
+ get { return linkData; }
+ set { linkData = value; }
+ }
+
+ public int Start {
+ get { return start; }
+ set {
+ if (start == value)
+ return;
+
+ start = value;
+
+ owner.sorted_links = null;
+ owner.CreateLinkPieces ();
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool Visited {
+ get { return visited; }
+ set {
+ if (visited != value)
+ Invalidate ();
+
+ visited = value;
+ }
+ }
+
+ internal bool Hovered {
+ get { return hovered; }
+ set {
+ if (hovered != value)
+ Invalidate ();
+ hovered = value;
+ }
+ }
+
+ internal bool Focused {
+ get { return focused; }
+ set {
+ if (focused != value)
+ Invalidate ();
+ focused = value;
+ }
+ }
+
+ internal bool Active {
+ get { return active; }
+ set {
+ if (active != value)
+ Invalidate ();
+ active = value;
+ }
+ }
+
+ internal LinkLabel Owner {
+ set { owner = value; }
+ }
+ #endregion
+
+ private void Invalidate ()
+ {
+ for (int i = 0; i < pieces.Count; i ++)
+ owner.Invalidate (((Piece)pieces[i]).region);
+ }
+
+ internal bool Contains (int x, int y)
+ {
+ foreach (Piece p in pieces) {
+ if (p.region.IsVisible (new Point (x,y)))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ class LinkComparer : IComparer
+ {
+ public int Compare (object x, object y)
+ {
+ Link l1 = (Link)x;
+ Link l2 = (Link)y;
+
+ return l1.Start - l2.Start;
+ }
+ }
+
+ //
+ // ShiftUI.LinkLabel.LinkCollection
+ //
+ public class LinkCollection : IList, ICollection, IEnumerable
+ {
+ private LinkLabel owner;
+ private bool links_added;
+
+ public LinkCollection (LinkLabel owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException ("owner");
+
+ this.owner = owner;
+ }
+
+ [Browsable (false)]
+ public int Count {
+ get { return owner.links.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual LinkLabel.Link this[int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException();
+
+ return (LinkLabel.Link) owner.links[index];
+ }
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException();
+
+ owner.links[index] = value;
+ }
+ }
+
+ public virtual Link this[string key] {
+ get {
+ if (string.IsNullOrEmpty (key))
+ return null;
+
+ foreach (Link l in owner.links)
+ if (string.Compare (l.Name, key, true) == 0)
+ return l;
+
+ return null;
+ }
+ }
+
+ public int Add (Link value)
+ {
+ value.Owner = owner;
+ /* remove the default 0,-1 link */
+ if (IsDefault) {
+ /* don't call Clear() here to save the additional CreateLinkPieces */
+ owner.links.Clear ();
+ }
+
+ int idx = owner.links.Add (value);
+ links_added = true;
+
+ owner.sorted_links = null;
+ owner.CheckLinks ();
+ owner.CreateLinkPieces ();
+
+ return idx;
+ }
+
+ public Link Add (int start, int length)
+ {
+ return Add (start, length, null);
+ }
+
+ internal bool IsDefault {
+ get {
+ return (Count == 1
+ && this[0].Start == 0
+ && this[0].length == -1);
+ }
+ }
+
+ public Link Add (int start, int length, object linkData)
+ {
+ Link link = new Link (owner);
+ link.Length = length;
+ link.Start = start;
+ link.LinkData = linkData;
+
+ int idx = Add (link);
+
+ return (Link) owner.links[idx];
+ }
+
+ public virtual void Clear ()
+ {
+ owner.links.Clear();
+ owner.sorted_links = null;
+ owner.CreateLinkPieces ();
+ }
+
+ public bool Contains (Link link)
+ {
+ return owner.links.Contains (link);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return !(this[key] == null);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return owner.links.GetEnumerator ();
+ }
+
+ public int IndexOf (Link link)
+ {
+ return owner.links.IndexOf (link);
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (string.IsNullOrEmpty (key))
+ return -1;
+
+ return IndexOf (this[key]);
+ }
+
+ public bool LinksAdded {
+ get { return this.links_added; }
+ }
+
+ public void Remove (LinkLabel.Link value)
+ {
+ owner.links.Remove (value);
+ owner.sorted_links = null;
+ owner.CreateLinkPieces ();
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ Remove (this[key]);
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index >= Count)
+ throw new ArgumentOutOfRangeException ("Invalid value for array index");
+
+ owner.links.Remove (owner.links[index]);
+ owner.sorted_links = null;
+ owner.CreateLinkPieces ();
+ }
+
+ bool IList.IsFixedSize {
+ get {return false;}
+ }
+
+ object IList.this[int index] {
+ get { return owner.links[index]; }
+ set { owner.links[index] = value; }
+ }
+
+ object ICollection.SyncRoot {
+ get {return this;}
+ }
+
+ bool ICollection.IsSynchronized {
+ get {return false;}
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ owner.links.CopyTo (dest, index);
+ }
+
+ int IList.Add (object value)
+ {
+ int idx = owner.links.Add (value);
+ owner.sorted_links = null;
+ owner.CheckLinks ();
+ owner.CreateLinkPieces ();
+ return idx;
+ }
+
+ bool IList.Contains (object link)
+ {
+ return Contains ((Link) link);
+ }
+
+ int IList.IndexOf (object link)
+ {
+ return owner.links.IndexOf (link);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ owner.links.Insert (index, value);
+ owner.sorted_links = null;
+ owner.CheckLinks ();
+ owner.CreateLinkPieces ();
+ }
+
+ void IList.Remove (object value)
+ {
+ Remove ((Link) value);
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public new bool UseCompatibleTextRendering {
+ get {
+ return use_compatible_text_rendering;
+ }
+ set {
+ use_compatible_text_rendering = value;
+ }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/ListBox.cs b/source/ShiftUI/Widgets/ListBox.cs
new file mode 100644
index 0000000..ed705f6
--- /dev/null
+++ b/source/ShiftUI/Widgets/ListBox.cs
@@ -0,0 +1,3093 @@
+/// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Mike Kestner <[email protected]>
+//
+
+// COMPLETE
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.ComponentModel.Design.Serialization;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+namespace ShiftUI
+{
+ [DefaultProperty("Items")]
+ [DefaultEvent("SelectedIndexChanged")]
+ //[Designer ("ShiftUI.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultBindingProperty ("SelectedValue")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class ListBox : ListWidget
+ {
+ public const int DefaultItemHeight = 13;
+ public const int NoMatches = -1;
+
+ internal enum ItemNavigation
+ {
+ First,
+ Last,
+ Next,
+ Previous,
+ NextPage,
+ PreviousPage,
+ PreviousColumn,
+ NextColumn
+ }
+
+ Hashtable item_heights;
+ private int item_height = -1;
+ private int column_width = 0;
+ private int requested_height;
+ private DrawMode draw_mode = DrawMode.Normal;
+ private int horizontal_extent = 0;
+ private bool horizontal_scrollbar = false;
+ private bool integral_height = true;
+ private bool multicolumn = false;
+ private bool scroll_always_visible = false;
+ private SelectedIndexCollection selected_indices;
+ private SelectedObjectCollection selected_items;
+ private SelectionMode selection_mode = SelectionMode.One;
+ private bool sorted = false;
+ private bool use_tabstops = true;
+ private int column_width_internal = 120;
+ private ImplicitVScrollBar vscrollbar;
+ private ImplicitHScrollBar hscrollbar;
+ private int hbar_offset;
+ private bool suspend_layout;
+ private bool ctrl_pressed = false;
+ private bool shift_pressed = false;
+ private bool explicit_item_height = false;
+ private int top_index = 0;
+ private int last_visible_index = 0;
+ private Rectangle items_area;
+ private int focused_item = -1;
+ private ObjectCollection items;
+ private IntegerCollection custom_tab_offsets;
+ private Padding padding;
+ private bool use_custom_tab_offsets;
+
+ public ListBox ()
+ {
+ items = CreateItemCollection ();
+ selected_indices = new SelectedIndexCollection (this);
+ selected_items = new SelectedObjectCollection (this);
+
+ requested_height = bounds.Height;
+ InternalBorderStyle = BorderStyle.Fixed3D;
+ BackColor = ThemeEngine.Current.ColorControl;
+
+ /* Vertical scrollbar */
+ vscrollbar = new ImplicitVScrollBar ();
+ vscrollbar.Minimum = 0;
+ vscrollbar.SmallChange = 1;
+ vscrollbar.LargeChange = 1;
+ vscrollbar.Maximum = 0;
+ vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
+ vscrollbar.Visible = false;
+
+ /* Horizontal scrollbar */
+ hscrollbar = new ImplicitHScrollBar ();
+ hscrollbar.Minimum = 0;
+ hscrollbar.SmallChange = 1;
+ hscrollbar.LargeChange = 1;
+ hscrollbar.Maximum = 0;
+ hscrollbar.Visible = false;
+ hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
+
+ Widgets.AddImplicit (vscrollbar);
+ Widgets.AddImplicit (hscrollbar);
+
+ /* Events */
+ MouseDown += new MouseEventHandler (OnMouseDownLB);
+ MouseMove += new MouseEventHandler (OnMouseMoveLB);
+ MouseUp += new MouseEventHandler (OnMouseUpLB);
+ MouseWheel += new MouseEventHandler (OnMouseWheelLB);
+ KeyUp += new KeyEventHandler (OnKeyUpLB);
+ GotFocus += new EventHandler (OnGotFocus);
+ LostFocus += new EventHandler (OnLostFocus);
+
+ SetStyle (Widgetstyles.UserPaint, false);
+
+ custom_tab_offsets = new IntegerCollection (this);
+ }
+
+ #region Events
+ static object DrawItemEvent = new object ();
+ static object MeasureItemEvent = new object ();
+ static object SelectedIndexChangedEvent = new object ();
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ public event DrawItemEventHandler DrawItem {
+ add { Events.AddHandler (DrawItemEvent, value); }
+ remove { Events.RemoveHandler (DrawItemEvent, value); }
+ }
+
+ public event MeasureItemEventHandler MeasureItem {
+ add { Events.AddHandler (MeasureItemEvent, value); }
+ remove { Events.RemoveHandler (MeasureItemEvent, value); }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler SelectedIndexChanged {
+ add { Events.AddHandler (SelectedIndexChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion // Events
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA Selection Pattern
+ static object UIASelectionModeChangedEvent = new object ();
+
+ internal event EventHandler UIASelectionModeChanged {
+ add { Events.AddHandler (UIASelectionModeChangedEvent, value); }
+ remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); }
+ }
+
+ internal void OnUIASelectionModeChangedEvent ()
+ {
+ EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ static object UIAFocusedItemChangedEvent = new object ();
+
+ internal event EventHandler UIAFocusedItemChanged {
+ add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
+ }
+
+ internal void OnUIAFocusedItemChangedEvent ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+ #endregion UIA Framework Events
+
+ #region Public Properties
+ public override Color BackColor {
+ get { return base.BackColor; }
+ set {
+ if (base.BackColor == value)
+ return;
+
+ base.BackColor = value;
+ base.Refresh (); // Careful. Calling the base method is not the same that calling
+ } // the overriden one that refresh also all the items
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set {
+ base.BackgroundImage = value;
+ base.Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue (BorderStyle.Fixed3D)]
+ [DispId(-504)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set {
+ InternalBorderStyle = value;
+ UpdateListBoxBounds ();
+ }
+ }
+
+ [DefaultValue (0)]
+ [Localizable (true)]
+ public int ColumnWidth {
+ get { return column_width; }
+ set {
+ if (value < 0)
+ throw new ArgumentException ("A value less than zero is assigned to the property.");
+
+ column_width = value;
+
+ if (value == 0)
+ ColumnWidthInternal = 120;
+ else
+ ColumnWidthInternal = value;
+
+ base.Refresh ();
+ }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams;}
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ public IntegerCollection CustomTabOffsets {
+ get { return custom_tab_offsets; }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (120, 96); }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [DefaultValue (DrawMode.Normal)]
+ public virtual DrawMode DrawMode {
+ get { return draw_mode; }
+ set {
+ if (!Enum.IsDefined (typeof (DrawMode), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
+
+ if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
+ throw new ArgumentException ("Cannot have variable height and multicolumn");
+
+ if (draw_mode == value)
+ return;
+
+ draw_mode = value;
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights = new Hashtable ();
+ else
+ item_heights = null;
+
+ if (Parent != null)
+ Parent.PerformLayout (this, "DrawMode");
+ base.Refresh ();
+ }
+ }
+
+ public override Font Font {
+ get { return base.Font; }
+ set { base.Font = value; }
+ }
+
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set {
+ if (base.ForeColor == value)
+ return;
+
+ base.ForeColor = value;
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (0)]
+ [Localizable (true)]
+ public int HorizontalExtent {
+ get { return horizontal_extent; }
+ set {
+ if (horizontal_extent == value)
+ return;
+
+ horizontal_extent = value;
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public bool HorizontalScrollbar {
+ get { return horizontal_scrollbar; }
+ set {
+ if (horizontal_scrollbar == value)
+ return;
+
+ horizontal_scrollbar = value;
+ UpdateScrollBars ();
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (true)]
+ [Localizable (true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public bool IntegralHeight {
+ get { return integral_height; }
+ set {
+ if (integral_height == value)
+ return;
+
+ integral_height = value;
+ UpdateListBoxBounds ();
+ }
+ }
+
+ [DefaultValue (13)]
+ [Localizable (true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public virtual int ItemHeight {
+ get {
+ if (item_height == -1) {
+ SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
+ item_height = (int) sz.Height;
+ }
+ return item_height;
+ }
+ set {
+ if (value > 255)
+ throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
+
+ explicit_item_height = true;
+ if (item_height == value)
+ return;
+
+ item_height = value;
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+ LayoutListBox ();
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetstringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [MergableProperty (false)]
+ public ObjectCollection Items {
+ get { return items; }
+ }
+
+ [DefaultValue (false)]
+ public bool MultiColumn {
+ get { return multicolumn; }
+ set {
+ if (multicolumn == value)
+ return;
+
+ if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
+ throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
+
+ multicolumn = value;
+ LayoutListBox ();
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return padding; }
+ set { padding = value; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public int PreferredHeight {
+ get {
+ int itemsHeight = 0;
+ if (draw_mode == DrawMode.Normal)
+ itemsHeight = FontHeight * items.Count;
+ else if (draw_mode == DrawMode.OwnerDrawFixed)
+ itemsHeight = ItemHeight * items.Count;
+ else if (draw_mode == DrawMode.OwnerDrawVariable) {
+ for (int i = 0; i < items.Count; i++)
+ itemsHeight += (int) item_heights [Items [i]];
+ }
+
+ return itemsHeight;
+ }
+ }
+
+ public override RightToLeft RightToLeft {
+ get { return base.RightToLeft; }
+ set {
+ base.RightToLeft = value;
+ if (base.RightToLeft == RightToLeft.Yes)
+ StringFormat.Alignment = StringAlignment.Far;
+ else
+ StringFormat.Alignment = StringAlignment.Near;
+ base.Refresh ();
+ }
+ }
+
+ // Only affects the Vertical ScrollBar
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public bool ScrollAlwaysVisible {
+ get { return scroll_always_visible; }
+ set {
+ if (scroll_always_visible == value)
+ return;
+
+ scroll_always_visible = value;
+ UpdateScrollBars ();
+ }
+ }
+
+ [Bindable(true)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override int SelectedIndex {
+ get {
+ if (selected_indices == null)
+ return -1;
+
+ return selected_indices.Count > 0 ? selected_indices [0] : -1;
+ }
+ set {
+ if (value < -1 || value >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (SelectionMode == SelectionMode.None)
+ throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
+
+ if (value == -1)
+ selected_indices.Clear ();
+ else
+ selected_indices.Add (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public SelectedIndexCollection SelectedIndices {
+ get { return selected_indices; }
+ }
+
+ [Bindable(true)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public object SelectedItem {
+ get {
+ if (SelectedItems.Count > 0)
+ return SelectedItems[0];
+ else
+ return null;
+ }
+ set {
+ if (value != null && !Items.Contains (value))
+ return; // FIXME: this is probably an exception
+
+ SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public SelectedObjectCollection SelectedItems {
+ get {return selected_items;}
+ }
+
+ [DefaultValue (SelectionMode.One)]
+ public virtual SelectionMode SelectionMode {
+ get { return selection_mode; }
+ set {
+ if (!Enum.IsDefined (typeof (SelectionMode), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
+
+ if (selection_mode == value)
+ return;
+
+ selection_mode = value;
+
+ switch (selection_mode) {
+ case SelectionMode.None:
+ SelectedIndices.Clear ();
+ break;
+
+ case SelectionMode.One:
+ // FIXME: Probably this can be improved
+ ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
+ for (int i = 1; i < old_selection.Count; i++)
+ SelectedIndices.Remove ((int)old_selection [i]);
+ break;
+
+ default:
+ break;
+ }
+
+ // UIA Framework: Generates SelectionModeChanged event.
+ OnUIASelectionModeChangedEvent ();
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool Sorted {
+ get { return sorted; }
+ set {
+ if (sorted == value)
+ return;
+
+ sorted = value;
+ if (sorted)
+ Sort ();
+ }
+ }
+
+ [Bindable (false)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public override string Text {
+ get {
+ if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
+ return GetItemText (SelectedItem);
+
+ return base.Text;
+ }
+ set {
+
+ base.Text = value;
+
+ if (SelectionMode == SelectionMode.None)
+ return;
+
+ int index;
+
+ index = FindStringExact (value);
+
+ if (index == -1)
+ return;
+
+ SelectedIndex = index;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int TopIndex {
+ get { return top_index; }
+ set {
+ if (value == top_index)
+ return;
+
+ if (value < 0 || value >= Items.Count)
+ return;
+
+ int page_size = (items_area.Height / ItemHeight);
+
+ if (Items.Count < page_size)
+ value = 0;
+ else if (!multicolumn)
+ top_index = Math.Min (value, Items.Count - page_size);
+ else
+ top_index = value;
+
+ UpdateTopItem ();
+ base.Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ [DefaultValue (false)]
+ public bool UseCustomTabOffsets {
+ get { return use_custom_tab_offsets; }
+ set {
+ if (use_custom_tab_offsets != value) {
+ use_custom_tab_offsets = value;
+ CalculateTabStops ();
+ }
+ }
+ }
+
+ [DefaultValue (true)]
+ public bool UseTabStops {
+ get { return use_tabstops; }
+ set {
+ if (use_tabstops == value)
+ return;
+
+ use_tabstops = value;
+ CalculateTabStops ();
+ }
+ }
+
+ protected override bool AllowSelection {
+ get {
+ return SelectionMode != SelectionMode.None;
+ }
+ }
+ #endregion Public Properties
+
+ #region Private Properties
+
+ private int ColumnWidthInternal {
+ get { return column_width_internal; }
+ set { column_width_internal = value; }
+ }
+
+ private int row_count = 1;
+ private int RowCount {
+ get {
+ return MultiColumn ? row_count : Items.Count;
+ }
+ }
+
+ #endregion Private Properties
+
+ #region UIA Framework Properties
+
+ internal ScrollBar UIAHScrollBar {
+ get { return hscrollbar; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return vscrollbar; }
+ }
+
+ #endregion UIA Framework Properties
+
+ #region Public Methods
+ [Obsolete ("this method has been deprecated")]
+ protected virtual void AddItemsCore (object[] value)
+ {
+ Items.AddRange (value);
+ }
+
+ public void BeginUpdate ()
+ {
+ suspend_layout = true;
+ }
+
+ public void ClearSelected ()
+ {
+ selected_indices.Clear ();
+ }
+
+ protected virtual ObjectCollection CreateItemCollection ()
+ {
+ return new ObjectCollection (this);
+ }
+
+ public void EndUpdate ()
+ {
+ suspend_layout = false;
+ LayoutListBox ();
+ base.Refresh ();
+ }
+
+ public int FindString (String s)
+ {
+ return FindString (s, -1);
+ }
+
+ public int FindString (string s, int startIndex)
+ {
+ if (Items.Count == 0)
+ return -1; // No exception throwing if empty
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
+
+ int i = startIndex;
+ while (true) {
+ string text = GetItemText (Items [i]);
+ if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
+ CompareOptions.IgnoreCase))
+ return i;
+
+ i = (i == Items.Count - 1) ? 0 : i + 1;
+ if (i == startIndex)
+ break;
+ }
+
+ return NoMatches;
+ }
+
+ public int FindStringExact (string s)
+ {
+ return FindStringExact (s, -1);
+ }
+
+ public int FindStringExact (string s, int startIndex)
+ {
+ if (Items.Count == 0)
+ return -1; // No exception throwing if empty
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
+
+ int i = startIndex;
+ while (true) {
+ if (String.Compare (GetItemText (Items[i]), s, true) == 0)
+ return i;
+
+ i = (i + 1 == Items.Count) ? 0 : i + 1;
+ if (i == startIndex)
+ break;
+ }
+
+ return NoMatches;
+ }
+
+ public int GetItemHeight (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
+
+ object o = Items [index];
+ if (item_heights.Contains (o))
+ return (int) item_heights [o];
+
+ MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
+ OnMeasureItem (args);
+ item_heights [o] = args.ItemHeight;
+ return args.ItemHeight;
+ }
+
+ return ItemHeight;
+ }
+
+ public Rectangle GetItemRectangle (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
+
+ Rectangle rect = new Rectangle ();
+
+ if (MultiColumn) {
+ int col = index / RowCount;
+ int y = index;
+ if (y < 0) // We convert it to valid positive value
+ y += RowCount * (top_index / RowCount);
+ rect.Y = (y % RowCount) * ItemHeight;
+ rect.X = (col - (top_index / RowCount)) * ColumnWidthInternal;
+ rect.Height = ItemHeight;
+ rect.Width = ColumnWidthInternal;
+ } else {
+ rect.X = 0;
+ rect.Height = GetItemHeight (index);
+ rect.Width = items_area.Width;
+
+ if (DrawMode == DrawMode.OwnerDrawVariable) {
+ rect.Y = 0;
+ if (index >= top_index) {
+ for (int i = top_index; i < index; i++) {
+ rect.Y += GetItemHeight (i);
+ }
+ } else {
+ for (int i = index; i < top_index; i++) {
+ rect.Y -= GetItemHeight (i);
+ }
+ }
+ } else {
+ rect.Y = ItemHeight * (index - top_index);
+ }
+ }
+
+ if (this is CheckedListBox)
+ rect.Width += 15;
+
+ return rect;
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
+ {
+ bounds.Height = requested_height;
+
+ return base.GetScaledBounds (bounds, factor, specified);
+ }
+
+ public bool GetSelected (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return SelectedIndices.Contains (index);
+ }
+
+ public int IndexFromPoint (Point p)
+ {
+ return IndexFromPoint (p.X, p.Y);
+ }
+
+ // Only returns visible points
+ public int IndexFromPoint (int x, int y)
+ {
+
+ if (Items.Count == 0) {
+ return -1;
+ }
+
+ for (int i = top_index; i <= last_visible_index; i++) {
+ if (GetItemRectangle (i).Contains (x,y) == true)
+ return i;
+ }
+
+ return -1;
+ }
+
+ protected override void OnChangeUICues (UICuesEventArgs e)
+ {
+ base.OnChangeUICues (e);
+ }
+
+ protected override void OnDataSourceChanged (EventArgs e)
+ {
+ base.OnDataSourceChanged (e);
+ BindDataItems ();
+
+ if (DataSource == null || DataManager == null) {
+ SelectedIndex = -1;
+ } else {
+ SelectedIndex = DataManager.Position;
+ }
+ }
+
+ protected override void OnDisplayMemberChanged (EventArgs e)
+ {
+ base.OnDisplayMemberChanged (e);
+
+ if (DataManager == null || !IsHandleCreated)
+ return;
+
+ BindDataItems ();
+ base.Refresh ();
+ }
+
+ protected virtual void OnDrawItem (DrawItemEventArgs e)
+ {
+ switch (DrawMode) {
+ case DrawMode.OwnerDrawFixed:
+ case DrawMode.OwnerDrawVariable:
+ DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ break;
+
+ default:
+ ThemeEngine.Current.DrawListBoxItem (this, e);
+ break;
+ }
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+
+ if (use_tabstops)
+ StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
+
+ if (explicit_item_height) {
+ base.Refresh ();
+ } else {
+ SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
+ item_height = (int) sz.Height;
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+ LayoutListBox ();
+ }
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+
+ LayoutListBox ();
+ EnsureVisible (focused_item);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnMeasureItem (MeasureItemEventArgs e)
+ {
+ if (draw_mode != DrawMode.OwnerDrawVariable)
+ return;
+
+ MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+ if (canvas_size.IsEmpty || MultiColumn)
+ LayoutListBox ();
+
+ Invalidate ();
+ }
+
+ protected override void OnSelectedIndexChanged (EventArgs e)
+ {
+ base.OnSelectedIndexChanged (e);
+
+ EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnSelectedValueChanged (EventArgs e)
+ {
+ base.OnSelectedValueChanged (e);
+ }
+
+ public override void Refresh ()
+ {
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights.Clear ();
+
+ base.Refresh ();
+ }
+
+ protected override void RefreshItem (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights.Remove (Items [index]);
+ }
+
+ protected override void RefreshItems ()
+ {
+ for (int i = 0; i < Items.Count; i++) {
+ RefreshItem (i);
+ }
+
+ LayoutListBox ();
+ Refresh ();
+ }
+
+ public override void ResetBackColor ()
+ {
+ base.ResetBackColor ();
+ }
+
+ public override void ResetForeColor ()
+ {
+ base.ResetForeColor ();
+ }
+
+ protected override void ScaleWidget (SizeF factor, BoundsSpecified specified)
+ {
+ base.ScaleWidget (factor, specified);
+ }
+
+ private int SnapHeightToIntegral (int height)
+ {
+ int border;
+
+ switch (border_style) {
+ case BorderStyle.Fixed3D:
+ border = ThemeEngine.Current.Border3DSize.Height;
+ break;
+ case BorderStyle.FixedSingle:
+ border = ThemeEngine.Current.BorderSize.Height;
+ break;
+ case BorderStyle.None:
+ default:
+ border = 0;
+ break;
+ }
+
+ height -= (2 * border);
+ height -= height % ItemHeight;
+ height += (2 * border);
+
+ return height;
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
+ requested_height = height;
+
+ if (IntegralHeight && IsHandleCreated)
+ height = SnapHeightToIntegral (height);
+
+ base.SetBoundsCore (x, y, width, height, specified);
+ UpdateScrollBars ();
+ last_visible_index = LastVisibleItem ();
+ }
+
+ protected override void SetItemCore (int index, object value)
+ {
+ if (index < 0 || index >= Items.Count)
+ return;
+
+ Items[index] = value;
+ }
+
+ protected override void SetItemsCore (IList value)
+ {
+ BeginUpdate ();
+ try {
+ Items.Clear ();
+ Items.AddItems (value);
+ } finally {
+ EndUpdate ();
+ }
+ }
+
+ public void SetSelected (int index, bool value)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (SelectionMode == SelectionMode.None)
+ throw new InvalidOperationException ();
+
+ if (value)
+ SelectedIndices.Add (index);
+ else
+ SelectedIndices.Remove (index);
+ }
+
+ protected virtual void Sort ()
+ {
+ Sort (true);
+ }
+
+ //
+ // Sometimes we could need to Sort, and request a Refresh
+ // in a different place, to not have the painting done twice
+ //
+ void Sort (bool paint)
+ {
+ if (Items.Count == 0)
+ return;
+
+ Items.Sort ();
+
+ if (paint)
+ base.Refresh ();
+ }
+
+ public override string ToString ()
+ {
+ return base.ToString ();
+ }
+
+ protected virtual void WmReflectCommand (ref Message m)
+ {
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ if ((Msg)m.Msg == Msg.WM_KEYDOWN) {
+ if (ProcessKeyMessage (ref m))
+ m.Result = IntPtr.Zero;
+ else {
+ HandleKeyDown ((Keys)m.WParam.ToInt32 ());
+ DefWndProc (ref m);
+ }
+
+ return;
+ }
+
+ base.WndProc (ref m);
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ private void CalculateTabStops ()
+ {
+ if (use_tabstops) {
+ if (use_custom_tab_offsets) {
+ float[] f = new float[custom_tab_offsets.Count];
+ custom_tab_offsets.CopyTo (f, 0);
+ StringFormat.SetTabStops (0, f);
+ }
+ else
+ StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
+ } else
+ StringFormat.SetTabStops (0, new float[0]);
+
+ this.Invalidate ();
+ }
+
+ private Size canvas_size;
+
+ private void LayoutListBox ()
+ {
+ if (!IsHandleCreated || suspend_layout)
+ return;
+
+ if (MultiColumn)
+ LayoutMultiColumn ();
+ else
+ LayoutSingleColumn ();
+
+ last_visible_index = LastVisibleItem ();
+ UpdateScrollBars ();
+ }
+
+ private void LayoutSingleColumn ()
+ {
+ int height, width;
+
+ switch (DrawMode) {
+ case DrawMode.OwnerDrawVariable:
+ height = 0;
+ width = HorizontalExtent;
+ for (int i = 0; i < Items.Count; i++) {
+ height += GetItemHeight (i);
+ }
+ break;
+
+ case DrawMode.OwnerDrawFixed:
+ height = Items.Count * ItemHeight;
+ width = HorizontalExtent;
+ break;
+
+ case DrawMode.Normal:
+ default:
+ height = Items.Count * ItemHeight;
+ width = 0;
+ for (int i = 0; i < Items.Count; i++) {
+ SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
+ int t = (int)sz.Width;
+
+ if (this is CheckedListBox)
+ t += 15;
+
+ if (t > width)
+ width = t;
+ }
+ break;
+ }
+
+ canvas_size = new Size (width, height);
+ }
+
+ private void LayoutMultiColumn ()
+ {
+ int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
+ row_count = Math.Max (1, usable_height / ItemHeight);
+
+ int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
+ Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
+ if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
+ usable_height = ClientRectangle.Height - hscrollbar.Height;
+ row_count = Math.Max (1, usable_height / ItemHeight);
+ cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
+ sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
+ }
+ canvas_size = sz;
+ }
+
+ internal void Draw (Rectangle clip, Graphics dc)
+ {
+ Theme theme = ThemeEngine.Current;
+
+ if (hscrollbar.Visible && vscrollbar.Visible) {
+ // Paint the dead space in the bottom right corner
+ Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
+ if (rect.IntersectsWith (clip))
+ dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
+ }
+
+ dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
+
+ if (Items.Count == 0)
+ return;
+
+ for (int i = top_index; i <= last_visible_index; i++) {
+ Rectangle rect = GetItemDisplayRectangle (i, top_index);
+
+ if (!clip.IntersectsWith (rect))
+ continue;
+
+ DrawItemState state = DrawItemState.None;
+
+ if (SelectedIndices.Contains (i))
+ state |= DrawItemState.Selected;
+
+ if (has_focus && FocusedItem == i)
+ state |= DrawItemState.Focus;
+
+ if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
+ rect.X -= hscrollbar.Value;
+ rect.Width += hscrollbar.Value;
+ }
+
+ Color fore_color = !Enabled ? ThemeEngine.Current.ColorGrayText :
+ (state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor;
+ OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, BackColor));
+ }
+ }
+
+ // Converts a GetItemRectangle to a one that we can display
+ internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
+ {
+ Rectangle item_rect;
+ Rectangle first_item_rect = GetItemRectangle (first_displayble);
+ item_rect = GetItemRectangle (index);
+ item_rect.X -= first_item_rect.X;
+ item_rect.Y -= first_item_rect.Y;
+
+ // Subtract the checkboxes from the width
+ if (this is CheckedListBox)
+ item_rect.Width -= 14;
+
+ return item_rect;
+ }
+
+ // Value Changed
+ private void HorizontalScrollEvent (object sender, EventArgs e)
+ {
+ if (multicolumn) {
+ int top_item = top_index;
+ int last_item = last_visible_index;
+
+ top_index = RowCount * hscrollbar.Value;
+ last_visible_index = LastVisibleItem ();
+
+ if (top_item != top_index || last_item != last_visible_index)
+ Invalidate (items_area);
+ }
+ else {
+ int old_offset = hbar_offset;
+ hbar_offset = hscrollbar.Value;
+
+ if (hbar_offset < 0)
+ hbar_offset = 0;
+
+ if (IsHandleCreated) {
+ XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
+
+ // Invalidate the previous selection border, to keep it properly updated.
+ Rectangle selection_border_area = new Rectangle (items_area.Width - (hbar_offset - old_offset) - 3, 0,
+ 3, items_area.Height);
+ Invalidate (selection_border_area);
+ }
+ }
+ }
+
+ // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
+ // has screen coordinates
+ private int IndexAtClientPoint (int x, int y)
+ {
+ if (Items.Count == 0)
+ return -1;
+
+ if (x < 0)
+ x = 0;
+ else if (x > ClientRectangle.Right)
+ x = ClientRectangle.Right;
+
+ if (y < 0)
+ y = 0;
+ else if (y > ClientRectangle.Bottom)
+ y = ClientRectangle.Bottom;
+
+ for (int i = top_index; i <= last_visible_index; i++)
+ if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
+ return i;
+
+ return -1;
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+
+ private int LastVisibleItem ()
+ {
+ Rectangle item_rect;
+ int top_y = items_area.Y + items_area.Height;
+ int i = 0;
+
+ if (top_index >= Items.Count)
+ return top_index;
+
+ for (i = top_index; i < Items.Count; i++) {
+ item_rect = GetItemDisplayRectangle (i, top_index);
+ if (MultiColumn) {
+ if (item_rect.X > items_area.Width)
+ return i - 1;
+ } else {
+ if (item_rect.Y + item_rect.Height > top_y)
+ return i;
+ }
+ }
+ return i - 1;
+ }
+
+ private void UpdateTopItem ()
+ {
+ if (MultiColumn) {
+ int col = top_index / RowCount;
+
+ if (col > hscrollbar.Maximum)
+ hscrollbar.Value = hscrollbar.Maximum;
+ else
+ hscrollbar.Value = col;
+ } else {
+ if (top_index > vscrollbar.Maximum)
+ vscrollbar.Value = vscrollbar.Maximum;
+ else
+ vscrollbar.Value = top_index;
+ Scroll (vscrollbar, vscrollbar.Value - top_index);
+ }
+ }
+
+ // Navigates to the indicated item and returns the new item
+ private int NavigateItemVisually (ItemNavigation navigation)
+ {
+ int page_size, columns, selected_index = -1;
+
+ if (multicolumn) {
+ columns = items_area.Width / ColumnWidthInternal;
+ page_size = columns * RowCount;
+ if (page_size == 0) {
+ page_size = RowCount;
+ }
+ } else {
+ page_size = items_area.Height / ItemHeight;
+ }
+
+ switch (navigation) {
+
+ case ItemNavigation.PreviousColumn: {
+ if (SelectedIndex - RowCount < 0) {
+ return -1;
+ }
+
+ if (SelectedIndex - RowCount < top_index) {
+ top_index = SelectedIndex - RowCount;
+ UpdateTopItem ();
+ }
+
+ selected_index = SelectedIndex - RowCount;
+ break;
+ }
+
+ case ItemNavigation.NextColumn: {
+ if (SelectedIndex + RowCount >= Items.Count) {
+ break;
+ }
+
+ if (SelectedIndex + RowCount > last_visible_index) {
+ top_index = SelectedIndex;
+ UpdateTopItem ();
+ }
+
+ selected_index = SelectedIndex + RowCount;
+ break;
+ }
+
+ case ItemNavigation.First: {
+ top_index = 0;
+ selected_index = 0;
+ UpdateTopItem ();
+ break;
+ }
+
+ case ItemNavigation.Last: {
+
+ int rows = items_area.Height / ItemHeight;
+
+ if (multicolumn) {
+ selected_index = Items.Count - 1;
+ break;
+ }
+ if (Items.Count < rows) {
+ top_index = 0;
+ selected_index = Items.Count - 1;
+ UpdateTopItem ();
+ } else {
+ top_index = Items.Count - rows;
+ selected_index = Items.Count - 1;
+ UpdateTopItem ();
+ }
+ break;
+ }
+
+ case ItemNavigation.Next: {
+ if (FocusedItem == Items.Count - 1)
+ return -1;
+
+ if (multicolumn) {
+ selected_index = FocusedItem + 1;
+ break;
+ }
+
+ int bottom = 0;
+ ArrayList heights = new ArrayList ();
+ if (draw_mode == DrawMode.OwnerDrawVariable) {
+ for (int i = top_index; i <= FocusedItem + 1; i++) {
+ int h = GetItemHeight (i);
+ bottom += h;
+ heights.Add (h);
+ }
+ } else {
+ bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
+ }
+
+ if (bottom >= items_area.Height) {
+ int overhang = bottom - items_area.Height;
+
+ int offset = 0;
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ while (overhang > 0)
+ overhang -= (int) heights [offset];
+ else
+ offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
+ top_index += offset;
+ UpdateTopItem ();
+ }
+ selected_index = FocusedItem + 1;
+ break;
+ }
+
+ case ItemNavigation.Previous: {
+ if (FocusedItem > 0) {
+ if (FocusedItem - 1 < top_index) {
+ top_index--;
+ UpdateTopItem ();
+ }
+ selected_index = FocusedItem - 1;
+ }
+ break;
+ }
+
+ case ItemNavigation.NextPage: {
+ if (Items.Count < page_size) {
+ NavigateItemVisually (ItemNavigation.Last);
+ break;
+ }
+
+ if (FocusedItem + page_size - 1 >= Items.Count) {
+ top_index = Items.Count - page_size;
+ UpdateTopItem ();
+ selected_index = Items.Count - 1;
+ }
+ else {
+ if (FocusedItem + page_size - 1 > last_visible_index) {
+ top_index = FocusedItem;
+ UpdateTopItem ();
+ }
+
+ selected_index = FocusedItem + page_size - 1;
+ }
+
+ break;
+ }
+
+ case ItemNavigation.PreviousPage: {
+
+ int rows = items_area.Height / ItemHeight;
+ if (FocusedItem - (rows - 1) <= 0) {
+ top_index = 0;
+ UpdateTopItem ();
+ selected_index = 0;
+ }
+ else {
+ if (SelectedIndex - (rows - 1) < top_index) {
+ top_index = FocusedItem - (rows - 1);
+ UpdateTopItem ();
+ }
+
+ selected_index = FocusedItem - (rows - 1);
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return selected_index;
+ }
+
+
+ private void OnGotFocus (object sender, EventArgs e)
+ {
+ if (Items.Count == 0)
+ return;
+
+ if (FocusedItem == -1)
+ FocusedItem = 0;
+
+ InvalidateItem (FocusedItem);
+ }
+
+ private void OnLostFocus (object sender, EventArgs e)
+ {
+ if (FocusedItem != -1)
+ InvalidateItem (FocusedItem);
+ }
+
+ private bool KeySearch (Keys key)
+ {
+ char c = (char) key;
+ if (!Char.IsLetterOrDigit (c))
+ return false;
+
+ int idx = FindString (c.ToString (), SelectedIndex);
+ if (idx != ListBox.NoMatches)
+ SelectedIndex = idx;
+
+ return true;
+ }
+
+ internal void HandleKeyDown (Keys key)
+ {
+ int new_item = -1;
+
+ if (Items.Count == 0)
+ return;
+
+ if (KeySearch (key))
+ return;
+
+ switch (key) {
+
+ case Keys.ControlKey:
+ ctrl_pressed = true;
+ break;
+
+ case Keys.ShiftKey:
+ shift_pressed = true;
+ break;
+
+ case Keys.Home:
+ new_item = NavigateItemVisually (ItemNavigation.First);
+ break;
+
+ case Keys.End:
+ new_item = NavigateItemVisually (ItemNavigation.Last);
+ break;
+
+ case Keys.Up:
+ new_item = NavigateItemVisually (ItemNavigation.Previous);
+ break;
+
+ case Keys.Down:
+ new_item = NavigateItemVisually (ItemNavigation.Next);
+ break;
+
+ case Keys.PageUp:
+ new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
+ break;
+
+ case Keys.PageDown:
+ new_item = NavigateItemVisually (ItemNavigation.NextPage);
+ break;
+
+ case Keys.Right:
+ if (multicolumn == true) {
+ new_item = NavigateItemVisually (ItemNavigation.NextColumn);
+ }
+ break;
+
+ case Keys.Left:
+ if (multicolumn == true) {
+ new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
+ }
+ break;
+
+ case Keys.Space:
+ if (selection_mode == SelectionMode.MultiSimple) {
+ SelectedItemFromNavigation (FocusedItem);
+ }
+ break;
+
+
+ default:
+ break;
+ }
+
+ if (new_item != -1) {
+ FocusedItem = new_item;
+
+ if (selection_mode != SelectionMode.MultiSimple)
+ SelectedItemFromNavigation (new_item);
+ }
+ }
+
+ private void OnKeyUpLB (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyCode) {
+ case Keys.ControlKey:
+ ctrl_pressed = false;
+ break;
+ case Keys.ShiftKey:
+ shift_pressed = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ internal void InvalidateItem (int index)
+ {
+ if (!IsHandleCreated)
+ return;
+ Rectangle bounds = GetItemDisplayRectangle (index, top_index);
+ if (ClientRectangle.IntersectsWith (bounds))
+ Invalidate (bounds);
+ }
+
+ internal virtual void OnItemClick (int index)
+ {
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+
+ int anchor = -1;
+ int[] prev_selection;
+ bool button_pressed = false;
+ Point button_pressed_loc = new Point (-1, -1);
+
+ private void SelectExtended (int index)
+ {
+ SuspendLayout ();
+
+ ArrayList new_selection = new ArrayList ();
+ int start = anchor < index ? anchor : index;
+ int end = anchor > index ? anchor : index;
+ for (int i = start; i <= end; i++)
+ new_selection.Add (i);
+
+ if (ctrl_pressed)
+ foreach (int i in prev_selection)
+ if (!new_selection.Contains (i))
+ new_selection.Add (i);
+
+ // Need to make a copy since we can't enumerate and modify the collection
+ // at the same time
+ ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
+ foreach (int i in sel_indices)
+ if (!new_selection.Contains (i))
+ selected_indices.Remove (i);
+
+ foreach (int i in new_selection)
+ if (!sel_indices.Contains (i))
+ selected_indices.AddCore (i);
+ ResumeLayout ();
+ }
+
+ private void OnMouseDownLB (object sender, MouseEventArgs e)
+ {
+ // Only do stuff with the left mouse button
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ if (index == -1)
+ return;
+
+ switch (SelectionMode) {
+ case SelectionMode.One:
+ SelectedIndices.AddCore (index); // Unselects previous one
+ break;
+
+ case SelectionMode.MultiSimple:
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.RemoveCore (index);
+ else
+ SelectedIndices.AddCore (index);
+ break;
+
+ case SelectionMode.MultiExtended:
+ shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
+ ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Widget) != 0;
+
+ if (shift_pressed) {
+ SelectedIndices.ClearCore ();
+ SelectExtended (index);
+ break;
+ }
+
+ anchor = index;
+
+ if (ctrl_pressed) {
+ prev_selection = new int [SelectedIndices.Count];
+ SelectedIndices.CopyTo (prev_selection, 0);
+
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.RemoveCore (index);
+ else
+ SelectedIndices.AddCore (index);
+
+ break;
+ }
+
+ SelectedIndices.ClearCore ();
+ SelectedIndices.AddCore (index);
+ break;
+
+ case SelectionMode.None:
+ break;
+ default:
+ return;
+ }
+
+ button_pressed = true;
+ button_pressed_loc = new Point (e.X, e.Y);
+ FocusedItem = index;
+ }
+
+ private void OnMouseMoveLB (object sender, MouseEventArgs e)
+ {
+ // Don't take into account MouseMove events generated with MouseDown
+ if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ if (index == -1)
+ return;
+
+ switch (SelectionMode) {
+ case SelectionMode.One:
+ SelectedIndices.AddCore (index); // Unselects previous one
+ break;
+
+ case SelectionMode.MultiSimple:
+ break;
+
+ case SelectionMode.MultiExtended:
+ SelectExtended (index);
+ break;
+
+ case SelectionMode.None:
+ break;
+ default:
+ return;
+ }
+
+ FocusedItem = index;
+ }
+
+ internal override void OnDragDropEnd (DragDropEffects effects)
+ {
+ // In the case of a DnD operation (started on MouseDown)
+ // there will be no MouseUp event, so we need to reset
+ // the state here
+ button_pressed = false;
+ }
+
+ private void OnMouseUpLB (object sender, MouseEventArgs e)
+ {
+ // Only do stuff with the left mouse button
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ if (e.Clicks > 1) {
+ OnDoubleClick (EventArgs.Empty);
+ OnMouseDoubleClick (e);
+ }
+ else if (e.Clicks == 1) {
+ OnClick (EventArgs.Empty);
+ OnMouseClick (e);
+ }
+
+ if (!button_pressed)
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ OnItemClick (index);
+ button_pressed = ctrl_pressed = shift_pressed = false;
+ }
+
+ private void Scroll (ScrollBar scrollbar, int delta)
+ {
+ if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
+ return;
+
+ int max;
+ if (scrollbar == hscrollbar)
+ max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
+ else
+ max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
+
+ int val = scrollbar.Value + delta;
+ if (val > max)
+ val = max;
+ else if (val < scrollbar.Minimum)
+ val = scrollbar.Minimum;
+ scrollbar.Value = val;
+ }
+
+ private void OnMouseWheelLB (object sender, MouseEventArgs me)
+ {
+ if (Items.Count == 0)
+ return;
+
+ int lines = me.Delta / 120;
+
+ if (MultiColumn)
+ Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
+ else
+ Scroll (vscrollbar, -lines);
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ if (suspend_layout)
+ return;
+
+ Draw (pevent.ClipRectangle, pevent.Graphics);
+ }
+
+ internal void RepositionScrollBars ()
+ {
+ if (vscrollbar.is_visible) {
+ vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
+ vscrollbar.Location = new Point (items_area.Width, 0);
+ }
+
+ if (hscrollbar.is_visible) {
+ hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
+ hscrollbar.Location = new Point (0, items_area.Height);
+ }
+ }
+
+ // An item navigation operation (mouse or keyboard) has caused to select a new item
+ internal void SelectedItemFromNavigation (int index)
+ {
+ switch (SelectionMode) {
+ case SelectionMode.None:
+ // .Net doesn't select the item, only ensures that it's visible
+ // and fires the selection related events
+ EnsureVisible (index);
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ break;
+ case SelectionMode.One: {
+ SelectedIndex = index;
+ break;
+ }
+ case SelectionMode.MultiSimple: {
+ if (SelectedIndex == -1) {
+ SelectedIndex = index;
+ } else {
+
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.Remove (index);
+ else {
+ SelectedIndices.AddCore (index);
+
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+ break;
+ }
+
+ case SelectionMode.MultiExtended: {
+ if (SelectedIndex == -1) {
+ SelectedIndex = index;
+ } else {
+
+ if (ctrl_pressed == false && shift_pressed == false) {
+ SelectedIndices.Clear ();
+ }
+
+ if (shift_pressed == true) {
+ ShiftSelection (index);
+ } else { // ctrl_pressed or single item
+ SelectedIndices.AddCore (index);
+
+ }
+
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ private void ShiftSelection (int index)
+ {
+ int shorter_item = -1, dist = Items.Count + 1, cur_dist;
+
+ foreach (int idx in selected_indices) {
+ if (idx > index) {
+ cur_dist = idx - index;
+ } else {
+ cur_dist = index - idx;
+ }
+
+ if (cur_dist < dist) {
+ dist = cur_dist;
+ shorter_item = idx;
+ }
+ }
+
+ if (shorter_item != -1) {
+ int start, end;
+
+ if (shorter_item > index) {
+ start = index;
+ end = shorter_item;
+ } else {
+ start = shorter_item;
+ end = index;
+ }
+
+ selected_indices.Clear ();
+ for (int idx = start; idx <= end; idx++) {
+ selected_indices.AddCore (idx);
+ }
+ }
+ }
+
+ internal int FocusedItem {
+ get { return focused_item; }
+ set {
+ if (focused_item == value)
+ return;
+
+ int prev = focused_item;
+
+ focused_item = value;
+
+ if (has_focus == false)
+ return;
+
+ if (prev != -1)
+ InvalidateItem (prev);
+
+ if (value != -1)
+ InvalidateItem (value);
+
+ // UIA Framework: Generates FocusedItemChanged event.
+ OnUIAFocusedItemChangedEvent ();
+ }
+ }
+
+ StringFormat string_format;
+ internal StringFormat StringFormat {
+ get {
+ if (string_format == null) {
+ string_format = new StringFormat ();
+ string_format.FormatFlags = StringFormatFlags.NoWrap;
+
+ if (RightToLeft == RightToLeft.Yes)
+ string_format.Alignment = StringAlignment.Far;
+ else
+ string_format.Alignment = StringAlignment.Near;
+ CalculateTabStops ();
+ }
+ return string_format;
+ }
+ }
+
+ internal virtual void CollectionChanged ()
+ {
+ if (sorted)
+ Sort (false);
+
+ if (Items.Count == 0) {
+ selected_indices.List.Clear ();
+ focused_item = -1;
+ top_index = 0;
+ }
+ if (Items.Count <= focused_item)
+ focused_item = Items.Count - 1;
+
+ if (!IsHandleCreated || suspend_layout)
+ return;
+
+ LayoutListBox ();
+
+ base.Refresh ();
+ }
+
+ void EnsureVisible (int index)
+ {
+ if (!IsHandleCreated || index == -1)
+ return;
+
+ if (index < top_index) {
+ top_index = index;
+ UpdateTopItem ();
+ Invalidate ();
+ } else if (!multicolumn) {
+ int rows = items_area.Height / ItemHeight;
+ if (index >= (top_index + rows))
+ top_index = index - rows + 1;
+
+ UpdateTopItem ();
+ } else {
+ int rows = Math.Max (1, items_area.Height / ItemHeight);
+ int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
+
+ if (index >= (top_index + (rows * cols))) {
+ int incolumn = index / rows;
+ top_index = (incolumn - (cols - 1)) * rows;
+
+ UpdateTopItem ();
+ Invalidate ();
+ }
+ }
+ }
+
+ private void UpdateListBoxBounds ()
+ {
+ if (IsHandleCreated)
+ SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
+ }
+
+ private void UpdateScrollBars ()
+ {
+ items_area = ClientRectangle;
+ if (UpdateHorizontalScrollBar ()) {
+ items_area.Height -= hscrollbar.Height;
+ if (UpdateVerticalScrollBar ()) {
+ items_area.Width -= vscrollbar.Width;
+ UpdateHorizontalScrollBar ();
+ }
+ } else if (UpdateVerticalScrollBar ()) {
+ items_area.Width -= vscrollbar.Width;
+ if (UpdateHorizontalScrollBar ()) {
+ items_area.Height -= hscrollbar.Height;
+ UpdateVerticalScrollBar ();
+ }
+ }
+
+ RepositionScrollBars ();
+ }
+
+ /* Determines if the horizontal scrollbar has to be displyed */
+ private bool UpdateHorizontalScrollBar ()
+ {
+ bool show = false;
+ bool enabled = true;
+
+ if (MultiColumn) {
+ if (canvas_size.Width > items_area.Width) {
+ show = true;
+ hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
+ } else if (ScrollAlwaysVisible == true) {
+ enabled = false;
+ show = true;
+ hscrollbar.Maximum = 0;
+ }
+ } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
+ show = true;
+ hscrollbar.Maximum = canvas_size.Width;
+ hscrollbar.LargeChange = Math.Max (0, items_area.Width);
+ } else if (scroll_always_visible && horizontal_scrollbar) {
+ show = true;
+ enabled = false;
+ hscrollbar.Maximum = 0;
+ }
+
+ hbar_offset = hscrollbar.Value;
+ hscrollbar.Enabled = enabled;
+ hscrollbar.Visible = show;
+
+ return show;
+ }
+
+ /* Determines if the vertical scrollbar has to be displyed */
+ private bool UpdateVerticalScrollBar ()
+ {
+ if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
+ vscrollbar.Visible = false;
+ return false;
+ } else if (Items.Count == 0) {
+ vscrollbar.Visible = true;
+ vscrollbar.Enabled = false;
+ vscrollbar.Maximum = 0;
+ return true;
+ }
+
+ bool show = false;
+ bool enabled = true;
+ if (canvas_size.Height > items_area.Height) {
+ show = true;
+ vscrollbar.Maximum = Items.Count - 1;
+ vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
+ } else if (ScrollAlwaysVisible) {
+ show = true;
+ enabled = false;
+ vscrollbar.Maximum = 0;
+ }
+
+ vscrollbar.Enabled = enabled;
+ vscrollbar.Visible = show;
+
+ return show;
+ }
+
+ // Value Changed
+ private void VerticalScrollEvent (object sender, EventArgs e)
+ {
+ int top_item = top_index;
+
+ top_index = /*row_count + */ vscrollbar.Value;
+ last_visible_index = LastVisibleItem ();
+
+ int delta = (top_item - top_index) * ItemHeight;
+ if (DrawMode == DrawMode.OwnerDrawVariable) {
+ delta = 0;
+
+ if (top_index < top_item)
+ for (int i = top_index; i < top_item; i++)
+ delta += GetItemHeight (i);
+ else
+ for (int i = top_item; i < top_index; i++)
+ delta -= GetItemHeight (i);
+ }
+
+ if (IsHandleCreated)
+ XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
+ }
+
+ #endregion Private Methods
+
+ public class IntegerCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+ private List<int> list;
+
+ #region Public Constructor
+ public IntegerCollection (ListBox owner)
+ {
+ this.owner = owner;
+ list = new List<int> ();
+ }
+ #endregion
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public int this [int index] {
+ get { return list[index]; }
+ set { list[index] = value; owner.CalculateTabStops (); }
+ }
+ #endregion
+
+ #region Public Methods
+ public int Add (int item)
+ {
+ // This collection does not allow duplicates
+ if (!list.Contains (item)) {
+ list.Add (item);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+
+ return list.IndexOf (item);
+ }
+
+ public void AddRange (int[] items)
+ {
+ AddItems (items);
+ }
+
+ public void AddRange (IntegerCollection value)
+ {
+ AddItems (value);
+ }
+
+ void AddItems (IList items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (int i in items)
+ if (!list.Contains (i))
+ list.Add (i);
+
+ list.Sort ();
+ }
+
+ public void Clear ()
+ {
+ list.Clear ();
+ owner.CalculateTabStops ();
+ }
+
+ public bool Contains (int item)
+ {
+ return list.Contains (item);
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ for (int i = 0; i < list.Count; i++)
+ destination.SetValue (list[i], index++);
+ }
+
+ public int IndexOf (int item)
+ {
+ return list.IndexOf (item);
+ }
+
+ public void Remove (int item)
+ {
+ list.Remove (item);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index < 0)
+ throw new IndexOutOfRangeException ();
+
+ list.RemoveAt (index);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+ #endregion
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+ #endregion
+
+ #region IList Members
+ int IList.Add (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ throw new ArgumentException ("item");
+ return Add (intValue.Value);
+ }
+
+ void IList.Clear ()
+ {
+ Clear ();
+ }
+
+ bool IList.Contains (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ return false;
+ return Contains (intValue.Value);
+ }
+
+ int IList.IndexOf (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ return -1;
+ return IndexOf (intValue.Value);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException (string.Format (
+ CultureInfo.InvariantCulture, "No items "
+ + "can be inserted into {0}, since it is"
+ + " a sorted collection.", this.GetType ()));
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ void IList.Remove (object value)
+ {
+ int? intValue = value as int?;
+ if (!intValue.HasValue)
+ throw new ArgumentException ("value");
+
+ Remove (intValue.Value);
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ RemoveAt (index);
+ }
+
+ object IList.this[int index] {
+ get { return this[index]; }
+ set { this[index] = (int)value; }
+ }
+ #endregion
+
+ #region ICollection Members
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+ #endregion
+ }
+
+ [ListBindable (false)]
+ public class ObjectCollection : IList, ICollection, IEnumerable
+ {
+ internal class ListObjectComparer : IComparer
+ {
+ public int Compare (object a, object b)
+ {
+ string str1 = a.ToString ();
+ string str2 = b.ToString ();
+ return str1.CompareTo (str2);
+ }
+ }
+
+ private ListBox owner;
+ internal ArrayList object_items = new ArrayList ();
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+ #endregion UIA Framework Events
+
+ public ObjectCollection (ListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ public ObjectCollection (ListBox owner, object[] value)
+ {
+ this.owner = owner;
+ AddRange (value);
+ }
+
+ public ObjectCollection (ListBox owner, ObjectCollection value)
+ {
+ this.owner = owner;
+ AddRange (value);
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return object_items.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public virtual object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return object_items[index];
+ }
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
+
+ object_items[index] = value;
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ owner.CollectionChanged ();
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return false; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public int Add (object item)
+ {
+ int idx;
+ object[] selectedItems = null;
+
+ // we need to remember the original selected items so that we can update the indices
+ if (owner.sorted) {
+ selectedItems = new object[owner.SelectedItems.Count];
+ owner.SelectedItems.CopyTo (selectedItems, 0);
+ }
+
+ idx = AddItem (item);
+ owner.CollectionChanged ();
+
+ // If we are sorted, the item probably moved indexes, get the real one
+ if (owner.sorted) {
+ // update indices of selected items
+ owner.SelectedIndices.Clear ();
+ for (int i = 0; i < selectedItems.Length; i++) {
+ owner.SelectedIndex = this.IndexOf (selectedItems [i]);
+ }
+ return this.IndexOf (item);
+ }
+
+ return idx;
+ }
+
+ public void AddRange (object[] items)
+ {
+ AddItems (items);
+ }
+
+ public void AddRange (ObjectCollection value)
+ {
+ AddItems (value);
+ }
+
+ internal void AddItems (IList items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (object mi in items)
+ AddItem (mi);
+
+ owner.CollectionChanged ();
+ }
+
+ public virtual void Clear ()
+ {
+ owner.selected_indices.ClearCore ();
+ object_items.Clear ();
+ owner.CollectionChanged ();
+
+ //UIA Framework event: Items list cleared
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
+ }
+
+ public bool Contains (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.Contains (value);
+ }
+
+ public void CopyTo (object[] destination, int arrayIndex)
+ {
+ object_items.CopyTo (destination, arrayIndex);
+ }
+
+ void ICollection.CopyTo (Array destination, int index)
+ {
+ object_items.CopyTo (destination, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return object_items.GetEnumerator ();
+ }
+
+ int IList.Add (object item)
+ {
+ return Add (item);
+ }
+
+ public int IndexOf (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.IndexOf (value);
+ }
+
+ public void Insert (int index, object item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ owner.BeginUpdate ();
+ object_items.Insert (index, item);
+ owner.CollectionChanged ();
+ owner.EndUpdate ();
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+ }
+
+ public void Remove (object value)
+ {
+ if (value == null)
+ return;
+
+ int index = IndexOf (value);
+ if (index != -1)
+ RemoveAt (index);
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ //UIA Framework element removed
+ object removed = object_items [index];
+ UpdateSelection (index);
+ object_items.RemoveAt (index);
+ owner.CollectionChanged ();
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
+ }
+ #endregion Public Methods
+
+ #region Private Methods
+ internal int AddItem (object item)
+ {
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ int cnt = object_items.Count;
+ object_items.Add (item);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+
+ return cnt;
+ }
+
+ // we receive the index to be removed
+ void UpdateSelection (int removed_index)
+ {
+ owner.selected_indices.Remove (removed_index);
+
+ if (owner.selection_mode != SelectionMode.None) {
+ int last_idx = object_items.Count - 1;
+
+ // if the last item was selected, remove it from selection,
+ // since it will become invalid after the removal
+ if (owner.selected_indices.Contains (last_idx)) {
+ owner.selected_indices.Remove (last_idx);
+
+ // in SelectionMode.One try to put the selection on the new last item
+ int new_idx = last_idx - 1;
+ if (owner.selection_mode == SelectionMode.One && new_idx > -1)
+ owner.selected_indices.Add (new_idx);
+ }
+ }
+
+ }
+
+ internal void Sort ()
+ {
+ object_items.Sort (new ListObjectComparer ());
+ }
+
+ #endregion Private Methods
+ }
+
+ public class SelectedIndexCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+ ArrayList selection;
+ bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
+
+ #region UIA Framework Events
+
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+
+ #endregion UIA Framework Events
+
+
+ public SelectedIndexCollection (ListBox owner)
+ {
+ this.owner = owner;
+ selection = new ArrayList ();
+ }
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return selection.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ public int this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ CheckSorted ();
+ return (int)selection [index];
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ bool IList.IsFixedSize{
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return selection; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public void Add (int index)
+ {
+ if (AddCore (index)) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ // Need to separate selection logic from events,
+ // since selection changes using keys/mouse handle them their own way
+ internal bool AddCore (int index)
+ {
+ if (selection.Contains (index))
+ return false;
+
+ if (index == -1) // Weird MS behaviour
+ return false;
+ if (index < -1 || index >= owner.Items.Count)
+ throw new ArgumentOutOfRangeException ("index");
+ if (owner.selection_mode == SelectionMode.None)
+ throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
+
+ if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
+ RemoveCore ((int)selection [0]);
+
+ selection.Add (index);
+ sorting_needed = true;
+ owner.EnsureVisible (index);
+ owner.FocusedItem = index;
+ owner.InvalidateItem (index);
+
+ // UIA Framework event: Selected item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
+
+ return true;
+ }
+
+ public void Clear ()
+ {
+ if (ClearCore ()) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ internal bool ClearCore ()
+ {
+ if (selection.Count == 0)
+ return false;
+
+ foreach (int index in selection)
+ owner.InvalidateItem (index);
+
+ selection.Clear ();
+
+ // UIA Framework event: Selected items list updated
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
+
+ return true;
+ }
+
+ public bool Contains (int selectedIndex)
+ {
+ foreach (int index in selection)
+ if (index == selectedIndex)
+ return true;
+ return false;
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ CheckSorted ();
+ selection.CopyTo (destination, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ CheckSorted ();
+ return selection.GetEnumerator ();
+ }
+
+ // FIXME: Probably we can avoid sorting when calling
+ // IndexOf (imagine a scenario where multiple removal of items
+ // happens)
+ public void Remove (int index)
+ {
+ // Separate logic from events here too
+ if (RemoveCore (index)) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ internal bool RemoveCore (int index)
+ {
+ int idx = IndexOf (index);
+ if (idx == -1)
+ return false;
+
+ selection.RemoveAt (idx);
+ owner.InvalidateItem (index);
+
+ // UIA Framework event: Selected item removed from selection
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
+
+ return true;
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ bool IList.Contains (object selectedIndex)
+ {
+ return Contains ((int)selectedIndex);
+ }
+
+ int IList.IndexOf (object selectedIndex)
+ {
+ return IndexOf ((int) selectedIndex);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ object IList.this[int index]{
+ get { return this [index]; }
+ set {throw new NotImplementedException (); }
+ }
+
+ public int IndexOf (int selectedIndex)
+ {
+ CheckSorted ();
+
+ for (int i = 0; i < selection.Count; i++)
+ if ((int)selection [i] == selectedIndex)
+ return i;
+
+ return -1;
+ }
+ #endregion Public Methods
+ internal ArrayList List {
+ get {
+ CheckSorted ();
+ return selection;
+ }
+ }
+
+ void CheckSorted ()
+ {
+ if (sorting_needed) {
+ sorting_needed = false;
+ selection.Sort ();
+ }
+ }
+ }
+
+ public class SelectedObjectCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+
+ public SelectedObjectCollection (ListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return owner.selected_indices.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return owner.items [owner.selected_indices [index]];
+ }
+ set {throw new NotSupportedException ();}
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return true; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public void Add (object value)
+ {
+ if (owner.selection_mode == SelectionMode.None)
+ throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
+
+ int idx = owner.items.IndexOf (value);
+ if (idx == -1)
+ return;
+
+ owner.selected_indices.Add (idx);
+ }
+
+ public void Clear ()
+ {
+ owner.selected_indices.Clear ();
+ }
+
+ public bool Contains (object selectedObject)
+ {
+ int idx = owner.items.IndexOf (selectedObject);
+ return idx == -1 ? false : owner.selected_indices.Contains (idx);
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ for (int i = 0; i < Count; i++)
+ destination.SetValue (this [i], index++);
+ }
+
+ public void Remove (object value)
+ {
+ if (value == null)
+ return;
+
+ int idx = owner.items.IndexOf (value);
+ if (idx == -1)
+ return;
+
+ owner.selected_indices.Remove (idx);
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public int IndexOf (object selectedObject)
+ {
+ int idx = owner.items.IndexOf (selectedObject);
+ return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ //FIXME: write an enumerator that uses selection.GetEnumerator
+ // so that invalidation is write on selection changes
+ object [] items = new object [Count];
+ for (int i = 0; i < Count; i++) {
+ items [i] = owner.items [owner.selected_indices [i]];
+ }
+
+ return items.GetEnumerator ();
+ }
+
+ #endregion Public Methods
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/ListControl.cs b/source/ShiftUI/Widgets/ListControl.cs
new file mode 100644
index 0000000..4b01499
--- /dev/null
+++ b/source/ShiftUI/Widgets/ListControl.cs
@@ -0,0 +1,509 @@
+// 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.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+//
+//
+
+// COMPLETE
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI
+{
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [LookupBindingProperties ("DataSource", "DisplayMember", "ValueMember", "SelectedValue")]
+ public abstract class ListWidget : Widget
+ {
+
+ private object data_source;
+ private BindingMemberInfo value_member;
+ private string display_member;
+ private CurrencyManager data_manager;
+ private BindingContext last_binding_context;
+ private IFormatProvider format_info;
+ private string format_string = string.Empty;
+ private bool formatting_enabled;
+
+ protected ListWidget ()
+ {
+ value_member = new BindingMemberInfo (string.Empty);
+ display_member = string.Empty;
+ SetStyle (Widgetstyles.StandardClick | Widgetstyles.UserPaint | Widgetstyles.UseTextForAccessibility, false);
+ }
+
+ #region Events
+ static object DataSourceChangedEvent = new object ();
+ static object DisplayMemberChangedEvent = new object ();
+ static object FormatEvent = new object ();
+ static object FormatInfoChangedEvent = new object ();
+ static object FormatStringChangedEvent = new object ();
+ static object FormattingEnabledChangedEvent = new object ();
+ static object SelectedValueChangedEvent = new object ();
+ static object ValueMemberChangedEvent = new object ();
+
+ public event EventHandler DataSourceChanged {
+ add { Events.AddHandler (DataSourceChangedEvent, value); }
+ remove { Events.RemoveHandler (DataSourceChangedEvent, value); }
+ }
+
+ public event EventHandler DisplayMemberChanged {
+ add { Events.AddHandler (DisplayMemberChangedEvent, value); }
+ remove { Events.RemoveHandler (DisplayMemberChangedEvent, value); }
+ }
+
+ public event ListWidgetConvertEventHandler Format {
+ add { Events.AddHandler (FormatEvent, value); }
+ remove { Events.RemoveHandler (FormatEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public event EventHandler FormatInfoChanged {
+ add { Events.AddHandler (FormatInfoChangedEvent, value); }
+ remove { Events.RemoveHandler (FormatInfoChangedEvent, value); }
+ }
+
+ public event EventHandler FormatStringChanged {
+ add { Events.AddHandler (FormatStringChangedEvent, value); }
+ remove { Events.RemoveHandler (FormatStringChangedEvent, value); }
+ }
+
+ public event EventHandler FormattingEnabledChanged {
+ add { Events.AddHandler (FormattingEnabledChangedEvent, value); }
+ remove { Events.RemoveHandler (FormattingEnabledChangedEvent, value); }
+ }
+
+ public event EventHandler SelectedValueChanged {
+ add { Events.AddHandler (SelectedValueChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedValueChangedEvent, value); }
+ }
+
+ public event EventHandler ValueMemberChanged {
+ add { Events.AddHandler (ValueMemberChangedEvent, value); }
+ remove { Events.RemoveHandler (ValueMemberChangedEvent, value); }
+ }
+
+ #endregion // Events
+
+ #region .NET 2.0 Public Properties
+ [Browsable (false)]
+ [DefaultValue (null)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public IFormatProvider FormatInfo {
+ get { return format_info; }
+ set {
+ if (format_info != value) {
+ format_info = value;
+ RefreshItems ();
+ OnFormatInfoChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue ("")]
+ [MergableProperty (false)]
+ //[Editor ("ShiftUI.Design.FormatStringEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string FormatString {
+ get { return format_string; }
+ set {
+ if (format_string != value) {
+ format_string = value;
+ RefreshItems ();
+ OnFormatStringChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool FormattingEnabled {
+ get { return formatting_enabled; }
+ set {
+ if (formatting_enabled != value) {
+ formatting_enabled = value;
+ RefreshItems ();
+ OnFormattingEnabledChanged (EventArgs.Empty);
+ }
+ }
+ }
+ #endregion
+
+ #region Public Properties
+
+ [DefaultValue(null)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [AttributeProvider (typeof (IListSource))]
+ [MWFCategory("Data")]
+ public object DataSource {
+ get { return data_source; }
+ set {
+ if (data_source == value)
+ return;
+
+ if (value == null)
+ display_member = String.Empty;
+ else if (!(value is IList || value is IListSource))
+ throw new Exception ("Complex DataBinding accepts as a data source " +
+ "either an IList or an IListSource");
+
+ data_source = value;
+ ConnectToDataSource ();
+ OnDataSourceChanged (EventArgs.Empty);
+ }
+ }
+
+ [DefaultValue("")]
+ //[Editor("ShiftUI.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [TypeConverter("ShiftUI.Design.DataMemberFieldConverter, " + Consts.AssemblySystem_Design)]
+ [MWFCategory("Data")]
+ public string DisplayMember {
+ get {
+ return display_member;
+ }
+ set {
+ if (value == null)
+ value = String.Empty;
+
+ if (display_member == value) {
+ return;
+ }
+
+ display_member = value;
+ ConnectToDataSource ();
+ OnDisplayMemberChanged (EventArgs.Empty);
+ }
+ }
+
+ public abstract int SelectedIndex {
+ get;
+ set;
+ }
+
+ [Bindable(BindableSupport.Yes)]
+ [Browsable(false)]
+ [DefaultValue(null)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public object SelectedValue {
+ get {
+ if (data_manager == null || SelectedIndex == -1)
+ return null;
+
+ object item = data_manager [SelectedIndex];
+ object fil = FilterItemOnProperty (item, ValueMember);
+ return fil;
+ }
+ set {
+ if (data_manager == null)
+ return;
+
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ PropertyDescriptorCollection col = data_manager.GetItemProperties ();
+ PropertyDescriptor prop = col.Find (ValueMember, true);
+
+ for (int i = 0; i < data_manager.Count; i++) {
+ if (value.Equals (prop.GetValue (data_manager [i]))) {
+ SelectedIndex = i;
+ return;
+ }
+ }
+ SelectedIndex = -1;
+ }
+ }
+
+ [DefaultValue("")]
+ //[Editor("ShiftUI.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [MWFCategory("Data")]
+ public string ValueMember {
+ get { return value_member.BindingMember; }
+ set {
+ BindingMemberInfo new_value = new BindingMemberInfo (value);
+
+ if (value_member.Equals (new_value))
+ return;
+
+ value_member = new_value;
+
+ if (display_member == string.Empty)
+ DisplayMember = value_member.BindingMember;
+
+ ConnectToDataSource ();
+ OnValueMemberChanged (EventArgs.Empty);
+ }
+ }
+
+ protected virtual bool AllowSelection {
+ get { return true; }
+ }
+
+ #endregion Public Properties
+
+ #region Private Properties
+
+ internal override bool ScaleChildrenInternal {
+ get { return false; }
+ }
+
+ #endregion Private Properties
+
+ #region Public Methods
+
+ protected object FilterItemOnProperty (object item)
+ {
+ return FilterItemOnProperty (item, string.Empty);
+ }
+
+ protected object FilterItemOnProperty (object item, string field)
+ {
+ if (item == null)
+ return null;
+
+ if (field == null || field == string.Empty)
+ return item;
+
+ PropertyDescriptor prop = null;
+
+ if (data_manager != null) {
+ PropertyDescriptorCollection col = data_manager.GetItemProperties ();
+ prop = col.Find (field, true);
+ } else {
+ PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (item);
+ prop = properties.Find (field, true);
+ }
+
+ if (prop == null)
+ return item;
+
+ return prop.GetValue (item);
+ }
+
+ public string GetItemText (object item)
+ {
+ object o = FilterItemOnProperty (item, DisplayMember);
+
+ if (o == null)
+ o = item;
+
+ string retval = o.ToString ();
+
+ if (FormattingEnabled) {
+ ListWidgetConvertEventArgs e = new ListWidgetConvertEventArgs (o, typeof (string), item);
+ OnFormat (e);
+
+ // The user provided their own value
+ if (e.Value.ToString () != retval)
+ return e.Value.ToString ();
+
+ if (o is IFormattable)
+ return ((IFormattable)o).ToString (string.IsNullOrEmpty (FormatString) ? null : FormatString, FormatInfo);
+ }
+
+ return retval;
+ }
+
+ protected CurrencyManager DataManager {
+ get { return data_manager; }
+ }
+
+ // Used only by ListBox to avoid to break Listbox's member signature
+ 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:
+ case Keys.ControlKey:
+ case Keys.Space:
+ case Keys.ShiftKey:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ // Since this event is fired twice for the same binding context instance
+ // (when the Widget is added to the form and when the form is shown),
+ // we only take into account the first time it happens
+ protected override void OnBindingContextChanged (EventArgs e)
+ {
+ base.OnBindingContextChanged (e);
+ if (last_binding_context == BindingContext)
+ return;
+
+ last_binding_context = BindingContext;
+ ConnectToDataSource ();
+
+ if (DataManager != null) {
+ SetItemsCore (DataManager.List);
+ if (AllowSelection)
+ SelectedIndex = DataManager.Position;
+ }
+ }
+
+ protected virtual void OnDataSourceChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [DataSourceChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnDisplayMemberChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [DisplayMemberChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnFormat (ListWidgetConvertEventArgs e)
+ {
+ ListWidgetConvertEventHandler eh = (ListWidgetConvertEventHandler)(Events[FormatEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnFormatInfoChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events[FormatInfoChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnFormatStringChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events[FormatStringChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnFormattingEnabledChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events[FormattingEnabledChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnSelectedIndexChanged (EventArgs e)
+ {
+ if (data_manager == null)
+ return;
+ if (data_manager.Position == SelectedIndex)
+ return;
+ data_manager.Position = SelectedIndex;
+ }
+
+ protected virtual void OnSelectedValueChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [SelectedValueChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnValueMemberChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ValueMemberChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected abstract void RefreshItem (int index);
+
+ protected virtual void RefreshItems ()
+ {
+ }
+
+ protected virtual void SetItemCore (int index, object value)
+ {
+ }
+
+ protected abstract void SetItemsCore (IList items);
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ internal void BindDataItems ()
+ {
+ SetItemsCore (data_manager != null ? data_manager.List : new object [0]);
+ }
+
+ private void ConnectToDataSource ()
+ {
+ if (BindingContext == null)
+ return;
+
+ CurrencyManager newDataMgr = null;
+ if (data_source != null)
+ newDataMgr = (CurrencyManager) BindingContext [data_source];
+ if (newDataMgr != data_manager) {
+ if (data_manager != null) {
+ // Disconnect handlers from previous manager
+ data_manager.PositionChanged -= new EventHandler (OnPositionChanged);
+ data_manager.ItemChanged -= new ItemChangedEventHandler (OnItemChanged);
+ }
+ if (newDataMgr != null) {
+ newDataMgr.PositionChanged += new EventHandler (OnPositionChanged);
+ newDataMgr.ItemChanged += new ItemChangedEventHandler (OnItemChanged);
+ }
+ data_manager = newDataMgr;
+ }
+ }
+
+ private void OnItemChanged (object sender, ItemChangedEventArgs e)
+ {
+ /* if the list has changed, tell our subclass to re-bind */
+ if (e.Index == -1)
+ SetItemsCore (data_manager.List);
+ else
+ RefreshItem (e.Index);
+
+ /* For the first added item, ItemChanged is fired _after_ PositionChanged,
+ * so we need to set Index _only_ for that case - normally we would do that
+ * in PositionChanged handler */
+ if (AllowSelection && SelectedIndex == -1 && data_manager.Count == 1)
+ SelectedIndex = data_manager.Position;
+ }
+
+ private void OnPositionChanged (object sender, EventArgs e)
+ {
+ /* For the first added item, PositionChanged is fired
+ * _before_ ItemChanged (items not yet added), which leave us in a temporary
+ * invalid state */
+ if (AllowSelection && data_manager.Count > 1)
+ SelectedIndex = data_manager.Position;
+ }
+
+ #endregion Private Methods
+ }
+}
diff --git a/source/ShiftUI/Widgets/ListView.cs b/source/ShiftUI/Widgets/ListView.cs
new file mode 100644
index 0000000..a2706a3
--- /dev/null
+++ b/source/ShiftUI/Widgets/ListView.cs
@@ -0,0 +1,6155 @@
+// 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 ([email protected])
+// Jordi Mas i Hernandez, [email protected]
+// Mike Kestner ([email protected])
+// Daniel Nauck (dna(at)mono-project(dot)de)
+// Carlos Alberto Cortez <[email protected]>
+//
+
+
+// NOT COMPLETE
+
+
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Globalization;
+using System.Collections.Generic;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultEvent ("SelectedIndexChanged")]
+ [DefaultProperty ("Items")]
+ //[Designer ("ShiftUI.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [Docking (DockingBehavior.Ask)]
+ [ToolboxWidget]
+ public class ListView : Widget
+ {
+ private ItemActivation activation = ItemActivation.Standard;
+ private ListViewAlignment alignment = ListViewAlignment.Top;
+ private bool allow_column_reorder;
+ private bool auto_arrange = true;
+ private bool check_boxes;
+ private readonly CheckedIndexCollection checked_indices;
+ private readonly CheckedListViewItemCollection checked_items;
+ private readonly ColumnHeaderCollection columns;
+ internal int focused_item_index = -1;
+ private bool full_row_select;
+ private bool grid_lines;
+ private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
+ private bool hide_selection = true;
+ private bool hover_selection;
+ private IComparer item_sorter;
+ private readonly ListViewItemCollection items;
+ private readonly ListViewGroupCollection groups;
+ private bool owner_draw;
+ private bool show_groups = true;
+ private bool label_edit;
+ private bool label_wrap = true;
+ private bool multiselect = true;
+ private bool scrollable = true;
+ private bool hover_pending;
+ private readonly SelectedIndexCollection selected_indices;
+ private readonly SelectedListViewItemCollection selected_items;
+ private SortOrder sort_order = SortOrder.None;
+ private ImageList state_image_list;
+ internal bool updating;
+ 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.
+ internal 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;
+ private int[] reordered_items_indices;
+ private Point [] items_location;
+ private ItemMatrixLocation [] items_matrix_location;
+ private Size item_size; // used for caching item size
+ private int custom_column_width; // used when using Columns with SmallIcon/List views
+ private int hot_item_index = -1;
+ private bool hot_tracking;
+ private ListViewInsertionMark insertion_mark;
+ private bool show_item_tooltips;
+ private ToolTip item_tooltip;
+ private Size tile_size;
+ private bool virtual_mode;
+ private int virtual_list_size;
+ private bool right_to_left_layout;
+ // selection is available after the first time the handle is created, *even* if later
+ // the handle is either recreated or destroyed - so keep this info around.
+ private bool is_selection_available;
+
+ // internal variables
+ internal ImageList large_image_list;
+ internal ImageList small_image_list;
+ internal Size text_size = Size.Empty;
+
+ #region Events
+ static object AfterLabelEditEvent = new object ();
+ static object BeforeLabelEditEvent = new object ();
+ static object ColumnClickEvent = new object ();
+ static object ItemActivateEvent = new object ();
+ static object ItemCheckEvent = new object ();
+ static object ItemDragEvent = new object ();
+ static object SelectedIndexChangedEvent = new object ();
+ static object DrawColumnHeaderEvent = new object();
+ static object DrawItemEvent = new object();
+ static object DrawSubItemEvent = new object();
+ static object ItemCheckedEvent = new object ();
+ static object ItemMouseHoverEvent = new object ();
+ static object ItemSelectionChangedEvent = new object ();
+ static object CacheVirtualItemsEvent = new object ();
+ static object RetrieveVirtualItemEvent = new object ();
+ static object RightToLeftLayoutChangedEvent = new object ();
+ static object SearchForVirtualItemEvent = new object ();
+ static object VirtualItemsSelectionRangeChangedEvent = new object ();
+
+ public event LabelEditEventHandler AfterLabelEdit {
+ add { Events.AddHandler (AfterLabelEditEvent, value); }
+ remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
+ }
+
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ public event LabelEditEventHandler BeforeLabelEdit {
+ add { Events.AddHandler (BeforeLabelEditEvent, value); }
+ remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
+ }
+
+ public event ColumnClickEventHandler ColumnClick {
+ add { Events.AddHandler (ColumnClickEvent, value); }
+ remove { Events.RemoveHandler (ColumnClickEvent, value); }
+ }
+
+ public event DrawListViewColumnHeaderEventHandler DrawColumnHeader {
+ add { Events.AddHandler(DrawColumnHeaderEvent, value); }
+ remove { Events.RemoveHandler(DrawColumnHeaderEvent, value); }
+ }
+
+ public event DrawListViewItemEventHandler DrawItem {
+ add { Events.AddHandler(DrawItemEvent, value); }
+ remove { Events.RemoveHandler(DrawItemEvent, value); }
+ }
+
+ public event DrawListViewSubItemEventHandler DrawSubItem {
+ add { Events.AddHandler(DrawSubItemEvent, value); }
+ remove { Events.RemoveHandler(DrawSubItemEvent, value); }
+ }
+
+ public event EventHandler ItemActivate {
+ add { Events.AddHandler (ItemActivateEvent, value); }
+ remove { Events.RemoveHandler (ItemActivateEvent, value); }
+ }
+
+ public event ItemCheckEventHandler ItemCheck {
+ add { Events.AddHandler (ItemCheckEvent, value); }
+ remove { Events.RemoveHandler (ItemCheckEvent, value); }
+ }
+
+ public event ItemCheckedEventHandler ItemChecked {
+ add { Events.AddHandler (ItemCheckedEvent, value); }
+ remove { Events.RemoveHandler (ItemCheckedEvent, value); }
+ }
+
+ public event ItemDragEventHandler ItemDrag {
+ add { Events.AddHandler (ItemDragEvent, value); }
+ remove { Events.RemoveHandler (ItemDragEvent, value); }
+ }
+
+ public event ListViewItemMouseHoverEventHandler ItemMouseHover {
+ add { Events.AddHandler (ItemMouseHoverEvent, value); }
+ remove { Events.RemoveHandler (ItemMouseHoverEvent, value); }
+ }
+
+ public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged {
+ add { Events.AddHandler (ItemSelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (ItemSelectionChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler SelectedIndexChanged {
+ add { Events.AddHandler (SelectedIndexChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ public event CacheVirtualItemsEventHandler CacheVirtualItems {
+ add { Events.AddHandler (CacheVirtualItemsEvent, value); }
+ remove { Events.RemoveHandler (CacheVirtualItemsEvent, value); }
+ }
+
+ public event RetrieveVirtualItemEventHandler RetrieveVirtualItem {
+ add { Events.AddHandler (RetrieveVirtualItemEvent, value); }
+ remove { Events.RemoveHandler (RetrieveVirtualItemEvent, value); }
+ }
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
+ remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
+ }
+
+ public event SearchForVirtualItemEventHandler SearchForVirtualItem {
+ add { Events.AddHandler (SearchForVirtualItemEvent, value); }
+ remove { Events.AddHandler (SearchForVirtualItemEvent, value); }
+ }
+
+ public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged {
+ add { Events.AddHandler (VirtualItemsSelectionRangeChangedEvent, value); }
+ remove { Events.RemoveHandler (VirtualItemsSelectionRangeChangedEvent, value); }
+ }
+
+ #endregion // Events
+
+ #region Public Constructors
+ public ListView ()
+ {
+ background_color = ThemeEngine.Current.ColorWindow;
+ groups = new ListViewGroupCollection (this);
+ items = new ListViewItemCollection (this);
+ items.Changed += new CollectionChangedHandler (OnItemsChanged);
+ checked_indices = new CheckedIndexCollection (this);
+ checked_items = new CheckedListViewItemCollection (this);
+ columns = new ColumnHeaderCollection (this);
+ foreground_color = SystemColors.WindowText;
+ selected_indices = new SelectedIndexCollection (this);
+ selected_items = new SelectedListViewItemCollection (this);
+ items_location = new Point [16];
+ items_matrix_location = new ItemMatrixLocation [16];
+ reordered_items_indices = new int [16];
+ item_tooltip = new ToolTip ();
+ item_tooltip.Active = false;
+ insertion_mark = new ListViewInsertionMark (this);
+
+ InternalBorderStyle = BorderStyle.Fixed3D;
+
+ header_control = new HeaderControl (this);
+ header_control.Visible = false;
+ Widgets.AddImplicit (header_control);
+
+ item_control = new ItemControl (this);
+ Widgets.AddImplicit (item_control);
+
+ h_scroll = new ImplicitHScrollBar ();
+ Widgets.AddImplicit (this.h_scroll);
+
+ v_scroll = new ImplicitVScrollBar ();
+ Widgets.AddImplicit (this.v_scroll);
+
+ 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);
+ SizeChanged += new EventHandler (ListView_SizeChanged);
+ GotFocus += new EventHandler (FocusChanged);
+ LostFocus += new EventHandler (FocusChanged);
+ MouseWheel += new MouseEventHandler(ListView_MouseWheel);
+ MouseEnter += new EventHandler (ListView_MouseEnter);
+ Invalidated += new InvalidateEventHandler (ListView_Invalidated);
+
+ BackgroundImageTiled = false;
+
+ this.SetStyle (Widgetstyles.UserPaint | Widgetstyles.StandardClick
+ | Widgetstyles.UseTextForAccessibility
+ , 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 Size ItemSize {
+ get {
+ if (view != View.Details)
+ return item_size;
+
+ Size size = new Size ();
+ size.Height = item_size.Height;
+ for (int i = 0; i < columns.Count; i++)
+ size.Width += columns [i].Wd;
+
+ return size;
+ }
+ set {
+ item_size = value;
+ }
+ }
+
+ internal int HotItemIndex {
+ get {
+ return hot_item_index;
+ }
+ set {
+ hot_item_index = value;
+ }
+ }
+
+ internal bool UsingGroups {
+ get {
+ return show_groups && groups.Count > 0 && view != View.List &&
+ Application.VisualStylesEnabled;
+ }
+ }
+
+ internal override bool ScaleChildrenInternal {
+ get { return false; }
+ }
+
+ internal bool UseCustomColumnWidth {
+ get {
+ return (view == View.List || view == View.SmallIcon) && columns.Count > 0;
+ }
+ }
+
+ internal ColumnHeader EnteredColumnHeader {
+ get {
+ return header_control.EnteredColumnHeader;
+ }
+ }
+ #endregion // Private Internal Properties
+
+ #region Protected Properties
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams; }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.ListViewDefaultSize; }
+ }
+ protected override bool DoubleBuffered {
+ get {
+ return base.DoubleBuffered;
+ }
+ set {
+ base.DoubleBuffered = value;
+ }
+ }
+ #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));
+ }
+ if (hot_tracking && value != ItemActivation.OneClick)
+ throw new ArgumentException ("When HotTracking is on, activation must be ItemActivation.OneClick");
+
+ 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;
+ item_control.BackColor = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get {
+ return base.BackgroundImageLayout;
+ }
+ set {
+ base.BackgroundImageLayout = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool BackgroundImageTiled {
+ get {
+ return item_control.BackgroundImageLayout == ImageLayout.Tile;
+ }
+ set {
+ ImageLayout new_image_layout = value ? ImageLayout.Tile : ImageLayout.None;
+ if (new_image_layout == item_control.BackgroundImageLayout)
+ return;
+
+ item_control.BackgroundImageLayout = new_image_layout;
+ }
+ }
+
+ [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) {
+ if (value && View == View.Tile)
+ throw new NotSupportedException ("CheckBoxes are not"
+ + " supported in Tile view. Choose a different"
+ + " view or set CheckBoxes to false.");
+
+ check_boxes = value;
+ this.Redraw (true);
+
+ //UIA Framework: Event used by ListView to set/unset Toggle Pattern
+ OnUIACheckBoxesChanged ();
+ }
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public CheckedIndexCollection CheckedIndices {
+ get { return checked_indices; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public CheckedListViewItemCollection CheckedItems {
+ get { return checked_items; }
+ }
+
+ //[Editor ("ShiftUI.Design.ColumnHeaderCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ //[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_index == -1)
+ return null;
+
+ return GetItemAtDisplayIndex (focused_item_index);
+ }
+ set {
+ if (value == null || value.ListView != this ||
+ !IsHandleCreated)
+ return;
+
+ SetFocusedItem (value.DisplayIndex);
+ }
+ }
+
+ 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 {
+ if (full_row_select != value) {
+ full_row_select = value;
+ InvalidateSelection ();
+ }
+ }
+ }
+
+ [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;
+ InvalidateSelection ();
+ }
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool HotTracking {
+ get {
+ return hot_tracking;
+ }
+ set {
+ if (hot_tracking == value)
+ return;
+
+ hot_tracking = value;
+ if (hot_tracking) {
+ hover_selection = true;
+ activation = ItemActivation.OneClick;
+ }
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool HoverSelection {
+ get { return hover_selection; }
+ set {
+ if (hot_tracking && value == false)
+ throw new ArgumentException ("When HotTracking is on, hover selection must be true");
+ hover_selection = value;
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Browsable (false)]
+ public ListViewInsertionMark InsertionMark {
+ get {
+ return insertion_mark;
+ }
+ }
+
+ //[Editor ("ShiftUI.Design.ListViewItemCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Localizable (true)]
+ [MergableProperty (false)]
+ public ListViewItemCollection Items {
+ get { return items; }
+ }
+
+ [DefaultValue (false)]
+ public bool LabelEdit {
+ get { return label_edit; }
+ set {
+ if (value != label_edit) {
+ label_edit = value;
+
+ // UIA Framework: Event used by Value Pattern in ListView.ListItem provider
+ OnUIALabelEditChanged ();
+ }
+
+ }
+ }
+
+ [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 {
+ if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
+ return null;
+ return item_sorter;
+ }
+ set {
+ if (item_sorter != value) {
+ item_sorter = value;
+ Sort ();
+ }
+ }
+ }
+
+ [DefaultValue (true)]
+ public bool MultiSelect {
+ get { return multiselect; }
+ set {
+ if (value != multiselect) {
+ multiselect = value;
+
+ // UIA Framework: Event used by Selection Pattern in ListView.ListItem provider
+ OnUIAMultiSelectChanged ();
+ }
+ }
+ }
+
+
+ [DefaultValue(false)]
+ public bool OwnerDraw {
+ get { return owner_draw; }
+ set {
+ owner_draw = value;
+ Redraw (true);
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get {
+ return base.Padding;
+ }
+ set {
+ base.Padding = value;
+ }
+ }
+
+ [MonoTODO ("RTL not supported")]
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public virtual bool RightToLeftLayout {
+ get { return right_to_left_layout; }
+ set {
+ if (right_to_left_layout != value) {
+ right_to_left_layout = value;
+ OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [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(true)]
+ public bool ShowGroups {
+ get { return show_groups; }
+ set {
+ if (show_groups != value) {
+ show_groups = value;
+ Redraw(true);
+
+ // UIA Framework: Used to update a11y Tree
+ OnUIAShowGroupsChanged ();
+ }
+ }
+ }
+
+ [LocalizableAttribute (true)]
+ [MergableProperty (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ //[Editor ("ShiftUI.Design.ListViewGroupCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public ListViewGroupCollection Groups {
+ get { return groups; }
+ }
+
+ [DefaultValue (false)]
+ public bool ShowItemToolTips {
+ get {
+ return show_item_tooltips;
+ }
+ set {
+ show_item_tooltips = value;
+ item_tooltip.Active = false;
+ }
+ }
+
+ [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 (!Enum.IsDefined (typeof (SortOrder), value)) {
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (SortOrder));
+ }
+
+ if (sort_order == value)
+ return;
+
+ sort_order = value;
+
+ if (virtual_mode) // Sorting is not allowed in virtual mode
+ return;
+
+ if (value == SortOrder.None) {
+ if (item_sorter != null) {
+ // ListViewItemSorter should never be reset for SmallIcon
+ // and LargeIcon view
+ if (View != View.SmallIcon && View != View.LargeIcon)
+ item_sorter = null;
+ }
+ this.Redraw (false);
+ } else {
+ if (item_sorter == null)
+ item_sorter = new ItemComparer (value);
+ if (item_sorter is ItemComparer) {
+ item_sorter = new ItemComparer (value);
+ }
+ Sort ();
+ }
+ }
+ }
+
+ private void OnImageListChanged (object sender, EventArgs args)
+ {
+ item_control.Invalidate ();
+ }
+
+ [DefaultValue (null)]
+ public ImageList StateImageList {
+ get { return state_image_list; }
+ set {
+ if (state_image_list == value)
+ return;
+
+ if (state_image_list != null)
+ state_image_list.Images.Changed -= new EventHandler (OnImageListChanged);
+
+ state_image_list = value;
+
+ if (state_image_list != null)
+ state_image_list.Images.Changed += new EventHandler (OnImageListChanged);
+
+ this.Redraw (true);
+ }
+ }
+
+ [Bindable (false)]
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (value == base.Text)
+ return;
+
+ base.Text = value;
+ this.Redraw (true);
+ }
+ }
+
+ [Browsable (true)]
+ public Size TileSize {
+ get {
+ return tile_size;
+ }
+ set {
+ if (value.Width <= 0 || value.Height <= 0)
+ throw new ArgumentOutOfRangeException ("value");
+
+ tile_size = value;
+ if (view == View.Tile)
+ Redraw (true);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public ListViewItem TopItem {
+ get {
+ if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
+ throw new InvalidOperationException ("Cannot get the top item in LargeIcon, SmallIcon or Tile view.");
+ // 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 {
+ int header_offset = header_control.Height;
+ for (int i = 0; i < items.Count; i++) {
+ Point item_loc = GetItemLocation (i);
+ if (item_loc.X >= 0 && item_loc.Y - header_offset >= 0)
+ return items [i];
+ }
+ return null;
+ }
+ }
+ set {
+ if (view == View.LargeIcon || view == View.SmallIcon || view == View.Tile)
+ throw new InvalidOperationException ("Cannot set the top item in LargeIcon, SmallIcon or Tile view.");
+
+ // .Net doesn't throw any exception in the cases below
+ if (value == null || value.ListView != this)
+ return;
+
+ // Take advantage this property is only valid for Details view.
+ SetScrollValue (v_scroll, item_size.Height * value.Index);
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ [DefaultValue (true)]
+ [Browsable (false)]
+ [MonoInternalNote ("Stub, not implemented")]
+ public bool UseCompatibleStateImageBehavior {
+ get {
+ return false;
+ }
+ set {
+ }
+ }
+
+ [DefaultValue (View.LargeIcon)]
+ public View View {
+ get { return view; }
+ set {
+ if (!Enum.IsDefined (typeof (View), value))
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (View));
+
+ if (view != value) {
+ if (CheckBoxes && value == View.Tile)
+ throw new NotSupportedException ("CheckBoxes are not"
+ + " supported in Tile view. Choose a different"
+ + " view or set CheckBoxes to false.");
+ if (VirtualMode && value == View.Tile)
+ throw new NotSupportedException ("VirtualMode is"
+ + " not supported in Tile view. Choose a different"
+ + " view or set ViewMode to false.");
+
+ h_scroll.Value = v_scroll.Value = 0;
+ view = value;
+ Redraw (true);
+
+ // UIA Framework: Event used to update UIA Tree.
+ OnUIAViewChanged ();
+ }
+ }
+ }
+
+ [DefaultValue (false)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public bool VirtualMode {
+ get {
+ return virtual_mode;
+ }
+ set {
+ if (virtual_mode == value)
+ return;
+
+ if (!virtual_mode && items.Count > 0)
+ throw new InvalidOperationException ();
+ if (value && view == View.Tile)
+ throw new NotSupportedException ("VirtualMode is"
+ + " not supported in Tile view. Choose a different"
+ + " view or set ViewMode to false.");
+
+ virtual_mode = value;
+ Redraw (true);
+ }
+ }
+
+ [DefaultValue (0)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public int VirtualListSize {
+ get {
+ return virtual_list_size;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentException ("value");
+
+ if (virtual_list_size == value)
+ return;
+
+ virtual_list_size = value;
+ if (virtual_mode) {
+ focused_item_index = -1;
+ selected_indices.Reset ();
+ 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;
+
+ Size item_size = ItemSize;
+ // In virtual mode we always have fixed positions, and we can infer the positon easily
+ if (virtual_mode) {
+ int first = 0;
+ switch (view) {
+ case View.Details:
+ first = v_marker / item_size.Height;
+ break;
+ case View.LargeIcon:
+ case View.SmallIcon:
+ first = (v_marker / (item_size.Height + y_spacing)) * cols;
+ break;
+ case View.List:
+ first = (h_marker / (item_size.Width * x_spacing)) * rows;
+ break;
+ }
+
+ if (first >= items.Count)
+ first = items.Count;
+
+ return first;
+ }
+ for (int i = 0; i < items.Count; i++) {
+ Rectangle item_rect = new Rectangle (GetItemLocation (i), item_size);
+ if (item_rect.Right >= 0 && item_rect.Bottom >= 0)
+ return i;
+ }
+
+ return 0;
+ }
+ }
+
+
+ internal int LastVisibleIndex {
+ get {
+ for (int i = FirstVisibleIndex; i < Items.Count; i++) {
+ if (View == View.List || Alignment == ListViewAlignment.Left) {
+ if (GetItemLocation (i).X > item_control.ClientRectangle.Right)
+ return i - 1;
+ } else {
+ if (GetItemLocation (i).Y > item_control.ClientRectangle.Bottom)
+ return i - 1;
+ }
+ }
+
+ return Items.Count - 1;
+ }
+ }
+
+ internal void OnSelectedIndexChanged ()
+ {
+ if (is_selection_available)
+ OnSelectedIndexChanged (EventArgs.Empty);
+ }
+
+ 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 (updating)
+ return;
+ // VirtualMode doesn't do any calculations until handle is created
+ if (virtual_mode && !IsHandleCreated)
+ return;
+
+
+ if (recalculate)
+ CalculateListView (this.alignment);
+
+ Invalidate (true);
+ }
+
+ void InvalidateSelection ()
+ {
+ foreach (int selected_index in SelectedIndices)
+ items [selected_index].Invalidate ();
+ }
+
+ const int text_padding = 15;
+
+ 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 (TextRenderer.MeasureString
+ (col.Text, this.Font));
+ size.Width += text_padding;
+ 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 (TextRenderer.MeasureString
+ (col.Text, this.Font)).Height;
+ else
+ ret_size.Height = this.Font.Height;
+ }
+ }
+
+ ret_size.Height += text_padding;
+
+ // 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
+ // or the sum of the text and indent count if we are on 2.0
+ private Size BiggestItem (int col)
+ {
+ Size temp = Size.Empty;
+ Size ret_size = Size.Empty;
+ bool use_indent_count = small_image_list != null;
+
+ // VirtualMode uses the first item text size
+ if (virtual_mode && items.Count > 0) {
+ ListViewItem item = items [0];
+ ret_size = Size.Ceiling (TextRenderer.MeasureString (item.SubItems[col].Text,
+ Font));
+
+ if (use_indent_count)
+ ret_size.Width += item.IndentCount * small_image_list.ImageSize.Width;
+ } else {
+ // 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 (TextRenderer.MeasureString
+ (item.SubItems [col].Text, Font));
+
+ if (use_indent_count)
+ temp.Width += item.IndentCount * small_image_list.ImageSize.Width;
+
+ if (temp.Width > ret_size.Width)
+ ret_size = temp;
+ }
+ }
+
+ // adjustment for space in Details view
+ if (!ret_size.IsEmpty && view == View.Details)
+ ret_size.Width += ThemeEngine.Current.ListViewItemPaddingWidth;
+
+ return ret_size;
+ }
+
+ const int max_wrap_padding = 30;
+
+ // 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;
+ int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
+ temp.Width += icon_w + max_wrap_padding;
+ // 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 += 2;
+ text_size.Height += 2;
+ }
+
+ private void SetScrollValue (ScrollBar scrollbar, int val)
+ {
+ int max;
+ if (scrollbar == h_scroll)
+ max = h_scroll.Maximum - h_scroll.LargeChange + 1;
+ else
+ max = v_scroll.Maximum - v_scroll.LargeChange + 1;
+
+ if (val > max)
+ val = max;
+ else if (val < scrollbar.Minimum)
+ val = scrollbar.Minimum;
+
+ scrollbar.Value = val;
+ }
+
+ private void Scroll (ScrollBar scrollbar, int delta)
+ {
+ if (delta == 0 || !scrollbar.Visible)
+ return;
+
+ SetScrollValue (scrollbar, scrollbar.Value + delta);
+ }
+
+ private void CalculateScrollBars ()
+ {
+ Rectangle client_area = ClientRectangle;
+ int height = client_area.Height;
+ int width = client_area.Width;
+ Size item_size;
+
+ if (!scrollable) {
+ h_scroll.Visible = false;
+ v_scroll.Visible = false;
+ item_control.Size = new Size (width, height);
+ header_control.Width = width;
+ return;
+ }
+
+ // Don't calculate if the view is not displayable
+ if (client_area.Height < 0 || client_area.Width < 0)
+ 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_size = ItemSize;
+
+ if (h_scroll.is_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;
+ }
+
+ if (view == View.List)
+ h_scroll.SmallChange = item_size.Width + ThemeEngine.Current.ListViewHorizontalSpacing;
+ else
+ h_scroll.SmallChange = Font.Height;
+
+ h_scroll.LargeChange = client_area.Width;
+ height -= h_scroll.Height;
+ }
+
+ if (v_scroll.is_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 height of
+ // 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 ? client_area.Height - h_scroll.Height : 0;
+ } else {
+ v_scroll.Maximum = layout_ht;
+ v_scroll.Height = client_area.Height;
+ }
+
+ if (view == View.Details) {
+ // Need to update Maximum if using LargeChange with value other than the visible area
+ int headerPlusOneItem = header_control.Height + item_size.Height;
+ v_scroll.LargeChange = v_scroll.Height > headerPlusOneItem ? v_scroll.Height - headerPlusOneItem : 0;
+ v_scroll.Maximum = v_scroll.Maximum > headerPlusOneItem ? v_scroll.Maximum - headerPlusOneItem : 0;
+ } else
+ v_scroll.LargeChange = v_scroll.Height;
+
+ v_scroll.SmallChange = item_size.Height;
+ width -= v_scroll.Width;
+ }
+
+ item_control.Size = new Size (width, height);
+
+ if (header_control.is_visible)
+ header_control.Width = width;
+ }
+
+ internal int GetReorderedColumnIndex (ColumnHeader column)
+ {
+ if (reordered_column_indices == null)
+ return column.Index;
+
+ for (int i = 0; i < Columns.Count; i++)
+ if (reordered_column_indices [i] == column.Index)
+ return i;
+
+ return -1;
+ }
+
+ internal ColumnHeader GetReorderedColumn (int index)
+ {
+ if (reordered_column_indices == null)
+ return Columns [index];
+ else
+ return Columns [reordered_column_indices [index]];
+ }
+
+ internal void ReorderColumn (ColumnHeader col, int index, bool fireEvent)
+ {
+ if (fireEvent) {
+ ColumnReorderedEventHandler eh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
+ if (eh != null){
+ ColumnReorderedEventArgs args = new ColumnReorderedEventArgs (col.Index, index, col);
+
+ eh (this, args);
+ if (args.Cancel) {
+ header_control.Invalidate ();
+ item_control.Invalidate ();
+ return;
+ }
+ }
+ }
+ int column_count = Columns.Count;
+
+ if (reordered_column_indices == null) {
+ reordered_column_indices = new int [column_count];
+ for (int i = 0; i < column_count; i++)
+ reordered_column_indices [i] = i;
+ }
+
+ if (reordered_column_indices [index] == col.Index)
+ return;
+
+ int[] curr = reordered_column_indices;
+ int [] result = new int [column_count];
+ int curr_idx = 0;
+ for (int i = 0; i < column_count; i++) {
+ if (curr_idx < column_count && curr [curr_idx] == col.Index)
+ curr_idx++;
+
+ if (i == index)
+ result [i] = col.Index;
+ else
+ result [i] = curr [curr_idx++];
+ }
+
+ ReorderColumns (result, true);
+ }
+
+ internal void ReorderColumns (int [] display_indices, bool redraw)
+ {
+ reordered_column_indices = display_indices;
+ for (int i = 0; i < Columns.Count; i++) {
+ ColumnHeader col = Columns [i];
+ col.InternalDisplayIndex = reordered_column_indices [i];
+ }
+ if (redraw && view == View.Details && IsHandleCreated) {
+ LayoutDetails ();
+ header_control.Invalidate ();
+ item_control.Invalidate ();
+ }
+ }
+
+ internal void AddColumn (ColumnHeader newCol, int index, bool redraw)
+ {
+ int column_count = Columns.Count;
+ newCol.SetListView (this);
+
+ int [] display_indices = new int [column_count];
+ for (int i = 0; i < column_count; i++) {
+ ColumnHeader col = Columns [i];
+ if (i == index) {
+ display_indices [i] = index;
+ } else {
+ int display_index = col.InternalDisplayIndex;
+ if (display_index < index) {
+ display_indices [i] = display_index;
+ } else {
+ display_indices [i] = (display_index + 1);
+ }
+ }
+ }
+
+ ReorderColumns (display_indices, redraw);
+ Invalidate ();
+ }
+
+ Size LargeIconItemSize
+ {
+ get {
+ int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
+ int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
+ int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
+ int w = Math.Max (text_size.Width, image_w);
+
+ if (check_boxes)
+ w += 2 + CheckBoxSize.Width;
+
+ 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 h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
+ int w = text_size.Width + image_w;
+
+ if (check_boxes)
+ w += 2 + CheckBoxSize.Width;
+
+ return new Size (w, h);
+ }
+ }
+
+ Size TileItemSize {
+ get {
+ // Calculate tile size if needed
+ // It appears that using Font.Size instead of a SizeF value can give us
+ // a slightly better approach to the proportions defined in .Net
+ if (tile_size == Size.Empty) {
+ int image_w = LargeImageList == null ? 0 : LargeImageList.ImageSize.Width;
+ int image_h = LargeImageList == null ? 0 : LargeImageList.ImageSize.Height;
+ int w = (int)Font.Size * ThemeEngine.Current.ListViewTileWidthFactor + image_w + 4;
+ int h = Math.Max ((int)Font.Size * ThemeEngine.Current.ListViewTileHeightFactor, image_h);
+
+ tile_size = new Size (w, h);
+ }
+
+ return tile_size;
+ }
+ }
+
+ int GetDetailsItemHeight ()
+ {
+ int item_height;
+ int checkbox_height = CheckBoxes ? CheckBoxSize.Height : 0;
+ int small_image_height = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
+ item_height = Math.Max (checkbox_height, text_size.Height);
+ item_height = Math.Max (item_height, small_image_height);
+ return item_height;
+ }
+
+ void SetItemLocation (int index, int x, int y, int row, int col)
+ {
+ Point old_location = items_location [index];
+ if (old_location.X == x && old_location.Y == y)
+ return;
+
+ items_location [index] = new Point (x, y);
+ items_matrix_location [index] = new ItemMatrixLocation (row, col);
+
+ //
+ // Initial position matches item's position in ListViewItemCollection
+ //
+ reordered_items_indices [index] = index;
+ }
+
+
+ void ShiftItemsPositions (int from, int to, bool forward)
+ {
+ if (forward) {
+ for (int i = to + 1; i > from; i--) {
+ reordered_items_indices [i] = reordered_items_indices [i - 1];
+
+ ListViewItem item = items [reordered_items_indices [i]];
+ item.Invalidate ();
+ item.DisplayIndex = i;
+ item.Invalidate ();
+ }
+ } else {
+ for (int i = from - 1; i < to; i++) {
+ reordered_items_indices [i] = reordered_items_indices [i + 1];
+
+ ListViewItem item = items [reordered_items_indices [i]];
+ item.Invalidate ();
+ item.DisplayIndex = i;
+ item.Invalidate ();
+ }
+ }
+ }
+
+ internal void ChangeItemLocation (int display_index, Point new_pos)
+ {
+ int new_display_index = GetDisplayIndexFromLocation (new_pos);
+ if (new_display_index == display_index)
+ return;
+
+ int item_index = reordered_items_indices [display_index];
+ ListViewItem item = items [item_index];
+
+ bool forward = new_display_index < display_index;
+ int index_from, index_to;
+ if (forward) {
+ index_from = new_display_index;
+ index_to = display_index - 1;
+ } else {
+ index_from = display_index + 1;
+ index_to = new_display_index;
+ }
+
+ ShiftItemsPositions (index_from, index_to, forward);
+
+ reordered_items_indices [new_display_index] = item_index;
+
+ item.Invalidate ();
+ item.DisplayIndex = new_display_index;
+ item.Invalidate ();
+ }
+
+ int GetDisplayIndexFromLocation (Point loc)
+ {
+ int display_index = -1;
+ Rectangle item_area;
+
+ // First item
+ if (loc.X < 0 || loc.Y < 0)
+ return 0;
+
+ // Adjustment to put in the next position refered by 'loc'
+ loc.X -= item_size.Width / 2;
+ if (loc.X < 0)
+ loc.X = 0;
+
+ for (int i = 0; i < items.Count; i++) {
+ item_area = new Rectangle (GetItemLocation (i), item_size);
+ item_area.Inflate (ThemeEngine.Current.ListViewHorizontalSpacing,
+ ThemeEngine.Current.ListViewVerticalSpacing);
+
+ if (item_area.Contains (loc)) {
+ display_index = i;
+ break;
+ }
+ }
+
+ // Put in in last position
+ if (display_index == -1)
+ display_index = items.Count - 1;
+
+ return display_index;
+ }
+
+ // When using groups, the items with no group assigned
+ // belong to the DefaultGroup
+ int GetDefaultGroupItems ()
+ {
+ int count = 0;
+ foreach (ListViewItem item in items)
+ if (item.Group == null)
+ count++;
+
+ return count;
+ }
+
+ // cache the spacing to let virtualmode compute the positions on the fly
+ int x_spacing;
+ int y_spacing;
+ int rows;
+ int cols;
+ int[,] item_index_matrix;
+
+ void CalculateRowsAndCols (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
+ {
+ Rectangle area = ClientRectangle;
+
+ if (UseCustomColumnWidth)
+ CalculateCustomColumnWidth ();
+ if (UsingGroups) {
+ // When groups are used the alignment is always top-aligned
+ rows = 0;
+ cols = 0;
+ int items = 0;
+
+ groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
+ for (int i = 0; i < groups.InternalCount; i++) {
+ ListViewGroup group = groups.GetInternalGroup (i);
+ int items_in_group = group.GetActualItemCount ();
+
+ if (items_in_group == 0)
+ continue;
+
+ int group_cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
+ if (group_cols <= 0)
+ group_cols = 1;
+ int group_rows = (int) Math.Ceiling ((double)items_in_group / (double)group_cols);
+
+ group.starting_row = rows;
+ group.rows = group_rows;
+ group.starting_item = items;
+ group.current_item = 0; // Reset layout
+
+ cols = Math.Max (group_cols, cols);
+ rows += group_rows;
+ items += items_in_group;
+ }
+ } else
+ {
+ // Simple matrix if no groups are used
+ if (left_aligned) {
+ rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(item_size.Height + y_spacing));
+ if (rows <= 0)
+ rows = 1;
+ cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
+ } else {
+ if (UseCustomColumnWidth)
+ cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width) / (double)(custom_column_width));
+ else
+ cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(item_size.Width + x_spacing));
+
+ if (cols < 1)
+ cols = 1;
+
+ rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
+ }
+ }
+
+ item_index_matrix = new int [rows, cols];
+ }
+
+ // When using custom column width, we look for the minimum one
+ void CalculateCustomColumnWidth ()
+ {
+ int min_width = Int32.MaxValue;
+ for (int i = 0; i < columns.Count; i++) {
+ int col_width = columns [i].Width;
+
+ if (col_width < min_width)
+ min_width = col_width;
+ }
+
+ custom_column_width = min_width;
+ }
+
+ void LayoutIcons (Size item_size, bool left_aligned, int x_spacing, int y_spacing)
+ {
+ header_control.Visible = false;
+ header_control.Size = Size.Empty;
+ item_control.Visible = true;
+ item_control.Location = Point.Empty;
+ ItemSize = item_size; // Cache item size
+ this.x_spacing = x_spacing;
+ this.y_spacing = y_spacing;
+
+ if (items.Count == 0)
+ return;
+
+ Size sz = item_size;
+
+ CalculateRowsAndCols (sz, left_aligned, x_spacing, y_spacing);
+
+ layout_wd = UseCustomColumnWidth ? cols * custom_column_width : cols * (sz.Width + x_spacing) - x_spacing;
+ layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
+
+ if (virtual_mode) { // no actual assignment is needed on items for virtual mode
+ item_control.Size = new Size (layout_wd, layout_ht);
+ return;
+ }
+
+ bool using_groups = UsingGroups;
+ if (using_groups) // the groups layout will override layout_ht
+ CalculateGroupsLayout (sz, y_spacing, 0);
+
+ int row = 0, col = 0;
+ int x = 0, y = 0;
+ int display_index = 0;
+
+ for (int i = 0; i < items.Count; i++) {
+ ListViewItem item = items [i];
+ if (using_groups) {
+ ListViewGroup group = item.Group;
+ if (group == null)
+ group = groups.DefaultGroup;
+
+ Point group_items_loc = group.items_area_location;
+ int current_item = group.current_item++;
+ int starting_row = group.starting_row;
+
+ display_index = group.starting_item + current_item;
+ row = (current_item / cols);
+ col = current_item % cols;
+
+ x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
+ y = row * (item_size.Height + y_spacing) + group_items_loc.Y;
+
+ SetItemLocation (display_index, x, y, row + starting_row, col);
+ SetItemAtDisplayIndex (display_index, i);
+ item_index_matrix [row + starting_row, col] = i;
+
+ } else
+ {
+ x = UseCustomColumnWidth ? col * custom_column_width : col * (item_size.Width + x_spacing);
+ y = row * (item_size.Height + y_spacing);
+ display_index = i; // Same as item index in Items
+
+ SetItemLocation (i, x, y, row, col);
+ item_index_matrix [row, col] = i;
+
+ if (left_aligned) {
+ row++;
+ if (row == rows) {
+ row = 0;
+ col++;
+ }
+ } else {
+ if (++col == cols) {
+ col = 0;
+ row++;
+ }
+ }
+ }
+
+ item.Layout ();
+ item.DisplayIndex = display_index;
+ item.SetPosition (new Point (x, y));
+ }
+
+ item_control.Size = new Size (layout_wd, layout_ht);
+ }
+
+ void CalculateGroupsLayout (Size item_size, int y_spacing, int y_origin)
+ {
+ int y = y_origin;
+ bool details = view == View.Details;
+
+ for (int i = 0; i < groups.InternalCount; i++) {
+ ListViewGroup group = groups.GetInternalGroup (i);
+ if (group.ItemCount == 0)
+ continue;
+
+ y += LayoutGroupHeader (group, y, item_size.Height, y_spacing, details ? group.ItemCount : group.rows);
+ }
+
+ layout_ht = y; // Update height taking into account Groups' headers heights
+ }
+
+ int LayoutGroupHeader (ListViewGroup group, int y_origin, int item_height, int y_spacing, int rows)
+ {
+ Rectangle client_area = ClientRectangle;
+ int header_height = Font.Height + 15; // one line height + some padding
+
+ group.HeaderBounds = new Rectangle (0, y_origin, client_area.Width - v_scroll.Width, header_height);
+ group.items_area_location = new Point (0, y_origin + header_height);
+
+ int items_area_height = ((item_height + y_spacing) * rows);
+ return header_height + items_area_height + 10; // Add a small bottom margin
+ }
+
+ void CalculateDetailsGroupItemsCount ()
+ {
+ int items = 0;
+
+ groups.DefaultGroup.ItemCount = GetDefaultGroupItems ();
+ for (int i = 0; i < groups.InternalCount; i++) {
+ ListViewGroup group = groups.GetInternalGroup (i);
+ int items_in_group = group.GetActualItemCount ();
+
+ if (items_in_group == 0)
+ continue;
+
+ group.starting_item = items;
+ group.current_item = 0; // Reset layout.
+ items += items_in_group;
+ }
+ }
+
+ void LayoutHeader ()
+ {
+ 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;
+ }
+
+ layout_wd = x;
+
+ if (x < ClientRectangle.Width)
+ x = ClientRectangle.Width;
+
+ if (header_style == ColumnHeaderStyle.None) {
+ header_control.Visible = false;
+ header_control.Size = Size.Empty;
+ layout_wd = ClientRectangle.Width;
+ } else {
+ header_control.Width = x;
+ header_control.Height = columns.Count > 0 ? columns [0].Ht : ThemeEngine.Current.ListViewGetHeaderHeight (this, Font);
+ header_control.Visible = true;
+ }
+ }
+
+ void LayoutDetails ()
+ {
+ LayoutHeader ();
+
+ if (columns.Count == 0) {
+ item_control.Visible = false;
+ layout_wd = ClientRectangle.Width;
+ layout_ht = ClientRectangle.Height;
+ return;
+ }
+
+ item_control.Visible = true;
+ item_control.Location = Point.Empty;
+ item_control.Width = ClientRectangle.Width;
+ AdjustChildrenZOrder ();
+
+ int item_height = GetDetailsItemHeight ();
+ ItemSize = new Size (0, item_height); // We only cache Height for details view
+ int y = header_control.Height;
+ layout_ht = y + (item_height * items.Count);
+ if (items.Count > 0 && grid_lines) // some space for bottom gridline
+ layout_ht += 2;
+
+ bool using_groups = UsingGroups;
+ if (using_groups) {
+ // Observe that this routines will override our layout_ht value
+ CalculateDetailsGroupItemsCount ();
+ CalculateGroupsLayout (ItemSize, 2, y);
+ }
+
+ if (virtual_mode) // no assgination on items is needed
+ return;
+
+ for (int i = 0; i < items.Count; i++) {
+ ListViewItem item = items [i];
+
+ int display_index;
+ int item_y;
+
+ if (using_groups) {
+ ListViewGroup group = item.Group;
+ if (group == null)
+ group = groups.DefaultGroup;
+
+ int current_item = group.current_item++;
+ Point group_items_loc = group.items_area_location;
+ display_index = group.starting_item + current_item;
+
+ y = item_y = current_item * (item_height + 2) + group_items_loc.Y;
+ SetItemLocation (display_index, 0, item_y, 0, 0);
+ SetItemAtDisplayIndex (display_index, i);
+ } else
+ {
+ display_index = i;
+ item_y = y;
+ SetItemLocation (i, 0, item_y, 0, 0);
+ y += item_height;
+ }
+
+ item.Layout ();
+ item.DisplayIndex = display_index;
+ item.SetPosition (new Point (0, item_y));
+ }
+ }
+
+ // Need to make sure HeaderControl is on top, and we can't simply use BringToFront since
+ // these Widgets are implicit, so we need to re-populate our collection.
+ void AdjustChildrenZOrder ()
+ {
+ SuspendLayout ();
+ Widgets.ClearImplicit ();
+ Widgets.AddImplicit (header_control);
+ Widgets.AddImplicit (item_control);
+ Widgets.AddImplicit (h_scroll);
+ Widgets.AddImplicit (v_scroll);
+ ResumeLayout ();
+ }
+
+ private void AdjustItemsPositionArray (int count)
+ {
+ // In virtual mode we compute the positions on the fly.
+ if (virtual_mode)
+ return;
+ if (items_location.Length >= count)
+ return;
+
+ // items_location, items_matrix_location and reordered_items_indices must keep the same length
+ count = Math.Max (count, items_location.Length * 2);
+ items_location = new Point [count];
+ items_matrix_location = new ItemMatrixLocation [count];
+ reordered_items_indices = new int [count];
+ }
+
+ private void CalculateListView (ListViewAlignment align)
+ {
+ CalcTextSize ();
+
+ AdjustItemsPositionArray (items.Count);
+
+ switch (view) {
+ case View.Details:
+ LayoutDetails ();
+ break;
+
+ case View.SmallIcon:
+ LayoutIcons (SmallIconItemSize, alignment == ListViewAlignment.Left,
+ ThemeEngine.Current.ListViewHorizontalSpacing, 2);
+ break;
+
+ case View.LargeIcon:
+ LayoutIcons (LargeIconItemSize, alignment == ListViewAlignment.Left,
+ ThemeEngine.Current.ListViewHorizontalSpacing,
+ ThemeEngine.Current.ListViewVerticalSpacing);
+ break;
+
+ case View.List:
+ LayoutIcons (SmallIconItemSize, true,
+ ThemeEngine.Current.ListViewHorizontalSpacing, 2);
+ break;
+ case View.Tile:
+ if (!Application.VisualStylesEnabled)
+ goto case View.LargeIcon;
+
+ LayoutIcons (TileItemSize, alignment == ListViewAlignment.Left,
+ ThemeEngine.Current.ListViewHorizontalSpacing,
+ ThemeEngine.Current.ListViewVerticalSpacing);
+ break;
+ }
+
+ CalculateScrollBars ();
+ }
+
+ internal Point GetItemLocation (int index)
+ {
+ Point loc = Point.Empty;
+ if (virtual_mode)
+ loc = GetFixedItemLocation (index);
+ else
+ loc = items_location [index];
+
+ loc.X -= h_marker; // Adjust to scroll
+ loc.Y -= v_marker;
+
+ return loc;
+ }
+
+ Point GetFixedItemLocation (int index)
+ {
+ Point loc = Point.Empty;
+
+ switch (view) {
+ case View.LargeIcon:
+ case View.SmallIcon:
+ loc.X = index % cols * (item_size.Width + x_spacing);
+ loc.Y = index / cols * (item_size.Height + y_spacing);
+ break;
+ case View.List:
+ loc.X = index / rows * (item_size.Width + x_spacing);
+ loc.Y = index % rows * (item_size.Height + y_spacing);
+ break;
+ case View.Details:
+ loc.Y = header_control.Height + (index * item_size.Height);
+ break;
+ }
+
+ return loc;
+ }
+
+ internal int GetItemIndex (int display_index)
+ {
+ if (virtual_mode)
+ return display_index; // no reordering in virtual mode.
+ return reordered_items_indices [display_index];
+ }
+
+ internal ListViewItem GetItemAtDisplayIndex (int display_index)
+ {
+ // in virtual mode there's no reordering at all.
+ if (virtual_mode)
+ return items [display_index];
+ return items [reordered_items_indices [display_index]];
+ }
+
+ internal void SetItemAtDisplayIndex (int display_index, int index)
+ {
+ reordered_items_indices [display_index] = 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;
+ }
+
+ if (!Char.IsLetterOrDigit ((char)ke.KeyCode))
+ return false;
+
+ keysearch_text += (char)ke.KeyCode;
+ keysearch_tickcnt = current_tickcnt;
+
+ int prev_focused = FocusedItem == null ? 0 : FocusedItem.DisplayIndex;
+ int start = prev_focused + 1 < Items.Count ? prev_focused + 1 : 0;
+
+ ListViewItem item = FindItemWithText (keysearch_text, false, start, true, true);
+ if (item != null && prev_focused != item.DisplayIndex) {
+ selected_indices.Clear ();
+
+ SetFocusedItem (item.DisplayIndex);
+ item.Selected = true;
+ EnsureVisible (GetItemIndex (item.DisplayIndex));
+ }
+
+ return true;
+ }
+
+ private void OnItemsChanged ()
+ {
+ ResetSearchString ();
+ }
+
+ private void ResetSearchString ()
+ {
+ keysearch_text = String.Empty;
+ }
+
+ int GetAdjustedIndex (Keys key)
+ {
+ int result = -1;
+
+ if (View == View.Details) {
+ switch (key) {
+ case Keys.Up:
+ result = FocusedItem.DisplayIndex - 1;
+ break;
+ case Keys.Down:
+ result = FocusedItem.DisplayIndex + 1;
+ if (result == items.Count)
+ result = -1;
+ break;
+ case Keys.PageDown:
+ int last_index = LastVisibleIndex;
+ Rectangle item_rect = new Rectangle (GetItemLocation (last_index), ItemSize);
+ if (item_rect.Bottom > item_control.ClientRectangle.Bottom)
+ last_index--;
+ if (FocusedItem.DisplayIndex == last_index) {
+ if (FocusedItem.DisplayIndex < Items.Count - 1) {
+ int page_size = item_control.Height / ItemSize.Height - 1;
+ result = FocusedItem.DisplayIndex + page_size - 1;
+ if (result >= Items.Count)
+ result = Items.Count - 1;
+ }
+ } else
+ result = last_index;
+ break;
+ case Keys.PageUp:
+ int first_index = FirstVisibleIndex;
+ if (GetItemLocation (first_index).Y < 0)
+ first_index++;
+ if (FocusedItem.DisplayIndex == first_index) {
+ if (first_index > 0) {
+ int page_size = item_control.Height / ItemSize.Height - 1;
+ result = first_index - page_size + 1;
+ if (result < 0)
+ result = 0;
+ }
+ } else
+ result = first_index;
+ break;
+ }
+ return result;
+ }
+
+ if (virtual_mode)
+ return GetFixedAdjustedIndex (key);
+
+ ItemMatrixLocation item_matrix_location = items_matrix_location [FocusedItem.DisplayIndex];
+ int row = item_matrix_location.Row;
+ int col = item_matrix_location.Col;
+
+ int adjusted_index = -1;
+
+ switch (key) {
+ case Keys.Left:
+ if (col == 0)
+ return -1;
+ adjusted_index = item_index_matrix [row, col - 1];
+ break;
+
+ case Keys.Right:
+ if (col == (cols - 1))
+ return -1;
+ while (item_index_matrix [row, col + 1] == 0) {
+ row--;
+ if (row < 0)
+ return -1;
+ }
+ adjusted_index = item_index_matrix [row, col + 1];
+ break;
+
+ case Keys.Up:
+ if (row == 0)
+ return -1;
+ while (item_index_matrix [row - 1, col] == 0 && row != 1) {
+ col--;
+ if (col < 0)
+ return -1;
+ }
+ adjusted_index = item_index_matrix [row - 1, col];
+ break;
+
+ case Keys.Down:
+ if (row == (rows - 1) || row == Items.Count - 1)
+ return -1;
+ while (item_index_matrix [row + 1, col] == 0) {
+ col--;
+ if (col < 0)
+ return -1;
+ }
+ adjusted_index = item_index_matrix [row + 1, col];
+ break;
+
+ default:
+ return -1;
+ }
+
+ return items [adjusted_index].DisplayIndex;
+ }
+
+ // Used for virtual mode, where items *cannot* be re-arranged
+ int GetFixedAdjustedIndex (Keys key)
+ {
+ int result;
+
+ switch (key) {
+ case Keys.Left:
+ if (view == View.List)
+ result = focused_item_index - rows;
+ else
+ result = focused_item_index - 1;
+ break;
+ case Keys.Right:
+ if (view == View.List)
+ result = focused_item_index + rows;
+ else
+ result = focused_item_index + 1;
+ break;
+ case Keys.Up:
+ if (view != View.List)
+ result = focused_item_index - cols;
+ else
+ result = focused_item_index - 1;
+ break;
+ case Keys.Down:
+ if (view != View.List)
+ result = focused_item_index + cols;
+ else
+ result = focused_item_index + 1;
+ break;
+ default:
+ return -1;
+
+ }
+
+ if (result < 0 || result >= items.Count)
+ result = focused_item_index;
+
+ return result;
+ }
+
+ ListViewItem selection_start;
+
+ private bool SelectItems (ArrayList sel_items)
+ {
+ bool changed = false;
+ foreach (ListViewItem item in SelectedItems)
+ if (!sel_items.Contains (item)) {
+ item.Selected = false;
+ changed = true;
+ }
+ foreach (ListViewItem item in sel_items)
+ if (!item.Selected) {
+ item.Selected = true;
+ changed = true;
+ }
+ return changed;
+ }
+
+ private void UpdateMultiSelection (int index, bool reselect)
+ {
+ bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
+ bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Widget) != 0;
+ ListViewItem item = GetItemAtDisplayIndex (index);
+
+ if (shift_pressed && selection_start != null) {
+ ArrayList list = new ArrayList ();
+ int start_index = selection_start.DisplayIndex;
+ int start = Math.Min (start_index, index);
+ int end = Math.Max (start_index, index);
+ if (View == View.Details) {
+ for (int i = start; i <= end; i++)
+ list.Add (GetItemAtDisplayIndex (i));
+ } else {
+ ItemMatrixLocation start_item_matrix_location = items_matrix_location [start];
+ ItemMatrixLocation end_item_matrix_location = items_matrix_location [end];
+ int left = Math.Min (start_item_matrix_location.Col, end_item_matrix_location.Col);
+ int right = Math.Max (start_item_matrix_location.Col, end_item_matrix_location.Col);
+ int top = Math.Min (start_item_matrix_location.Row, end_item_matrix_location.Row);
+ int bottom = Math.Max (start_item_matrix_location.Row, end_item_matrix_location.Row);
+
+ for (int i = 0; i < items.Count; i++) {
+ ItemMatrixLocation item_matrix_loc = items_matrix_location [i];
+
+ if (item_matrix_loc.Row >= top && item_matrix_loc.Row <= bottom &&
+ item_matrix_loc.Col >= left && item_matrix_loc.Col <= right)
+ list.Add (GetItemAtDisplayIndex (i));
+ }
+ }
+ SelectItems (list);
+ } else if (ctrl_pressed) {
+ item.Selected = !item.Selected;
+ selection_start = item;
+ } else {
+ if (!reselect) {
+ // do not unselect, and reselect the item
+ foreach (int itemIndex in SelectedIndices) {
+ if (index == itemIndex)
+ continue;
+ items [itemIndex].Selected = false;
+ }
+ } else {
+ SelectedItems.Clear ();
+ item.Selected = true;
+ }
+ selection_start = item;
+ }
+ }
+
+ internal override bool InternalPreProcessMessage (ref Message msg)
+ {
+ if (msg.Msg == (int)Msg.WM_KEYDOWN) {
+ Keys key_data = (Keys)msg.WParam.ToInt32();
+
+ HandleNavKeys (key_data);
+ }
+
+ return base.InternalPreProcessMessage (ref msg);
+ }
+
+ bool HandleNavKeys (Keys key_data)
+ {
+ if (Items.Count == 0 || !item_control.Visible)
+ return false;
+
+ if (FocusedItem == null)
+ SetFocusedItem (0);
+
+ switch (key_data) {
+ case Keys.End:
+ SelectIndex (Items.Count - 1);
+ break;
+
+ case Keys.Home:
+ SelectIndex (0);
+ break;
+
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.PageUp:
+ case Keys.PageDown:
+ SelectIndex (GetAdjustedIndex (key_data));
+ break;
+
+ case Keys.Space:
+ SelectIndex (focused_item_index);
+ ToggleItemsCheckState ();
+ break;
+ case Keys.Enter:
+ if (selected_indices.Count > 0)
+ OnItemActivate (EventArgs.Empty);
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ void ToggleItemsCheckState ()
+ {
+ if (!CheckBoxes)
+ return;
+
+ // Don't modify check state if StateImageList has less than 2 elements
+ if (StateImageList != null && StateImageList.Images.Count < 2)
+ return;
+
+ if (SelectedIndices.Count > 0) {
+ for (int i = 0; i < SelectedIndices.Count; i++) {
+ ListViewItem item = Items [SelectedIndices [i]];
+ item.Checked = !item.Checked;
+ }
+ return;
+ }
+
+ if (FocusedItem != null) {
+ FocusedItem.Checked = !FocusedItem.Checked;
+ SelectIndex (FocusedItem.Index);
+ }
+ }
+
+ void SelectIndex (int display_index)
+ {
+ if (display_index == -1)
+ return;
+
+ if (MultiSelect)
+ UpdateMultiSelection (display_index, true);
+ else if (!GetItemAtDisplayIndex (display_index).Selected)
+ GetItemAtDisplayIndex (display_index).Selected = true;
+
+ SetFocusedItem (display_index);
+ EnsureVisible (GetItemIndex (display_index)); // Index in Items collection, not display index
+ }
+
+ private void ListView_KeyDown (object sender, KeyEventArgs ke)
+ {
+ if (ke.Handled || Items.Count == 0 || !item_control.Visible)
+ return;
+
+ if (ke.Alt || ke.Widget)
+ return;
+
+ ke.Handled = KeySearchString (ke);
+ }
+
+ private MouseEventArgs TranslateMouseEventArgs (MouseEventArgs args)
+ {
+ Point loc = PointToClient (Widget.MousePosition);
+ return new MouseEventArgs (args.Button, args.Clicks, loc.X, loc.Y, args.Delta);
+ }
+
+ internal class ItemControl : Widget {
+
+ ListView owner;
+ ListViewItem clicked_item;
+ ListViewItem last_clicked_item;
+ bool hover_processed = false;
+ bool checking = false;
+ ListViewItem prev_hovered_item;
+ ListViewItem prev_tooltip_item;
+ int clicks;
+ Point drag_begin = new Point (-1, -1);
+ internal int dragged_item_index = -1;
+
+ ListViewLabelEditTextBox edit_text_box;
+ internal ListViewItem edit_item;
+ LabelEditEventArgs edit_args;
+
+ public ItemControl (ListView owner)
+ {
+ this.owner = owner;
+ this.SetStyle (Widgetstyles.DoubleBuffer, true);
+ DoubleClick += new EventHandler(ItemsDoubleClick);
+ MouseDown += new MouseEventHandler(ItemsMouseDown);
+ MouseMove += new MouseEventHandler(ItemsMouseMove);
+ MouseHover += new EventHandler(ItemsMouseHover);
+ MouseUp += new MouseEventHandler(ItemsMouseUp);
+ }
+
+ void ItemsDoubleClick (object sender, EventArgs e)
+ {
+ if (owner.activation == ItemActivation.Standard)
+ owner.OnItemActivate (EventArgs.Empty);
+ }
+
+ enum BoxSelect {
+ None,
+ Normal,
+ Shift,
+ Widget
+ }
+
+ BoxSelect box_select_mode = BoxSelect.None;
+ IList prev_selection;
+ Point box_select_start;
+
+ Rectangle box_select_rect;
+ internal Rectangle BoxSelectRectangle {
+ get { return box_select_rect; }
+ set {
+ if (box_select_rect == value)
+ return;
+
+ InvalidateBoxSelectRect ();
+ box_select_rect = value;
+ InvalidateBoxSelectRect ();
+ }
+ }
+
+ void InvalidateBoxSelectRect ()
+ {
+ if (BoxSelectRectangle.Size.IsEmpty)
+ return;
+
+ Rectangle edge = BoxSelectRectangle;
+ edge.X -= 1;
+ edge.Y -= 1;
+ edge.Width += 2;
+ edge.Height = 2;
+ Invalidate (edge);
+ edge.Y = BoxSelectRectangle.Bottom - 1;
+ Invalidate (edge);
+ edge.Y = BoxSelectRectangle.Y - 1;
+ edge.Width = 2;
+ edge.Height = BoxSelectRectangle.Height + 2;
+ Invalidate (edge);
+ edge.X = BoxSelectRectangle.Right - 1;
+ Invalidate (edge);
+ }
+
+ private Rectangle CalculateBoxSelectRectangle (Point pt)
+ {
+ int left = Math.Min (box_select_start.X, pt.X);
+ int right = Math.Max (box_select_start.X, pt.X);
+ int top = Math.Min (box_select_start.Y, pt.Y);
+ int bottom = Math.Max (box_select_start.Y, pt.Y);
+ return Rectangle.FromLTRB (left, top, right, bottom);
+ }
+
+ bool BoxIntersectsItem (int index)
+ {
+ Rectangle r = new Rectangle (owner.GetItemLocation (index), owner.ItemSize);
+ if (owner.View != View.Details) {
+ r.X += r.Width / 4;
+ r.Y += r.Height / 4;
+ r.Width /= 2;
+ r.Height /= 2;
+ }
+ return BoxSelectRectangle.IntersectsWith (r);
+ }
+
+ bool BoxIntersectsText (int index)
+ {
+ Rectangle r = owner.GetItemAtDisplayIndex (index).TextBounds;
+ return BoxSelectRectangle.IntersectsWith (r);
+ }
+
+ ArrayList BoxSelectedItems {
+ get {
+ ArrayList result = new ArrayList ();
+ for (int i = 0; i < owner.Items.Count; i++) {
+ bool intersects;
+ // Can't iterate over specific items properties in virtualmode
+ if (owner.View == View.Details && !owner.FullRowSelect && !owner.VirtualMode)
+ intersects = BoxIntersectsText (i);
+ else
+ intersects = BoxIntersectsItem (i);
+
+ if (intersects)
+ result.Add (owner.GetItemAtDisplayIndex (i));
+ }
+ return result;
+ }
+ }
+
+ private bool PerformBoxSelection (Point pt)
+ {
+ if (box_select_mode == BoxSelect.None)
+ return false;
+
+ BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
+
+ ArrayList box_items = BoxSelectedItems;
+
+ ArrayList items;
+
+ switch (box_select_mode) {
+
+ case BoxSelect.Normal:
+ items = box_items;
+ break;
+
+ case BoxSelect.Widget:
+ items = new ArrayList ();
+ foreach (int index in prev_selection)
+ if (!box_items.Contains (owner.Items [index]))
+ items.Add (owner.Items [index]);
+ foreach (ListViewItem item in box_items)
+ if (!prev_selection.Contains (item.Index))
+ items.Add (item);
+ break;
+
+ case BoxSelect.Shift:
+ items = box_items;
+ foreach (ListViewItem item in box_items)
+ prev_selection.Remove (item.Index);
+ foreach (int index in prev_selection)
+ items.Add (owner.Items [index]);
+ break;
+
+ default:
+ throw new Exception ("Unexpected Selection mode: " + box_select_mode);
+ }
+
+ SuspendLayout ();
+ owner.SelectItems (items);
+ ResumeLayout ();
+
+ return true;
+ }
+
+ private void ItemsMouseDown (object sender, MouseEventArgs me)
+ {
+ owner.OnMouseDown (owner.TranslateMouseEventArgs (me));
+ if (owner.items.Count == 0)
+ return;
+
+ bool box_selecting = false;
+ Size item_size = owner.ItemSize;
+ Point pt = new Point (me.X, me.Y);
+ for (int i = 0; i < owner.items.Count; i++) {
+ Rectangle item_rect = new Rectangle (owner.GetItemLocation (i), item_size);
+ if (!item_rect.Contains (pt))
+ continue;
+
+ // Actual item in 'i' position
+ ListViewItem item = owner.GetItemAtDisplayIndex (i);
+
+ if (item.CheckRectReal.Contains (pt)) {
+ // Don't modify check state if we have only one image
+ // and if we are in 1.1 profile only take into account
+ // double clicks
+ if (owner.StateImageList != null && owner.StateImageList.Images.Count < 2
+ )
+ return;
+
+ // Generate an extra ItemCheck event when we got two clicks
+ // (Match weird .Net behaviour)
+ if (me.Clicks == 2)
+ item.Checked = !item.Checked;
+
+ item.Checked = !item.Checked;
+ checking = true;
+ return;
+ }
+
+ if (owner.View == View.Details) {
+ bool over_text = item.TextBounds.Contains (pt);
+ if (owner.FullRowSelect) {
+ clicked_item = item;
+ bool over_item_column = (me.X > owner.Columns[0].X && me.X < owner.Columns[0].X + owner.Columns[0].Width);
+ if (!over_text && over_item_column && owner.MultiSelect)
+ box_selecting = true;
+ } else if (over_text)
+ clicked_item = item;
+ else
+ owner.SetFocusedItem (i);
+ } else
+ clicked_item = item;
+
+ break;
+ }
+
+
+ if (clicked_item != null) {
+ bool changed = !clicked_item.Selected;
+ if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
+ owner.SetFocusedItem (clicked_item.DisplayIndex);
+
+ if (owner.MultiSelect) {
+ bool reselect = (!owner.LabelEdit || changed);
+ if (me.Button == MouseButtons.Left || (XplatUI.State.ModifierKeys == Keys.None && changed))
+ owner.UpdateMultiSelection (clicked_item.DisplayIndex, reselect);
+ } else {
+ clicked_item.Selected = true;
+ }
+
+ // Side-effects of changing the selection can possibly result in ItemsMouseUp() being called and
+ // and clicked_item being set to null. (See Xamarin bug 23591.) In such a case, assume
+ // that there's nothing more we can do here.
+ if (clicked_item == null)
+ return;
+
+ if (owner.VirtualMode && changed) {
+ // Broken event - It's not fired from Item.Selected also
+ ListViewVirtualItemsSelectionRangeChangedEventArgs args =
+ new ListViewVirtualItemsSelectionRangeChangedEventArgs (0, owner.items.Count - 1, false);
+
+ owner.OnVirtualItemsSelectionRangeChanged (args);
+ }
+ // Report clicks only if the item was clicked. On MS the
+ // clicks are only raised if you click an item
+ clicks = me.Clicks;
+ if (me.Clicks > 1) {
+ if (owner.CheckBoxes)
+ clicked_item.Checked = !clicked_item.Checked;
+ } else if (me.Clicks == 1) {
+ if (owner.LabelEdit && !changed)
+ BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
+ }
+
+ drag_begin = me.Location;
+ dragged_item_index = clicked_item.Index;
+ } else {
+ if (owner.MultiSelect)
+ box_selecting = true;
+ else if (owner.SelectedItems.Count > 0)
+ owner.SelectedItems.Clear ();
+ }
+
+ if (box_selecting) {
+ Keys mods = XplatUI.State.ModifierKeys;
+ if ((mods & Keys.Shift) != 0)
+ box_select_mode = BoxSelect.Shift;
+ else if ((mods & Keys.Widget) != 0)
+ box_select_mode = BoxSelect.Widget;
+ else
+ box_select_mode = BoxSelect.Normal;
+ box_select_start = pt;
+ prev_selection = owner.SelectedIndices.List.Clone () as IList;
+ }
+ }
+
+ private void ItemsMouseMove (object sender, MouseEventArgs me)
+ {
+ bool done = PerformBoxSelection (new Point (me.X, me.Y));
+
+ owner.OnMouseMove (owner.TranslateMouseEventArgs (me));
+
+ if (done)
+ return;
+ if ((me.Button != MouseButtons.Left && me.Button != MouseButtons.Right) &&
+ !hover_processed && owner.Activation != ItemActivation.OneClick
+ && !owner.ShowItemToolTips
+ )
+ return;
+
+ Point pt = PointToClient (Widget.MousePosition);
+ ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
+
+ if (hover_processed && item != null && item != prev_hovered_item) {
+ hover_processed = false;
+ XplatUI.ResetMouseHover (Handle);
+ }
+
+ // Need to invalidate the item in HotTracking to show/hide the underline style
+ if (owner.Activation == ItemActivation.OneClick) {
+ if (item == null && owner.HotItemIndex != -1) {
+ if (owner.HotTracking)
+ Invalidate (owner.Items [owner.HotItemIndex].Bounds); // Previous one
+
+ Cursor = Cursors.Default;
+ owner.HotItemIndex = -1;
+ } else if (item != null && owner.HotItemIndex == -1) {
+ if (owner.HotTracking)
+ Invalidate (item.Bounds);
+
+ Cursor = Cursors.Hand;
+ owner.HotItemIndex = item.Index;
+ }
+ }
+
+ if (me.Button == MouseButtons.Left || me.Button == MouseButtons.Right) {
+ if (drag_begin != new Point (-1, -1)) {
+ Rectangle r = new Rectangle (drag_begin, SystemInformation.DragSize);
+ if (!r.Contains (me.X, me.Y)) {
+ ListViewItem dragged_item = owner.items [dragged_item_index];
+ owner.OnItemDrag (new ItemDragEventArgs (me.Button, dragged_item));
+
+ drag_begin = new Point (-1, -1);
+ dragged_item_index = -1;
+ }
+ }
+ }
+
+ if (owner.ShowItemToolTips) {
+ if (item == null) {
+ owner.item_tooltip.Active = false;
+ prev_tooltip_item = null;
+ } else if (item != prev_tooltip_item && item.ToolTipText.Length > 0) {
+ owner.item_tooltip.Active = true;
+ owner.item_tooltip.SetToolTip (owner, item.ToolTipText);
+ prev_tooltip_item = item;
+ }
+ }
+
+ }
+
+ private void ItemsMouseHover (object sender, EventArgs e)
+ {
+ if (owner.hover_pending) {
+ owner.OnMouseHover (e);
+ owner.hover_pending = false;
+ }
+
+ if (Capture)
+ return;
+
+ hover_processed = true;
+ Point pt = PointToClient (Widget.MousePosition);
+ ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
+ if (item == null)
+ return;
+
+ prev_hovered_item = item;
+
+ if (owner.HoverSelection) {
+ if (owner.MultiSelect)
+ owner.UpdateMultiSelection (item.Index, true);
+ else
+ item.Selected = true;
+
+ owner.SetFocusedItem (item.DisplayIndex);
+ Select (); // Make sure we have the focus, since MouseHover doesn't give it to us
+ }
+
+ owner.OnItemMouseHover (new ListViewItemMouseHoverEventArgs (item));
+ }
+
+ void HandleClicks (MouseEventArgs me)
+ {
+ // if the click is not on an item,
+ // clicks remains as 0
+ if (clicks > 1) {
+ owner.OnDoubleClick (EventArgs.Empty);
+ owner.OnMouseDoubleClick (me);
+ } else if (clicks == 1) {
+ owner.OnClick (EventArgs.Empty);
+ owner.OnMouseClick (me);
+ }
+
+ clicks = 0;
+ }
+
+ private void ItemsMouseUp (object sender, MouseEventArgs me)
+ {
+ MouseEventArgs owner_me = owner.TranslateMouseEventArgs (me);
+ HandleClicks (owner_me);
+
+ Capture = false;
+ if (owner.Items.Count == 0) {
+ ResetMouseState ();
+ owner.OnMouseUp (owner_me);
+ 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;
+ }
+ }
+ } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
+ // Need this to clean up background clicks
+ owner.SelectedItems.Clear ();
+ }
+
+ ResetMouseState ();
+ owner.OnMouseUp (owner_me);
+ }
+
+ private void ResetMouseState ()
+ {
+ clicked_item = null;
+ box_select_start = Point.Empty;
+ BoxSelectRectangle = Rectangle.Empty;
+ prev_selection = null;
+ box_select_mode = BoxSelect.None;
+ checking = false;
+
+ // Clean these bits in case the mouse buttons were
+ // released before firing ItemDrag
+ dragged_item_index = -1;
+ drag_begin = new Point (-1, -1);
+ }
+
+ private void LabelEditFinished (object sender, EventArgs e)
+ {
+ EndEdit (edit_item);
+ }
+
+ private void LabelEditCancelled (object sender, EventArgs e)
+ {
+ edit_args.SetLabel (null);
+ EndEdit (edit_item);
+ }
+
+ private void LabelTextChanged (object sender, EventArgs e)
+ {
+ if (edit_args != null)
+ edit_args.SetLabel (edit_text_box.Text);
+ }
+
+ internal void BeginEdit (ListViewItem item)
+ {
+ if (edit_item != null)
+ EndEdit (edit_item);
+
+ if (edit_text_box == null) {
+ edit_text_box = new ListViewLabelEditTextBox ();
+ edit_text_box.BorderStyle = BorderStyle.FixedSingle;
+ edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
+ edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
+ edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
+ edit_text_box.Visible = false;
+ Widgets.Add (edit_text_box);
+ }
+
+ item.EnsureVisible();
+
+ edit_text_box.Reset ();
+
+ switch (owner.view) {
+ case View.List:
+ case View.SmallIcon:
+ case View.Details:
+ edit_text_box.TextAlign = HorizontalAlignment.Left;
+ edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
+ SizeF sizef = TextRenderer.MeasureString (item.Text, item.Font);
+ edit_text_box.Width = (int)sizef.Width + 4;
+ edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
+ edit_text_box.WordWrap = false;
+ edit_text_box.Multiline = false;
+ break;
+ case View.LargeIcon:
+ edit_text_box.TextAlign = HorizontalAlignment.Center;
+ edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
+ sizef = TextRenderer.MeasureString (item.Text, item.Font);
+ edit_text_box.Width = (int)sizef.Width + 4;
+ edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
+ edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
+ edit_text_box.WordWrap = true;
+ edit_text_box.Multiline = true;
+ break;
+ }
+
+ edit_item = item;
+
+ edit_text_box.Text = item.Text;
+ edit_text_box.Font = item.Font;
+ edit_text_box.Visible = true;
+ edit_text_box.Focus ();
+ edit_text_box.SelectAll ();
+
+ edit_args = new LabelEditEventArgs (owner.Items.IndexOf (edit_item));
+ owner.OnBeforeLabelEdit (edit_args);
+
+ if (edit_args.CancelEdit)
+ EndEdit (item);
+ }
+
+ internal void CancelEdit (ListViewItem item)
+ {
+ // do nothing if there's no item being edited, or if the
+ // item being edited is not the one passed in
+ if (edit_item == null || edit_item != item)
+ return;
+
+ edit_args.SetLabel (null);
+ EndEdit (item);
+ }
+
+ internal void EndEdit (ListViewItem item)
+ {
+ // do nothing if there's no item being edited, or if the
+ // item being edited is not the one passed in
+ if (edit_item == null || edit_item != item)
+ return;
+
+ if (edit_text_box != null) {
+ if (edit_text_box.Visible)
+ edit_text_box.Visible = false;
+ // ensure listview gets focus
+ owner.Focus ();
+ }
+
+ // Same as TreeView.EndEdit: need to have focus in synch
+ Application.DoEvents ();
+
+ //
+ // Create a new instance, since we could get a call to BeginEdit
+ // from the handler and have fields out of synch
+ //
+ LabelEditEventArgs args = new LabelEditEventArgs (item.Index, edit_args.Label);
+ edit_item = null;
+
+ owner.OnAfterLabelEdit (args);
+ if (!args.CancelEdit && args.Label != null)
+ item.Text = args.Label;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pe)
+ {
+ ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_KILLFOCUS:
+ owner.Select (false, true);
+ break;
+ case Msg.WM_SETFOCUS:
+ owner.Select (false, true);
+ break;
+ case Msg.WM_LBUTTONDOWN:
+ if (!Focused)
+ owner.Select (false, true);
+ break;
+ case Msg.WM_RBUTTONDOWN:
+ if (!Focused)
+ owner.Select (false, true);
+ break;
+ default:
+ break;
+ }
+ base.WndProc (ref m);
+ }
+ }
+
+ internal class ListViewLabelEditTextBox : TextBox
+ {
+ int max_width = -1;
+ int min_width = -1;
+
+ int max_height = -1;
+ int min_height = -1;
+
+ int old_number_lines = 1;
+
+ SizeF text_size_one_char;
+
+ public ListViewLabelEditTextBox ()
+ {
+ min_height = DefaultSize.Height;
+ text_size_one_char = TextRenderer.MeasureString ("B", Font);
+ }
+
+ public int MaxWidth {
+ set {
+ if (value < min_width)
+ max_width = min_width;
+ else
+ max_width = value;
+ }
+ }
+
+ public int MaxHeight {
+ set {
+ if (value < min_height)
+ max_height = min_height;
+ else
+ max_height = value;
+ }
+ }
+
+ public new int Width {
+ get {
+ return base.Width;
+ }
+ set {
+ min_width = value;
+ base.Width = value;
+ }
+ }
+
+ public override Font Font {
+ get {
+ return base.Font;
+ }
+ set {
+ base.Font = value;
+ text_size_one_char = TextRenderer.MeasureString ("B", Font);
+ }
+ }
+
+ protected override void OnTextChanged (EventArgs e)
+ {
+ SizeF text_size = TextRenderer.MeasureString (Text, Font);
+
+ int new_width = (int)text_size.Width + 8;
+
+ if (!Multiline)
+ ResizeTextBoxWidth (new_width);
+ else {
+ if (Width != max_width)
+ ResizeTextBoxWidth (new_width);
+
+ int number_lines = Lines.Length;
+
+ if (number_lines != old_number_lines) {
+ int new_height = number_lines * (int)text_size_one_char.Height + 4;
+ old_number_lines = number_lines;
+
+ ResizeTextBoxHeight (new_height);
+ }
+ }
+
+ base.OnTextChanged (e);
+ }
+
+ protected override bool IsInputKey (Keys key_data)
+ {
+ if ((key_data & Keys.Alt) == 0) {
+ switch (key_data & Keys.KeyCode) {
+ case Keys.Enter:
+ return true;
+ case Keys.Escape:
+ return true;
+ }
+ }
+ return base.IsInputKey (key_data);
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ if (!Visible)
+ return;
+
+ switch (e.KeyCode) {
+ case Keys.Return:
+ Visible = false;
+ e.Handled = true;
+ OnEditingFinished (e);
+ break;
+ case Keys.Escape:
+ Visible = false;
+ e.Handled = true;
+ OnEditingCancelled (e);
+ break;
+ }
+ }
+
+ protected override void OnLostFocus (EventArgs e)
+ {
+ if (Visible) {
+ OnEditingFinished (e);
+ }
+ }
+
+ protected void OnEditingCancelled (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [EditingCancelledEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected void OnEditingFinished (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [EditingFinishedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void ResizeTextBoxWidth (int new_width)
+ {
+ if (new_width > max_width)
+ base.Width = max_width;
+ else
+ if (new_width >= min_width)
+ base.Width = new_width;
+ else
+ base.Width = min_width;
+ }
+
+ private void ResizeTextBoxHeight (int new_height)
+ {
+ if (new_height > max_height)
+ base.Height = max_height;
+ else
+ if (new_height >= min_height)
+ base.Height = new_height;
+ else
+ base.Height = min_height;
+ }
+
+ public void Reset ()
+ {
+ max_width = -1;
+ min_width = -1;
+
+ max_height = -1;
+
+ old_number_lines = 1;
+
+ Text = String.Empty;
+
+ Size = DefaultSize;
+ }
+
+ static object EditingCancelledEvent = new object ();
+ public event EventHandler EditingCancelled {
+ add { Events.AddHandler (EditingCancelledEvent, value); }
+ remove { Events.RemoveHandler (EditingCancelledEvent, value); }
+ }
+
+ static object EditingFinishedEvent = new object ();
+ public event EventHandler EditingFinished {
+ add { Events.AddHandler (EditingFinishedEvent, value); }
+ remove { Events.RemoveHandler (EditingFinishedEvent, value); }
+ }
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pe)
+ {
+ if (updating)
+ return;
+
+ CalculateScrollBars ();
+ }
+
+ void FocusChanged (object o, EventArgs args)
+ {
+ if (Items.Count == 0)
+ return;
+
+ if (FocusedItem == null)
+ SetFocusedItem (0);
+
+ ListViewItem focused_item = FocusedItem;
+
+ if (focused_item.ListView != null) {
+ focused_item.Invalidate ();
+ focused_item.Layout ();
+ focused_item.Invalidate ();
+ }
+ }
+
+ private void ListView_Invalidated (object sender, InvalidateEventArgs e)
+ {
+ // When the ListView is invalidated, we need to invalidate
+ // the child Widgets.
+ header_control.Invalidate ();
+ item_control.Invalidate ();
+ }
+
+ private void ListView_MouseEnter (object sender, EventArgs args)
+ {
+ hover_pending = true; // Need a hover event for every Enter/Leave cycle
+ }
+
+ private void ListView_MouseWheel (object sender, MouseEventArgs me)
+ {
+ if (Items.Count == 0)
+ return;
+
+ int lines = me.Delta / 120;
+
+ if (lines == 0)
+ return;
+
+ switch (View) {
+ case View.Details:
+ case View.SmallIcon:
+ Scroll (v_scroll, -ItemSize.Height * SystemInformation.MouseWheelScrollLines * lines);
+ break;
+ case View.LargeIcon:
+ Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * lines);
+ break;
+ case View.List:
+ Scroll (h_scroll, -ItemSize.Width * lines);
+ break;
+ case View.Tile:
+ if (!Application.VisualStylesEnabled)
+ goto case View.LargeIcon;
+
+ Scroll (v_scroll, -(ItemSize.Height + ThemeEngine.Current.ListViewVerticalSpacing) * 2 * lines);
+ break;
+ }
+ }
+
+ private void ListView_SizeChanged (object sender, EventArgs e)
+ {
+ Redraw (true);
+ }
+
+ private void SetFocusedItem (int display_index)
+ {
+ if (display_index != -1)
+ GetItemAtDisplayIndex (display_index).Focused = true;
+ else if (focused_item_index != -1 && focused_item_index < items.Count) // Previous focused item
+ GetItemAtDisplayIndex (focused_item_index).Focused = false;
+ focused_item_index = display_index;
+ if (display_index == -1)
+ OnUIAFocusedItemChanged ();
+ // otherwise the event will have been fired
+ // when the ListViewItem's Focused was set
+ }
+
+ private void HorizontalScroller (object sender, EventArgs e)
+ {
+ item_control.EndEdit (item_control.edit_item);
+
+ // 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)
+ {
+ item_control.EndEdit (item_control.edit_item);
+
+ // 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;
+ if (header_control.Visible) {
+ area.Y += header_control.Height;
+ area.Height -= header_control.Height;
+ }
+
+ v_marker = v_scroll.Value;
+ XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
+ }
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+ #endregion // Internal Methods Properties
+
+ #region Protected Methods
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ is_selection_available = true;
+ for (int i = 0; i < SelectedItems.Count; i++)
+ OnSelectedIndexChanged (EventArgs.Empty);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ large_image_list = null;
+ small_image_list = null;
+ state_image_list = null;
+
+ foreach (ColumnHeader col in columns)
+ col.SetListView (null);
+
+ if (!virtual_mode) // In virtual mode we don't save the items
+ foreach (ListViewItem item in items)
+ item.Owner = 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)
+ {
+ LabelEditEventHandler eh = (LabelEditEventHandler)(Events [AfterLabelEditEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnBackgroundImageChanged (EventArgs e)
+ {
+ item_control.BackgroundImage = BackgroundImage;
+ base.OnBackgroundImageChanged (e);
+ }
+
+ protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
+ {
+ LabelEditEventHandler eh = (LabelEditEventHandler)(Events [BeforeLabelEditEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnColumnClick (ColumnClickEventArgs e)
+ {
+ ColumnClickEventHandler eh = (ColumnClickEventHandler)(Events [ColumnClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
+ {
+ DrawListViewColumnHeaderEventHandler eh = (DrawListViewColumnHeaderEventHandler)(Events[DrawColumnHeaderEvent]);
+ if (eh != null)
+ eh(this, e);
+ }
+
+ protected internal virtual void OnDrawItem(DrawListViewItemEventArgs e)
+ {
+ DrawListViewItemEventHandler eh = (DrawListViewItemEventHandler)(Events[DrawItemEvent]);
+ if (eh != null)
+ eh(this, e);
+ }
+
+ protected internal virtual void OnDrawSubItem(DrawListViewSubItemEventArgs e)
+ {
+ DrawListViewSubItemEventHandler eh = (DrawListViewSubItemEventHandler)(Events[DrawSubItemEvent]);
+ if (eh != null)
+ eh(this, e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ Redraw (true);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ CalculateListView (alignment);
+ if (!virtual_mode) // Sorting is not allowed in virtual mode
+ Sort ();
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnItemActivate (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ItemActivateEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnItemCheck (ItemCheckEventArgs ice)
+ {
+ ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
+ if (eh != null)
+ eh (this, ice);
+ }
+
+ protected internal virtual void OnItemChecked (ItemCheckedEventArgs e)
+ {
+ ItemCheckedEventHandler eh = (ItemCheckedEventHandler)(Events [ItemCheckedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnItemDrag (ItemDragEventArgs e)
+ {
+ ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnItemMouseHover (ListViewItemMouseHoverEventArgs e)
+ {
+ ListViewItemMouseHoverEventHandler eh = (ListViewItemMouseHoverEventHandler)(Events [ItemMouseHoverEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnItemSelectionChanged (ListViewItemSelectionChangedEventArgs e)
+ {
+ ListViewItemSelectionChangedEventHandler eh =
+ (ListViewItemSelectionChangedEventHandler) Events [ItemSelectionChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnMouseHover (EventArgs e)
+ {
+ base.OnMouseHover (e);
+ }
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ protected virtual void OnSelectedIndexChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnSystemColorsChanged (EventArgs e)
+ {
+ base.OnSystemColorsChanged (e);
+ }
+
+ protected internal virtual void OnCacheVirtualItems (CacheVirtualItemsEventArgs e)
+ {
+ CacheVirtualItemsEventHandler eh = (CacheVirtualItemsEventHandler)Events [CacheVirtualItemsEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnRetrieveVirtualItem (RetrieveVirtualItemEventArgs e)
+ {
+ RetrieveVirtualItemEventHandler eh = (RetrieveVirtualItemEventHandler)Events [RetrieveVirtualItemEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)Events[RightToLeftLayoutChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnSearchForVirtualItem (SearchForVirtualItemEventArgs e)
+ {
+ SearchForVirtualItemEventHandler eh = (SearchForVirtualItemEventHandler) Events [SearchForVirtualItemEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnVirtualItemsSelectionRangeChanged (ListViewVirtualItemsSelectionRangeChangedEventArgs e)
+ {
+ ListViewVirtualItemsSelectionRangeChangedEventHandler eh =
+ (ListViewVirtualItemsSelectionRangeChangedEventHandler) Events [VirtualItemsSelectionRangeChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected void RealizeProperties ()
+ {
+ // FIXME: TODO
+ }
+
+ protected void UpdateExtendedStyles ()
+ {
+ // FIXME: TODO
+ }
+
+ bool refocusing = false;
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_KILLFOCUS:
+ Widget receiver = Widget.FromHandle (m.WParam);
+ if (receiver == item_control) {
+ has_focus = false;
+ refocusing = true;
+ return;
+ }
+ break;
+ case Msg.WM_SETFOCUS:
+ if (refocusing) {
+ has_focus = true;
+ refocusing = false;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ base.WndProc (ref m);
+ }
+ #endregion // Protected Methods
+
+ #region Public Instance Methods
+ public void ArrangeIcons ()
+ {
+ ArrangeIcons (this.alignment);
+ }
+
+ public void ArrangeIcons (ListViewAlignment value)
+ {
+ // Icons are arranged only if view is set to LargeIcon or SmallIcon
+ if (view == View.LargeIcon || view == View.SmallIcon)
+ Redraw (true);
+ }
+
+ public void AutoResizeColumn (int columnIndex, ColumnHeaderAutoResizeStyle headerAutoResize)
+ {
+ if (columnIndex < 0 || columnIndex >= columns.Count)
+ throw new ArgumentOutOfRangeException ("columnIndex");
+
+ columns [columnIndex].AutoResize (headerAutoResize);
+ }
+
+ public void AutoResizeColumns (ColumnHeaderAutoResizeStyle headerAutoResize)
+ {
+ BeginUpdate ();
+ foreach (ColumnHeader col in columns)
+ col.AutoResize (headerAutoResize);
+ EndUpdate ();
+ }
+
+ 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 || updating)
+ return;
+
+ Rectangle view_rect = item_control.ClientRectangle;
+ // Avoid direct access to items in virtual mode, and use item bounds otherwise, since we could have reordered items
+ Rectangle bounds = virtual_mode ? new Rectangle (GetItemLocation (index), ItemSize) : items [index].Bounds;
+
+ if (view == View.Details && header_style != ColumnHeaderStyle.None) {
+ view_rect.Y += header_control.Height;
+ view_rect.Height -= header_control.Height;
+ }
+
+ if (view_rect.Contains (bounds))
+ return;
+
+ if (View != View.Details) {
+ if (bounds.Left < 0)
+ h_scroll.Value += bounds.Left;
+ // Don't shift right unless right-to-left layout is active. (Xamarin bug 22483)
+ else if (this.RightToLeftLayout && bounds.Right > view_rect.Right)
+ h_scroll.Value += (bounds.Right - view_rect.Right);
+ }
+
+ if (bounds.Top < view_rect.Y)
+ v_scroll.Value += bounds.Top - view_rect.Y;
+ else if (bounds.Bottom > view_rect.Bottom)
+ v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
+ }
+
+ public ListViewItem FindItemWithText (string text)
+ {
+ if (items.Count == 0)
+ return null;
+
+ return FindItemWithText (text, true, 0, true);
+ }
+
+ public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex)
+ {
+ return FindItemWithText (text, includeSubItemsInSearch, startIndex, true, false);
+ }
+
+ public ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch)
+ {
+ return FindItemWithText (text, includeSubItemsInSearch, startIndex, isPrefixSearch, false);
+ }
+
+ internal ListViewItem FindItemWithText (string text, bool includeSubItemsInSearch, int startIndex, bool isPrefixSearch, bool roundtrip)
+ {
+ if (startIndex < 0 || startIndex >= items.Count)
+ throw new ArgumentOutOfRangeException ("startIndex");
+
+ if (text == null)
+ throw new ArgumentNullException ("text");
+
+ if (virtual_mode) {
+ SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (true,
+ isPrefixSearch, includeSubItemsInSearch, text, Point.Empty,
+ SearchDirectionHint.Down, startIndex);
+
+ OnSearchForVirtualItem (args);
+ int idx = args.Index;
+ if (idx >= 0 && idx < virtual_list_size)
+ return items [idx];
+
+ return null;
+ }
+
+ int i = startIndex;
+ while (true) {
+ ListViewItem lvi = items [i];
+
+ if (isPrefixSearch) { // prefix search
+ if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (lvi.Text, text, CompareOptions.IgnoreCase))
+ return lvi;
+ } else if (String.Compare (lvi.Text, text, true) == 0) // match
+ return lvi;
+
+ if (i + 1 >= items.Count) {
+ if (!roundtrip)
+ break;
+
+ i = 0;
+ } else
+ i++;
+
+ if (i == startIndex)
+ break;
+ }
+
+ // Subitems have a minor priority, so we have to do a second linear search
+ // Also, we don't need to to a roundtrip search for them by now
+ if (includeSubItemsInSearch) {
+ for (i = startIndex; i < items.Count; i++) {
+ ListViewItem lvi = items [i];
+ foreach (ListViewItem.ListViewSubItem sub_item in lvi.SubItems)
+ if (isPrefixSearch) {
+ if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (sub_item.Text,
+ text, CompareOptions.IgnoreCase))
+ return lvi;
+ } else if (String.Compare (sub_item.Text, text, true) == 0)
+ return lvi;
+ }
+ }
+
+ return null;
+ }
+
+ public ListViewItem FindNearestItem (SearchDirectionHint searchDirection, int x, int y)
+ {
+ return FindNearestItem (searchDirection, new Point (x, y));
+ }
+
+ public ListViewItem FindNearestItem (SearchDirectionHint dir, Point point)
+ {
+ if (dir < SearchDirectionHint.Left || dir > SearchDirectionHint.Down)
+ throw new ArgumentOutOfRangeException ("searchDirection");
+
+ if (view != View.LargeIcon && view != View.SmallIcon)
+ throw new InvalidOperationException ();
+
+ if (virtual_mode) {
+ SearchForVirtualItemEventArgs args = new SearchForVirtualItemEventArgs (false,
+ false, false, String.Empty, point,
+ dir, 0);
+
+ OnSearchForVirtualItem (args);
+ int idx = args.Index;
+ if (idx >= 0 && idx < virtual_list_size)
+ return items [idx];
+
+ return null;
+ }
+
+ ListViewItem item = null;
+ int min_dist = Int32.MaxValue;
+
+ //
+ // It looks like .Net does a previous adjustment
+ //
+ switch (dir) {
+ case SearchDirectionHint.Up:
+ point.Y -= item_size.Height;
+ break;
+ case SearchDirectionHint.Down:
+ point.Y += item_size.Height;
+ break;
+ case SearchDirectionHint.Left:
+ point.X -= item_size.Width;
+ break;
+ case SearchDirectionHint.Right:
+ point.X += item_size.Width;
+ break;
+ }
+
+ for (int i = 0; i < items.Count; i++) {
+ Point item_loc = GetItemLocation (i);
+
+ if (dir == SearchDirectionHint.Up) {
+ if (point.Y < item_loc.Y)
+ continue;
+ } else if (dir == SearchDirectionHint.Down) {
+ if (point.Y > item_loc.Y)
+ continue;
+ } else if (dir == SearchDirectionHint.Left) {
+ if (point.X < item_loc.X)
+ continue;
+ } else if (dir == SearchDirectionHint.Right) {
+ if (point.X > item_loc.X)
+ continue;
+ }
+
+ int x_dist = point.X - item_loc.X;
+ int y_dist = point.Y - item_loc.Y;
+
+ int dist = x_dist * x_dist + y_dist * y_dist;
+ if (dist < min_dist) {
+ item = items [i];
+ min_dist = dist;
+ }
+ }
+
+ return item;
+ }
+
+ public ListViewItem GetItemAt (int x, int y)
+ {
+ Size item_size = ItemSize;
+ for (int i = 0; i < items.Count; i++) {
+ Rectangle item_rect = items [i].Bounds;
+ if (item_rect.Contains (x, y))
+ return items [i];
+ }
+
+ 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 ("index");
+
+ return items [index].GetBounds (portion);
+ }
+
+ public ListViewHitTestInfo HitTest (Point point)
+ {
+ return HitTest (point.X, point.Y);
+ }
+
+ public ListViewHitTestInfo HitTest (int x, int y)
+ {
+ if (x < 0)
+ throw new ArgumentOutOfRangeException ("x");
+ if (y < 0)
+ throw new ArgumentOutOfRangeException ("y");
+
+ ListViewItem item = GetItemAt (x, y);
+ if (item == null)
+ return new ListViewHitTestInfo (null, null, ListViewHitTestLocations.None);
+
+ ListViewHitTestLocations locations = 0;
+ if (item.GetBounds (ItemBoundsPortion.Label).Contains (x, y))
+ locations |= ListViewHitTestLocations.Label;
+ else if (item.GetBounds (ItemBoundsPortion.Icon).Contains (x, y))
+ locations |= ListViewHitTestLocations.Image;
+ else if (item.CheckRectReal.Contains (x, y))
+ locations |= ListViewHitTestLocations.StateImage;
+
+ ListViewItem.ListViewSubItem subitem = null;
+ if (view == View.Details)
+ foreach (ListViewItem.ListViewSubItem si in item.SubItems)
+ if (si.Bounds.Contains (x, y)) {
+ subitem = si;
+ break;
+ }
+
+ return new ListViewHitTestInfo (item, subitem, locations);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public void RedrawItems (int startIndex, int endIndex, bool invalidateOnly)
+ {
+ if (startIndex < 0 || startIndex >= items.Count)
+ throw new ArgumentOutOfRangeException ("startIndex");
+ if (endIndex < 0 || endIndex >= items.Count)
+ throw new ArgumentOutOfRangeException ("endIndex");
+ if (startIndex > endIndex)
+ throw new ArgumentException ("startIndex");
+
+ if (updating)
+ return;
+
+ for (int i = startIndex; i <= endIndex; i++)
+ items [i].Invalidate ();
+
+ if (!invalidateOnly)
+ Update ();
+ }
+
+ public void Sort ()
+ {
+ if (virtual_mode)
+ throw new InvalidOperationException ();
+
+ Sort (true);
+ }
+
+ // we need this overload to reuse the logic for sorting, while allowing
+ // redrawing to be done by caller or have it done by this method when
+ // sorting is really performed
+ //
+ // ListViewItemCollection's Add and AddRange methods call this overload
+ // with redraw set to false, as they take care of redrawing themselves
+ // (they even want to redraw the listview if no sort is performed, as
+ // an item was added), while ListView.Sort () only wants to redraw if
+ // sorting was actually performed
+ private void Sort (bool redraw)
+ {
+ if (!IsHandleCreated || item_sorter == null) {
+ return;
+ }
+
+ items.Sort (item_sorter);
+ if (redraw)
+ this.Redraw (true);
+ }
+
+ public override string ToString ()
+ {
+ int count = this.Items.Count;
+
+ if (count == 0)
+ return string.Format ("ShiftUI.ListView, Items.Count: 0");
+ else
+ return string.Format ("ShiftUI.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
+ }
+ #endregion // Public Instance Methods
+
+
+ #region Subclasses
+
+ internal class HeaderControl : Widget {
+
+ ListView owner;
+ bool column_resize_active = false;
+ ColumnHeader resize_column;
+ ColumnHeader clicked_column;
+ ColumnHeader drag_column;
+ int drag_x;
+ int drag_to_index = -1;
+ ColumnHeader entered_column_header;
+
+ public HeaderControl (ListView owner)
+ {
+ this.owner = owner;
+ this.SetStyle (Widgetstyles.DoubleBuffer, true);
+ MouseDown += new MouseEventHandler (HeaderMouseDown);
+ MouseMove += new MouseEventHandler (HeaderMouseMove);
+ MouseUp += new MouseEventHandler (HeaderMouseUp);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ }
+
+ internal ColumnHeader EnteredColumnHeader {
+ get { return entered_column_header; }
+ private set {
+ if (entered_column_header == value)
+ return;
+ if (ThemeEngine.Current.ListViewHasHotHeaderStyle) {
+ Region region_to_invalidate = new Region ();
+ region_to_invalidate.MakeEmpty ();
+ if (entered_column_header != null)
+ region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
+ entered_column_header = value;
+ if (entered_column_header != null)
+ region_to_invalidate.Union (GetColumnHeaderInvalidateArea (entered_column_header));
+ Invalidate (region_to_invalidate);
+ region_to_invalidate.Dispose ();
+ } else
+ entered_column_header = value;
+ }
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ EnteredColumnHeader = null;
+ }
+
+ 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 as ICloneable).Clone ();
+ drag_column.Rect = clicked_column.Rect;
+ drag_to_index = GetReorderedIndex (clicked_column);
+ }
+ clicked_column.Pressed = true;
+ Invalidate (clicked_column);
+ return;
+ }
+ }
+
+ void Invalidate (ColumnHeader columnHeader)
+ {
+ Invalidate (GetColumnHeaderInvalidateArea (columnHeader));
+ }
+
+ Rectangle GetColumnHeaderInvalidateArea (ColumnHeader columnHeader)
+ {
+ Rectangle bounds = columnHeader.Rect;
+ bounds.X -= owner.h_marker;
+ return bounds;
+ }
+
+ void StopResize ()
+ {
+ column_resize_active = false;
+ resize_column = null;
+ Capture = false;
+ Cursor = Cursors.Default;
+ }
+
+ private void HeaderMouseMove (object sender, MouseEventArgs me)
+ {
+ Point pt = new Point (me.X + owner.h_marker, me.Y);
+
+ if (column_resize_active) {
+ int width = pt.X - resize_column.X;
+ if (width < 0)
+ width = 0;
+
+ if (!owner.CanProceedWithResize (resize_column, width)){
+ StopResize ();
+ return;
+ }
+ resize_column.Width = width;
+ return;
+ }
+
+ resize_column = null;
+
+ if (clicked_column != null) {
+ if (owner.AllowColumnReorder) {
+ Rectangle r;
+
+ r = drag_column.Rect;
+ r.X = clicked_column.Rect.X + me.X - drag_x;
+ drag_column.Rect = r;
+
+ int x = me.X + owner.h_marker;
+ ColumnHeader over = ColumnAtX (x);
+ if (over == null)
+ drag_to_index = owner.Columns.Count;
+ else 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)
+ Invalidate (clicked_column);
+ }
+ return;
+ }
+
+ for (int i = 0; i < owner.Columns.Count; i++) {
+ Rectangle zone = owner.Columns [i].Rect;
+ if (zone.Contains (pt))
+ EnteredColumnHeader = owner.Columns [i];
+ zone.X = zone.Right - 5;
+ zone.Width = 10;
+ if (zone.Contains (pt)) {
+ if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
+ i++;
+ 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) {
+ int column_idx = resize_column.Index;
+ StopResize ();
+ owner.RaiseColumnWidthChanged (column_idx);
+ return;
+ }
+
+ if (clicked_column != null && clicked_column.Pressed) {
+ clicked_column.Pressed = false;
+ Invalidate (clicked_column);
+ 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, true);
+ drag_to_index = -1;
+ Invalidate ();
+ }
+
+ clicked_column = null;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pe)
+ {
+ if (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);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_SETFOCUS:
+ owner.Focus ();
+ break;
+ default:
+ base.WndProc (ref m);
+ break;
+ }
+ }
+ }
+
+ private class ItemComparer : IComparer {
+ readonly SortOrder sort_order;
+
+ public ItemComparer (SortOrder sortOrder)
+ {
+ sort_order = sortOrder;
+ }
+
+ public int Compare (object x, object y)
+ {
+ ListViewItem item_x = x as ListViewItem;
+ ListViewItem item_y = y as ListViewItem;
+ if (sort_order == SortOrder.Ascending)
+ return String.Compare (item_x.Text, item_y.Text);
+ else
+ return String.Compare (item_y.Text, item_x.Text);
+ }
+ }
+
+ [ListBindable (false)]
+ public class CheckedIndexCollection : IList, ICollection, IEnumerable
+ {
+ private readonly ListView owner;
+
+ #region Public Constructor
+ public CheckedIndexCollection (ListView owner)
+ {
+ this.owner = owner;
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return owner.CheckedItems.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ public int this [int index] {
+ get {
+ int [] indices = GetIndices ();
+ if (index < 0 || index >= indices.Length)
+ throw new ArgumentOutOfRangeException ("index");
+ return indices [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)
+ {
+ int [] indices = GetIndices ();
+ for (int i = 0; i < indices.Length; i++) {
+ if (indices [i] == checkedIndex)
+ return true;
+ }
+ return false;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ int [] indices = GetIndices ();
+ return indices.GetEnumerator ();
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ int [] indices = GetIndices ();
+ Array.Copy (indices, 0, dest, index, indices.Length);
+ }
+
+ 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)
+ {
+ if (!(checkedIndex is int))
+ return false;
+ return Contains ((int) checkedIndex);
+ }
+
+ int IList.IndexOf (object checkedIndex)
+ {
+ if (!(checkedIndex is int))
+ return -1;
+ return IndexOf ((int) 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)
+ {
+ int [] indices = GetIndices ();
+ for (int i = 0; i < indices.Length; i++) {
+ if (indices [i] == checkedIndex)
+ return i;
+ }
+ return -1;
+ }
+ #endregion // Public Methods
+
+ private int [] GetIndices ()
+ {
+ ArrayList checked_items = owner.CheckedItems.List;
+ int [] indices = new int [checked_items.Count];
+ for (int i = 0; i < checked_items.Count; i++) {
+ ListViewItem item = (ListViewItem) checked_items [i];
+ indices [i] = item.Index;
+ }
+ return indices;
+ }
+ } // CheckedIndexCollection
+
+ [ListBindable (false)]
+ public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
+ {
+ private readonly ListView owner;
+ private ArrayList list;
+
+ #region Public Constructor
+ public CheckedListViewItemCollection (ListView owner)
+ {
+ this.owner = owner;
+ this.owner.Items.Changed += new CollectionChangedHandler (
+ ItemsCollection_Changed);
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get {
+ if (!owner.CheckBoxes)
+ return 0;
+ return List.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ public ListViewItem this [int index] {
+ get {
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+ ArrayList checked_items = List;
+ if (index < 0 || index >= checked_items.Count)
+ throw new ArgumentOutOfRangeException ("index");
+ return (ListViewItem) checked_items [index];
+ }
+ }
+
+ public virtual ListViewItem this [string key] {
+ get {
+ int idx = IndexOfKey (key);
+ return idx == -1 ? null : (ListViewItem) List [idx];
+ }
+ }
+
+ 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 (ListViewItem item)
+ {
+ if (!owner.CheckBoxes)
+ return false;
+ return List.Contains (item);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return IndexOfKey (key) != -1;
+ }
+
+ public void CopyTo (Array dest, int index)
+ {
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+ if (!owner.CheckBoxes)
+ return;
+ List.CopyTo (dest, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+ if (!owner.CheckBoxes)
+ return (new ListViewItem [0]).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)
+ {
+ if (!(item is ListViewItem))
+ return false;
+ return Contains ((ListViewItem) item);
+ }
+
+ int IList.IndexOf (object item)
+ {
+ if (!(item is ListViewItem))
+ return -1;
+ return IndexOf ((ListViewItem) 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)
+ {
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+ if (!owner.CheckBoxes)
+ return -1;
+ return List.IndexOf (item);
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+ if (key == null || key.Length == 0)
+ return -1;
+
+ ArrayList checked_items = List;
+ for (int i = 0; i < checked_items.Count; i++) {
+ ListViewItem item = (ListViewItem) checked_items [i];
+ if (String.Compare (key, item.Name, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+ #endregion // Public Methods
+
+ internal ArrayList List {
+ get {
+ if (list == null) {
+ list = new ArrayList ();
+ foreach (ListViewItem item in owner.Items) {
+ if (item.Checked)
+ list.Add (item);
+ }
+ }
+ return list;
+ }
+ }
+
+ internal void Reset ()
+ {
+ // force re-population of list
+ list = null;
+ }
+
+ private void ItemsCollection_Changed ()
+ {
+ Reset ();
+ }
+ } // CheckedListViewItemCollection
+
+ [ListBindable (false)]
+ public class ColumnHeaderCollection : IList, ICollection, IEnumerable
+ {
+ internal ArrayList list;
+ private ListView owner;
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListViewProvider uses the events when View is Details.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add {
+ if (owner != null)
+ owner.Events.AddHandler (UIACollectionChangedEvent, value);
+ }
+ remove {
+ if (owner != null)
+ owner.Events.RemoveHandler (UIACollectionChangedEvent, value);
+ }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ if (owner == null)
+ return;
+
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+
+ #endregion UIA Framework Events
+
+ #region Public Constructor
+ public ColumnHeaderCollection (ListView owner)
+ {
+ list = new ArrayList ();
+ this.owner = owner;
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual ColumnHeader this [int index] {
+ get {
+ if (index < 0 || index >= list.Count)
+ throw new ArgumentOutOfRangeException ("index");
+ return (ColumnHeader) list [index];
+ }
+ }
+
+ public virtual ColumnHeader this [string key] {
+ get {
+ int idx = IndexOfKey (key);
+ if (idx == -1)
+ return null;
+
+ return (ColumnHeader) list [idx];
+ }
+ }
+
+ 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 = list.Add (value);
+ owner.AddColumn (value, idx, true);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ return idx;
+ }
+
+ public virtual ColumnHeader Add (string text, int width, HorizontalAlignment textAlign)
+ {
+ string str = text;
+ ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
+ this.Add (colHeader);
+ return colHeader;
+ }
+
+ public virtual ColumnHeader Add (string text)
+ {
+ return Add (String.Empty, text);
+ }
+
+ public virtual ColumnHeader Add (string text, int width)
+ {
+ return Add (String.Empty, text, width);
+ }
+
+ public virtual ColumnHeader Add (string key, string text)
+ {
+ ColumnHeader colHeader = new ColumnHeader ();
+ colHeader.Name = key;
+ colHeader.Text = text;
+ Add (colHeader);
+ return colHeader;
+ }
+
+ public virtual ColumnHeader Add (string key, string text, int width)
+ {
+ return Add (key, text, width, HorizontalAlignment.Left, -1);
+ }
+
+ public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
+ {
+ ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
+ colHeader.ImageIndex = imageIndex;
+ Add (colHeader);
+ return colHeader;
+ }
+
+ public virtual ColumnHeader Add (string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
+ {
+ ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
+ colHeader.ImageKey = imageKey;
+ Add (colHeader);
+ return colHeader;
+ }
+
+ public virtual void AddRange (ColumnHeader [] values)
+ {
+ foreach (ColumnHeader colHeader in values) {
+ int idx = list.Add (colHeader);
+ owner.AddColumn (colHeader, idx, false);
+ }
+
+ owner.Redraw (true);
+ }
+
+ public virtual void Clear ()
+ {
+ foreach (ColumnHeader col in list)
+ col.SetListView (null);
+ list.Clear ();
+ owner.ReorderColumns (new int [0], true);
+
+ //UIA Framework event: Items cleared
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
+
+ }
+
+ public bool Contains (ColumnHeader value)
+ {
+ return list.Contains (value);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return IndexOfKey (key) != -1;
+ }
+
+ public 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 virtual int IndexOfKey (string key)
+ {
+ if (key == null || key.Length == 0)
+ return -1;
+
+ for (int i = 0; i < list.Count; i++) {
+ ColumnHeader col = (ColumnHeader) list [i];
+ if (String.Compare (key, col.Name, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+
+ 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");
+
+ list.Insert (index, value);
+ owner.AddColumn (value, index, true);
+
+ //UIA Framework event: Item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+ }
+
+ public void Insert (int index, string text)
+ {
+ Insert (index, String.Empty, text);
+ }
+
+ public void Insert (int index, string text, int width)
+ {
+ Insert (index, String.Empty, text, width);
+ }
+
+ public void Insert (int index, string key, string text)
+ {
+ ColumnHeader colHeader = new ColumnHeader ();
+ colHeader.Name = key;
+ colHeader.Text = text;
+ Insert (index, colHeader);
+ }
+
+ public void Insert (int index, string key, string text, int width)
+ {
+ ColumnHeader colHeader = new ColumnHeader (key, text, width, HorizontalAlignment.Left);
+ Insert (index, colHeader);
+ }
+
+ public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, int imageIndex)
+ {
+ ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
+ colHeader.ImageIndex = imageIndex;
+ Insert (index, colHeader);
+ }
+
+ public void Insert (int index, string key, string text, int width, HorizontalAlignment textAlign, string imageKey)
+ {
+ ColumnHeader colHeader = new ColumnHeader (key, text, width, textAlign);
+ colHeader.ImageKey = imageKey;
+ Insert (index, colHeader);
+ }
+
+ public void Insert (int index, string text, int width, HorizontalAlignment textAlign)
+ {
+ string str = text;
+ ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
+ this.Insert (index, colHeader);
+ }
+
+ public virtual void Remove (ColumnHeader column)
+ {
+ if (!Contains (column))
+ return;
+
+ list.Remove (column);
+ column.SetListView (null);
+
+ int rem_display_index = column.InternalDisplayIndex;
+ int [] display_indices = new int [list.Count];
+ for (int i = 0; i < display_indices.Length; i++) {
+ ColumnHeader col = (ColumnHeader) list [i];
+ int display_index = col.InternalDisplayIndex;
+ if (display_index < rem_display_index) {
+ display_indices [i] = display_index;
+ } else {
+ display_indices [i] = (display_index - 1);
+ }
+ }
+
+ column.InternalDisplayIndex = -1;
+ owner.ReorderColumns (display_indices, true);
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, column));
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ int idx = IndexOfKey (key);
+ if (idx != -1)
+ RemoveAt (idx);
+ }
+
+ public virtual void RemoveAt (int index)
+ {
+ if (index < 0 || index >= list.Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ ColumnHeader col = (ColumnHeader) list [index];
+ Remove (col);
+ }
+ #endregion // Public Methods
+
+
+ } // ColumnHeaderCollection
+
+ [ListBindable (false)]
+ public class ListViewItemCollection : IList, ICollection, IEnumerable
+ {
+ private readonly ArrayList list;
+ private ListView owner;
+ private ListViewGroup group;
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListViewProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add {
+ if (owner != null)
+ owner.Events.AddHandler (UIACollectionChangedEvent, value);
+ }
+ remove {
+ if (owner != null)
+ owner.Events.RemoveHandler (UIACollectionChangedEvent, value);
+ }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ if (owner == null)
+ return;
+
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+
+ #endregion UIA Framework Events
+
+ // The collection can belong to a ListView (main) or to a ListViewGroup (sub-collection)
+ // In the later case ListViewItem.ListView never gets modified
+ private bool is_main_collection = true;
+
+ #region Public Constructor
+ public ListViewItemCollection (ListView owner)
+ {
+ list = new ArrayList (0);
+ this.owner = owner;
+ }
+ #endregion // Public Constructor
+
+ internal ListViewItemCollection (ListView owner, ListViewGroup group) : this (owner)
+ {
+ this.group = group;
+ is_main_collection = false;
+ }
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get {
+ if (owner != null && owner.VirtualMode)
+ return owner.VirtualListSize;
+
+ return list.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual ListViewItem this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (owner != null && owner.VirtualMode)
+ return RetrieveVirtualItemFromOwner (index);
+ return (ListViewItem) list [index];
+ }
+
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ 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");
+
+ if (value.ListView != null && value.ListView != owner)
+ throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
+
+ if (is_main_collection)
+ value.Owner = owner;
+ else {
+ if (value.Group != null)
+ value.Group.Items.Remove (value);
+
+ value.SetGroup (group);
+ }
+
+ //UIA Framework event: Item Replaced
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, list [index]));
+
+ list [index] = value;
+
+ CollectionChanged (true);
+
+ //UIA Framework event: Item Replaced
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ }
+ }
+
+ public virtual ListViewItem this [string key] {
+ get {
+ int idx = IndexOfKey (key);
+ if (idx == -1)
+ return null;
+
+ return this [idx];
+ }
+ }
+
+ 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 {
+ //UIA Framework event: Item Replaced
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, this [index]));
+
+ if (value is ListViewItem)
+ this [index] = (ListViewItem) value;
+ else
+ this [index] = new ListViewItem (value.ToString ());
+
+ OnChange ();
+ //UIA Framework event: Item Replaced
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+ }
+ }
+ #endregion // Public Properties
+
+ #region Public Methods
+ public virtual ListViewItem Add (ListViewItem value)
+ {
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ AddItem (value);
+
+ // Item is ignored until it has been added to the ListView
+ if (is_main_collection || value.ListView != null)
+ CollectionChanged (true);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ 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 virtual ListViewItem Add (string text, string imageKey)
+ {
+ ListViewItem item = new ListViewItem (text, imageKey);
+ return this.Add (item);
+ }
+
+ public virtual ListViewItem Add (string key, string text, int imageIndex)
+ {
+ ListViewItem item = new ListViewItem (text, imageIndex);
+ item.Name = key;
+ return this.Add (item);
+ }
+
+ public virtual ListViewItem Add (string key, string text, string imageKey)
+ {
+ ListViewItem item = new ListViewItem (text, imageKey);
+ item.Name = key;
+ return this.Add (item);
+ }
+
+ public void AddRange (ListViewItem [] items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("Argument cannot be null!", "items");
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ owner.BeginUpdate ();
+
+ foreach (ListViewItem item in items) {
+ AddItem (item);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+ }
+
+ owner.EndUpdate ();
+
+ CollectionChanged (true);
+ }
+
+ public void AddRange (ListViewItemCollection items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("Argument cannot be null!", "items");
+
+ ListViewItem[] itemArray = new ListViewItem[items.Count];
+ items.CopyTo (itemArray,0);
+ this.AddRange (itemArray);
+ }
+
+ public virtual void Clear ()
+ {
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+ if (is_main_collection && owner != null) {
+ owner.SetFocusedItem (-1);
+ owner.h_scroll.Value = owner.v_scroll.Value = 0;
+
+ // first remove any item in the groups that *are* part of this LV too
+ foreach (ListViewGroup group in owner.groups)
+ group.Items.ClearItemsWithSameListView ();
+
+ foreach (ListViewItem item in list) {
+ owner.item_control.CancelEdit (item);
+ item.Owner = null;
+ }
+ }
+ else
+ foreach (ListViewItem item in list)
+ item.SetGroup (null);
+
+ list.Clear ();
+ CollectionChanged (false);
+
+ //UIA Framework event: Items Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
+
+ }
+
+ // This method is intended to be used from ListViewGroup.Items, not from ListView.Items,
+ // added for performance reasons (avoid calling manually Remove for every item on ListViewGroup.Items)
+ void ClearItemsWithSameListView ()
+ {
+ if (is_main_collection)
+ return;
+
+ int counter = list.Count - 1;
+ while (counter >= 0) {
+ ListViewItem item = list [counter] as ListViewItem;
+
+ // remove only if the items in group have being added to the ListView too
+ if (item.ListView == group.ListView) {
+ list.RemoveAt (counter);
+ item.SetGroup (null);
+ }
+
+ counter--;
+ }
+ }
+
+ public bool Contains (ListViewItem item)
+ {
+ return IndexOf (item) != -1;
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return IndexOfKey (key) != -1;
+ }
+
+ public void CopyTo (Array dest, int index)
+ {
+ list.CopyTo (dest, index);
+ }
+
+ public ListViewItem [] Find (string key, bool searchAllSubItems)
+ {
+ if (key == null)
+ return new ListViewItem [0];
+
+ List<ListViewItem> temp_list = new List<ListViewItem> ();
+
+ for (int i = 0; i < list.Count; i++) {
+ ListViewItem lvi = (ListViewItem) list [i];
+ if (String.Compare (key, lvi.Name, true) == 0)
+ temp_list.Add (lvi);
+ }
+
+ ListViewItem [] retval = new ListViewItem [temp_list.Count];
+ temp_list.CopyTo (retval);
+
+ return retval;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ // This enumerator makes a copy of the collection so
+ // it can be deleted from in a foreach
+ return new Widget.WidgetCollection.WidgetCollectionEnumerator (list);
+ }
+
+ int IList.Add (object item)
+ {
+ int result;
+ ListViewItem li;
+
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ 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");
+
+ if (li.ListView != null && li.ListView != owner)
+ throw new ArgumentException ("Cannot add or insert the item '" + li.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
+ }
+ else
+ li = new ListViewItem (item.ToString ());
+
+ li.Owner = owner;
+
+
+ result = list.Add (li);
+ CollectionChanged (true);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, li));
+
+ return result;
+ }
+
+ bool IList.Contains (object item)
+ {
+ return Contains ((ListViewItem) item);
+ }
+
+ int IList.IndexOf (object item)
+ {
+ return IndexOf ((ListViewItem) item);
+ }
+
+ void IList.Insert (int index, object item)
+ {
+ if (item is ListViewItem)
+ this.Insert (index, (ListViewItem) item);
+ else
+ this.Insert (index, item.ToString ());
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, this [index]));
+ }
+
+ void IList.Remove (object item)
+ {
+ Remove ((ListViewItem) item);
+ }
+
+ public int IndexOf (ListViewItem item)
+ {
+ if (owner != null && owner.VirtualMode) {
+ for (int i = 0; i < Count; i++)
+ if (RetrieveVirtualItemFromOwner (i) == item)
+ return i;
+
+ return -1;
+ }
+
+ return list.IndexOf (item);
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (key == null || key.Length == 0)
+ return -1;
+
+ for (int i = 0; i < Count; i++) {
+ ListViewItem lvi = this [i];
+ if (String.Compare (key, lvi.Name, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public ListViewItem Insert (int index, ListViewItem item)
+ {
+ if (index < 0 || index > list.Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ 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");
+
+ if (item.ListView != null && item.ListView != owner)
+ throw new ArgumentException ("Cannot add or insert the item '" + item.Text + "' in more than one place. You must first remove it from its current location or clone it.", "item");
+
+ if (is_main_collection)
+ item.Owner = owner;
+ else {
+ if (item.Group != null)
+ item.Group.Items.Remove (item);
+
+ item.SetGroup (group);
+ }
+
+ list.Insert (index, item);
+
+ if (is_main_collection || item.ListView != null)
+ CollectionChanged (true);
+
+ // force an update of the selected info if the new item is selected.
+ if (item.Selected)
+ item.SetSelectedCore (true);
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+
+ 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 ListViewItem Insert (int index, string text, string imageKey)
+ {
+ ListViewItem lvi = new ListViewItem (text, imageKey);
+ return Insert (index, lvi);
+ }
+
+ public virtual ListViewItem Insert (int index, string key, string text, int imageIndex)
+ {
+ ListViewItem lvi = new ListViewItem (text, imageIndex);
+ lvi.Name = key;
+ return Insert (index, lvi);
+ }
+
+ public virtual ListViewItem Insert (int index, string key, string text, string imageKey)
+ {
+ ListViewItem lvi = new ListViewItem (text, imageKey);
+ lvi.Name = key;
+ return Insert (index, lvi);
+ }
+
+ public virtual void Remove (ListViewItem item)
+ {
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ int idx = list.IndexOf (item);
+ if (idx != -1)
+ RemoveAt (idx);
+ }
+
+ public virtual void RemoveAt (int index)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (owner != null && owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ ListViewItem item = (ListViewItem) list [index];
+
+ bool selection_changed = false;
+ if (is_main_collection && owner != null) {
+
+ int display_index = item.DisplayIndex;
+ if (item.Focused && display_index + 1 == Count) // Last item
+ owner.SetFocusedItem (display_index == 0 ? -1 : display_index - 1);
+
+ selection_changed = owner.SelectedIndices.Contains (index);
+ owner.item_control.CancelEdit (item);
+ }
+
+ list.RemoveAt (index);
+
+ if (is_main_collection) {
+ item.Owner = null;
+ if (item.Group != null)
+ item.Group.Items.Remove (item);
+ } else
+ item.SetGroup (null);
+
+ CollectionChanged (false);
+ if (selection_changed && owner != null)
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, item));
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ int idx = IndexOfKey (key);
+ if (idx != -1)
+ RemoveAt (idx);
+ }
+
+ #endregion // Public Methods
+
+ internal ListView Owner {
+ get {
+ return owner;
+ }
+ set {
+ owner = value;
+ }
+ }
+
+ internal ListViewGroup Group {
+ get {
+ return group;
+ }
+ set {
+ group = value;
+ }
+ }
+
+ void AddItem (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");
+
+ if (value.ListView != null && value.ListView != owner)
+ throw new ArgumentException ("Cannot add or insert the item '" + value.Text + "' in more than one place. You must first remove it from its current location or clone it.", "value");
+ if (is_main_collection)
+ value.Owner = owner;
+ else {
+ if (value.Group != null)
+ value.Group.Items.Remove (value);
+
+ value.SetGroup (group);
+ }
+
+ list.Add (value);
+
+ // force an update of the selected info if the new item is selected.
+ if (value.Selected)
+ value.SetSelectedCore (true);
+ }
+
+ void CollectionChanged (bool sort)
+ {
+ if (owner != null) {
+ if (sort)
+ owner.Sort (false);
+
+ OnChange ();
+ owner.Redraw (true);
+ }
+ }
+
+ ListViewItem RetrieveVirtualItemFromOwner (int displayIndex)
+ {
+ RetrieveVirtualItemEventArgs args = new RetrieveVirtualItemEventArgs (displayIndex);
+
+ owner.OnRetrieveVirtualItem (args);
+ ListViewItem retval = args.Item;
+ retval.Owner = owner;
+ retval.DisplayIndex = displayIndex;
+ retval.Layout ();
+
+ return retval;
+ }
+
+ internal event CollectionChangedHandler Changed;
+
+ internal void Sort (IComparer comparer)
+ {
+ list.Sort (comparer);
+ OnChange ();
+ }
+
+ internal void OnChange ()
+ {
+ if (Changed != null)
+ Changed ();
+ }
+ } // ListViewItemCollection
+
+
+ // In normal mode, the selection information resides in the Items,
+ // making SelectedIndexCollection.List read-only
+ //
+ // In virtual mode, SelectedIndexCollection directly saves the selection
+ // information, instead of getting it from Items, making List read-and-write
+ [ListBindable (false)]
+ public class SelectedIndexCollection : IList, ICollection, IEnumerable
+ {
+ private readonly ListView owner;
+ private ArrayList list;
+
+ #region Public Constructor
+ public SelectedIndexCollection (ListView owner)
+ {
+ this.owner = owner;
+ owner.Items.Changed += new CollectionChangedHandler (ItemsCollection_Changed);
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get {
+ if (!owner.is_selection_available)
+ return 0;
+
+ return List.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return false;
+ }
+ }
+
+ public int this [int index] {
+ get {
+ if (!owner.is_selection_available || index < 0 || index >= List.Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ return (int) List [index];
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get {
+ return false;
+ }
+ }
+
+ 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 int Add (int itemIndex)
+ {
+ if (itemIndex < 0 || itemIndex >= owner.Items.Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ if (owner.virtual_mode && !owner.is_selection_available)
+ return -1;
+
+ owner.Items [itemIndex].Selected = true;
+
+ if (!owner.is_selection_available)
+ return 0;
+
+ return List.Count;
+ }
+
+ public void Clear ()
+ {
+ if (!owner.is_selection_available)
+ return;
+
+ int [] indexes = (int []) List.ToArray (typeof (int));
+ foreach (int index in indexes)
+ owner.Items [index].Selected = false;
+ }
+
+ public bool Contains (int selectedIndex)
+ {
+ return IndexOf (selectedIndex) != -1;
+ }
+
+ public void CopyTo (Array dest, int index)
+ {
+ List.CopyTo (dest, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return List.GetEnumerator ();
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ("Add operation is not supported.");
+ }
+
+ void IList.Clear ()
+ {
+ Clear ();
+ }
+
+ bool IList.Contains (object selectedIndex)
+ {
+ if (!(selectedIndex is int))
+ return false;
+ return Contains ((int) selectedIndex);
+ }
+
+ int IList.IndexOf (object selectedIndex)
+ {
+ if (!(selectedIndex is int))
+ return -1;
+ return IndexOf ((int) 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)
+ {
+ if (!owner.is_selection_available)
+ return -1;
+
+ return List.IndexOf (selectedIndex);
+ }
+
+ public void Remove (int itemIndex)
+ {
+ if (itemIndex < 0 || itemIndex >= owner.Items.Count)
+ throw new ArgumentOutOfRangeException ("itemIndex");
+
+ owner.Items [itemIndex].Selected = false;
+ }
+ #endregion // Public Methods
+
+ internal ArrayList List {
+ get {
+ if (list == null) {
+ list = new ArrayList ();
+ if (!owner.VirtualMode)
+ for (int i = 0; i < owner.Items.Count; i++) {
+ if (owner.Items [i].Selected)
+ list.Add (i);
+ }
+ }
+ return list;
+ }
+ }
+
+ internal void Reset ()
+ {
+ // force re-population of list
+ list = null;
+ }
+
+ private void ItemsCollection_Changed ()
+ {
+ Reset ();
+ }
+
+ internal void RemoveIndex (int index)
+ {
+ int idx = List.BinarySearch (index);
+ if (idx != -1)
+ List.RemoveAt (idx);
+ }
+
+ // actually store index in the collection
+ // also, keep the collection sorted, as .Net does
+ internal void InsertIndex (int index)
+ {
+ int iMin = 0;
+ int iMax = List.Count - 1;
+ while (iMin <= iMax) {
+ int iMid = (iMin + iMax) / 2;
+ int current_index = (int) List [iMid];
+
+ if (current_index == index)
+ return; // Already added
+ if (current_index > index)
+ iMax = iMid - 1;
+ else
+ iMin = iMid + 1;
+ }
+
+ List.Insert (iMin, index);
+ }
+
+ } // SelectedIndexCollection
+
+ [ListBindable (false)]
+ public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
+ {
+ private readonly ListView owner;
+
+ #region Public Constructor
+ public SelectedListViewItemCollection (ListView owner)
+ {
+ this.owner = owner;
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get {
+ return owner.SelectedIndices.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ public ListViewItem this [int index] {
+ get {
+ if (!owner.is_selection_available || index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ int item_index = owner.SelectedIndices [index];
+ return owner.Items [item_index];
+ }
+ }
+
+ public virtual ListViewItem this [string key] {
+ get {
+ int idx = IndexOfKey (key);
+ if (idx == -1)
+ return null;
+
+ return this [idx];
+ }
+ }
+
+ 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 void Clear ()
+ {
+ owner.SelectedIndices.Clear ();
+ }
+
+ public bool Contains (ListViewItem item)
+ {
+ return IndexOf (item) != -1;
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return IndexOfKey (key) != -1;
+ }
+
+ public void CopyTo (Array dest, int index)
+ {
+ if (!owner.is_selection_available)
+ return;
+ if (index > Count) // Throws ArgumentException instead of IOOR exception
+ throw new ArgumentException ("index");
+
+ for (int i = 0; i < Count; i++)
+ dest.SetValue (this [i], index++);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ if (!owner.is_selection_available)
+ return (new ListViewItem [0]).GetEnumerator ();
+
+ ListViewItem [] items = new ListViewItem [Count];
+ for (int i = 0; i < Count; i++)
+ items [i] = this [i];
+
+ return items.GetEnumerator ();
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ("Add operation is not supported.");
+ }
+
+ bool IList.Contains (object item)
+ {
+ if (!(item is ListViewItem))
+ return false;
+ return Contains ((ListViewItem) item);
+ }
+
+ int IList.IndexOf (object item)
+ {
+ if (!(item is ListViewItem))
+ return -1;
+ return IndexOf ((ListViewItem) 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)
+ {
+ if (!owner.is_selection_available)
+ return -1;
+
+ for (int i = 0; i < Count; i++)
+ if (this [i] == item)
+ return i;
+
+ return -1;
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (!owner.is_selection_available || key == null || key.Length == 0)
+ return -1;
+
+ for (int i = 0; i < Count; i++) {
+ ListViewItem item = this [i];
+ if (String.Compare (item.Name, key, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+ #endregion // Public Methods
+
+ } // SelectedListViewItemCollection
+
+ internal delegate void CollectionChangedHandler ();
+
+ struct ItemMatrixLocation
+ {
+ int row;
+ int col;
+
+ public ItemMatrixLocation (int row, int col)
+ {
+ this.row = row;
+ this.col = col;
+
+ }
+
+ public int Col {
+ get {
+ return col;
+ }
+ set {
+ col = value;
+ }
+ }
+
+ public int Row {
+ get {
+ return row;
+ }
+ set {
+ row = value;
+ }
+ }
+
+ }
+
+ #endregion // Subclasses
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+ }
+
+ protected override void OnMouseLeave (EventArgs e)
+ {
+ base.OnMouseLeave (e);
+ }
+
+ //
+ // ColumnReorder event
+ //
+ static object ColumnReorderedEvent = new object ();
+ public event ColumnReorderedEventHandler ColumnReordered {
+ add { Events.AddHandler (ColumnReorderedEvent, value); }
+ remove { Events.RemoveHandler (ColumnReorderedEvent, value); }
+ }
+
+ protected virtual void OnColumnReordered (ColumnReorderedEventArgs e)
+ {
+ ColumnReorderedEventHandler creh = (ColumnReorderedEventHandler) (Events [ColumnReorderedEvent]);
+
+ if (creh != null)
+ creh (this, e);
+ }
+
+ //
+ // ColumnWidthChanged
+ //
+ static object ColumnWidthChangedEvent = new object ();
+ public event ColumnWidthChangedEventHandler ColumnWidthChanged {
+ add { Events.AddHandler (ColumnWidthChangedEvent, value); }
+ remove { Events.RemoveHandler (ColumnWidthChangedEvent, value); }
+ }
+
+ protected virtual void OnColumnWidthChanged (ColumnWidthChangedEventArgs e)
+ {
+ ColumnWidthChangedEventHandler eh = (ColumnWidthChangedEventHandler) (Events[ColumnWidthChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ void RaiseColumnWidthChanged (int resize_column)
+ {
+ ColumnWidthChangedEventArgs n = new ColumnWidthChangedEventArgs (resize_column);
+
+ OnColumnWidthChanged (n);
+ }
+
+ //
+ // ColumnWidthChanging
+ //
+ static object ColumnWidthChangingEvent = new object ();
+ public event ColumnWidthChangingEventHandler ColumnWidthChanging {
+ add { Events.AddHandler (ColumnWidthChangingEvent, value); }
+ remove { Events.RemoveHandler (ColumnWidthChangingEvent, value); }
+ }
+
+ protected virtual void OnColumnWidthChanging (ColumnWidthChangingEventArgs e)
+ {
+ ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
+ if (cwceh != null)
+ cwceh (this, e);
+ }
+
+ //
+ // 2.0 profile based implementation
+ //
+ bool CanProceedWithResize (ColumnHeader col, int width)
+ {
+ ColumnWidthChangingEventHandler cwceh = (ColumnWidthChangingEventHandler) (Events[ColumnWidthChangingEvent]);
+ if (cwceh == null)
+ return true;
+
+ ColumnWidthChangingEventArgs changing = new ColumnWidthChangingEventArgs (col.Index, width);
+ cwceh (this, changing);
+ return !changing.Cancel;
+ }
+
+ internal void RaiseColumnWidthChanged (ColumnHeader column)
+ {
+ int index = Columns.IndexOf (column);
+ RaiseColumnWidthChanged (index);
+ }
+
+
+ #region UIA Framework: Methods, Properties and Events
+
+ static object UIALabelEditChangedEvent = new object ();
+ static object UIAShowGroupsChangedEvent = new object ();
+ static object UIAMultiSelectChangedEvent = new object ();
+ static object UIAViewChangedEvent = new object ();
+ static object UIACheckBoxesChangedEvent = new object ();
+ static object UIAFocusedItemChangedEvent = new object ();
+
+ internal Rectangle UIAHeaderControl {
+ get { return header_control.Bounds; }
+ }
+
+ internal int UIAColumns {
+ get { return cols; }
+ }
+
+ internal int UIARows {
+ get { return rows; }
+ }
+
+ internal ListViewGroup UIADefaultListViewGroup
+ {
+ get { return groups.DefaultGroup; }
+ }
+
+ internal ScrollBar UIAHScrollBar {
+ get { return h_scroll; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return v_scroll; }
+ }
+
+ internal event EventHandler UIAShowGroupsChanged {
+ add { Events.AddHandler (UIAShowGroupsChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAShowGroupsChangedEvent, value); }
+ }
+
+ internal event EventHandler UIACheckBoxesChanged {
+ add { Events.AddHandler (UIACheckBoxesChangedEvent, value); }
+ remove { Events.RemoveHandler (UIACheckBoxesChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAMultiSelectChanged {
+ add { Events.AddHandler (UIAMultiSelectChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAMultiSelectChangedEvent, value); }
+ }
+
+ internal event EventHandler UIALabelEditChanged {
+ add { Events.AddHandler (UIALabelEditChangedEvent, value); }
+ remove { Events.RemoveHandler (UIALabelEditChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAViewChanged {
+ add { Events.AddHandler (UIAViewChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAViewChangedEvent, value); }
+ }
+
+ internal event EventHandler UIAFocusedItemChanged {
+ add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
+ }
+
+ internal Rectangle UIAGetHeaderBounds (ListViewGroup group)
+ {
+ return group.HeaderBounds;
+ }
+
+ internal int UIAItemsLocationLength
+ {
+ get { return items_location.Length; }
+ }
+
+ private void OnUIACheckBoxesChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIACheckBoxesChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void OnUIAShowGroupsChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAShowGroupsChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void OnUIAMultiSelectChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAMultiSelectChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void OnUIALabelEditChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIALabelEditChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void OnUIAViewChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAViewChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ internal void OnUIAFocusedItemChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ #endregion // UIA Framework: Methods, Properties and Events
+
+ }
+}
diff --git a/source/ShiftUI/Widgets/ListViewItem.cs b/source/ShiftUI/Widgets/ListViewItem.cs
new file mode 100644
index 0000000..b9473e1
--- /dev/null
+++ b/source/ShiftUI/Widgets/ListViewItem.cs
@@ -0,0 +1,1638 @@
+// 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 Novell, Inc. (http://www.novell.com)
+//
+// Author:
+// Ravindra ([email protected])
+// Mike Kestner <[email protected]>
+// Daniel Nauck (dna(at)mono-project(dot)de)
+
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Runtime.Serialization;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultProperty ("Text")]
+ [DesignTimeVisible (false)]
+ [Serializable]
+ [ToolboxItem (false)]
+ [TypeConverter (typeof (ListViewItemConverter))]
+ public class ListViewItem : ICloneable, ISerializable
+ {
+ #region Instance Variables
+ private int image_index = -1;
+ private bool is_checked = false;
+ private int state_image_index = -1;
+ private ListViewSubItemCollection sub_items;
+ private object tag;
+ private bool use_item_style = true;
+ int display_index = -1; // actual position in ListView
+ private ListViewGroup group = null;
+ private string name = String.Empty;
+ private string image_key = String.Empty;
+ string tooltip_text = String.Empty;
+ int indent_count;
+ Point position = new Point (-1, -1); // cached to mimic .Net behaviour
+ Rectangle bounds = Rectangle.Empty;
+ Rectangle checkbox_rect; // calculated by CalcListViewItem method
+ Rectangle icon_rect;
+ Rectangle item_rect;
+ Rectangle label_rect;
+ ListView owner;
+ Font font;
+ Font hot_font; // cached font for hot tracking
+ bool selected;
+
+ internal int row;
+ internal int col;
+
+
+ #region UIA Framework: Methods, Properties and Events
+
+ internal event EventHandler UIATextChanged;
+
+ internal event LabelEditEventHandler UIASubItemTextChanged;
+
+ internal void OnUIATextChanged ()
+ {
+ if (UIATextChanged != null)
+ UIATextChanged (this, EventArgs.Empty);
+ }
+
+ internal void OnUIASubItemTextChanged (LabelEditEventArgs args)
+ {
+ //If our index is 0 we also generate TextChanged for the ListViewItem
+ //because ListViewItem.Text is the same as ListViewItem.SubItems [0].Text
+ if (args.Item == 0)
+ OnUIATextChanged ();
+
+ if (UIASubItemTextChanged != null)
+ UIASubItemTextChanged (this, args);
+ }
+
+ #endregion // UIA Framework: Methods, Properties and Events
+
+
+ #endregion Instance Variables
+
+ #region Public Constructors
+ public ListViewItem () : this (string.Empty)
+ {
+ }
+
+ public ListViewItem (string text) : this (text, -1)
+ {
+ }
+
+ public ListViewItem (string [] items) : this (items, -1)
+ {
+ }
+
+ public ListViewItem (ListViewItem.ListViewSubItem [] subItems, int imageIndex)
+ {
+ this.sub_items = new ListViewSubItemCollection (this, null);
+ for (int i = 0; i < subItems.Length; i++)
+ sub_items.Add (subItems [i]);
+ this.image_index = imageIndex;
+ }
+
+ public ListViewItem (string text, int imageIndex)
+ {
+ this.image_index = imageIndex;
+ this.sub_items = new ListViewSubItemCollection (this, text);
+ }
+
+ public ListViewItem (string [] items, int imageIndex)
+ {
+ this.sub_items = new ListViewSubItemCollection (this, null);
+ if (items != null) {
+ for (int i = 0; i < items.Length; i++)
+ sub_items.Add (new ListViewSubItem (this, items [i]));
+ }
+ this.image_index = imageIndex;
+ }
+
+ public ListViewItem (string [] items, int imageIndex, Color foreColor,
+ Color backColor, Font font) : this (items, imageIndex)
+ {
+ ForeColor = foreColor;
+ BackColor = backColor;
+ this.font = font;
+ }
+
+ public ListViewItem(string[] items, string imageKey) : this(items)
+ {
+ this.ImageKey = imageKey;
+ }
+
+ public ListViewItem(string text, string imageKey) : this(text)
+ {
+ this.ImageKey = imageKey;
+ }
+
+ public ListViewItem(ListViewSubItem[] subItems, string imageKey)
+ {
+ this.sub_items = new ListViewSubItemCollection (this, null);
+ for (int i = 0; i < subItems.Length; i++)
+ this.sub_items.Add (subItems [i]);
+ this.ImageKey = imageKey;
+ }
+
+ public ListViewItem(string[] items, string imageKey, Color foreColor,
+ Color backColor, Font font) : this(items, imageKey)
+ {
+ ForeColor = foreColor;
+ BackColor = backColor;
+ this.font = font;
+ }
+
+ public ListViewItem(ListViewGroup group) : this()
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string text, ListViewGroup group) : this(text)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string[] items, ListViewGroup group) : this(items)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(ListViewSubItem[] subItems, int imageIndex, ListViewGroup group)
+ : this(subItems, imageIndex)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(ListViewSubItem[] subItems, string imageKey, ListViewGroup group)
+ : this(subItems, imageKey)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string text, int imageIndex, ListViewGroup group)
+ : this(text, imageIndex)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string text, string imageKey, ListViewGroup group)
+ : this(text, imageKey)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string[] items, int imageIndex, ListViewGroup group)
+ : this(items, imageIndex)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string[] items, string imageKey, ListViewGroup group)
+ : this(items, imageKey)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string[] items, int imageIndex, Color foreColor, Color backColor,
+ Font font, ListViewGroup group)
+ : this(items, imageIndex, foreColor, backColor, font)
+ {
+ Group = group;
+ }
+
+ public ListViewItem(string[] items, string imageKey, Color foreColor, Color backColor,
+ Font font, ListViewGroup group)
+ : this(items, imageKey, foreColor, backColor, font)
+ {
+ Group = group;
+ }
+ #endregion // Public Constructors
+
+ protected ListViewItem (SerializationInfo info, StreamingContext context)
+ {
+ Deserialize (info, context);
+ }
+
+ #region Public Instance Properties
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Color BackColor {
+ get {
+ if (sub_items.Count > 0)
+ return sub_items[0].BackColor;
+
+ if (owner != null)
+ return owner.BackColor;
+
+ return ThemeEngine.Current.ColorWindow;
+ }
+ set { SubItems [0].BackColor = value; }
+ }
+
+ [Browsable (false)]
+ public Rectangle Bounds {
+ get {
+ return GetBounds (ItemBoundsPortion.Entire);
+ }
+ }
+
+ [DefaultValue (false)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public bool Checked {
+ get { return is_checked; }
+ set {
+ if (is_checked == value)
+ return;
+
+ if (owner != null) {
+ CheckState current_value = is_checked ? CheckState.Checked : CheckState.Unchecked;
+ CheckState new_value = value ? CheckState.Checked : CheckState.Unchecked;
+
+ ItemCheckEventArgs icea = new ItemCheckEventArgs (Index,
+ new_value, current_value);
+ owner.OnItemCheck (icea);
+
+ if (new_value != current_value) {
+ // force re-population of list
+ owner.CheckedItems.Reset ();
+ is_checked = new_value == CheckState.Checked;
+ Invalidate ();
+
+ ItemCheckedEventArgs args = new ItemCheckedEventArgs (this);
+ owner.OnItemChecked (args);
+ }
+ } else
+ is_checked = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public bool Focused {
+ get {
+ if (owner == null)
+ return false;
+
+ // In virtual mode the checks are always done using indexes
+ if (owner.VirtualMode)
+ return Index == owner.focused_item_index;
+
+ // Light check
+ return owner.FocusedItem == this;
+
+ }
+ set {
+ if (owner == null)
+ return;
+
+ if (Focused == value)
+ return;
+
+ ListViewItem prev_focused_item = owner.FocusedItem;
+ if (prev_focused_item != null)
+ prev_focused_item.UpdateFocusedState ();
+
+ owner.focused_item_index = value ? Index : -1;
+ if (value)
+ owner.OnUIAFocusedItemChanged ();
+
+ UpdateFocusedState ();
+ }
+ }
+
+ [Localizable (true)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Font Font {
+ get {
+ if (font != null)
+ return font;
+ else if (owner != null)
+ return owner.Font;
+
+ return ThemeEngine.Current.DefaultFont;
+ }
+ set {
+ if (font == value)
+ return;
+
+ font = value;
+ hot_font = null;
+
+ if (owner != null)
+ Layout ();
+ Invalidate ();
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Color ForeColor {
+ get {
+ if (sub_items.Count > 0)
+ return sub_items[0].ForeColor;
+
+ if (owner != null)
+ return owner.ForeColor;
+
+ return ThemeEngine.Current.ColorWindowText;
+ }
+ set { SubItems [0].ForeColor = value; }
+ }
+
+ [DefaultValue (-1)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
+ //typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable (true)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
+ public int ImageIndex {
+ get { return image_index; }
+ set {
+ if (value < -1)
+ throw new ArgumentException ("Invalid ImageIndex. It must be greater than or equal to -1.");
+
+ image_index = value;
+ image_key = String.Empty;
+
+ if (owner != null)
+ Layout ();
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue ("")]
+ [LocalizableAttribute (true)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
+ //typeof (System.Drawing.Design.UITypeEditor))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ public string ImageKey {
+ get {
+ return image_key;
+ }
+ set {
+ image_key = value == null ? String.Empty : value;
+ image_index = -1;
+
+ if (owner != null)
+ Layout ();
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ public ImageList ImageList {
+ get {
+ if (owner == null)
+ return null;
+ else if (owner.View == View.LargeIcon)
+ return owner.large_image_list;
+ else
+ return owner.small_image_list;
+ }
+ }
+
+ [DefaultValue (0)]
+ public int IndentCount {
+ get {
+ return indent_count;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("value");
+
+ if (value == indent_count)
+ return;
+
+ indent_count = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ public int Index {
+ get {
+ if (owner == null)
+ return -1;
+ if (owner.VirtualMode)
+ return display_index;
+
+ if (display_index == -1)
+ return owner.Items.IndexOf (this);
+
+ return owner.GetItemIndex (display_index);
+ }
+ }
+
+ [Browsable (false)]
+ public ListView ListView {
+ get { return owner; }
+ }
+
+ [Browsable (false)]
+ [Localizable (true)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public string Name {
+ get {
+ return name;
+ }
+ set {
+ name = value == null ? String.Empty : value;
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Browsable (false)]
+ public Point Position {
+ get {
+ if (owner != null && owner.VirtualMode)
+ return owner.GetItemLocation (display_index);
+
+ if (owner != null && !owner.IsHandleCreated)
+ return new Point (-1, -1);
+
+ return position;
+ }
+ set {
+ if (owner == null || owner.View == View.Details || owner.View == View.List)
+ return;
+
+ if (owner.VirtualMode)
+ throw new InvalidOperationException ();
+
+ owner.ChangeItemLocation (display_index, value);
+ }
+ }
+
+ // When ListView uses VirtualMode, selection state info
+ // lives in the ListView, not in the item
+ // Also, in VirtualMode we can't Reset() the selection
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public bool Selected {
+ get {
+ if (owner != null && owner.VirtualMode)
+ return owner.SelectedIndices.Contains (Index);
+
+ return selected;
+ }
+ set {
+ if (selected == value && owner != null && !owner.VirtualMode)
+ return;
+
+ SetSelectedCore (value);
+ }
+ }
+
+ // Expose this method as internal so we can force an update in the selection.
+ internal void SetSelectedCore (bool value)
+ {
+ if (owner != null) {
+ if (value && !owner.MultiSelect)
+ owner.SelectedIndices.Clear ();
+ if (owner.VirtualMode) {
+ if (value)
+ owner.SelectedIndices.InsertIndex (Index);
+ else
+ owner.SelectedIndices.RemoveIndex (Index);
+ } else {
+ selected = value;
+ owner.SelectedIndices.Reset (); // force re-population of list
+ }
+
+ owner.OnItemSelectionChanged (new ListViewItemSelectionChangedEventArgs (this, Index, value));
+ owner.OnSelectedIndexChanged ();
+ Invalidate ();
+ } else
+ selected = value;
+ }
+
+ [DefaultValue (-1)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
+ //typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable (true)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [RelatedImageListAttribute ("ListView.StateImageList")]
+ [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
+ public int StateImageIndex {
+ get { return state_image_index; }
+ set {
+ if (value < -1 || value > 14)
+ throw new ArgumentOutOfRangeException ("Invalid StateImageIndex. It must be in the range of [-1, 14].");
+
+ state_image_index = value;
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[Editor ("ShiftUI.Design.ListViewSubItemCollectionEditor, " + Consts.AssemblySystem_Design,
+ //typeof (System.Drawing.Design.UITypeEditor))]
+ public ListViewSubItemCollection SubItems {
+ get {
+ if (sub_items.Count == 0)
+ this.sub_items.Add (string.Empty);
+ return sub_items;
+ }
+ }
+
+ [Bindable (true)]
+ [DefaultValue (null)]
+ [Localizable (false)]
+ [TypeConverter (typeof (StringConverter))]
+ public object Tag {
+ get { return tag; }
+ set { tag = value; }
+ }
+
+ [Localizable (true)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public string Text {
+ get {
+ if (this.sub_items.Count > 0)
+ return this.sub_items [0].Text;
+ else
+ return string.Empty;
+ }
+ set {
+ if (SubItems [0].Text == value)
+ return;
+
+ sub_items [0].Text = value;
+
+ if (owner != null)
+ Layout ();
+ Invalidate ();
+
+ //UIA Framework: Generates Text changed
+ OnUIATextChanged ();
+
+ }
+ }
+
+ [DefaultValue (true)]
+ public bool UseItemStyleForSubItems {
+ get { return use_item_style; }
+ set { use_item_style = value; }
+ }
+
+ [LocalizableAttribute(true)]
+ [DefaultValue (null)]
+ public ListViewGroup Group {
+ get { return this.group; }
+ set {
+ if (group != value) {
+ if (value == null)
+ group.Items.Remove (this);
+ else
+ value.Items.Add (this);
+
+ group = value;
+ }
+ }
+ }
+
+ [DefaultValue ("")]
+ public string ToolTipText {
+ get {
+ return tooltip_text;
+ }
+ set {
+ if (value == null)
+ value = String.Empty;
+
+ tooltip_text = value;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Public Instance Methods
+ public void BeginEdit ()
+ {
+ if (owner != null && owner.LabelEdit) {
+ owner.item_control.BeginEdit (this);
+ }
+ // FIXME: TODO
+ // if (owner != null && owner.LabelEdit
+ // && owner.Activation == ItemActivation.Standard)
+ // allow editing
+ // else
+ // throw new InvalidOperationException ();
+ }
+
+ public virtual object Clone ()
+ {
+ ListViewItem clone = new ListViewItem ();
+ clone.image_index = this.image_index;
+ clone.is_checked = this.is_checked;
+ clone.selected = this.selected;
+ clone.font = this.font;
+ clone.state_image_index = this.state_image_index;
+ clone.sub_items = new ListViewSubItemCollection (this, null);
+
+ foreach (ListViewSubItem subItem in this.sub_items)
+ clone.sub_items.Add (subItem.Text, subItem.ForeColor,
+ subItem.BackColor, subItem.Font);
+ clone.tag = this.tag;
+ clone.use_item_style = this.use_item_style;
+ clone.owner = null;
+ clone.name = name;
+ clone.tooltip_text = tooltip_text;
+
+ return clone;
+ }
+
+ public virtual void EnsureVisible ()
+ {
+ if (this.owner != null) {
+ owner.EnsureVisible (owner.Items.IndexOf (this));
+ }
+ }
+
+ public ListViewItem FindNearestItem (SearchDirectionHint searchDirection)
+ {
+ if (owner == null)
+ return null;
+
+ Point loc = owner.GetItemLocation (display_index);
+ return owner.FindNearestItem (searchDirection, loc);
+ }
+
+ public Rectangle GetBounds (ItemBoundsPortion portion)
+ {
+ if (owner == null)
+ return Rectangle.Empty;
+
+ Rectangle rect;
+
+ switch (portion) {
+ case ItemBoundsPortion.Icon:
+ rect = icon_rect;
+ break;
+
+ case ItemBoundsPortion.Label:
+ rect = label_rect;
+ break;
+
+ case ItemBoundsPortion.ItemOnly:
+ rect = item_rect;
+ break;
+
+ case ItemBoundsPortion.Entire:
+ rect = bounds;
+ break;
+
+ default:
+ throw new ArgumentException ("Invalid value for portion.");
+ }
+
+ Point item_loc = owner.GetItemLocation (DisplayIndex);
+ rect.X += item_loc.X;
+ rect.Y += item_loc.Y;
+ return rect;
+ }
+
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ Serialize (info, context);
+ }
+
+ public ListViewSubItem GetSubItemAt (int x, int y)
+ {
+ if (owner != null && owner.View != View.Details)
+ return null;
+
+ foreach (ListViewSubItem sub_item in sub_items)
+ if (sub_item.Bounds.Contains (x, y))
+ return sub_item;
+
+ return null;
+ }
+
+ public virtual void Remove ()
+ {
+ if (owner == null)
+ return;
+
+ owner.item_control.CancelEdit (this);
+ owner.Items.Remove (this);
+ owner = null;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("ListViewItem: {0}", this.Text);
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Methods
+ protected virtual void Deserialize (SerializationInfo info, StreamingContext context)
+ {
+ sub_items = new ListViewSubItemCollection (this, null);
+ int sub_items_count = 0;
+
+ foreach (SerializationEntry entry in info) {
+ switch (entry.Name) {
+ case "Text":
+ sub_items.Add ((string)entry.Value);
+ break;
+ case "Font":
+ font = (Font)entry.Value;
+ break;
+ case "Checked":
+ is_checked = (bool)entry.Value;
+ break;
+ case "ImageIndex":
+ image_index = (int)entry.Value;
+ break;
+ case "StateImageIndex":
+ state_image_index = (int)entry.Value;
+ break;
+ case "UseItemStyleForSubItems":
+ use_item_style = (bool)entry.Value;
+ break;
+ case "SubItemCount":
+ sub_items_count = (int)entry.Value;
+ break;
+ case "Group":
+ group = (ListViewGroup)entry.Value;
+ break;
+ case "ImageKey":
+ if (image_index == -1)
+ image_key = (string)entry.Value;
+ break;
+ }
+ }
+
+ Type subitem_type = typeof (ListViewSubItem);
+ if (sub_items_count > 0) {
+ sub_items.Clear (); // .net fixup
+ Text = info.GetString ("Text");
+ for (int i = 0; i < sub_items_count - 1; i++)
+ sub_items.Add ((ListViewSubItem)info.GetValue ("SubItem" + (i + 1), subitem_type));
+ }
+
+ // After any sub item has been added.
+ ForeColor = (Color)info.GetValue ("ForeColor", typeof (Color));
+ BackColor = (Color)info.GetValue ("BackColor", typeof (Color));
+ }
+
+ protected virtual void Serialize (SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue ("Text", Text);
+ info.AddValue ("Font", Font);
+ info.AddValue ("ImageIndex", image_index);
+ info.AddValue ("Checked", is_checked);
+ info.AddValue ("StateImageIndex", state_image_index);
+ info.AddValue ("UseItemStyleForSubItems", use_item_style);
+ info.AddValue ("BackColor", BackColor);
+ info.AddValue ("ForeColor", ForeColor);
+ info.AddValue ("ImageKey", image_key);
+ if (group != null)
+ info.AddValue ("Group", group);
+ if (sub_items.Count > 1) {
+ info.AddValue ("SubItemCount", sub_items.Count);
+ for (int i = 1; i < sub_items.Count; i++) {
+ info.AddValue ("SubItem" + i, sub_items [i]);
+ }
+ }
+ }
+ #endregion // Protected Methods
+
+ #region Private Internal Methods
+ internal Rectangle CheckRectReal {
+ get {
+ Rectangle rect = checkbox_rect;
+ Point item_loc = owner.GetItemLocation (DisplayIndex);
+ rect.X += item_loc.X;
+ rect.Y += item_loc.Y;
+ return rect;
+ }
+ }
+
+ Rectangle text_bounds;
+ internal Rectangle TextBounds {
+ get {
+ // Call Layout() if it hasn't been called before.
+ if (owner.VirtualMode && bounds == new Rectangle (-1, -1, -1, -1))
+ Layout ();
+ Rectangle result = text_bounds;
+ Point loc = owner.GetItemLocation (DisplayIndex);
+ result.X += loc.X;
+ result.Y += loc.Y;
+ return result;
+ }
+ }
+
+ internal int DisplayIndex {
+ get {
+ // Special case for Details view
+ // and no columns (which means no Layout at all)
+ if (display_index == -1)
+ return owner.Items.IndexOf (this);
+
+ return display_index;
+ }
+ set {
+ display_index = value;
+ }
+ }
+
+ internal bool Hot {
+ get {
+ return Index == owner.HotItemIndex;
+ }
+ }
+
+ internal Font HotFont {
+ get {
+ if (hot_font == null)
+ hot_font = new Font (Font, Font.Style | FontStyle.Underline);
+
+ return hot_font;
+ }
+ }
+
+ internal ListView Owner {
+ set {
+ if (owner == value)
+ return;
+
+ owner = value;
+ }
+ }
+
+ internal void SetGroup (ListViewGroup group)
+ {
+ this.group = group;
+ }
+
+ internal void SetPosition (Point position)
+ {
+ this.position = position;
+ }
+
+ // When focus changed, we need to invalidate area
+ // with previous layout and with the new one
+ void UpdateFocusedState ()
+ {
+ if (owner != null) {
+ Invalidate ();
+ Layout ();
+ Invalidate ();
+ }
+ }
+
+ internal void Invalidate ()
+ {
+ if (owner == null || owner.item_control == null || owner.updating)
+ return;
+
+ // Add some padding to bounds (focused extra space, selection)
+ Rectangle rect = Bounds;
+ rect.Inflate (1, 1);
+ owner.item_control.Invalidate (rect);
+ }
+
+ internal void Layout ()
+ {
+ if (owner == null)
+ return;
+ int item_ht;
+ Rectangle total;
+ Size text_size = owner.text_size;
+
+ checkbox_rect = Rectangle.Empty;
+ if (owner.CheckBoxes)
+ checkbox_rect.Size = owner.CheckBoxSize;
+ switch (owner.View) {
+ case View.Details:
+ // LAMESPEC: MSDN says, "In all views except the details
+ // view of the ListView, this value specifies the same
+ // bounding rectangle as the Entire value." Actually, it
+ // returns same bounding rectangles for Item and Entire
+ // values in the case of Details view.
+
+ int x_offset = 0;
+ if (owner.SmallImageList != null)
+ x_offset = indent_count * owner.SmallImageList.ImageSize.Width;
+
+ // Handle reordered column
+ if (owner.Columns.Count > 0)
+ checkbox_rect.X = owner.Columns[0].Rect.X + x_offset;
+
+ icon_rect = label_rect = Rectangle.Empty;
+ icon_rect.X = checkbox_rect.Right + 2;
+ item_ht = owner.ItemSize.Height;
+
+ if (owner.SmallImageList != null)
+ icon_rect.Width = owner.SmallImageList.ImageSize.Width;
+
+ label_rect.Height = icon_rect.Height = item_ht;
+ checkbox_rect.Y = item_ht - checkbox_rect.Height;
+
+ label_rect.X = icon_rect.Width > 0 ? icon_rect.Right + 1 : icon_rect.Right;
+
+ if (owner.Columns.Count > 0)
+ label_rect.Width = owner.Columns[0].Wd - label_rect.X + checkbox_rect.X;
+ else
+ label_rect.Width = text_size.Width;
+
+ SizeF text_sz = TextRenderer.MeasureString (Text, Font);
+ text_bounds = label_rect;
+ text_bounds.Width = (int) text_sz.Width;
+
+ item_rect = total = Rectangle.Union
+ (Rectangle.Union (checkbox_rect, icon_rect), label_rect);
+ bounds.Size = total.Size;
+
+ item_rect.Width = 0;
+ bounds.Width = 0;
+ for (int i = 0; i < owner.Columns.Count; i++) {
+ item_rect.Width += owner.Columns [i].Wd;
+ bounds.Width += owner.Columns [i].Wd;
+ }
+
+ // Bounds for sub items
+ int n = Math.Min (owner.Columns.Count, sub_items.Count);
+ for (int i = 0; i < n; i++) {
+ Rectangle col_rect = owner.Columns [i].Rect;
+ sub_items [i].SetBounds (col_rect.X, 0, col_rect.Width, item_ht);
+ }
+ break;
+
+ case View.LargeIcon:
+ label_rect = icon_rect = Rectangle.Empty;
+
+ SizeF sz = TextRenderer.MeasureString (Text, Font);
+ if ((int) sz.Width > text_size.Width) {
+ if (Focused && owner.InternalContainsFocus) {
+ int text_width = text_size.Width;
+ StringFormat format = new StringFormat ();
+ format.Alignment = StringAlignment.Center;
+ sz = TextRenderer.MeasureString (Text, Font, text_width, format);
+ text_size.Height = (int) sz.Height;
+ } else
+ text_size.Height = 2 * (int) sz.Height;
+ }
+
+ if (owner.LargeImageList != null) {
+ icon_rect.Width = owner.LargeImageList.ImageSize.Width;
+ icon_rect.Height = owner.LargeImageList.ImageSize.Height;
+ }
+
+ if (checkbox_rect.Height > icon_rect.Height)
+ icon_rect.Y = checkbox_rect.Height - icon_rect.Height;
+ else
+ checkbox_rect.Y = icon_rect.Height - checkbox_rect.Height;
+
+ if (text_size.Width <= icon_rect.Width) {
+ icon_rect.X = checkbox_rect.Width + 1;
+ label_rect.X = icon_rect.X + (icon_rect.Width - text_size.Width) / 2;
+ label_rect.Y = icon_rect.Bottom + 2;
+ label_rect.Size = text_size;
+ } else {
+ int centerX = text_size.Width / 2;
+ icon_rect.X = checkbox_rect.Width + 1 + centerX - icon_rect.Width / 2;
+ label_rect.X = checkbox_rect.Width + 1;
+ label_rect.Y = icon_rect.Bottom + 2;
+ label_rect.Size = text_size;
+ }
+
+ item_rect = Rectangle.Union (icon_rect, label_rect);
+ total = Rectangle.Union (item_rect, checkbox_rect);
+ bounds.Size = total.Size;
+ break;
+
+ case View.List:
+ case View.SmallIcon:
+ label_rect = icon_rect = Rectangle.Empty;
+ icon_rect.X = checkbox_rect.Width + 1;
+ item_ht = Math.Max (owner.CheckBoxSize.Height, text_size.Height);
+
+ if (owner.SmallImageList != null) {
+ item_ht = Math.Max (item_ht, owner.SmallImageList.ImageSize.Height);
+ icon_rect.Width = owner.SmallImageList.ImageSize.Width;
+ icon_rect.Height = owner.SmallImageList.ImageSize.Height;
+ }
+
+ checkbox_rect.Y = item_ht - checkbox_rect.Height;
+ label_rect.X = icon_rect.Right + 1;
+ label_rect.Width = text_size.Width;
+ label_rect.Height = icon_rect.Height = item_ht;
+
+ item_rect = Rectangle.Union (icon_rect, label_rect);
+ total = Rectangle.Union (item_rect, checkbox_rect);
+ bounds.Size = total.Size;
+ break;
+ case View.Tile:
+ if (!Application.VisualStylesEnabled)
+ goto case View.LargeIcon;
+
+ label_rect = icon_rect = Rectangle.Empty;
+
+ if (owner.LargeImageList != null) {
+ icon_rect.Width = owner.LargeImageList.ImageSize.Width;
+ icon_rect.Height = owner.LargeImageList.ImageSize.Height;
+ }
+
+ int separation = 2;
+ SizeF tsize = TextRenderer.MeasureString (Text, Font);
+ int main_item_height = (int) Math.Ceiling (tsize.Height);
+ int main_item_width = (int) Math.Ceiling (tsize.Width);
+ sub_items [0].bounds.Height = main_item_height;
+
+ // Set initial values for subitem's layout
+ int total_height = main_item_height;
+ int max_subitem_width = main_item_width;
+
+ int count = Math.Min (owner.Columns.Count, sub_items.Count);
+ for (int i = 1; i < count; i++) { // Ignore first column and first subitem
+ ListViewSubItem sub_item = sub_items [i];
+ if (sub_item.Text == null || sub_item.Text.Length == 0)
+ continue;
+
+ tsize = TextRenderer.MeasureString (sub_item.Text, sub_item.Font);
+
+ int width = (int)Math.Ceiling (tsize.Width);
+ if (width > max_subitem_width)
+ max_subitem_width = width;
+
+ int height = (int)Math.Ceiling (tsize.Height);
+ total_height += height + separation;
+
+ sub_item.bounds.Height = height;
+ }
+
+ max_subitem_width = Math.Min (max_subitem_width, owner.TileSize.Width - (icon_rect.Width + 4));
+ label_rect.X = icon_rect.Right + 4;
+ label_rect.Y = owner.TileSize.Height / 2 - total_height / 2;
+ label_rect.Width = max_subitem_width;
+ label_rect.Height = total_height;
+
+ // Main item - always set bounds for it
+ sub_items [0].SetBounds (label_rect.X, label_rect.Y, max_subitem_width, sub_items [0].bounds.Height);
+
+ // Second pass to assign bounds for every sub item
+ int current_y = sub_items [0].bounds.Bottom + separation;
+ for (int j = 1; j < count; j++) {
+ ListViewSubItem sub_item = sub_items [j];
+ if (sub_item.Text == null || sub_item.Text.Length == 0)
+ continue;
+
+ sub_item.SetBounds (label_rect.X, current_y, max_subitem_width, sub_item.bounds.Height);
+ current_y += sub_item.Bounds.Height + separation;
+ }
+
+ item_rect = Rectangle.Union (icon_rect, label_rect);
+ bounds.Size = item_rect.Size;
+ break;
+ }
+
+ }
+ #endregion // Private Internal Methods
+
+ #region Subclasses
+
+ [DefaultProperty ("Text")]
+ [DesignTimeVisible (false)]
+ [Serializable]
+ [ToolboxItem (false)]
+ [TypeConverter (typeof(ListViewSubItemConverter))]
+ public class ListViewSubItem
+ {
+ [NonSerialized]
+ internal ListViewItem owner;
+ private string text = string.Empty;
+ private string name;
+ private object userData;
+ private SubItemStyle style;
+ [NonSerialized]
+ internal Rectangle bounds;
+
+
+ #region UIA Framework: Methods, Properties and Events
+
+ [field:NonSerialized]
+ internal event EventHandler UIATextChanged;
+
+ private void OnUIATextChanged ()
+ {
+ if (UIATextChanged != null)
+ UIATextChanged (this, EventArgs.Empty);
+ }
+
+ #endregion // UIA Framework: Methods, Properties and Events
+
+
+ #region Public Constructors
+ public ListViewSubItem ()
+ : this (null, string.Empty, Color.Empty,
+ Color.Empty, null)
+ {
+ }
+
+ public ListViewSubItem (ListViewItem owner, string text)
+ : this (owner, text, Color.Empty,
+ Color.Empty, null)
+ {
+ }
+
+ public ListViewSubItem (ListViewItem owner, string text, Color foreColor,
+ Color backColor, Font font)
+ {
+ this.owner = owner;
+ Text = text;
+ this.style = new SubItemStyle (foreColor,
+ backColor, font);
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ public Color BackColor {
+ get {
+ if (style.backColor != Color.Empty)
+ return style.backColor;
+ if (this.owner != null && this.owner.ListView != null)
+ return this.owner.ListView.BackColor;
+ return ThemeEngine.Current.ColorWindow;
+ }
+ set {
+ style.backColor = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ public Rectangle Bounds {
+ get {
+ Rectangle retval = bounds;
+ if (owner != null) {
+ retval.X += owner.Bounds.X;
+ retval.Y += owner.Bounds.Y;
+ }
+
+ return retval;
+ }
+ }
+
+ [Localizable (true)]
+ public Font Font {
+ get {
+ if (style.font != null)
+ return style.font;
+ else if (owner != null)
+ return owner.Font;
+ return ThemeEngine.Current.DefaultFont;
+ }
+ set {
+ if (style.font == value)
+ return;
+ style.font = value;
+ Invalidate ();
+ }
+ }
+
+ public Color ForeColor {
+ get {
+ if (style.foreColor != Color.Empty)
+ return style.foreColor;
+ if (this.owner != null && this.owner.ListView != null)
+ return this.owner.ListView.ForeColor;
+ return ThemeEngine.Current.ColorWindowText;
+ }
+ set {
+ style.foreColor = value;
+ Invalidate ();
+ }
+ }
+
+ [Localizable (true)]
+ public string Name {
+ get {
+ if (name == null)
+ return string.Empty;
+ return name;
+ }
+ set {
+ name = value;
+ }
+ }
+
+ [TypeConverter (typeof (StringConverter))]
+ [BindableAttribute (true)]
+ [DefaultValue (null)]
+ [Localizable (false)]
+ public object Tag {
+ get {
+ return userData;
+ }
+ set {
+ userData = value;
+ }
+ }
+
+ [Localizable (true)]
+ public string Text {
+ get { return text; }
+ set {
+ if(text == value)
+ return;
+
+ if(value == null)
+ text = string.Empty;
+ else
+ text = value;
+
+ Invalidate ();
+
+ // UIA Framework: Generates SubItem TextChanged
+ OnUIATextChanged ();
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Public Methods
+ public void ResetStyle ()
+ {
+ style.Reset ();
+ Invalidate ();
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("ListViewSubItem {{0}}", text);
+ }
+ #endregion // Public Methods
+
+ #region Private Methods
+ private void Invalidate ()
+ {
+ if (owner == null || owner.owner == null)
+ return;
+
+ owner.Invalidate ();
+ }
+
+ [OnDeserialized]
+ void OnDeserialized (StreamingContext context)
+ {
+ name = null;
+ userData = null;
+ }
+
+ internal int Height {
+ get {
+ return bounds.Height;
+ }
+ }
+
+ internal void SetBounds (int x, int y, int width, int height)
+ {
+ bounds = new Rectangle (x, y, width, height);
+ }
+
+ #endregion // Private Methods
+
+ [Serializable]
+ class SubItemStyle
+ {
+ public SubItemStyle ()
+ {
+ }
+
+ public SubItemStyle (Color foreColor, Color backColor, Font font)
+ {
+ this.foreColor = foreColor;
+ this.backColor = backColor;
+ this.font = font;
+ }
+
+ public void Reset ()
+ {
+ foreColor = Color.Empty;
+ backColor = Color.Empty;
+ font = null;
+ }
+
+ public Color backColor;
+ public Color foreColor;
+ public Font font;
+ }
+ }
+
+ public class ListViewSubItemCollection : IList, ICollection, IEnumerable
+ {
+ private ArrayList list;
+ internal ListViewItem owner;
+
+ #region Public Constructors
+ public ListViewSubItemCollection (ListViewItem owner) : this (owner, owner.Text)
+ {
+ }
+ #endregion // Public Constructors
+
+ internal ListViewSubItemCollection (ListViewItem owner, string text)
+ {
+ this.owner = owner;
+ this.list = new ArrayList ();
+ if (text != null)
+ Add (text);
+ }
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public ListViewSubItem this [int index] {
+ get { return (ListViewSubItem) list [index]; }
+ set {
+ value.owner = owner;
+ list [index] = value;
+ owner.Layout ();
+ owner.Invalidate ();
+ }
+ }
+
+ public virtual ListViewSubItem this [string key] {
+ get {
+ int idx = IndexOfKey (key);
+ if (idx == -1)
+ return null;
+
+ return (ListViewSubItem) list [idx];
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return list.IsSynchronized; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return list.SyncRoot; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return list.IsFixedSize; }
+ }
+
+ object IList.this [int index] {
+ get { return this [index]; }
+ set {
+ if (! (value is ListViewSubItem))
+ throw new ArgumentException ("Not of type ListViewSubItem", "value");
+ this [index] = (ListViewSubItem) value;
+ }
+ }
+ #endregion // Public Properties
+
+ #region Public Methods
+ public ListViewSubItem Add (ListViewSubItem item)
+ {
+ AddSubItem (item);
+ owner.Layout ();
+ owner.Invalidate ();
+ return item;
+ }
+
+ public ListViewSubItem Add (string text)
+ {
+ ListViewSubItem item = new ListViewSubItem (owner, text);
+ return Add (item);
+ }
+
+ public ListViewSubItem Add (string text, Color foreColor,
+ Color backColor, Font font)
+ {
+ ListViewSubItem item = new ListViewSubItem (owner, text,
+ foreColor, backColor, font);
+ return Add (item);
+ }
+
+ public void AddRange (ListViewSubItem [] items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (ListViewSubItem item in items) {
+ if (item == null)
+ continue;
+ AddSubItem (item);
+ }
+ owner.Layout ();
+ owner.Invalidate ();
+ }
+
+ public void AddRange (string [] items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (string item in items) {
+ if (item == null)
+ continue;
+ AddSubItem (new ListViewSubItem (owner, item));
+ }
+ owner.Layout ();
+ owner.Invalidate ();
+ }
+
+ public void AddRange (string [] items, Color foreColor,
+ Color backColor, Font font)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (string item in items) {
+ if (item == null)
+ continue;
+
+ AddSubItem (new ListViewSubItem (owner, item, foreColor, backColor, font));
+ }
+ owner.Layout ();
+ owner.Invalidate ();
+ }
+
+ void AddSubItem (ListViewSubItem subItem)
+ {
+ subItem.owner = owner;
+ list.Add (subItem);
+
+ //UIA Framework
+ subItem.UIATextChanged += OnUIASubItemTextChanged;
+ }
+
+ public void Clear ()
+ {
+ list.Clear ();
+ }
+
+ public bool Contains (ListViewSubItem subItem)
+ {
+ return list.Contains (subItem);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ return IndexOfKey (key) != -1;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ list.CopyTo (dest, index);
+ }
+
+ int IList.Add (object item)
+ {
+ if (! (item is ListViewSubItem)) {
+ throw new ArgumentException ("Not of type ListViewSubItem", "item");
+ }
+
+ ListViewSubItem sub_item = (ListViewSubItem) item;
+ sub_item.owner = this.owner;
+ //UIA Framework
+ sub_item.UIATextChanged += OnUIASubItemTextChanged;
+ return list.Add (sub_item);
+ }
+
+ bool IList.Contains (object subItem)
+ {
+ if (! (subItem is ListViewSubItem)) {
+ throw new ArgumentException ("Not of type ListViewSubItem", "subItem");
+ }
+
+ return this.Contains ((ListViewSubItem) subItem);
+ }
+
+ int IList.IndexOf (object subItem)
+ {
+ if (! (subItem is ListViewSubItem)) {
+ throw new ArgumentException ("Not of type ListViewSubItem", "subItem");
+ }
+
+ return this.IndexOf ((ListViewSubItem) subItem);
+ }
+
+ void IList.Insert (int index, object item)
+ {
+ if (! (item is ListViewSubItem)) {
+ throw new ArgumentException ("Not of type ListViewSubItem", "item");
+ }
+
+ this.Insert (index, (ListViewSubItem) item);
+ }
+
+ void IList.Remove (object item)
+ {
+ if (! (item is ListViewSubItem)) {
+ throw new ArgumentException ("Not of type ListViewSubItem", "item");
+ }
+
+ this.Remove ((ListViewSubItem) item);
+ }
+
+ public int IndexOf (ListViewSubItem subItem)
+ {
+ return list.IndexOf (subItem);
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (key == null || key.Length == 0)
+ return -1;
+
+ for (int i = 0; i < list.Count; i++) {
+ ListViewSubItem l = (ListViewSubItem) list [i];
+ if (String.Compare (l.Name, key, true) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public void Insert (int index, ListViewSubItem item)
+ {
+ item.owner = this.owner;
+ list.Insert (index, item);
+ owner.Layout ();
+ owner.Invalidate ();
+
+ //UIA Framework
+ item.UIATextChanged += OnUIASubItemTextChanged;
+ }
+
+ public void Remove (ListViewSubItem item)
+ {
+ list.Remove (item);
+ owner.Layout ();
+ owner.Invalidate ();
+
+ //UIA Framework
+ item.UIATextChanged -= OnUIASubItemTextChanged;
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ int idx = IndexOfKey (key);
+ if (idx != -1)
+ RemoveAt (idx);
+ }
+
+ public void RemoveAt (int index)
+ {
+ //UIA Framework
+ if (index >= 0 && index < list.Count)
+ ((ListViewSubItem) list [index]).UIATextChanged -= OnUIASubItemTextChanged;
+
+ list.RemoveAt (index);
+
+ }
+ #endregion // Public Methods
+ #region UIA Event Handler
+
+ private void OnUIASubItemTextChanged (object sender, EventArgs args)
+ {
+ owner.OnUIASubItemTextChanged (new LabelEditEventArgs (list.IndexOf (sender)));
+ }
+
+ #endregion
+
+
+ }
+ #endregion // Subclasses
+ }
+}
diff --git a/source/ShiftUI/Widgets/MonthCalendar.cs b/source/ShiftUI/Widgets/MonthCalendar.cs
new file mode 100644
index 0000000..24d7c8f
--- /dev/null
+++ b/source/ShiftUI/Widgets/MonthCalendar.cs
@@ -0,0 +1,2430 @@
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// John BouAntoun [email protected]
+//
+// REMAINING TODO:
+// - get the date_cell_size and title_size to be pixel perfect match of SWF
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace ShiftUI {
+ [DefaultBindingProperty("SelectionRange")]
+ [ClassInterface(ClassInterfaceType.AutoDispatch)]
+ [ComVisible(true)]
+ [DefaultProperty("SelectionRange")]
+ [DefaultEvent("DateChanged")]
+ //[Designer ("ShiftUI.Design.MonthCalendarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ public class MonthCalendar : Widget {
+ #region Local variables
+ ArrayList annually_bolded_dates;
+ ArrayList monthly_bolded_dates;
+ ArrayList bolded_dates;
+ Size calendar_dimensions;
+ Day first_day_of_week;
+ DateTime max_date;
+ int max_selection_count;
+ DateTime min_date;
+ int scroll_change;
+ SelectionRange selection_range;
+ bool show_today;
+ bool show_today_circle;
+ bool show_week_numbers;
+ Color title_back_color;
+ Color title_fore_color;
+ DateTime today_date;
+ bool today_date_set;
+ Color trailing_fore_color;
+ Timer timer;
+ Timer updown_timer;
+ const int initial_delay = 500;
+ const int subsequent_delay = 100;
+ private bool is_year_going_up;
+ private bool is_year_going_down;
+ private bool is_mouse_moving_year;
+ private int year_moving_count;
+ private bool date_selected_event_pending;
+ bool right_to_left_layout;
+
+ // internal variables used
+ internal bool show_year_updown;
+ internal DateTime current_month; // the month that is being displayed in top left corner of the grid
+ internal DateTimePicker owner; // used if this control is popped up
+ internal int button_x_offset;
+ internal Size button_size;
+ internal Size title_size;
+ internal Size date_cell_size;
+ internal Size calendar_spacing;
+ internal int divider_line_offset;
+ internal DateTime clicked_date;
+ internal Rectangle clicked_rect;
+ internal bool is_date_clicked;
+ internal bool is_previous_clicked;
+ internal bool is_next_clicked;
+ internal bool is_shift_pressed;
+ internal DateTime first_select_start_date;
+ internal int last_clicked_calendar_index;
+ internal Rectangle last_clicked_calendar_rect;
+ internal Font bold_font; // Cache the font in FontStyle.Bold
+ internal StringFormat centered_format; // Cache centered string format
+ private Point month_title_click_location;
+ // this is used to see which item was actually clicked on in the beginning
+ // so that we know which item to fire on timer
+ // 0: date clicked
+ // 1: previous clicked
+ // 2: next clicked
+ private bool[] click_state;
+
+
+
+ #endregion // Local variables
+
+ #region Public Constructors
+
+ public MonthCalendar () {
+ // set up the control painting
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.StandardClick, false);
+
+ // mouse down timer
+ timer = new Timer ();
+ timer.Interval = 500;
+ timer.Enabled = false;
+
+ // initialise default values
+ DateTime now = DateTime.Now.Date;
+ selection_range = new SelectionRange (now, now);
+ today_date = now;
+ current_month = new DateTime (now.Year , now.Month, 1);
+
+ // iniatialise local members
+ annually_bolded_dates = null;
+ bolded_dates = null;
+ calendar_dimensions = new Size (1,1);
+ first_day_of_week = Day.Default;
+ max_date = new DateTime (9998, 12, 31);
+ max_selection_count = 7;
+ min_date = new DateTime (1753, 1, 1);
+ monthly_bolded_dates = null;
+ scroll_change = 0;
+ show_today = true;
+ show_today_circle = true;
+ show_week_numbers = false;
+ title_back_color = ThemeEngine.Current.ColorActiveCaption;
+ title_fore_color = ThemeEngine.Current.ColorActiveCaptionText;
+ today_date_set = false;
+ trailing_fore_color = SystemColors.GrayText;
+ bold_font = new Font (Font, Font.Style | FontStyle.Bold);
+ centered_format = new StringFormat (StringFormat.GenericTypographic);
+ centered_format.FormatFlags = centered_format.FormatFlags | StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap | StringFormatFlags.FitBlackBox;
+ centered_format.FormatFlags &= ~StringFormatFlags.NoClip;
+ centered_format.LineAlignment = StringAlignment.Center;
+ centered_format.Alignment = StringAlignment.Center;
+
+ // Set default values
+ ForeColor = SystemColors.WindowText;
+ BackColor = ThemeEngine.Current.ColorWindow;
+
+ // intiailise internal variables used
+ button_x_offset = 5;
+ button_size = new Size (22, 17);
+ // default settings based on 8.25 pt San Serif Font
+ // Not sure of algorithm used to establish this
+ date_cell_size = new Size (24, 16); // default size at san-serif 8.25
+ divider_line_offset = 4;
+ calendar_spacing = new Size (4, 5); // horiz and vert spacing between months in a calendar grid
+
+ // set some state info
+ clicked_date = now;
+ is_date_clicked = false;
+ is_previous_clicked = false;
+ is_next_clicked = false;
+ is_shift_pressed = false;
+ click_state = new bool [] {false, false, false};
+ first_select_start_date = now;
+ month_title_click_location = Point.Empty;
+
+ // set up context menus
+ SetUpTodayMenu ();
+ SetUpMonthMenu ();
+
+ // event handlers
+ timer.Tick += new EventHandler (TimerHandler);
+ MouseMove += new MouseEventHandler (MouseMoveHandler);
+ MouseDown += new MouseEventHandler (MouseDownHandler);
+ KeyDown += new KeyEventHandler (KeyDownHandler);
+ MouseUp += new MouseEventHandler (MouseUpHandler);
+ KeyUp += new KeyEventHandler (KeyUpHandler);
+
+ // this replaces paint so call the control version
+ base.Paint += new PaintEventHandler (PaintHandler);
+
+ Size = DefaultSize;
+ }
+
+ // called when this control is added to date time picker
+ internal MonthCalendar (DateTimePicker owner) : this () {
+ this.owner = owner;
+ this.is_visible = false;
+ this.Size = this.DefaultSize;
+ }
+
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+
+ // dates to make bold on calendar annually (recurring)
+ [Localizable (true)]
+ public DateTime [] AnnuallyBoldedDates {
+ set {
+ if (annually_bolded_dates == null)
+ annually_bolded_dates = new ArrayList (value);
+ else {
+ annually_bolded_dates.Clear ();
+ annually_bolded_dates.AddRange (value);
+ }
+
+ UpdateBoldedDates ();
+ }
+ get {
+ if (annually_bolded_dates == null || annually_bolded_dates.Count == 0) {
+ return new DateTime [0];
+ }
+ DateTime [] result = new DateTime [annually_bolded_dates.Count];
+ annually_bolded_dates.CopyTo (result);
+ return result;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get {
+ return base.BackgroundImage;
+ }
+ set {
+ base.BackgroundImage = value;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override ImageLayout BackgroundImageLayout {
+ get {
+ return base.BackgroundImageLayout;
+ }
+ set {
+ base.BackgroundImageLayout = value;
+ }
+ }
+
+ // the back color for the main part of the calendar
+ public override Color BackColor {
+ set {
+ base.BackColor = value;
+ }
+ get {
+ return base.BackColor;
+ }
+ }
+
+ // specific dates to make bold on calendar (non-recurring)
+ [Localizable (true)]
+ public DateTime[] BoldedDates {
+ set {
+ if (bolded_dates == null) {
+ bolded_dates = new ArrayList (value);
+ } else {
+ bolded_dates.Clear ();
+ bolded_dates.AddRange (value);
+ }
+
+ UpdateBoldedDates ();
+ }
+ get {
+ if (bolded_dates == null || bolded_dates.Count == 0)
+ return new DateTime [0];
+
+ DateTime [] result = new DateTime [bolded_dates.Count];
+ bolded_dates.CopyTo (result);
+ return result;
+ }
+ }
+
+ // the configuration of the monthly grid display - only allowed to display at most,
+ // 1 calendar year at a time, will be trimmed to fit it properly
+ [Localizable (true)]
+ public Size CalendarDimensions {
+ set {
+ if (value.Width < 0 || value.Height < 0) {
+ throw new ArgumentException ();
+ }
+ if (calendar_dimensions != value) {
+ // squeeze the grid into 1 calendar year
+ if (value.Width * value.Height > 12) {
+ // iteratively reduce the largest dimension till our
+ // product is less than 12
+ if (value.Width > 12 && value.Height > 12) {
+ calendar_dimensions = new Size (4, 3);
+ } else if (value.Width > 12) {
+ for (int i = 12; i > 0; i--) {
+ if (i * value.Height <= 12) {
+ calendar_dimensions = new Size (i, value.Height);
+ break;
+ }
+ }
+ } else if (value.Height > 12) {
+ for (int i = 12; i > 0; i--) {
+ if (i * value.Width <= 12) {
+ calendar_dimensions = new Size (value.Width, i);
+ break;
+ }
+ }
+ }
+ } else {
+ calendar_dimensions = value;
+ }
+ this.Invalidate ();
+ }
+ }
+ get {
+ return calendar_dimensions;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get {
+ return base.DoubleBuffered;
+ }
+ set {
+ base.DoubleBuffered = value;
+ }
+ }
+
+ // the first day of the week to display
+ [Localizable (true)]
+ [DefaultValue (Day.Default)]
+ public Day FirstDayOfWeek {
+ set {
+ if (first_day_of_week != value) {
+ first_day_of_week = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return first_day_of_week;
+ }
+ }
+
+ // the fore color for the main part of the calendar
+ public override Color ForeColor {
+ set {
+ base.ForeColor = value;
+ }
+ get {
+ return base.ForeColor;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new ImeMode ImeMode {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ // the maximum date allowed to be selected on this month calendar
+ public DateTime MaxDate {
+ set {
+ if (value < MinDate) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "Value of '{0}' is not valid for 'MaxDate'. 'MaxDate' " +
+ "must be greater than or equal to MinDate.",
+ value.ToString ("d", CultureInfo.CurrentCulture));
+ throw new ArgumentOutOfRangeException ("MaxDate",
+ msg);
+ }
+
+ if (max_date == value)
+ return;
+
+ max_date = value;
+
+ if (max_date < selection_range.Start || max_date < selection_range.End) {
+ DateTime start = max_date < selection_range.Start ? max_date : selection_range.Start;
+ DateTime end = max_date < selection_range.End ? max_date : selection_range.End;
+ SelectionRange = new SelectionRange (start, end);
+ }
+ }
+ get {
+ return max_date;
+ }
+ }
+
+ // the maximum number of selectable days
+ [DefaultValue (7)]
+ public int MaxSelectionCount {
+ set {
+ if (value < 1) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "Value of '{0}' is not valid for 'MaxSelectionCount'. " +
+ "'MaxSelectionCount' must be greater than or equal to {1}.",
+ value, 1);
+ throw new ArgumentOutOfRangeException ("MaxSelectionCount",
+ msg);
+ }
+
+ // can't set selectioncount less than already selected dates
+ if ((SelectionEnd - SelectionStart).Days > value) {
+ throw new ArgumentException();
+ }
+
+ if (max_selection_count != value) {
+ max_selection_count = value;
+ this.OnUIAMaxSelectionCountChanged ();
+ }
+ }
+ get {
+ return max_selection_count;
+ }
+ }
+
+ // the minimum date allowed to be selected on this month calendar
+ public DateTime MinDate {
+ set {
+ DateTime absoluteMinDate = new DateTime (1753, 1, 1);
+
+ if (value < absoluteMinDate) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "Value of '{0}' is not valid for 'MinDate'. 'MinDate' " +
+ "must be greater than or equal to {1}.",
+ value.ToString ("d", CultureInfo.CurrentCulture),
+ absoluteMinDate.ToString ("d", CultureInfo.CurrentCulture));
+ throw new ArgumentOutOfRangeException ("MinDate",
+ msg);
+ }
+
+ if (value > MaxDate) {
+ string msg = string.Format (CultureInfo.CurrentCulture,
+ "Value of '{0}' is not valid for 'MinDate'. 'MinDate' " +
+ "must be less than MaxDate.",
+ value.ToString ("d", CultureInfo.CurrentCulture));
+ throw new ArgumentOutOfRangeException ("MinDate",
+ msg);
+ }
+
+ if (min_date == value)
+ return;
+
+ min_date = value;
+
+ if (min_date > selection_range.Start || min_date > selection_range.End) {
+ DateTime start = min_date > selection_range.Start ? min_date : selection_range.Start;
+ DateTime end = min_date > selection_range.End ? min_date : selection_range.End;
+ SelectionRange = new SelectionRange (start, end);
+ }
+ }
+ get {
+ return min_date;
+ }
+ }
+
+ // dates to make bold on calendar monthly (recurring)
+ [Localizable (true)]
+ public DateTime[] MonthlyBoldedDates {
+ set {
+ if (monthly_bolded_dates == null) {
+ monthly_bolded_dates = new ArrayList (value);
+ } else {
+ monthly_bolded_dates.Clear ();
+ monthly_bolded_dates.AddRange (value);
+ }
+
+ UpdateBoldedDates ();
+ }
+ get {
+ if (monthly_bolded_dates == null || monthly_bolded_dates.Count == 0)
+ return new DateTime [0];
+
+ DateTime [] result = new DateTime [monthly_bolded_dates.Count];
+ monthly_bolded_dates.CopyTo (result);
+ return result;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [Browsable (false)]
+ // Padding should not have any effect on the appearance of MonthCalendar.
+ public new Padding Padding {
+ get {
+ return base.Padding;
+ }
+ set {
+ base.Padding = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public virtual bool RightToLeftLayout {
+ get {
+ return right_to_left_layout;
+ }
+ set {
+ right_to_left_layout = value;
+ }
+ }
+
+ // the ammount by which to scroll this calendar by
+ [DefaultValue (0)]
+ public int ScrollChange {
+ set {
+ if (value < 0 || value > 20000) {
+ throw new ArgumentException();
+ }
+
+ if (scroll_change != value) {
+ scroll_change = value;
+ }
+ }
+ get {
+ return scroll_change;
+ }
+ }
+
+
+ // the last selected date
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public DateTime SelectionEnd {
+ set {
+ if (value < MinDate || value > MaxDate) {
+ throw new ArgumentException();
+ }
+
+ if (SelectionRange.End != value) {
+ DateTime old_end = SelectionRange.End;
+ // make sure the end obeys the max selection range count
+ if (value < SelectionRange.Start) {
+ SelectionRange.Start = value;
+ }
+ if (value.AddDays((MaxSelectionCount-1)*-1) > SelectionRange.Start) {
+ SelectionRange.Start = value.AddDays((MaxSelectionCount-1)*-1);
+ }
+ SelectionRange.End = value;
+ this.InvalidateDateRange (new SelectionRange (old_end, SelectionRange.End));
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ this.OnUIASelectionChanged ();
+ }
+ }
+ get {
+ return SelectionRange.End;
+ }
+ }
+
+ [Bindable(true)]
+ // the range of selected dates
+ public SelectionRange SelectionRange {
+ set {
+ if (selection_range != value) {
+ if (value.Start < MinDate)
+ throw new ArgumentException ("SelectionStart cannot be less than MinDate");
+ else if (value.End > MaxDate)
+ throw new ArgumentException ("SelectionEnd cannot be greated than MaxDate");
+
+ SelectionRange old_range = selection_range;
+
+ // make sure the end obeys the max selection range count
+ if (value.End.AddDays((MaxSelectionCount-1)*-1) > value.Start) {
+ selection_range = new SelectionRange (value.End.AddDays((MaxSelectionCount-1)*-1), value.End);
+ } else {
+ selection_range = value;
+ }
+ SelectionRange visible_range = this.GetDisplayRange(true);
+ if(visible_range.Start > selection_range.End) {
+ this.current_month = new DateTime (selection_range.Start.Year, selection_range.Start.Month, 1);
+ this.Invalidate ();
+ } else if (visible_range.End < selection_range.Start) {
+ int year_diff = selection_range.End.Year - visible_range.End.Year;
+ int month_diff = selection_range.End.Month - visible_range.End.Month;
+ this.current_month = current_month.AddMonths(year_diff * 12 + month_diff);
+ this.Invalidate ();
+ }
+ // invalidate the selected range changes
+ DateTime diff_start = old_range.Start;
+ DateTime diff_end = old_range.End;
+ // now decide which region is greated
+ if (old_range.Start > SelectionRange.Start) {
+ diff_start = SelectionRange.Start;
+ } else if (old_range.Start == SelectionRange.Start) {
+ if (old_range.End < SelectionRange.End) {
+ diff_start = old_range.End;
+ } else {
+ diff_start = SelectionRange.End;
+ }
+ }
+ if (old_range.End < SelectionRange.End) {
+ diff_end = SelectionRange.End;
+ } else if (old_range.End == SelectionRange.End) {
+ if (old_range.Start < SelectionRange.Start) {
+ diff_end = SelectionRange.Start;
+ } else {
+ diff_end = old_range.Start;
+ }
+ }
+
+
+ // invalidate the region required
+ SelectionRange new_range = new SelectionRange (diff_start, diff_end);
+ if (new_range.End != old_range.End || new_range.Start != old_range.Start)
+ this.InvalidateDateRange (new_range);
+ // raise date changed event
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ this.OnUIASelectionChanged ();
+ }
+ }
+ get {
+ return selection_range;
+ }
+ }
+
+ // the first selected date
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public DateTime SelectionStart {
+ set {
+ if (value < MinDate || value > MaxDate) {
+ throw new ArgumentException();
+ }
+
+ if (SelectionRange.Start != value) {
+ // make sure the end obeys the max selection range count
+ if (value > SelectionRange.End) {
+ SelectionRange.End = value;
+ } else if (value.AddDays(MaxSelectionCount-1) < SelectionRange.End) {
+ SelectionRange.End = value.AddDays(MaxSelectionCount-1);
+ }
+ SelectionRange.Start = value;
+ DateTime new_month = new DateTime(value.Year, value.Month, 1);
+ if (current_month != new_month)
+ current_month = new_month;
+
+ this.Invalidate ();
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ this.OnUIASelectionChanged ();
+ }
+ }
+ get {
+ return selection_range.Start;
+ }
+ }
+
+ // whether or not to show todays date
+ [DefaultValue (true)]
+ public bool ShowToday {
+ set {
+ if (show_today != value) {
+ show_today = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_today;
+ }
+ }
+
+ // whether or not to show a circle around todays date
+ [DefaultValue (true)]
+ public bool ShowTodayCircle {
+ set {
+ if (show_today_circle != value) {
+ show_today_circle = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_today_circle;
+ }
+ }
+
+ // whether or not to show numbers beside each row of weeks
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public bool ShowWeekNumbers {
+ set {
+ if (show_week_numbers != value) {
+ show_week_numbers = value;
+ // The values here don't matter, SetBoundsCore will calculate its own
+ SetBoundsCore (Left, Top, Width, Height, BoundsSpecified.Width);
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_week_numbers;
+ }
+ }
+
+ // the rectangle size required to render one month based on current font
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Size SingleMonthSize {
+ get {
+ if (this.Font == null) {
+ throw new InvalidOperationException();
+ }
+
+ // multiplier is sucked out from the font size
+ int multiplier = this.Font.Height;
+
+ // establis how many columns and rows we have
+ int column_count = (ShowWeekNumbers) ? 8 : 7;
+ int row_count = 7; // not including the today date
+
+ // set the date_cell_size and the title_size
+ date_cell_size = new Size ((int) Math.Ceiling (1.8 * multiplier), multiplier);
+ title_size = new Size ((date_cell_size.Width * column_count), 2 * multiplier);
+
+ return new Size (column_count * date_cell_size.Width, row_count * date_cell_size.Height + title_size.Height);
+ }
+ }
+
+ [Localizable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public new Size Size {
+ get {
+ return base.Size;
+ }
+ set {
+ base.Size = value;
+ }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+ set {
+ base.Text = value;
+ }
+ }
+
+ // the back color for the title of the calendar and the
+ // forecolor for the day of the week text
+ public Color TitleBackColor {
+ set {
+ if (title_back_color != value) {
+ title_back_color = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return title_back_color;
+ }
+ }
+
+ // the fore color for the title of the calendar
+ public Color TitleForeColor {
+ set {
+ if (title_fore_color != value) {
+ title_fore_color = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return title_fore_color;
+ }
+ }
+
+ // the date this calendar is using to refer to today's date
+ public DateTime TodayDate {
+ set {
+ today_date_set = true;
+ if (today_date != value) {
+ today_date = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return today_date;
+ }
+ }
+
+ // tells if user specifically set today_date for this control
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public bool TodayDateSet {
+ get {
+ return today_date_set;
+ }
+ }
+
+ // the color used for trailing dates in the calendar
+ public Color TrailingForeColor {
+ set {
+ if (trailing_fore_color != value) {
+ trailing_fore_color = value;
+ SelectionRange bounds = this.GetDisplayRange (false);
+ SelectionRange visible_bounds = this.GetDisplayRange (true);
+ this.InvalidateDateRange (new SelectionRange (bounds.Start, visible_bounds.Start));
+ this.InvalidateDateRange (new SelectionRange (bounds.End, visible_bounds.End));
+ }
+ }
+ get {
+ return trailing_fore_color;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+
+ // overloaded to allow controll to be windowed for drop down
+ protected override CreateParams CreateParams {
+ get {
+ if (this.owner == null) {
+ return base.CreateParams;
+ } else {
+ CreateParams cp = base.CreateParams;
+ cp.Style ^= (int) WindowStyles.WS_CHILD;
+ cp.Style |= (int) WindowStyles.WS_POPUP;
+ cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
+
+ return cp;
+ }
+ }
+ }
+
+ // not sure what to put in here - just doing a base() call - jba
+ protected override ImeMode DefaultImeMode {
+ get {
+ return base.DefaultImeMode;
+ }
+ }
+
+ protected override Padding DefaultMargin {
+ get {
+ return new Padding (9);
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ Size single_month = SingleMonthSize;
+ // get the width
+ int width = calendar_dimensions.Width * single_month.Width;
+ if (calendar_dimensions.Width > 1) {
+ width += (calendar_dimensions.Width - 1) * calendar_spacing.Width;
+ }
+
+ // get the height
+ int height = calendar_dimensions.Height * single_month.Height;
+ if (this.ShowToday) {
+ height += date_cell_size.Height + 2; // add the height of the "Today: " ...
+ }
+ if (calendar_dimensions.Height > 1) {
+ height += (calendar_dimensions.Height - 1) * calendar_spacing.Height;
+ }
+
+ // add the 1 pixel boundary
+ if (width > 0) {
+ width += 2;
+ }
+ if (height > 0) {
+ height +=2;
+ }
+
+ return new Size (width, height);
+ }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+
+ // add a date to the anually bolded date arraylist
+ public void AddAnnuallyBoldedDate (DateTime date) {
+ if (annually_bolded_dates == null)
+ annually_bolded_dates = new ArrayList ();
+ if (!annually_bolded_dates.Contains (date))
+ annually_bolded_dates.Add (date);
+ }
+
+ // add a date to the normal bolded date arraylist
+ public void AddBoldedDate (DateTime date) {
+ if (bolded_dates == null)
+ bolded_dates = new ArrayList ();
+ if (!bolded_dates.Contains (date))
+ bolded_dates.Add (date);
+ }
+
+ // add a date to the anually monthly date arraylist
+ public void AddMonthlyBoldedDate (DateTime date) {
+ if (monthly_bolded_dates == null)
+ monthly_bolded_dates = new ArrayList ();
+ if (!monthly_bolded_dates.Contains (date))
+ monthly_bolded_dates.Add (date);
+ }
+
+ // if visible = true, return only the dates of full months, else return all dates visible
+ public SelectionRange GetDisplayRange (bool visible) {
+ DateTime start;
+ DateTime end;
+ start = new DateTime (current_month.Year, current_month.Month, 1);
+ end = start.AddMonths (calendar_dimensions.Width * calendar_dimensions.Height);
+ end = end.AddDays(-1);
+
+ // process all visible dates if needed (including the grayed out dates
+ if (!visible) {
+ start = GetFirstDateInMonthGrid (start);
+ end = GetLastDateInMonthGrid (end);
+ }
+
+ return new SelectionRange (start, end);
+ }
+
+ // HitTest overload that recieve's x and y co-ordinates as separate ints
+ public HitTestInfo HitTest (int x, int y) {
+ return HitTest (new Point (x, y));
+ }
+
+ // returns a HitTestInfo for MonthCalendar element's under the specified point
+ public HitTestInfo HitTest (Point point) {
+ return HitTest (point, out last_clicked_calendar_index, out last_clicked_calendar_rect);
+ }
+
+ // clears all the annually bolded dates
+ public void RemoveAllAnnuallyBoldedDates () {
+ if (annually_bolded_dates != null)
+ annually_bolded_dates.Clear ();
+ }
+
+ // clears all the normal bolded dates
+ public void RemoveAllBoldedDates () {
+ if (bolded_dates != null)
+ bolded_dates.Clear ();
+ }
+
+ // clears all the monthly bolded dates
+ public void RemoveAllMonthlyBoldedDates () {
+ if (monthly_bolded_dates != null)
+ monthly_bolded_dates.Clear ();
+ }
+
+ // clears the specified annually bolded date (only compares day and month)
+ // only removes the first instance of the match
+ public void RemoveAnnuallyBoldedDate (DateTime date) {
+ if (annually_bolded_dates == null)
+ return;
+
+ for (int i = 0; i < annually_bolded_dates.Count; i++) {
+ DateTime dt = (DateTime) annually_bolded_dates [i];
+ if (dt.Day == date.Day && dt.Month == date.Month) {
+ annually_bolded_dates.RemoveAt (i);
+ return;
+ }
+ }
+ }
+
+ // clears all the normal bolded date
+ // only removes the first instance of the match
+ public void RemoveBoldedDate (DateTime date) {
+ if (bolded_dates == null)
+ return;
+
+ for (int i = 0; i < bolded_dates.Count; i++) {
+ DateTime dt = (DateTime) bolded_dates [i];
+ if (dt.Year == date.Year && dt.Month == date.Month && dt.Day == date.Day) {
+ bolded_dates.RemoveAt (i);
+ return;
+ }
+ }
+ }
+
+ // clears the specified monthly bolded date (only compares day and month)
+ // only removes the first instance of the match
+ public void RemoveMonthlyBoldedDate (DateTime date) {
+ if (monthly_bolded_dates == null)
+ return;
+
+ for (int i = 0; i < monthly_bolded_dates.Count; i++) {
+ DateTime dt = (DateTime) monthly_bolded_dates [i];
+ if (dt.Day == date.Day && dt.Month == date.Month) {
+ monthly_bolded_dates.RemoveAt (i);
+ return;
+ }
+ }
+ }
+
+ // sets the calendar_dimensions. If product is > 12, the larger dimension is reduced to make product < 12
+ public void SetCalendarDimensions(int x, int y) {
+ this.CalendarDimensions = new Size(x, y);
+ }
+
+ // sets the currently selected date as date
+ public void SetDate (DateTime date) {
+ this.SetSelectionRange (date.Date, date.Date);
+ }
+
+ // utility method set the SelectionRange property using individual dates
+ public void SetSelectionRange (DateTime date1, DateTime date2) {
+ this.SelectionRange = new SelectionRange(date1, date2);
+ }
+
+ public override string ToString () {
+ return this.GetType().Name + ", " + this.SelectionRange.ToString ();
+ }
+
+ // usually called after an AddBoldedDate method is called
+ // formats monthly and daily bolded dates according to the current calendar year
+ public void UpdateBoldedDates () {
+ Invalidate ();
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+
+ // not sure why this needs to be overriden
+ protected override void CreateHandle () {
+ base.CreateHandle ();
+ }
+
+ // not sure why this needs to be overriden
+ protected override void Dispose (bool disposing) {
+ base.Dispose (disposing);
+ }
+
+ // Handle arrow keys
+ protected override bool IsInputKey (Keys keyData) {
+ switch (keyData) {
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.Right:
+ case Keys.Left:
+ return true;
+ default:
+ break;
+ }
+
+ return base.IsInputKey (keyData);
+ }
+
+ // not sure why this needs to be overriden
+ protected override void OnBackColorChanged (EventArgs e) {
+ base.OnBackColorChanged (e);
+ this.Invalidate ();
+ }
+
+ // raises the date changed event
+ protected virtual void OnDateChanged (DateRangeEventArgs drevent) {
+ DateRangeEventHandler eh = (DateRangeEventHandler) (Events [DateChangedEvent]);
+ if (eh != null)
+ eh (this, drevent);
+ }
+
+ // raises the DateSelected event
+ protected virtual void OnDateSelected (DateRangeEventArgs drevent) {
+ DateRangeEventHandler eh = (DateRangeEventHandler) (Events [DateSelectedEvent]);
+ if (eh != null)
+ eh (this, drevent);
+ }
+
+ protected override void OnFontChanged (EventArgs e) {
+ // Update size based on new font's space requirements
+ Size = new Size (CalendarDimensions.Width * SingleMonthSize.Width,
+ CalendarDimensions.Height * SingleMonthSize.Height);
+ bold_font = new Font (Font, Font.Style | FontStyle.Bold);
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnForeColorChanged (EventArgs e) {
+ base.OnForeColorChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e) {
+ EventHandler eh = (EventHandler) (Events [RightToLeftLayoutChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ // i think this is overriden to not allow the control to be changed to an arbitrary size
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ // only allow sizes = default size to be set
+ Size default_size = DefaultSize;
+ Size min_size = default_size;
+ Size max_size = new Size (default_size.Width + SingleMonthSize.Width + calendar_spacing.Width,
+ default_size.Height + SingleMonthSize.Height + calendar_spacing.Height);
+ int x_mid_point = (max_size.Width + min_size.Width)/2;
+ int y_mid_point = (max_size.Height + min_size.Height)/2;
+
+ if (width < x_mid_point) {
+ width = min_size.Width;
+ } else {
+ width = max_size.Width;
+ }
+ if (height < y_mid_point) {
+ height = min_size.Height;
+ } else {
+ height = max_size.Height;
+ }
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+
+ protected override void WndProc (ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region public events
+ static object DateChangedEvent = new object ();
+ static object DateSelectedEvent = new object ();
+ static object RightToLeftLayoutChangedEvent = new object ();
+
+ // fired when the date is changed (either explicitely or implicitely)
+ // when navigating the month selector
+ public event DateRangeEventHandler DateChanged {
+ add { Events.AddHandler (DateChangedEvent, value); }
+ remove { Events.RemoveHandler (DateChangedEvent, value); }
+ }
+
+ // fired when the user explicitely clicks on date to select it
+ public event DateRangeEventHandler DateSelected {
+ add { Events.AddHandler (DateSelectedEvent, value); }
+ remove { Events.RemoveHandler (DateSelectedEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged
+ {
+ add { base.BackgroundImageLayoutChanged += value;}
+ remove { base.BackgroundImageLayoutChanged += value;}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Click {
+ add {base.Click += value; }
+ remove {base.Click -= value;}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add {base.DoubleClick += value; }
+ remove {base.DoubleClick -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value;}
+ remove { base.MouseClick -= value;}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add {base.PaddingChanged += value;}
+ remove {base.PaddingChanged -= value;}
+ }
+
+ // XXX check this out
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint;
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add {Events.AddHandler (RightToLeftLayoutChangedEvent, value);}
+ remove {Events.RemoveHandler (RightToLeftLayoutChangedEvent, value);}
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion // public events
+
+ #region internal properties
+
+ private void AddYears (int years, bool fast)
+ {
+ DateTime newDate;
+ if (fast) {
+ if (!(CurrentMonth.Year + years * 5 > MaxDate.Year)) {
+ newDate = CurrentMonth.AddYears (years * 5);
+ if (MaxDate >= newDate && MinDate <= newDate) {
+ CurrentMonth = newDate;
+ return;
+ }
+ }
+ }
+ if (!(CurrentMonth.Year + years > MaxDate.Year)) {
+ newDate = CurrentMonth.AddYears (years);
+ if (MaxDate >= newDate && MinDate <= newDate) {
+ CurrentMonth = newDate;
+ }
+ }
+ }
+
+ internal bool IsYearGoingUp {
+ get {
+ return is_year_going_up;
+ }
+ set {
+ if (value) {
+ is_year_going_down = false;
+ year_moving_count = (is_year_going_up ? year_moving_count + 1 : 1);
+ if (is_year_going_up)
+ year_moving_count++;
+ else {
+ year_moving_count = 1;
+ }
+ AddYears (1, year_moving_count > 10);
+ if (is_mouse_moving_year)
+ StartHideTimer ();
+ } else {
+ year_moving_count = 0;
+ }
+ is_year_going_up = value;
+ Invalidate ();
+ }
+ }
+
+ internal bool IsYearGoingDown {
+ get {
+ return is_year_going_down;
+ }
+ set
+ {
+ if (value) {
+ is_year_going_up = false;
+ year_moving_count = (is_year_going_down ? year_moving_count + 1 : 1);
+ if (is_year_going_down)
+ year_moving_count++;
+ else {
+ year_moving_count = 1;
+ }
+ AddYears (-1, year_moving_count > 10);
+ if (is_mouse_moving_year)
+ StartHideTimer ();
+ } else {
+ year_moving_count = 0;
+ }
+ is_year_going_down = value;
+ Invalidate ();
+ }
+ }
+
+ internal bool ShowYearUpDown {
+ get {
+ return show_year_updown;
+ }
+ set {
+ if (show_year_updown != value) {
+ show_year_updown = value;
+ Invalidate ();
+ }
+ }
+ }
+
+ internal DateTime CurrentMonth {
+ set {
+ // only interested in if the month (not actual date) has change
+ if (value < MinDate || value > MaxDate) {
+ return;
+ }
+
+ if (value.Month != current_month.Month ||
+ value.Year != current_month.Year) {
+ this.SelectionRange = new SelectionRange(
+ this.SelectionStart.Add(value.Subtract(current_month)),
+ this.SelectionEnd.Add(value.Subtract(current_month)));
+ current_month = value;
+ UpdateBoldedDates();
+ this.Invalidate();
+ }
+ }
+ get {
+ return current_month;
+ }
+ }
+
+ #endregion // internal properties
+
+ #region internal/private methods
+ internal HitTestInfo HitTest (
+ Point point,
+ out int calendar_index,
+ out Rectangle calendar_rect) {
+ // start by initialising the ref parameters
+ calendar_index = -1;
+ calendar_rect = Rectangle.Empty;
+
+ // before doing all the hard work, see if the today's date wasn't clicked
+ Rectangle today_rect = new Rectangle (
+ ClientRectangle.X,
+ ClientRectangle.Bottom - date_cell_size.Height,
+ 7 * date_cell_size.Width,
+ date_cell_size.Height);
+ if (today_rect.Contains (point) && this.ShowToday) {
+ return new HitTestInfo(HitArea.TodayLink, point, DateTime.Now);
+ }
+
+ Size month_size = SingleMonthSize;
+ // define calendar rect's that this thing can land in
+ Rectangle[] calendars = new Rectangle [CalendarDimensions.Width * CalendarDimensions.Height];
+ for (int i=0; i < CalendarDimensions.Width * CalendarDimensions.Height; i ++) {
+ if (i == 0) {
+ calendars[i] = new Rectangle (
+ new Point (ClientRectangle.X + 1, ClientRectangle.Y + 1),
+ month_size);
+ } else {
+ // calendar on the next row
+ if (i % CalendarDimensions.Width == 0) {
+ calendars[i] = new Rectangle (
+ new Point (calendars[i-CalendarDimensions.Width].X, calendars[i-CalendarDimensions.Width].Bottom + calendar_spacing.Height),
+ month_size);
+ } else {
+ // calendar on the next column
+ calendars[i] = new Rectangle (
+ new Point (calendars[i-1].Right + calendar_spacing.Width, calendars[i-1].Y),
+ month_size);
+ }
+ }
+ }
+
+ // through each trying to find a match
+ for (int i = 0; i < calendars.Length ; i++) {
+ if (calendars[i].Contains (point)) {
+ // check the title section
+ Rectangle title_rect = new Rectangle (
+ calendars[i].Location,
+ title_size);
+ if (title_rect.Contains (point) ) {
+ // make sure it's not a previous button
+ if (i == 0) {
+ Rectangle button_rect = new Rectangle(
+ new Point (calendars[i].X + button_x_offset, (title_size.Height - button_size.Height)/2),
+ button_size);
+ if (button_rect.Contains (point)) {
+ return new HitTestInfo (HitArea.PrevMonthButton, point, new DateTime (1, 1, 1));
+ }
+ }
+ // make sure it's not the next button
+ if (i % CalendarDimensions.Height == 0 && i % CalendarDimensions.Width == calendar_dimensions.Width - 1) {
+ Rectangle button_rect = new Rectangle(
+ new Point (calendars[i].Right - button_x_offset - button_size.Width, (title_size.Height - button_size.Height)/2),
+ button_size);
+ if (button_rect.Contains (point)) {
+ return new HitTestInfo (HitArea.NextMonthButton, point, new DateTime (1, 1, 1));
+ }
+ }
+
+ // indicate which calendar and month it was
+ calendar_index = i;
+ calendar_rect = calendars[i];
+
+ // make sure it's not the month or the year of the calendar
+ if (GetMonthNameRectangle (title_rect, i).Contains (point)) {
+ return new HitTestInfo (HitArea.TitleMonth, point, new DateTime (1, 1, 1));
+ }
+ Rectangle year, up, down;
+ GetYearNameRectangles (title_rect, i, out year, out up, out down);
+ if (year.Contains (point)) {
+ return new HitTestInfo (HitArea.TitleYear, point, new DateTime (1, 1, 1), HitAreaExtra.YearRectangle);
+ } else if (up.Contains (point)) {
+ return new HitTestInfo (HitArea.TitleYear, point, new DateTime (1, 1, 1), HitAreaExtra.UpButton);
+ } else if (down.Contains (point)) {
+ return new HitTestInfo (HitArea.TitleYear, point, new DateTime (1, 1, 1), HitAreaExtra.DownButton);
+ }
+
+ // return the hit test in the title background
+ return new HitTestInfo (HitArea.TitleBackground, point, new DateTime (1, 1, 1));
+ }
+
+ Point date_grid_location = new Point (calendars[i].X, title_rect.Bottom);
+
+ // see if it's in the Week numbers
+ if (ShowWeekNumbers) {
+ Rectangle weeks_rect = new Rectangle (
+ date_grid_location,
+ new Size (date_cell_size.Width,Math.Max (calendars[i].Height - title_rect.Height, 0)));
+ if (weeks_rect.Contains (point)) {
+ return new HitTestInfo(HitArea.WeekNumbers, point, DateTime.Now);
+ }
+
+ // move the location of the grid over
+ date_grid_location.X += date_cell_size.Width;
+ }
+
+ // see if it's in the week names
+ Rectangle day_rect = new Rectangle (
+ date_grid_location,
+ new Size (Math.Max (calendars[i].Right - date_grid_location.X, 0), date_cell_size.Height));
+ if (day_rect.Contains (point)) {
+ return new HitTestInfo (HitArea.DayOfWeek, point, new DateTime (1, 1, 1));
+ }
+
+ // finally see if it was a date that was clicked
+ Rectangle date_grid = new Rectangle (
+ new Point (day_rect.X, day_rect.Bottom),
+ new Size (day_rect.Width, Math.Max(calendars[i].Bottom - day_rect.Bottom, 0)));
+ if (date_grid.Contains (point)) {
+ clicked_rect = date_grid;
+ // okay so it's inside the grid, get the offset
+ Point offset = new Point (point.X - date_grid.X, point.Y - date_grid.Y);
+ int row = offset.Y / date_cell_size.Height;
+ int col = offset.X / date_cell_size.Width;
+ // establish our first day of the month
+ DateTime calendar_month = this.CurrentMonth.AddMonths(i);
+ DateTime first_day = GetFirstDateInMonthGrid (calendar_month);
+ DateTime time = first_day.AddDays ((row * 7) + col);
+ // establish which date was clicked
+ if (time.Year != calendar_month.Year || time.Month != calendar_month.Month) {
+ if (time < calendar_month && i == 0) {
+ return new HitTestInfo (HitArea.PrevMonthDate, point, new DateTime (1, 1, 1), time);
+ } else if (time > calendar_month && i == CalendarDimensions.Width*CalendarDimensions.Height - 1) {
+ return new HitTestInfo (HitArea.NextMonthDate, point, new DateTime (1, 1, 1), time);
+ }
+ return new HitTestInfo (HitArea.Nowhere, point, new DateTime (1, 1, 1));
+ }
+ return new HitTestInfo(HitArea.Date, point, time);
+ }
+ }
+ }
+
+ return new HitTestInfo ();
+ }
+
+ // returns the date of the first cell of the specified month grid
+ internal DateTime GetFirstDateInMonthGrid (DateTime month) {
+ // convert the first_day_of_week into a DayOfWeekEnum
+ DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
+ // find the first day of the month
+ DateTime first_date_of_month = new DateTime (month.Year, month.Month, 1);
+ DayOfWeek first_day_of_month = first_date_of_month.DayOfWeek;
+ // adjust for the starting day of the week
+ int offset = first_day_of_month - first_day;
+ if (offset < 0) {
+ offset += 7;
+ }
+ return first_date_of_month.AddDays (-1*offset);
+ }
+
+ // returns the date of the last cell of the specified month grid
+ internal DateTime GetLastDateInMonthGrid (DateTime month)
+ {
+ DateTime start = GetFirstDateInMonthGrid(month);
+ return start.AddDays ((7 * 6)-1);
+ }
+
+ internal bool IsBoldedDate (DateTime date) {
+ // check bolded dates
+ if (bolded_dates != null && bolded_dates.Count > 0) {
+ foreach (DateTime bolded_date in bolded_dates) {
+ if (bolded_date.Date == date.Date) {
+ return true;
+ }
+ }
+ }
+ // check monthly dates
+ if (monthly_bolded_dates != null && monthly_bolded_dates.Count > 0) {
+ foreach (DateTime bolded_date in monthly_bolded_dates) {
+ if (bolded_date.Day == date.Day) {
+ return true;
+ }
+ }
+ }
+ // check yearly dates
+ if (annually_bolded_dates != null && annually_bolded_dates.Count > 0) {
+ foreach (DateTime bolded_date in annually_bolded_dates) {
+ if (bolded_date.Month == date.Month && bolded_date.Day == date.Day) {
+ return true;
+ }
+ }
+ }
+
+ return false; // no match
+ }
+
+ // initialise the 'go to today' context menu
+ private void SetUpTodayMenu () {
+ }
+
+ // initialise the month context menu
+ private void SetUpMonthMenu () {
+
+ }
+
+ // returns the first date of the month
+ private DateTime GetFirstDateInMonth (DateTime date) {
+ return new DateTime (date.Year, date.Month, 1);
+ }
+
+ // returns the last date of the month
+ private DateTime GetLastDateInMonth (DateTime date) {
+ return new DateTime (date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
+ }
+
+ // called in response to users seletion with shift key
+ private void AddTimeToSelection (int delta, bool isDays)
+ {
+ DateTime cursor_point;
+ DateTime end_point;
+ // okay we add the period to the date that is not the same as the
+ // start date when shift was first clicked.
+ if (SelectionStart != first_select_start_date) {
+ cursor_point = SelectionStart;
+ } else {
+ cursor_point = SelectionEnd;
+ }
+ // add the days
+ if (isDays) {
+ end_point = cursor_point.AddDays (delta);
+ } else {
+ // delta must be months
+ end_point = cursor_point.AddMonths (delta);
+ }
+ // set the new selection range
+ SelectionRange range = new SelectionRange (first_select_start_date, end_point);
+ if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
+ // okay the date is beyond what is allowed, lets set the maximum we can
+ if (range.Start != first_select_start_date) {
+ range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
+ } else {
+ range.End = range.Start.AddDays (MaxSelectionCount-1);
+ }
+ }
+
+ // Avoid re-setting SelectionRange to the same value and fire an extra DateChanged event
+ if (range.Start != selection_range.Start || range.End != selection_range.End)
+ SelectionRange = range;
+ }
+
+ // attempts to add the date to the selection without throwing exception
+ private void SelectDate (DateTime date) {
+ // try and add the new date to the selction range
+ SelectionRange range = null;
+ if (is_shift_pressed || (click_state [0])) {
+ range = new SelectionRange (first_select_start_date, date);
+ if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
+ // okay the date is beyond what is allowed, lets set the maximum we can
+ if (range.Start != first_select_start_date) {
+ range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
+ } else {
+ range.End = range.Start.AddDays (MaxSelectionCount-1);
+ }
+ }
+ } else {
+ if (date >= MinDate && date <= MaxDate) {
+ range = new SelectionRange (date, date);
+ first_select_start_date = date;
+ }
+ }
+
+ // Only set if we re actually getting a different range (avoid an extra DateChanged event)
+ if (range != null && range.Start != selection_range.Start || range.End != selection_range.End)
+ SelectionRange = range;
+ }
+
+ // gets the week of the year
+ internal int GetWeekOfYear (DateTime date) {
+ // convert the first_day_of_week into a DayOfWeekEnum
+ DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
+ // find the first day of the year
+ DayOfWeek first_day_of_year = new DateTime (date.Year, 1, 1).DayOfWeek;
+ // adjust for the starting day of the week
+ int offset = first_day_of_year - first_day;
+ int week = ((date.DayOfYear + offset) / 7) + 1;
+ return week;
+ }
+
+ // convert a Day enum into a DayOfWeek enum
+ internal DayOfWeek GetDayOfWeek (Day day) {
+ if (day == Day.Default) {
+ return Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
+ } else {
+ return (DayOfWeek) DayOfWeek.Parse (typeof (DayOfWeek), day.ToString ());
+ }
+ }
+
+ // returns the rectangle for themonth name
+ internal Rectangle GetMonthNameRectangle (Rectangle title_rect, int calendar_index) {
+ DateTime this_month = this.current_month.AddMonths (calendar_index);
+ Size title_text_size = TextRenderer.MeasureString (this_month.ToString ("MMMM yyyy"), this.Font).ToSize ();
+ Size month_size = TextRenderer.MeasureString (this_month.ToString ("MMMM"), this.Font).ToSize ();
+ // return only the month name part of that
+ return new Rectangle (
+ new Point (
+ title_rect.X + ((title_rect.Width - title_text_size.Width)/2),
+ title_rect.Y + ((title_rect.Height - title_text_size.Height)/2)),
+ month_size);
+ }
+
+ internal void GetYearNameRectangles (Rectangle title_rect, int calendar_index, out Rectangle year_rect, out Rectangle up_rect, out Rectangle down_rect)
+ {
+ DateTime this_month = this.current_month.AddMonths (calendar_index);
+ SizeF title_text_size = TextRenderer.MeasureString (this_month.ToString ("MMMM yyyy"), this.bold_font, int.MaxValue, centered_format);
+ SizeF year_size = TextRenderer.MeasureString (this_month.ToString ("yyyy"), this.bold_font, int.MaxValue, centered_format);
+ // find out how much space the title took
+ RectangleF text_rect = new RectangleF (
+ new PointF (
+ title_rect.X + ((title_rect.Width - title_text_size.Width) / 2),
+ title_rect.Y + ((title_rect.Height - title_text_size.Height) / 2)),
+ title_text_size);
+ // return only the rect of the year
+ year_rect = new Rectangle (
+ new Point (
+ ((int)(text_rect.Right - year_size.Width + 1)),
+ (int)text_rect.Y),
+ new Size ((int)(year_size.Width + 1), (int)(year_size.Height + 1)));
+
+ year_rect.Inflate (0, 1);
+ up_rect = new Rectangle ();
+ up_rect.Location = new Point (year_rect.X + year_rect.Width + 2, year_rect.Y);
+ up_rect.Size = new Size (16, year_rect.Height / 2);
+ down_rect = new Rectangle ();
+ down_rect.Location = new Point (up_rect.X, up_rect.Y + up_rect.Height + 1);
+ down_rect.Size = up_rect.Size;
+ }
+
+ // returns the rectangle for the year in the title
+ internal Rectangle GetYearNameRectangle (Rectangle title_rect, int calendar_index) {
+ Rectangle result, discard;
+ GetYearNameRectangles (title_rect, calendar_index, out result, out discard, out discard);
+ return result;
+ }
+
+ // determine if date is allowed to be drawn in month
+ internal bool IsValidWeekToDraw (DateTime month, DateTime date, int row, int col) {
+ DateTime tocheck = month.AddMonths (-1);
+ if ((month.Year == date.Year && month.Month == date.Month) ||
+ (tocheck.Year == date.Year && tocheck.Month == date.Month)) {
+ return true;
+ }
+
+ // check the railing dates (days in the month after the last month in grid)
+ if (row == CalendarDimensions.Height - 1 && col == CalendarDimensions.Width - 1) {
+ tocheck = month.AddMonths (1);
+ return (tocheck.Year == date.Year && tocheck.Month == date.Month) ;
+ }
+
+ return false;
+ }
+
+ // set one item clicked and all others off
+ private void SetItemClick(HitTestInfo hti)
+ {
+ switch(hti.HitArea) {
+ case HitArea.NextMonthButton:
+ this.is_previous_clicked = false;
+ this.is_next_clicked = true;
+ this.is_date_clicked = false;
+ break;
+ case HitArea.PrevMonthButton:
+ this.is_previous_clicked = true;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ case HitArea.PrevMonthDate:
+ case HitArea.NextMonthDate:
+ case HitArea.Date:
+ this.clicked_date = hti.hit_time;
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = true;
+ break;
+ default :
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ }
+ }
+
+ // called when today context menu is clicked
+ private void TodayMenuItemClickHandler (object sender, EventArgs e)
+ {
+ this.SetSelectionRange (DateTime.Now.Date, DateTime.Now.Date);
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ }
+
+ // called when month context menu is clicked
+ private void MonthMenuItemClickHandler (object sender, EventArgs e) {
+ MenuItem item = sender as MenuItem;
+ if (item != null && month_title_click_location != Point.Empty) {
+ // establish which month we want to move to
+ if (item.Parent == null) {
+ return;
+ }
+ int new_month = item.Parent.MenuItems.IndexOf (item) + 1;
+ if (new_month == 0) {
+ return;
+ }
+ // okay let's establish which calendar was hit
+ Size month_size = this.SingleMonthSize;
+ for (int i=0; i < CalendarDimensions.Height; i++) {
+ for (int j=0; j < CalendarDimensions.Width; j++) {
+ int month_index = (i * CalendarDimensions.Width) + j;
+ Rectangle month_rect = new Rectangle ( new Point (0, 0), month_size);
+ if (j == 0) {
+ month_rect.X = this.ClientRectangle.X + 1;
+ } else {
+ month_rect.X = this.ClientRectangle.X + 1 + ((j)*(month_size.Width+calendar_spacing.Width));
+ }
+ if (i == 0) {
+ month_rect.Y = this.ClientRectangle.Y + 1;
+ } else {
+ month_rect.Y = this.ClientRectangle.Y + 1 + ((i)*(month_size.Height+calendar_spacing.Height));
+ }
+ // see if the point is inside
+ if (month_rect.Contains (month_title_click_location)) {
+ DateTime clicked_month = CurrentMonth.AddMonths (month_index);
+ // get the month that we want to move to
+ int month_offset = new_month - clicked_month.Month;
+
+ // move forward however more months we need to
+ this.CurrentMonth = this.CurrentMonth.AddMonths (month_offset);
+ break;
+ }
+ }
+ }
+
+ // clear the point
+ month_title_click_location = Point.Empty;
+ }
+ }
+
+ // raised on the timer, for mouse hold clicks
+ private void TimerHandler (object sender, EventArgs e) {
+ // now find out which area was click
+ if (this.Capture) {
+ HitTestInfo hti = this.HitTest (this.PointToClient (MousePosition));
+ // see if it was clicked on the prev or next mouse
+ if (click_state [1] || click_state [2]) {
+ // invalidate the area where the mouse was last held
+ DoMouseUp ();
+ // register the click
+ if (hti.HitArea == HitArea.PrevMonthButton ||
+ hti.HitArea == HitArea.NextMonthButton) {
+ DoButtonMouseDown (hti);
+ click_state [1] = (hti.HitArea == HitArea.PrevMonthButton);
+ click_state [2] = !click_state [1];
+ }
+ if (timer.Interval != 300) {
+ timer.Interval = 300;
+ }
+ }
+ } else {
+ timer.Enabled = false;
+ }
+ }
+
+ // selects one of the buttons
+ private void DoButtonMouseDown (HitTestInfo hti) {
+ // show the click then move on
+ SetItemClick(hti);
+ if (hti.HitArea == HitArea.PrevMonthButton) {
+ // invalidate the prev monthbutton
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.X + 1 + button_x_offset,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ int scroll = (scroll_change == 0 ? CalendarDimensions.Width * CalendarDimensions.Height : scroll_change);
+ this.CurrentMonth = this.CurrentMonth.AddMonths (-scroll);
+ } else {
+ // invalidate the next monthbutton
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ int scroll = (scroll_change == 0 ? CalendarDimensions.Width * CalendarDimensions.Height : scroll_change);
+ this.CurrentMonth = this.CurrentMonth.AddMonths (scroll);
+ }
+ }
+
+ // selects the clicked date
+ private void DoDateMouseDown (HitTestInfo hti) {
+ SetItemClick(hti);
+ }
+
+ // event run on the mouse up event
+ private void DoMouseUp () {
+
+ IsYearGoingDown = false;
+ IsYearGoingUp = false;
+ is_mouse_moving_year = false;
+
+ // invalidate the next monthbutton
+ if (this.is_next_clicked) {
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ }
+ // invalidate the prev monthbutton
+ if (this.is_previous_clicked) {
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.X + 1 + button_x_offset,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ }
+ if (this.is_date_clicked) {
+ // invalidate the area under the cursor, to remove focus rect
+ this.InvalidateDateRange (new SelectionRange (clicked_date, clicked_date));
+ }
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ }
+
+ // needed when in windowed mode to close the calendar if no
+ // part of it has focus.
+ private void UpDownTimerTick(object sender, EventArgs e)
+ {
+ if (IsYearGoingUp) {
+ IsYearGoingUp = true;
+ }
+ if (IsYearGoingDown) {
+ IsYearGoingDown = true;
+ }
+
+ if (!IsYearGoingDown && !IsYearGoingUp) {
+ updown_timer.Enabled = false;
+ } else if (IsYearGoingDown || IsYearGoingUp) {
+ updown_timer.Interval = subsequent_delay;
+ }
+ }
+
+ // Needed when in windowed mode.
+ private void StartHideTimer ()
+ {
+ if (updown_timer == null) {
+ updown_timer = new Timer ();
+ updown_timer.Tick += new EventHandler (UpDownTimerTick);
+ }
+ updown_timer.Interval = initial_delay;
+ updown_timer.Enabled = true;
+ }
+
+ // occurs when mouse moves around control, used for selection
+ private void MouseMoveHandler (object sender, MouseEventArgs e) {
+ HitTestInfo hti = this.HitTest (e.X, e.Y);
+ // clear the last clicked item
+ if (click_state [0]) {
+ // register the click
+ if (hti.HitArea == HitArea.PrevMonthDate ||
+ hti.HitArea == HitArea.NextMonthDate ||
+ hti.HitArea == HitArea.Date)
+ {
+ Rectangle prev_rect = clicked_rect;
+ DateTime prev_clicked = clicked_date;
+ DoDateMouseDown (hti);
+ if (owner == null) {
+ click_state [0] = true;
+ } else {
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ }
+
+ if (prev_clicked != clicked_date) {
+ // select date after updating click_state and clicked_date
+ SelectDate (clicked_date);
+ date_selected_event_pending = true;
+
+ Rectangle invalid = Rectangle.Union (prev_rect, clicked_rect);
+ Invalidate (invalid);
+ }
+ }
+
+ }
+ }
+
+ // to check if the mouse has come down on this control
+ private void MouseDownHandler (object sender, MouseEventArgs e)
+ {
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ // clear the click_state variables
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+
+ // disable the timer if it was enabled
+ if (timer.Enabled) {
+ timer.Stop ();
+ timer.Enabled = false;
+ }
+
+ Point point = new Point (e.X, e.Y);
+ // figure out if we are in drop down mode and a click happened outside us
+ if (this.owner != null) {
+ if (!this.ClientRectangle.Contains (point)) {
+ this.owner.HideMonthCalendar ();
+ return;
+ }
+ }
+
+ //establish where was hit
+ HitTestInfo hti = this.HitTest(point);
+ // hide the year numeric up down if it was clicked
+ if (ShowYearUpDown && hti.HitArea != HitArea.TitleYear) {
+ ShowYearUpDown = false;
+ }
+ switch (hti.HitArea) {
+ case HitArea.PrevMonthButton:
+ case HitArea.NextMonthButton:
+ DoButtonMouseDown (hti);
+ click_state [1] = (hti.HitArea == HitArea.PrevMonthDate);
+ click_state [2] = !click_state [1];
+ timer.Interval = 750;
+ timer.Start ();
+ break;
+ case HitArea.Date:
+ case HitArea.PrevMonthDate:
+ case HitArea.NextMonthDate:
+ DoDateMouseDown (hti);
+
+ // select date before updating click_state
+ SelectDate (clicked_date);
+ date_selected_event_pending = true;
+
+ // leave clicked state blank if drop down window
+ if (owner == null) {
+ click_state [0] = true;
+ } else {
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ }
+
+ break;
+ case HitArea.TitleMonth:
+ month_title_click_location = hti.Point;
+ if (this.Capture && owner != null) {
+ Capture = false;
+ Capture = true;
+ }
+ break;
+ case HitArea.TitleYear:
+ // place the numeric up down
+ if (ShowYearUpDown) {
+ if (hti.hit_area_extra == HitAreaExtra.UpButton) {
+ is_mouse_moving_year = true;
+ IsYearGoingUp = true;
+ } else if (hti.hit_area_extra == HitAreaExtra.DownButton) {
+ is_mouse_moving_year = true;
+ IsYearGoingDown = true;
+ }
+ return;
+ } else {
+ ShowYearUpDown = true;
+ }
+ break;
+ case HitArea.TodayLink:
+ this.SetSelectionRange (DateTime.Now.Date, DateTime.Now.Date);
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ default:
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ }
+ }
+
+ // raised by any key down events
+ private void KeyDownHandler (object sender, KeyEventArgs e) {
+ // send keys to the year_updown control, let it handle it
+ if(ShowYearUpDown) {
+ switch (e.KeyCode) {
+ case Keys.Enter:
+ ShowYearUpDown = false;
+ IsYearGoingDown = false;
+ IsYearGoingUp = false;
+ break;
+ case Keys.Up: {
+ IsYearGoingUp = true;
+ break;
+ }
+ case Keys.Down: {
+ IsYearGoingDown = true;
+ break;
+ }
+ }
+ } else {
+ if (!is_shift_pressed && e.Shift) {
+ first_select_start_date = SelectionStart;
+ is_shift_pressed = e.Shift;
+ e.Handled = true;
+ }
+ switch (e.KeyCode) {
+ case Keys.Home:
+ // set the date to the start of the month
+ if (is_shift_pressed) {
+ DateTime date = GetFirstDateInMonth (first_select_start_date);
+ if (date < first_select_start_date.AddDays ((MaxSelectionCount-1)*-1)) {
+ date = first_select_start_date.AddDays ((MaxSelectionCount-1)*-1);
+ }
+ this.SetSelectionRange (date, first_select_start_date);
+ } else {
+ DateTime date = GetFirstDateInMonth (this.SelectionStart);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.End:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ DateTime date = GetLastDateInMonth (first_select_start_date);
+ if (date > first_select_start_date.AddDays (MaxSelectionCount-1)) {
+ date = first_select_start_date.AddDays (MaxSelectionCount-1);
+ }
+ this.SetSelectionRange (date, first_select_start_date);
+ } else {
+ DateTime date = GetLastDateInMonth (this.SelectionStart);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.PageUp:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-1, false);
+ } else {
+ DateTime date = this.SelectionStart.AddMonths (-1);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.PageDown:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (1, false);
+ } else {
+ DateTime date = this.SelectionStart.AddMonths (1);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.Up:
+ // set the back 1 week
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-7, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (-7);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.Down:
+ // set the date forward 1 week
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (7, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (7);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.Left:
+ // move one left
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-1, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (-1);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.Right:
+ // move one left
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (1, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (1);
+ this.SetSelectionRange (date, date);
+ }
+ e.Handled = true;
+ break;
+ case Keys.F4:
+ // Close ourselves on Alt-F4 if we are a popup
+ if (e.Alt && owner != null) {
+ this.Hide ();
+ e.Handled = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // to check if the mouse has come up on this control
+ private void MouseUpHandler (object sender, MouseEventArgs e)
+ {
+ if ((e.Button & MouseButtons.Left) == 0) {
+ return;
+ }
+
+ if (timer.Enabled) {
+ timer.Stop ();
+ }
+ // clear the click state array
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ // do the regulare mouseup stuff
+ this.DoMouseUp ();
+
+ if (date_selected_event_pending) {
+ OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ date_selected_event_pending = false;
+ }
+ }
+
+ // raised by any key up events
+ private void KeyUpHandler (object sender, KeyEventArgs e) {
+ is_shift_pressed = e.Shift ;
+ e.Handled = true;
+ IsYearGoingUp = false;
+ IsYearGoingDown = false;
+ }
+
+ // paint this control now
+ private void PaintHandler (object sender, PaintEventArgs pe) {
+ if (Width <= 0 || Height <= 0 || Visible == false)
+ return;
+
+ Draw (pe.ClipRectangle, pe.Graphics);
+
+ // fire the new paint handler
+ if (this.Paint != null)
+ {
+ this.Paint (sender, pe);
+ }
+ }
+
+ // returns the region of the control that needs to be redrawn
+ private void InvalidateDateRange (SelectionRange range) {
+ SelectionRange bounds = this.GetDisplayRange (false);
+
+ if (range.End < bounds.Start || range.Start > bounds.End) {
+ // don't invalidate anything, as the modified date range
+ // is outside the visible bounds of this control
+ return;
+ }
+ // adjust the start and end to be inside the visible range
+ if (range.Start < bounds.Start) {
+ range = new SelectionRange (bounds.Start, range.End);
+ }
+ if (range.End > bounds.End) {
+ range = new SelectionRange (range.Start, bounds.End);
+ }
+ // now invalidate the date rectangles as series of rows
+ DateTime last_month = this.current_month.AddMonths ((CalendarDimensions.Width * CalendarDimensions.Height)).AddDays (-1);
+ DateTime current = range.Start;
+ while (current <= range.End) {
+ DateTime month_end = new DateTime (current.Year, current.Month, 1).AddMonths (1).AddDays (-1);;
+ Rectangle start_rect;
+ Rectangle end_rect;
+ // see if entire selection is in this current month
+ if (range.End <= month_end && current < last_month) {
+ // the end is the last date
+ if (current < this.current_month) {
+ start_rect = GetDateRowRect (current_month, current_month);
+ } else {
+ start_rect = GetDateRowRect (current, current);
+ }
+ end_rect = GetDateRowRect (current, range.End);
+ } else if (current < last_month) {
+ // otherwise it simply means we have a selection spaning
+ // multiple months simply set rectangle inside the current month
+ start_rect = GetDateRowRect (current, current);
+ end_rect = GetDateRowRect (month_end, month_end);
+ } else {
+ // it's outside the visible range
+ start_rect = GetDateRowRect (last_month, last_month.AddDays (1));
+ end_rect = GetDateRowRect (last_month, range.End);
+ }
+ // push to the next month
+ current = month_end.AddDays (1);
+ // invalidate from the start row to the end row for this month
+ this.Invalidate (
+ new Rectangle (
+ start_rect.X,
+ start_rect.Y,
+ start_rect.Width,
+ Math.Max (end_rect.Bottom - start_rect.Y, 0)));
+ }
+ }
+
+ // gets the rect of the row where the specified date appears on the specified month
+ private Rectangle GetDateRowRect (DateTime month, DateTime date) {
+ // first get the general rect of the supplied month
+ Size month_size = SingleMonthSize;
+ Rectangle month_rect = Rectangle.Empty;
+ for (int i=0; i < CalendarDimensions.Width*CalendarDimensions.Height; i++) {
+ DateTime this_month = this.current_month.AddMonths (i);
+ if (month.Year == this_month.Year && month.Month == this_month.Month) {
+ month_rect = new Rectangle (
+ this.ClientRectangle.X + 1 + (month_size.Width * (i%CalendarDimensions.Width)) + (this.calendar_spacing.Width * (i%CalendarDimensions.Width)),
+ this.ClientRectangle.Y + 1 + (month_size.Height * (i/CalendarDimensions.Width)) + (this.calendar_spacing.Height * (i/CalendarDimensions.Width)),
+ month_size.Width,
+ month_size.Height);
+ break;
+ }
+ }
+ // now find out where in the month the supplied date is
+ if (month_rect == Rectangle.Empty) {
+ return Rectangle.Empty;
+ }
+ // find out which row this date is in
+ int row = -1;
+ DateTime first_date = GetFirstDateInMonthGrid (month);
+ DateTime end_date = first_date.AddDays (7);
+ for (int i=0; i < 6; i++) {
+ if (date >= first_date && date < end_date) {
+ row = i;
+ break;
+ }
+ first_date = end_date;
+ end_date = end_date.AddDays (7);
+ }
+ // ensure it's a valid row
+ if (row < 0) {
+ return Rectangle.Empty;
+ }
+ int x_offset = (this.ShowWeekNumbers) ? date_cell_size.Width : 0;
+ int y_offset = title_size.Height + (date_cell_size.Height * (row + 1));
+ return new Rectangle (
+ month_rect.X + x_offset,
+ month_rect.Y + y_offset,
+ date_cell_size.Width * 7,
+ date_cell_size.Height);
+ }
+
+ internal void Draw (Rectangle clip_rect, Graphics dc)
+ {
+ ThemeEngine.Current.DrawMonthCalendar (dc, clip_rect, this);
+ }
+
+ internal override bool InternalCapture {
+ get {
+ return base.InternalCapture;
+ }
+ set {
+ // Don't allow internal capture when DateTimePicker is using us
+ // Widget sets this on MouseDown
+ if (owner == null)
+ base.InternalCapture = value;
+ }
+ }
+
+ #endregion //internal methods
+
+ #region internal drawing methods
+
+
+ #endregion // internal drawing methods
+
+ #region inner classes and enumerations
+
+ // enumeration about what type of area on the calendar was hit
+ public enum HitArea {
+ Nowhere,
+ TitleBackground,
+ TitleMonth,
+ TitleYear,
+ NextMonthButton,
+ PrevMonthButton,
+ CalendarBackground,
+ Date,
+ NextMonthDate,
+ PrevMonthDate,
+ DayOfWeek,
+ WeekNumbers,
+ TodayLink
+ }
+
+ internal enum HitAreaExtra {
+ YearRectangle,
+ UpButton,
+ DownButton
+ }
+
+ // info regarding to a hit test on this calendar
+ public sealed class HitTestInfo {
+
+ private HitArea hit_area;
+ private Point point;
+ private DateTime time;
+
+ internal HitAreaExtra hit_area_extra;
+ internal DateTime hit_time;
+
+ // default constructor
+ internal HitTestInfo () {
+ hit_area = HitArea.Nowhere;
+ point = new Point (0, 0);
+ time = DateTime.Now;
+ }
+
+ // overload receives all properties
+ internal HitTestInfo (HitArea hit_area, Point point, DateTime time) {
+ this.hit_area = hit_area;
+ this.point = point;
+ this.time = time;
+ this.hit_time = time;
+ }
+
+ // overload receives all properties
+ internal HitTestInfo (HitArea hit_area, Point point, DateTime time, DateTime hit_time)
+ {
+ this.hit_area = hit_area;
+ this.point = point;
+ this.time = time;
+ this.hit_time = hit_time;
+ }
+
+ internal HitTestInfo (HitArea hit_area, Point point, DateTime time, HitAreaExtra hit_area_extra)
+ {
+ this.hit_area = hit_area;
+ this.hit_area_extra = hit_area_extra;
+ this.point = point;
+ this.time = time;
+ }
+
+ // the type of area that was hit
+ public HitArea HitArea {
+ get {
+ return hit_area;
+ }
+ }
+
+ // the point that is being test
+ public Point Point {
+ get {
+ return point;
+ }
+ }
+
+ // the date under the hit test point, only valid if HitArea is Date
+ public DateTime Time {
+ get {
+ return time;
+ }
+ }
+ }
+
+ #endregion // inner classes
+
+ #region UIA Framework: Methods, Properties and Events
+
+ static object UIAMaxSelectionCountChangedEvent = new object ();
+ static object UIASelectionChangedEvent = new object ();
+
+ internal event EventHandler UIAMaxSelectionCountChanged {
+ add { Events.AddHandler (UIAMaxSelectionCountChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAMaxSelectionCountChangedEvent, value); }
+ }
+
+ internal event EventHandler UIASelectionChanged {
+ add { Events.AddHandler (UIASelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (UIASelectionChangedEvent, value); }
+ }
+
+ private void OnUIAMaxSelectionCountChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAMaxSelectionCountChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void OnUIASelectionChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIASelectionChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/NotifyIcon.cs b/source/ShiftUI/Widgets/NotifyIcon.cs
new file mode 100644
index 0000000..c30006a
--- /dev/null
+++ b/source/ShiftUI/Widgets/NotifyIcon.cs
@@ -0,0 +1,753 @@
+// 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) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+//
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Text;
+
+namespace ShiftUI {
+ [DefaultProperty("Text")]
+ [DefaultEvent("MouseDoubleClick")]
+ //[Designer ("ShiftUI.Design.NotifyIconDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxItemFilter("ShiftUI", ToolboxItemFilterType.Allow)]
+ public sealed class NotifyIcon : Component {
+ #region Local Variables
+ private Icon icon;
+ private Bitmap icon_bitmap;
+ private string text;
+ private bool visible;
+ private NotifyIconWindow window;
+ private bool systray_active;
+ private ToolTip tooltip;
+ private bool double_click;
+ private string balloon_text;
+ private string balloon_title;
+ private ToolTipIcon balloon_icon;
+ private ContextMenuStrip context_menu_strip;
+ private object tag;
+ #endregion // Local Variables
+
+ #region NotifyIconWindow Class
+ internal class NotifyIconWindow : Form {
+ NotifyIcon owner;
+ Rectangle rect;
+
+ public NotifyIconWindow(NotifyIcon owner) {
+ this.owner = owner;
+ is_visible = false;
+ rect = new Rectangle(0, 0, 1, 1);
+
+ FormBorderStyle = FormBorderStyle.None;
+
+ //CreateControl();
+
+ SizeChanged += new EventHandler(HandleSizeChanged);
+
+ // Events that need to be sent to our parent
+ DoubleClick += new EventHandler(HandleDoubleClick);
+ MouseDown +=new MouseEventHandler(HandleMouseDown);
+ MouseUp +=new MouseEventHandler(HandleMouseUp);
+ MouseMove +=new MouseEventHandler(HandleMouseMove);
+ ContextMenuStrip = owner.context_menu_strip;
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp;
+
+ cp = base.CreateParams;
+
+ cp.Parent = IntPtr.Zero;
+ cp.Style = (int)WindowStyles.WS_POPUP;
+ cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
+
+ cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW);
+
+ return cp;
+ }
+ }
+
+ protected override void WndProc(ref Message m) {
+ switch((Msg)m.Msg) {
+ //
+ // NotifyIcon does CONTEXTMENU on mouse up, not down
+ // so we swallow the message here, and handle it on our own
+ //
+ case Msg.WM_CONTEXTMENU:
+ return;
+
+ case Msg.WM_USER: {
+ switch ((Msg)m.LParam.ToInt32()) {
+ case Msg.WM_LBUTTONDOWN: {
+ owner.OnMouseDown (new MouseEventArgs(MouseButtons.Left, 1, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_LBUTTONUP: {
+ owner.OnMouseUp (new MouseEventArgs(MouseButtons.Left, 1, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_LBUTTONDBLCLK: {
+ owner.OnDoubleClick (EventArgs.Empty);
+ owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_MOUSEMOVE: {
+ owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_RBUTTONDOWN: {
+ owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_RBUTTONUP: {
+ owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.WM_RBUTTONDBLCLK: {
+ owner.OnDoubleClick (EventArgs.Empty);
+ owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ return;
+ }
+
+ case Msg.NIN_BALLOONUSERCLICK: {
+ owner.OnBalloonTipClicked (EventArgs.Empty);
+ return;
+ }
+
+ case Msg.NIN_BALLOONSHOW: {
+ owner.OnBalloonTipShown (EventArgs.Empty);
+ return;
+ }
+
+ case Msg.NIN_BALLOONHIDE:
+ case Msg.NIN_BALLOONTIMEOUT: {
+ owner.OnBalloonTipClosed (EventArgs.Empty);
+ return;
+ }
+ }
+ return;
+ }
+ }
+ base.WndProc (ref m);
+ }
+
+ internal void CalculateIconRect() {
+ int x;
+ int y;
+ int size;
+
+ // Icons are always square. Try to center them in the window
+ if (ClientRectangle.Width < ClientRectangle.Height) {
+ size = ClientRectangle.Width;
+ } else {
+ size = ClientRectangle.Height;
+ }
+ x = this.ClientRectangle.Width / 2 - size / 2;
+ y = this.ClientRectangle.Height / 2 - size / 2;
+ rect = new Rectangle(x, y, size, size);
+
+ Bounds = new Rectangle (0, 0, size, size);
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs e) {
+ if (owner.icon != null) {
+ // At least in Gnome, the background of the panel is the same as the Menu, so we go for it
+ // instead of (most of the time) plain white.
+ e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Menu), rect);
+ e.Graphics.DrawImage(owner.icon_bitmap,
+ rect,
+ new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
+ GraphicsUnit.Pixel);
+
+ }
+ }
+
+ internal void InternalRecreateHandle () {
+ base.RecreateHandle ();
+ }
+
+ private void HandleSizeChanged(object sender, EventArgs e) {
+ owner.Recalculate ();
+ }
+
+ private void HandleDoubleClick (object sender, EventArgs e)
+ {
+ owner.OnDoubleClick (e);
+ owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Widget.MousePosition.X, Widget.MousePosition.Y, 0));
+ }
+
+ private void HandleMouseDown (object sender, MouseEventArgs e)
+ {
+ owner.OnMouseDown (e);
+ }
+
+ private void HandleMouseUp (object sender, MouseEventArgs e)
+ {
+ owner.OnMouseUp (e);
+ }
+
+ private void HandleMouseMove (object sender, MouseEventArgs e)
+ {
+ owner.OnMouseMove (e);
+ }
+ }
+ #endregion // NotifyIconWindow Class
+
+ #region NotifyIconBalloonWindow Class
+ internal class BalloonWindow : Form
+ {
+ private IntPtr owner;
+ private Timer timer;
+
+ private string title;
+ private string text;
+ private ToolTipIcon icon;
+
+ public BalloonWindow (IntPtr owner)
+ {
+ this.owner = owner;
+
+ StartPosition = FormStartPosition.Manual;
+ FormBorderStyle = FormBorderStyle.None;
+
+ MouseDown += new MouseEventHandler (HandleMouseDown);
+
+ timer = new Timer ();
+ timer.Enabled = false;
+ timer.Tick += new EventHandler (HandleTimer);
+ }
+
+ public IntPtr OwnerHandle {
+ get {
+ return owner;
+ }
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ timer.Stop();
+ timer.Dispose();
+ }
+ base.Dispose (disposing);
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp;
+
+ cp = base.CreateParams;
+
+ cp.Style = (int)WindowStyles.WS_POPUP;
+ cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
+
+ cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
+
+ return cp;
+ }
+ }
+
+ public new void Close () {
+ base.Close ();
+ XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
+ }
+
+ protected override void OnShown (EventArgs e)
+ {
+ base.OnShown (e);
+ timer.Start ();
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
+ base.OnPaint (e);
+ }
+
+ private void Recalculate ()
+ {
+ Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
+
+ Left = rect.Left;
+ Top = rect.Top;
+ Width = rect.Width;
+ Height = rect.Height;
+ }
+
+ // To be used when we have a "close button" inside balloon.
+ //private void HandleClick (object sender, EventArgs e)
+ //{
+ // Close ();
+ //}
+
+ private void HandleMouseDown (object sender, MouseEventArgs e)
+ {
+ XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
+ base.Close ();
+ }
+
+ private void HandleTimer (object sender, EventArgs e)
+ {
+ timer.Stop ();
+ XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
+ base.Close ();
+ }
+
+ internal StringFormat Format {
+ get {
+ StringFormat format = new StringFormat ();
+ format.Alignment = StringAlignment.Near;
+ format.HotkeyPrefix = HotkeyPrefix.Hide;
+
+ return format;
+ }
+ }
+
+ public new ToolTipIcon Icon {
+ get { return this.icon; }
+ set {
+ if (value == this.icon)
+ return;
+
+ this.icon = value;
+ Recalculate ();
+ }
+ }
+
+ public string Title {
+ get { return this.title; }
+ set {
+ if (value == this.title)
+ return;
+
+ this.title = value;
+ Recalculate ();
+ }
+ }
+
+ public override string Text {
+ get { return this.text; }
+ set {
+ if (value == this.text)
+ return;
+
+ this.text = value;
+ Recalculate ();
+ }
+ }
+
+ public int Timeout {
+ get { return timer.Interval; }
+ set {
+ // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
+ if (value < 10000)
+ timer.Interval = 10000;
+ else if (value > 30000)
+ timer.Interval = 30000;
+ else
+ timer.Interval = value;
+ }
+ }
+ }
+ #endregion // NotifyIconBalloonWindow Class
+
+ #region Public Constructors
+ public NotifyIcon() {
+ window = new NotifyIconWindow(this);
+ systray_active = false;
+
+ balloon_title = "";
+ balloon_text = "";
+ }
+
+ public NotifyIcon(System.ComponentModel.IContainer container) : this() {
+ }
+ #endregion // Public Constructors
+
+ #region Public Methods
+ public void ShowBalloonTip (int timeout)
+ {
+ ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
+ }
+
+ public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
+ {
+ XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
+ }
+ #endregion Public Methods
+
+ #region Private Methods
+ private void OnBalloonTipClicked (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnBalloonTipClosed (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnBalloonTipShown (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnClick (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnDoubleClick (EventArgs e)
+ {
+ double_click = true;
+ EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnMouseClick (MouseEventArgs e)
+ {
+ MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnMouseDoubleClick (MouseEventArgs e)
+ {
+ MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnMouseDown (MouseEventArgs e)
+ {
+ MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void OnMouseUp (MouseEventArgs e)
+ {
+ if ((e.Button & MouseButtons.Right) == MouseButtons.Right) {
+ if (context_menu_strip != null) {
+ XplatUI.SetForegroundWindow (window.Handle);
+ context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
+ }
+ }
+
+ MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ if (!double_click) {
+ OnClick (EventArgs.Empty);
+ OnMouseClick (e);
+ double_click = false;
+ }
+ }
+
+ private void OnMouseMove (MouseEventArgs e)
+ {
+ MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void Recalculate ()
+ {
+ window.CalculateIconRect ();
+
+ if (!Visible || (text == string.Empty && icon == null)) {
+ HideSystray ();
+ } else {
+
+ if (systray_active)
+ UpdateSystray ();
+ else
+ ShowSystray ();
+ }
+ }
+
+ private void ShowSystray()
+ {
+ if (icon == null)
+ return;
+
+ icon_bitmap = icon.ToBitmap();
+
+ systray_active = true;
+ XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
+ }
+
+ private void HideSystray()
+ {
+ if (!systray_active) {
+ return;
+ }
+
+ systray_active = false;
+ XplatUI.SystrayRemove(window.Handle, ref tooltip);
+ }
+
+ private void UpdateSystray()
+ {
+ if (icon_bitmap != null) {
+ icon_bitmap.Dispose();
+ }
+
+ if (icon != null) {
+ icon_bitmap = icon.ToBitmap();
+ }
+
+ window.Invalidate();
+ XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
+ }
+ #endregion // Private Methods
+
+ #region Public Instance Properties
+ [DefaultValue ("None")]
+ public ToolTipIcon BalloonTipIcon {
+ get { return this.balloon_icon; }
+ set {
+ if (value == this.balloon_icon)
+ return;
+
+ this.balloon_icon = value;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue ("")]
+ //[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
+ //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+ public string BalloonTipText {
+ get { return this.balloon_text; }
+ set {
+ if (value == this.balloon_text)
+ return;
+
+ this.balloon_text = value;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue ("")]
+ public string BalloonTipTitle {
+ get { return this.balloon_title; }
+ set {
+ if (value == this.balloon_title)
+ return;
+
+ this.balloon_title = value;
+ }
+ }
+
+
+
+ [DefaultValue (null)]
+ public ContextMenuStrip ContextMenuStrip {
+ get { return this.context_menu_strip; }
+ set {
+ if (this.context_menu_strip != value) {
+ this.context_menu_strip = value;
+ window.ContextMenuStrip = value;
+ }
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(null)]
+ public Icon Icon {
+ get {
+ return icon;
+ }
+
+ set {
+ if (icon != value) {
+ icon = value;
+ Recalculate ();
+ }
+ }
+ }
+
+ [Localizable (false)]
+ [Bindable (true)]
+ [TypeConverter (typeof (StringConverter))]
+ [DefaultValue (null)]
+ public object Tag {
+ get { return this.tag; }
+ set { this.tag = value; }
+ }
+
+ [DefaultValue ("")]
+ //[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
+ // typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable (true)]
+ public string Text {
+ get {
+ return text;
+ }
+
+ set {
+ if (text != value) {
+ if (value.Length >= 64) {
+ throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
+ }
+ text = value;
+ Recalculate ();
+ }
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(false)]
+ public bool Visible {
+ get {
+ return visible;
+ }
+
+ set {
+ if (visible != value) {
+ visible = value;
+
+ // Let our control know, too
+ window.is_visible = value;
+
+ if (visible) {
+ ShowSystray ();
+ } else {
+ HideSystray();
+ }
+ }
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Methods
+ protected override void Dispose(bool disposing) {
+ if (visible)
+ HideSystray();
+
+ if (icon_bitmap != null) {
+ icon_bitmap.Dispose();
+ }
+
+ if (disposing)
+ icon = null;
+
+ base.Dispose (disposing);
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object ClickEvent = new object ();
+ static object DoubleClickEvent = new object ();
+ static object MouseDownEvent = new object ();
+ static object MouseMoveEvent = new object ();
+ static object MouseUpEvent = new object ();
+ static object BalloonTipClickedEvent = new object ();
+ static object BalloonTipClosedEvent = new object ();
+ static object BalloonTipShownEvent = new object ();
+ static object MouseClickEvent = new object ();
+ static object MouseDoubleClickEvent = new object ();
+
+ [MWFCategory("Action")]
+ public event EventHandler BalloonTipClicked {
+ add { Events.AddHandler (BalloonTipClickedEvent, value); }
+ remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
+ }
+
+ [MWFCategory("Action")]
+ public event EventHandler BalloonTipClosed {
+ add { Events.AddHandler (BalloonTipClosedEvent, value); }
+ remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
+ }
+
+ [MWFCategory("Action")]
+ public event EventHandler BalloonTipShown {
+ add { Events.AddHandler (BalloonTipShownEvent, value); }
+ remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
+ }
+
+ [MWFCategory("Action")]
+ public event MouseEventHandler MouseClick {
+ add { Events.AddHandler (MouseClickEvent, value); }
+ remove { Events.RemoveHandler (MouseClickEvent, value); }
+ }
+
+ [MWFCategory ("Action")]
+ public event MouseEventHandler MouseDoubleClick {
+ add { Events.AddHandler (MouseDoubleClickEvent, value); }
+ remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
+ }
+
+ [MWFCategory("Action")]
+ public event EventHandler Click {
+ add { Events.AddHandler (ClickEvent, value); }
+ remove { Events.RemoveHandler (ClickEvent, value); }
+ }
+
+ [MWFCategory("Action")]
+ public event EventHandler DoubleClick {
+ add { Events.AddHandler (DoubleClickEvent, value); }
+ remove { Events.RemoveHandler (DoubleClickEvent, value); }
+ }
+
+ public event MouseEventHandler MouseDown {
+ add { Events.AddHandler (MouseDownEvent, value); }
+ remove { Events.RemoveHandler (MouseDownEvent, value); }
+ }
+
+ public event MouseEventHandler MouseMove {
+ add { Events.AddHandler (MouseMoveEvent, value); }
+ remove { Events.RemoveHandler (MouseMoveEvent, value); }
+ }
+
+ public event MouseEventHandler MouseUp {
+ add { Events.AddHandler (MouseUpEvent, value); }
+ remove { Events.RemoveHandler (MouseUpEvent, value); }
+ }
+
+ #endregion // Events
+ }
+}
diff --git a/source/ShiftUI/Widgets/Panel.cs b/source/ShiftUI/Widgets/Panel.cs
new file mode 100644
index 0000000..3f0be8a
--- /dev/null
+++ b/source/ShiftUI/Widgets/Panel.cs
@@ -0,0 +1,187 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+//
+
+// COMPLETE
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [DefaultProperty("BorderStyle")]
+ [DefaultEvent("Paint")]
+ //[Designer ("ShiftUI.Design.PanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [Docking (DockingBehavior.Ask)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class Panel : ScrollableWidget {
+ #region Constructors & Destructors
+ public Panel () {
+ base.TabStop = false;
+ SetStyle(Widgetstyles.Selectable, false);
+ SetStyle (Widgetstyles.SupportsTransparentBackColor, true);
+ }
+ #endregion // Constructors & Destructors
+
+ #region Public Instance Properties
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ public override bool AutoSize {
+ get { return base.AutoSize; }
+ set { base.AutoSize = value; }
+ }
+
+ [Browsable (true)]
+ [DefaultValue (AutoSizeMode.GrowOnly)]
+ [Localizable (true)]
+ public virtual AutoSizeMode AutoSizeMode {
+ get { return base.GetAutoSizeMode (); }
+ set { base.SetAutoSizeMode (value); }
+ }
+
+ [DefaultValue(BorderStyle.None)]
+ [DispId(-504)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set { InternalBorderStyle = value; }
+ }
+
+ [DefaultValue(false)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set {
+ if (value == TabStop)
+ return;
+ base.TabStop = value;
+ }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (value == Text)
+ return;
+ base.Text = value;
+ Refresh ();
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.PanelDefaultSize; }
+ }
+ #endregion // Proteced Instance Properties
+
+ #region Public Instance Methods
+ public override string ToString ()
+ {
+ return base.ToString () + ", BorderStyle: " + BorderStyle;
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override void OnResize(EventArgs eventargs) {
+ base.OnResize (eventargs);
+ Invalidate(true);
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region Events
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler AutoSizeChanged {
+ add { base.AutoSizeChanged += value; }
+ remove { base.AutoSizeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion
+
+ #region Internal Methods
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ Size retsize = Size.Empty;
+
+ foreach (Widget child in Widgets) {
+ if (child.Dock == DockStyle.Fill) {
+ if (child.Bounds.Right > retsize.Width)
+ retsize.Width = child.Bounds.Right;
+ } else if (child.Dock != DockStyle.Top && child.Dock != DockStyle.Bottom && (child.Anchor & AnchorStyles.Right) == 0 && (child.Bounds.Right + child.Margin.Right) > retsize.Width)
+ retsize.Width = child.Bounds.Right + child.Margin.Right;
+
+ if (child.Dock == DockStyle.Fill) {
+ if (child.Bounds.Bottom > retsize.Height)
+ retsize.Height = child.Bounds.Bottom;
+ } else if (child.Dock != DockStyle.Left && child.Dock != DockStyle.Right && (child.Anchor & AnchorStyles.Bottom) == 0 && (child.Bounds.Bottom + child.Margin.Bottom) > retsize.Height)
+ retsize.Height = child.Bounds.Bottom + child.Margin.Bottom;
+ }
+
+ return retsize;
+ }
+ #endregion
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/PictureBox.cs b/source/ShiftUI/Widgets/PictureBox.cs
new file mode 100644
index 0000000..1574907
--- /dev/null
+++ b/source/ShiftUI/Widgets/PictureBox.cs
@@ -0,0 +1,618 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+//
+
+// COMPLETE
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Net;
+
+namespace ShiftUI {
+ [DefaultProperty("Image")]
+ //[Designer("ShiftUI.Design.PictureBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [Docking (DockingBehavior.Ask)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [DefaultBindingProperty ("Image")]
+ [ToolboxWidget]
+ public class PictureBox : Widget, ISupportInitialize
+ {
+ #region Fields
+ private Image image;
+ private PictureBoxSizeMode size_mode;
+ private Image error_image;
+ private string image_location;
+ private Image initial_image;
+ private bool wait_on_load;
+ private WebClient image_download;
+ private bool image_from_url;
+ private int no_update;
+ #endregion // Fields
+
+ private EventHandler frame_handler;
+
+ #region Public Constructor
+ public PictureBox ()
+ {
+ //recalc = true;
+ no_update = 0;
+
+ SetStyle (Widgetstyles.OptimizedDoubleBuffer, true);
+ SetStyle (Widgetstyles.Opaque, false);
+ SetStyle (Widgetstyles.Selectable, false);
+ SetStyle (Widgetstyles.SupportsTransparentBackColor, true);
+ HandleCreated += new EventHandler(PictureBox_HandleCreated);
+ initial_image = null;
+ error_image = null;
+ }
+ #endregion // Public Constructor
+
+ #region Public Properties
+ [DefaultValue(PictureBoxSizeMode.Normal)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public PictureBoxSizeMode SizeMode {
+ get { return size_mode; }
+ set {
+ if (size_mode == value)
+ return;
+ size_mode = value;
+
+ if (size_mode == PictureBoxSizeMode.AutoSize) {
+ AutoSize = true;
+ SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
+ } else {
+ AutoSize = false;
+ SetAutoSizeMode (AutoSizeMode.GrowOnly);
+ }
+
+ UpdateSize ();
+ if (no_update == 0) {
+ Invalidate ();
+ }
+
+ OnSizeModeChanged (EventArgs.Empty);
+ }
+ }
+
+ [Bindable (true)]
+ [Localizable(true)]
+ public Image Image {
+ get { return image; }
+ set { ChangeImage (value, false); }
+ }
+
+ [DefaultValue(BorderStyle.None)]
+ [DispId(-504)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set { InternalBorderStyle = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool CausesValidation {
+ get { return base.CausesValidation; }
+ set { base.CausesValidation = value; }
+ }
+
+ [Localizable (true)]
+ [RefreshProperties (RefreshProperties.All)]
+ public Image ErrorImage {
+ get { return error_image; }
+ set { error_image = value; }
+ }
+
+ [RefreshProperties (RefreshProperties.All)]
+ [Localizable(true)]
+ public Image InitialImage {
+ get { return initial_image; }
+ set { initial_image = value; }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (null)]
+ [RefreshProperties (RefreshProperties.All)]
+ public string ImageLocation {
+ get { return image_location; }
+ set {
+ image_location = value;
+
+ if (!string.IsNullOrEmpty (value)) {
+ if (WaitOnLoad)
+ Load (value);
+ else
+ LoadAsync (value);
+ } else if (image_from_url)
+ ChangeImage (null, true);
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public bool WaitOnLoad {
+ get { return wait_on_load; }
+ set { wait_on_load = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new ImeMode ImeMode {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override RightToLeft RightToLeft {
+ get { return base.RightToLeft; }
+ set { base.RightToLeft = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new int TabIndex {
+ get { return base.TabIndex; }
+ set { base.TabIndex = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override ImeMode DefaultImeMode {
+ get { return base.DefaultImeMode; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Font Font {
+ get { return base.Font; }
+ set { base.Font = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set { base.ForeColor = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool AllowDrop {
+ get { return base.AllowDrop; }
+ set { base.AllowDrop = value; }
+ }
+ #endregion // Public Properties
+
+ #region Protected Instance Methods
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.PictureBoxDefaultSize; }
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (image != null) {
+ StopAnimation ();
+ image = null;
+ }
+ initial_image = null;
+
+ base.Dispose (disposing);
+ }
+
+ protected override void OnPaint (PaintEventArgs pe)
+ {
+ ThemeEngine.Current.DrawPictureBox (pe.Graphics, pe.ClipRectangle, this);
+ base.OnPaint (pe);
+ }
+
+ protected override void OnVisibleChanged (EventArgs e)
+ {
+ base.OnVisibleChanged (e);
+ }
+
+ protected virtual void OnSizeModeChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [SizeModeChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnEnabledChanged (EventArgs e)
+ {
+ base.OnEnabledChanged (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnLoadCompleted (AsyncCompletedEventArgs e)
+ {
+ AsyncCompletedEventHandler eh = (AsyncCompletedEventHandler)(Events[LoadCompletedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnLoadProgressChanged (ProgressChangedEventArgs e)
+ {
+ ProgressChangedEventHandler eh = (ProgressChangedEventHandler)(Events[LoadProgressChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+
+ Invalidate ();
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ if (image == null)
+ return base.GetPreferredSizeCore (proposedSize);
+ else
+ return image.Size;
+ }
+ #endregion // Protected Instance Methods
+
+ #region ISupportInitialize Interface
+ void System.ComponentModel.ISupportInitialize.BeginInit() {
+ no_update++;
+ }
+
+ void System.ComponentModel.ISupportInitialize.EndInit() {
+ if (no_update > 0) {
+ no_update--;
+ }
+ if (no_update == 0) {
+ Invalidate ();
+ }
+ }
+ #endregion // ISupportInitialize Interface
+
+ #region Private Properties
+ private WebClient ImageDownload {
+ get {
+ if (image_download == null)
+ image_download = new WebClient ();
+
+ return image_download;
+ }
+ }
+ #endregion
+
+ #region Private Methods
+
+ private void ChangeImage (Image value, bool from_url)
+ {
+ StopAnimation ();
+
+ image_from_url = from_url;
+ image = value;
+
+ if (IsHandleCreated) {
+ UpdateSize ();
+ if (image != null && ImageAnimator.CanAnimate (image)) {
+ frame_handler = new EventHandler (OnAnimateImage);
+ ImageAnimator.Animate (image, frame_handler);
+ }
+ if (no_update == 0) {
+ Invalidate ();
+ }
+ }
+ }
+
+ private void StopAnimation ()
+ {
+ if (frame_handler == null)
+ return;
+ ImageAnimator.StopAnimate (image, frame_handler);
+ frame_handler = null;
+ }
+
+ private void UpdateSize ()
+ {
+ if (image == null)
+ return;
+
+ if (Parent != null)
+ Parent.PerformLayout (this, "AutoSize");
+ }
+
+ private void OnAnimateImage (object sender, EventArgs e)
+ {
+ // This is called from a worker thread,BeginInvoke is used
+ // so the control is updated from the correct thread
+
+ // Check if we have a handle again, since it may have gotten
+ // destroyed since the last time we checked.
+ if (!IsHandleCreated)
+ return;
+
+ BeginInvoke (new EventHandler (UpdateAnimatedImage), new object [] { this, e });
+ }
+
+ private void UpdateAnimatedImage (object sender, EventArgs e)
+ {
+ // Check if we have a handle again, since it may have gotten
+ // destroyed since the last time we checked.
+ if (!IsHandleCreated)
+ return;
+
+ ImageAnimator.UpdateFrames (image);
+ Refresh ();
+ }
+
+ private void PictureBox_HandleCreated(object sender, EventArgs e) {
+ UpdateSize ();
+ if (image != null && ImageAnimator.CanAnimate (image)) {
+ frame_handler = new EventHandler (OnAnimateImage);
+ ImageAnimator.Animate (image, frame_handler);
+ }
+ if (no_update == 0) {
+ Invalidate ();
+ }
+ }
+
+ void ImageDownload_DownloadDataCompleted (object sender, DownloadDataCompletedEventArgs e)
+ {
+ if (e.Error != null && !e.Cancelled)
+ Image = error_image;
+ else if (e.Error == null && !e.Cancelled)
+ using (MemoryStream ms = new MemoryStream (e.Result))
+ Image = Image.FromStream (ms);
+
+ ImageDownload.DownloadProgressChanged -= new DownloadProgressChangedEventHandler (ImageDownload_DownloadProgressChanged);
+ ImageDownload.DownloadDataCompleted -= new DownloadDataCompletedEventHandler (ImageDownload_DownloadDataCompleted);
+ image_download = null;
+
+ OnLoadCompleted (e);
+ }
+
+ private void ImageDownload_DownloadProgressChanged (object sender, DownloadProgressChangedEventArgs e)
+ {
+ OnLoadProgressChanged (new ProgressChangedEventArgs (e.ProgressPercentage, e.UserState));
+ }
+ #endregion // Private Methods
+
+ #region Public Instance Methods
+ public void CancelAsync ()
+ {
+ if (image_download != null)
+ image_download.CancelAsync ();
+ }
+
+ public void Load ()
+ {
+ Load (image_location);
+ }
+
+ public void Load (string url)
+ {
+ if (string.IsNullOrEmpty (url))
+ throw new InvalidOperationException ("ImageLocation not specified.");
+
+ image_location = url;
+
+ if (url.Contains ("://"))
+ using (Stream s = ImageDownload.OpenRead (url))
+ ChangeImage (Image.FromStream (s), true);
+ else
+ ChangeImage (Image.FromFile (url), true);
+ }
+
+ public void LoadAsync ()
+ {
+ LoadAsync (image_location);
+ }
+
+ public void LoadAsync (string url)
+ {
+ // If WaitOnLoad is true, do not do async
+ if (wait_on_load) {
+ Load (url);
+ return;
+ }
+
+ if (string.IsNullOrEmpty (url))
+ throw new InvalidOperationException ("ImageLocation not specified.");
+
+ image_location = url;
+ ChangeImage (InitialImage, true);
+
+ if (ImageDownload.IsBusy)
+ ImageDownload.CancelAsync ();
+
+ Uri uri = null;
+ try {
+ uri = new Uri (url);
+ } catch (UriFormatException) {
+ uri = new Uri (Path.GetFullPath (url));
+ }
+
+ ImageDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler (ImageDownload_DownloadProgressChanged);
+ ImageDownload.DownloadDataCompleted += new DownloadDataCompletedEventHandler (ImageDownload_DownloadDataCompleted);
+ ImageDownload.DownloadDataAsync (uri);
+ }
+
+ public override string ToString() {
+ return String.Format("{0}, SizeMode: {1}", base.ToString (), SizeMode);
+ }
+ #endregion
+
+ #region Events
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler CausesValidationChanged {
+ add { base.CausesValidationChanged += value; }
+ remove { base.CausesValidationChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler Enter {
+ add { base.Enter += value; }
+ remove { base.Enter -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler FontChanged {
+ add { base.FontChanged += value; }
+ remove { base.FontChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler Leave {
+ add { base.Leave += value; }
+ remove { base.Leave -= value; }
+ }
+
+ static object LoadCompletedEvent = new object ();
+ static object LoadProgressChangedEvent = new object ();
+
+ public event AsyncCompletedEventHandler LoadCompleted {
+ add { Events.AddHandler (LoadCompletedEvent, value); }
+ remove { Events.RemoveHandler (LoadCompletedEvent, value); }
+ }
+
+ public event ProgressChangedEventHandler LoadProgressChanged {
+ add { Events.AddHandler (LoadProgressChangedEvent, value); }
+ remove { Events.RemoveHandler (LoadProgressChangedEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler RightToLeftChanged {
+ add { base.RightToLeftChanged += value; }
+ remove { base.RightToLeftChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabIndexChanged {
+ add { base.TabIndexChanged += value; }
+ remove { base.TabIndexChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ static object SizeModeChangedEvent = new object ();
+ public event EventHandler SizeModeChanged {
+ add { Events.AddHandler (SizeModeChangedEvent, value); }
+ remove { Events.RemoveHandler (SizeModeChangedEvent, value); }
+ }
+
+ #endregion // Events
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/PictureBoxSizeMode.cs b/source/ShiftUI/Widgets/PictureBoxSizeMode.cs
new file mode 100644
index 0000000..80319a4
--- /dev/null
+++ b/source/ShiftUI/Widgets/PictureBoxSizeMode.cs
@@ -0,0 +1,38 @@
+// 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 Novell, Inc.
+//
+// Authors:
+// Jackson Harper ([email protected])
+//
+
+
+namespace ShiftUI {
+
+ public enum PictureBoxSizeMode {
+ Normal = 0,
+ StretchImage = 1,
+ AutoSize = 2,
+ CenterImage = 3,
+ Zoom = 4
+ }
+}
+
+
diff --git a/source/ShiftUI/Widgets/ProgressBar.cs b/source/ShiftUI/Widgets/ProgressBar.cs
new file mode 100644
index 0000000..89d06d0
--- /dev/null
+++ b/source/ShiftUI/Widgets/ProgressBar.cs
@@ -0,0 +1,533 @@
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez [email protected]
+// Peter Dennis Bartok [email protected]
+//
+//
+
+using System.Drawing;
+using System.ComponentModel;
+using System.Drawing.Imaging;
+using System.Drawing.Drawing2D;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultProperty ("Value")]
+ [DefaultBindingProperty ("Value")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class ProgressBar : Widget
+ {
+ #region Local Variables
+ private int maximum;
+ private int minimum;
+ internal int step;
+ internal int val;
+ internal DateTime start = DateTime.Now;
+ internal Rectangle client_area = new Rectangle ();
+ internal ProgressBarStyle style;
+ Timer marquee_timer;
+ bool right_to_left_layout;
+ private static readonly Color defaultForeColor = SystemColors.Highlight;
+ #endregion // Local Variables
+
+ #region events
+ static object RightToLeftLayoutChangedEvent = new object ();
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler CausesValidationChanged {
+ add { base.CausesValidationChanged += value; }
+ remove { base.CausesValidationChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Enter {
+ add { base.Enter += value; }
+ remove { base.Enter -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler FontChanged {
+ add { base.FontChanged += value; }
+ remove { base.FontChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Leave {
+ add { base.Leave += value; }
+ remove { base.Leave -= value; }
+ }
+
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ [Browsable(false)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
+ remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion Events
+
+ #region Public Constructors
+ public ProgressBar()
+ {
+ maximum = 100;
+ minimum = 0;
+ step = 10;
+ val = 0;
+
+ base.Resize += new EventHandler (OnResizeTB);
+
+ SetStyle (Widgetstyles.UserPaint |
+ Widgetstyles.Selectable |
+ Widgetstyles.ResizeRedraw |
+ Widgetstyles.Opaque |
+ Widgetstyles.UseTextForAccessibility
+ , false);
+
+ force_double_buffer = true;
+
+ ForeColor = defaultForeColor;
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override bool AllowDrop
+ {
+ get { return base.AllowDrop; }
+ set {
+ base.AllowDrop = value;
+ }
+ }
+
+ // Setting this property in MS .Net 1.1 does not have any visual effect and it
+ // does not fire a BackgroundImageChanged event
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Image BackgroundImage
+ {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new bool CausesValidation
+ {
+ get { return base.CausesValidation; }
+ set { base.CausesValidation = value; }
+ }
+
+ protected override CreateParams CreateParams
+ {
+ get { return base.CreateParams; }
+ }
+
+ protected override ImeMode DefaultImeMode
+ {
+ get { return base.DefaultImeMode; }
+ }
+
+ protected override Size DefaultSize
+ {
+ get { return ThemeEngine.Current.ProgressBarDefaultSize; }
+ }
+
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get { return base.DoubleBuffered; }
+ set { base.DoubleBuffered = value; }
+ }
+
+ // Setting this property in MS .Net 1.1 does not have any visual effect and it
+ // does not fire a FontChanged event
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Font Font
+ {
+ get { return base.Font; }
+ set { base.Font = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new ImeMode ImeMode
+ {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [DefaultValue (100)]
+ public int Maximum
+ {
+ get {
+ return maximum;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("Maximum",
+ string.Format("Value '{0}' must be greater than or equal to 0.", value ));
+
+ maximum = value;
+ minimum = Math.Min (minimum, maximum);
+ val = Math.Min (val, maximum);
+ Refresh ();
+ }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [DefaultValue (0)]
+ public int Minimum {
+ get {
+ return minimum;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("Minimum",
+ string.Format("Value '{0}' must be greater than or equal to 0.", value ));
+
+ minimum = value;
+ maximum = Math.Max (maximum, minimum);
+ val = Math.Max (val, minimum);
+ Refresh ();
+ }
+ }
+
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(false)]
+ [MonoTODO ("RTL is not supported")]
+ public virtual bool RightToLeftLayout {
+ get { return right_to_left_layout;}
+ set {
+ if (right_to_left_layout != value) {
+ right_to_left_layout = value;
+ OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue (10)]
+ public int Step
+ {
+ get { return step; }
+ set {
+ step = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable (true)]
+ [DefaultValue (ProgressBarStyle.Blocks)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public ProgressBarStyle Style {
+ get {
+ return style;
+ }
+
+ set {
+ if (value != ProgressBarStyle.Blocks && value != ProgressBarStyle.Continuous
+ && value != ProgressBarStyle.Marquee)
+ throw new InvalidEnumArgumentException ("value", unchecked((int)value), typeof (ProgressBarStyle));
+ if (style != value) {
+ style = value;
+
+ if (style == ProgressBarStyle.Marquee) {
+ if (marquee_timer == null) {
+ marquee_timer = new Timer ();
+ marquee_timer.Interval = 10;
+ marquee_timer.Tick += new EventHandler (marquee_timer_Tick);
+ }
+ marquee_timer.Start ();
+ } else {
+ if (marquee_timer != null) {
+ marquee_timer.Stop ();
+ }
+ Refresh ();
+ }
+ }
+ }
+ }
+
+ void marquee_timer_Tick (object sender, EventArgs e)
+ {
+ Invalidate ();
+ }
+
+ int marquee_animation_speed = 100;
+ [DefaultValue (100)]
+ public int MarqueeAnimationSpeed {
+ get {
+ return marquee_animation_speed;
+ }
+
+ set {
+ marquee_animation_speed = value;
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new bool TabStop
+ {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Bindable(false)]
+ public override string Text
+ {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ [Bindable(true)]
+ [DefaultValue (0)]
+ public int Value
+ {
+ get {
+ return val;
+ }
+ set {
+ if (value < Minimum || value > Maximum)
+ throw new ArgumentOutOfRangeException ("Value", string.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
+ val = value;
+ Refresh ();
+ }
+ }
+
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ }
+
+ public void Increment (int value)
+ {
+ if (Style == ProgressBarStyle.Marquee)
+ throw new InvalidOperationException ("Increment should not be called if the style is Marquee.");
+
+ int newValue = Value + value;
+
+ if (newValue < Minimum)
+ newValue = Minimum;
+
+ if (newValue > Maximum)
+ newValue = Maximum;
+
+ Value = newValue;
+ Refresh ();
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ UpdateAreas ();
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected override void OnForeColorChanged (EventArgs e)
+ {
+ base.OnForeColorChanged (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged(EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [RightToLeftLayoutChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ public void PerformStep ()
+ {
+ if (Style == ProgressBarStyle.Marquee)
+ throw new InvalidOperationException ("PerformStep should not be called if the style is Marquee.");
+
+ Increment (Step);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override void ResetForeColor ()
+ {
+ ForeColor = defaultForeColor;
+ }
+
+ public override string ToString()
+ {
+ return string.Format ("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
+ GetType().FullName,
+ Minimum.ToString (),
+ Maximum.ToString (),
+ Value.ToString () );
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Private Instance Methods
+
+ private void UpdateAreas ()
+ {
+ client_area.X = client_area.Y = 2;
+ client_area.Width = Width - 4;
+ client_area.Height = Height - 4;
+ }
+
+ private void OnResizeTB (Object o, EventArgs e)
+ {
+ if (Width <= 0 || Height <= 0)
+ return;
+
+ UpdateAreas ();
+ Invalidate(); // Invalidate the full surface, blocks will not match
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ ThemeEngine.Current.DrawProgressBar (pevent.Graphics, pevent.ClipRectangle, this);
+ }
+
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/PropertiesTab.cs b/source/ShiftUI/Widgets/PropertiesTab.cs
new file mode 100644
index 0000000..a92fd33
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertiesTab.cs
@@ -0,0 +1,76 @@
+// 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) 2005-2008 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers [email protected]
+// Ivan N. Zlatev [email protected]
+//
+
+using System;
+using System.ComponentModel;
+using ShiftUI.Design;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ public class PropertiesTab : PropertyTab
+ {
+ public PropertiesTab ()
+ {
+ }
+
+ public override PropertyDescriptorCollection GetProperties (object component, Attribute[] attributes)
+ {
+ return GetProperties (null, component, attributes);
+ }
+
+ public override PropertyDescriptorCollection GetProperties (ITypeDescriptorContext context, object component, Attribute[] attributes)
+ {
+ if (component == null)
+ return new PropertyDescriptorCollection (null);
+ if (attributes == null)
+ attributes = new Attribute[] { BrowsableAttribute.Yes };
+
+ PropertyDescriptorCollection properties = null;
+ TypeConverter converter = TypeDescriptor.GetConverter (component);
+ if (converter != null && converter.GetPropertiesSupported ())
+ properties = converter.GetProperties (context, component, attributes);
+ if (properties == null) // try 3: TypeDescriptor
+ properties = TypeDescriptor.GetProperties (component, attributes);
+ return properties;
+ }
+
+ public override PropertyDescriptor GetDefaultProperty (object obj)
+ {
+ if (obj == null)
+ return null;
+
+ return TypeDescriptor.GetDefaultProperty (obj);
+ }
+
+ public override string HelpKeyword {
+ get { return "vs.properties"; }
+ }
+
+ public override string TabName {
+ get { return "Properties"; }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/PropertyGrid.cs b/source/ShiftUI/Widgets/PropertyGrid.cs
new file mode 100644
index 0000000..36af17c
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertyGrid.cs
@@ -0,0 +1,1626 @@
+// 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-2008 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+
+// NOT COMPLETE
+
+using System;
+using System.IO;
+using System.Drawing;
+using System.Drawing.Design;
+using System.ComponentModel;
+using System.Collections;
+using System.ComponentModel.Design;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using ShiftUI.Design;
+using ShiftUI.PropertyGridInternal;
+
+namespace ShiftUI
+{
+ [Designer("ShiftUI.Design.PropertyGridDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class PropertyGrid : ShiftUI.ContainerWidget
+ {
+ #region Private Members
+
+
+ private const string UNCATEGORIZED_CATEGORY_LABEL = "Misc";
+ private AttributeCollection browsable_attributes = null;
+ private bool can_show_commands = false;
+ private Color commands_back_color;
+ private Color commands_fore_color;
+ private bool commands_visible;
+ private bool commands_visible_if_available;
+ private Point context_menu_default_location;
+ private bool large_buttons;
+ private Color line_color;
+ private PropertySort property_sort;
+ private PropertyTabCollection property_tabs;
+ private GridEntry selected_grid_item;
+ private GridEntry root_grid_item;
+ private object[] selected_objects;
+ private PropertyTab properties_tab;
+ private PropertyTab selected_tab;
+
+ private ImageList toolbar_imagelist;
+ private Image categorized_image;
+ private Image alphabetical_image;
+ private Image propertypages_image;
+ private PropertyToolBarButton categorized_toolbarbutton;
+ private PropertyToolBarButton alphabetic_toolbarbutton;
+ private PropertyToolBarButton propertypages_toolbarbutton;
+ private PropertyToolBarSeparator separator_toolbarbutton;
+ private bool events_tab_visible;
+
+ private PropertyToolBar toolbar;
+
+ private PropertyGridView property_grid_view;
+ private Splitter splitter;
+ private Panel help_panel;
+ private Label help_title_label;
+ private Label help_description_label;
+ private MenuItem reset_menuitem;
+ private MenuItem description_menuitem;
+
+ private Color category_fore_color;
+ private Color commands_active_link_color;
+ private Color commands_disabled_link_color;
+ private Color commands_link_color;
+ #endregion // Private Members
+
+ #region Contructors
+ public PropertyGrid ()
+ {
+ selected_objects = new object[0];
+ property_tabs = new PropertyTabCollection(this);
+
+ line_color = SystemColors.ScrollBar;
+ category_fore_color = line_color;
+ commands_visible = false;
+ commands_visible_if_available = false;
+ property_sort = PropertySort.CategorizedAlphabetical;
+ property_grid_view = new PropertyGridView(this);
+
+ splitter = new Splitter();
+ splitter.Dock = DockStyle.Bottom;
+
+ help_panel = new Panel();
+ help_panel.Dock = DockStyle.Bottom;
+ //help_panel.DockPadding.All = 3;
+ help_panel.Height = 50;
+ help_panel.BackColor = SystemColors.Control;
+
+
+ help_title_label = new Label();
+ help_title_label.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
+ help_title_label.Name = "help_title_label";
+ help_title_label.Font = new Font(this.Font,FontStyle.Bold);
+ help_title_label.Location = new Point(2,2);
+ help_title_label.Height = 17;
+ help_title_label.Width = help_panel.Width - 4;
+
+
+ help_description_label = new Label();
+ help_description_label.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
+ help_description_label.AutoEllipsis = true;
+ help_description_label.AutoSize = false;
+ help_description_label.Font = this.Font;
+ help_description_label.Location = new Point(2,help_title_label.Top+help_title_label.Height);
+ help_description_label.Width = help_panel.Width - 4;
+ help_description_label.Height = help_panel.Height - help_description_label.Top - 2;
+
+ help_panel.Widgets.Add(help_description_label);
+ help_panel.Widgets.Add(help_title_label);
+ help_panel.Paint+=new PaintEventHandler(help_panel_Paint);
+
+ toolbar = new PropertyToolBar();
+ toolbar.Dock = DockStyle.Top;
+ categorized_toolbarbutton = new PropertyToolBarButton ();
+ categorized_toolbarbutton.Pushed = true;
+ alphabetic_toolbarbutton = new PropertyToolBarButton ();
+ propertypages_toolbarbutton = new PropertyToolBarButton ();
+ separator_toolbarbutton = new PropertyToolBarSeparator ();
+ context_menu_default_location = Point.Empty;
+
+ toolbar.Appearance = ToolBarAppearance.Flat;
+ toolbar.AutoSize = false;
+
+ toolbar.Location = new System.Drawing.Point(0, 0);
+ toolbar.ShowToolTips = true;
+ toolbar.Size = new System.Drawing.Size(256, 27);
+ toolbar.TabIndex = 0;
+
+ toolbar.Items.AddRange (new ToolStripItem [] {categorized_toolbarbutton,
+ alphabetic_toolbarbutton,
+ new PropertyToolBarSeparator (),
+ propertypages_toolbarbutton});
+ //toolbar.ButtonSize = new System.Drawing.Size (20, 20);
+ categorized_toolbarbutton.Click += new EventHandler (toolbarbutton_clicked);
+ alphabetic_toolbarbutton.Click += new EventHandler (toolbarbutton_clicked);
+ propertypages_toolbarbutton.Click += new EventHandler (toolbarbutton_clicked);
+
+ categorized_toolbarbutton.Style = ToolBarButtonStyle.ToggleButton;
+ categorized_toolbarbutton.ToolTipText = "Categorized";
+
+ alphabetic_toolbarbutton.Style = ToolBarButtonStyle.ToggleButton;
+ alphabetic_toolbarbutton.ToolTipText = "Alphabetic";
+
+ propertypages_toolbarbutton.Enabled = false;
+ propertypages_toolbarbutton.Style = ToolBarButtonStyle.ToggleButton;
+ propertypages_toolbarbutton.ToolTipText = "Property Pages";
+
+ properties_tab = CreatePropertyTab (this.DefaultTabType);
+ selected_tab = properties_tab;
+ RefreshToolbar (property_tabs);
+
+ BorderHelperControl helper = new BorderHelperControl ();
+ helper.Dock = DockStyle.Fill;
+ helper.Widgets.Add (property_grid_view);
+
+ this.Widgets.Add(helper);
+ this.Widgets.Add(toolbar);
+ this.Widgets.Add(splitter);
+ this.Widgets.Add(help_panel);
+ this.Name = "PropertyGrid";
+ this.Size = new System.Drawing.Size(256, 400);
+ }
+ #endregion // Constructors
+
+ #region Public Instance Properties
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ public AttributeCollection BrowsableAttributes {
+ get {
+ if (browsable_attributes == null) {
+ browsable_attributes = new AttributeCollection (new Attribute[] {
+ BrowsableAttribute.Yes });
+ }
+ return browsable_attributes;
+ }
+ set {
+ if (browsable_attributes == value)
+ return;
+
+ if (browsable_attributes == null || browsable_attributes.Count == 0)
+ browsable_attributes = null;
+ else
+ browsable_attributes = value;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool AutoScroll {
+ get {
+ return base.AutoScroll;
+ }
+ set {
+ base.AutoScroll = value;
+ }
+ }
+
+ public override Color BackColor {
+ get {
+ return base.BackColor;
+ }
+
+ set {
+ base.BackColor = value;
+ toolbar.BackColor = value;
+ Refresh ();
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get {
+ return base.BackgroundImage;
+ }
+ set {
+ base.BackgroundImage = value;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [Browsable(false)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ public virtual bool CanShowCommands {
+ get {
+ return can_show_commands;
+ }
+ }
+
+ [DefaultValue(typeof(Color), "ControlText")]
+ public Color CategoryForeColor {
+ get {
+ return category_fore_color;
+ }
+ set {
+ if (category_fore_color != value) {
+ category_fore_color = value;
+ Invalidate ();
+ }
+ }
+ }
+
+ public Color CommandsBackColor {
+ get {
+ return commands_back_color;
+ }
+
+ set {
+ if (commands_back_color == value) {
+ return;
+ }
+ commands_back_color = value;
+ }
+ }
+
+ public Color CommandsForeColor {
+ get {
+ return commands_fore_color;
+ }
+
+ set {
+ if (commands_fore_color == value) {
+ return;
+ }
+ commands_fore_color = value;
+ }
+ }
+
+ public Color CommandsActiveLinkColor {
+ get {
+ return commands_active_link_color;
+ }
+ set {
+ commands_active_link_color = value;
+ }
+ }
+
+ public Color CommandsDisabledLinkColor {
+ get {
+ return commands_disabled_link_color;
+ }
+ set {
+ commands_disabled_link_color = value;
+ }
+ }
+
+ public Color CommandsLinkColor {
+ get {
+ return commands_link_color;
+ }
+ set {
+ commands_link_color = value;
+ }
+ }
+
+ [BrowsableAttribute (false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ [MonoTODO ("Commands are not implemented yet.")]
+ public virtual bool CommandsVisible {
+ get {
+ return commands_visible;
+ }
+ }
+
+ [DefaultValue (true)]
+ public virtual bool CommandsVisibleIfAvailable {
+ get {
+ return commands_visible_if_available;
+ }
+
+ set {
+ if (commands_visible_if_available == value) {
+ return;
+ }
+ commands_visible_if_available = value;
+ }
+ }
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ public Point ContextMenuDefaultLocation {
+ get {
+ return context_menu_default_location;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public new Widget.WidgetCollection Widgets {
+ get {
+ return base.Widgets;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+ set {
+ base.ForeColor = value;
+ }
+ }
+
+ [DefaultValue ("Color [Widget]")]
+ public Color HelpBackColor {
+ get {
+ return help_panel.BackColor;
+ }
+ set {
+ help_panel.BackColor = value;
+ }
+ }
+
+ [DefaultValue ("Color [ControlText]")]
+ public Color HelpForeColor {
+ get {
+ return help_panel.ForeColor;
+ }
+
+ set {
+ help_panel.ForeColor = value;
+ }
+ }
+
+ [DefaultValue(true)]
+ [Localizable(true)]
+ public virtual bool HelpVisible {
+ get {
+ return help_panel.Visible;
+ }
+
+ set {
+ splitter.Visible = value;
+ help_panel.Visible = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool LargeButtons {
+ get {
+ return large_buttons;
+ }
+
+ set {
+ if (large_buttons == value) {
+ return;
+ }
+
+ large_buttons = value;
+ }
+ }
+
+ [DefaultValue ("Color [InactiveBorder]")]
+ public Color LineColor {
+ get {
+ return line_color;
+ }
+
+ set {
+ if (line_color == value) {
+ return;
+ }
+
+ line_color = value;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ [DefaultValue(PropertySort.CategorizedAlphabetical)]
+ public PropertySort PropertySort {
+ get {
+ return property_sort;
+ }
+
+ set {
+ if (!Enum.IsDefined (typeof (PropertySort), value))
+ throw new InvalidEnumArgumentException ("value", (int) value, typeof (PropertySort));
+ if (property_sort == value)
+ return;
+
+ // we do not need to update the the grid items and fire
+ // a PropertySortChanged event when switching between
+ // Categorized and CateogizedAlphabetical
+ bool needUpdate = (property_sort & PropertySort.Categorized) == 0 ||
+ (value & PropertySort.Categorized) == 0;
+ property_sort = value;
+ if (needUpdate) {
+ UpdateSortLayout (root_grid_item);
+ // update selection
+ if (selected_grid_item != null) {
+ if (selected_grid_item.GridItemType == GridItemType.Category &&
+ (value == PropertySort.Alphabetical || value == PropertySort.NoSort))
+ SelectItemCore (null, null);
+ else
+ SelectItemCore (null, selected_grid_item);
+ }
+ property_grid_view.UpdateView ();
+
+ EventHandler eh = (EventHandler)(Events [PropertySortChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+ UpdatePropertySortButtonsState ();
+ }
+ }
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ public PropertyTabCollection PropertyTabs {
+ get { return property_tabs; }
+ }
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ public GridItem SelectedGridItem {
+ get { return selected_grid_item; }
+ set {
+ if (value == null)
+ throw new ArgumentException ("GridItem specified to PropertyGrid.SelectedGridItem must be a valid GridItem.");
+ if (value != selected_grid_item) {
+ GridEntry oldItem = selected_grid_item;
+ SelectItemCore (oldItem, (GridEntry)value);
+ OnSelectedGridItemChanged (new SelectedGridItemChangedEventArgs (oldItem, value));
+ }
+ }
+ }
+
+ internal GridItem RootGridItem {
+ get { return root_grid_item; }
+ }
+
+ private void UpdateHelp (GridItem item)
+ {
+ if (item == null) {
+ help_title_label.Text = string.Empty;
+ help_description_label.Text = string.Empty;
+ } else {
+ help_title_label.Text = item.Label;
+ if (item.PropertyDescriptor != null)
+ this.help_description_label.Text = item.PropertyDescriptor.Description;
+ }
+ }
+
+ private void SelectItemCore (GridEntry oldItem, GridEntry item)
+ {
+ UpdateHelp (item);
+ selected_grid_item = item;
+ property_grid_view.SelectItem (oldItem, item);
+ }
+
+ internal void OnPropertyValueChangedInternal (GridItem item, object property_value)
+ {
+ property_grid_view.UpdateView ();
+ OnPropertyValueChanged (new PropertyValueChangedEventArgs (item, property_value));
+ }
+
+ internal void OnExpandItem (GridEntry item)
+ {
+ property_grid_view.ExpandItem (item);
+ }
+
+ internal void OnCollapseItem (GridEntry item)
+ {
+ property_grid_view.CollapseItem (item);
+ }
+
+ internal DialogResult ShowError (string text)
+ {
+ return this.ShowError (text, MessageBoxButtons.OK);
+ }
+
+ internal DialogResult ShowError (string text, MessageBoxButtons buttons)
+ {
+ if (text == null)
+ throw new ArgumentNullException ("text");
+ MessageBox.Show (text, "Properties Window");
+ return DialogResult.OK;
+ }
+
+ [DefaultValue(null)]
+ [TypeConverter("ShiftUI.PropertyGrid+SelectedObjectConverter, " + Consts.AssemblySystem_Windows_Forms)]
+ public object SelectedObject {
+ get {
+ if (selected_objects.Length > 0)
+ return selected_objects[0];
+ return null;
+ }
+
+ set {
+ if (selected_objects != null && selected_objects.Length == 1 && selected_objects[0] == value)
+ return;
+ if (value == null)
+ SelectedObjects = new object[0];
+ else
+ SelectedObjects = new object[] {value};
+
+ }
+ }
+
+ [BrowsableAttribute(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public object[] SelectedObjects {
+ get {
+ return selected_objects;
+ }
+
+ set {
+ root_grid_item = null;
+ SelectItemCore (null, null); // unselect current item in the view
+ if (value != null) {
+ for (int i = 0; i < value.Length; i++) {
+ if (value [i] == null)
+ throw new ArgumentException (String.Format ("Item {0} in the objs array is null.", i));
+ }
+ selected_objects = value;
+ } else {
+ selected_objects = new object [0];
+ }
+
+ ShowEventsButton (false);
+ PopulateGrid (selected_objects);
+ RefreshTabs(PropertyTabScope.Component);
+ if (root_grid_item != null)
+ SelectItemCore (null, GetDefaultPropertyItem (root_grid_item, selected_tab));
+ property_grid_view.UpdateView ();
+ OnSelectedObjectsChanged (EventArgs.Empty);
+ }
+ }
+
+ [BrowsableAttribute(false)]
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public PropertyTab SelectedTab {
+ get { return selected_tab; }
+ }
+
+ public override ISite Site {
+ get { return base.Site; }
+ set { base.Site = value; }
+ }
+
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ [DefaultValue(true)]
+ public virtual bool ToolbarVisible {
+ get { return toolbar.Visible; }
+ set {
+ if (toolbar.Visible == value) {
+ return;
+ }
+
+ toolbar.Visible = value;
+ }
+ }
+
+ protected ToolStripRenderer ToolStripRenderer {
+ get {
+ if (toolbar != null) {
+ return toolbar.Renderer;
+ }
+ return null;
+ }
+ set {
+ if (toolbar != null) {
+ toolbar.Renderer = value;
+ }
+ }
+ }
+
+ [DefaultValue ("Color [Window]")]
+ public Color ViewBackColor {
+ get { return property_grid_view.BackColor; }
+ set {
+ if (property_grid_view.BackColor == value) {
+ return;
+ }
+
+ property_grid_view.BackColor = value;
+ }
+ }
+
+ [DefaultValue ("Color [WindowText]")]
+ public Color ViewForeColor {
+ get { return property_grid_view.ForeColor; }
+ set {
+ if (property_grid_view.ForeColor == value) {
+ return;
+ }
+
+ property_grid_view.ForeColor = value;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool UseCompatibleTextRendering {
+ get { return use_compatible_text_rendering; }
+ set {
+ if (use_compatible_text_rendering != value) {
+ use_compatible_text_rendering = value;
+ if (Parent != null)
+ Parent.PerformLayout (this, "UseCompatibleTextRendering");
+ Invalidate ();
+ }
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+
+ protected override Size DefaultSize {
+ get { return base.DefaultSize; }
+ }
+
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
+ protected virtual Type DefaultTabType {
+ get { return typeof(PropertiesTab); }
+ }
+
+ protected bool DrawFlatToolbar {
+ get { return (toolbar.Appearance == ToolBarAppearance.Flat); }
+ set {
+ if (value)
+ toolbar.Appearance = ToolBarAppearance.Flat;
+ else
+ toolbar.Appearance = ToolBarAppearance.Normal;
+ }
+ }
+
+ protected internal override bool ShowFocusCues {
+ get { return base.ShowFocusCues; }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+
+ protected override void Dispose(bool disposing) {
+ base.Dispose(disposing);
+ }
+
+ public void CollapseAllGridItems ()
+ {
+ GridEntry category = FindCategoryItem (selected_grid_item);
+ if (category != null)
+ SelectedGridItem = category;
+ CollapseItemRecursive (root_grid_item);
+ property_grid_view.UpdateView ();
+ }
+
+ private void CollapseItemRecursive (GridItem item)
+ {
+ if (item == null)
+ return;
+
+ foreach (GridItem child in item.GridItems) {
+ CollapseItemRecursive (child);
+ if (child.Expandable)
+ child.Expanded = false;
+ }
+ }
+
+ private GridEntry FindCategoryItem (GridEntry entry)
+ {
+ if (entry == null || (property_sort != PropertySort.Categorized &&
+ property_sort != PropertySort.CategorizedAlphabetical))
+ return null;
+
+ if (entry.GridItemType == GridItemType.Category)
+ return entry;
+
+ GridEntry category = null;
+ GridItem current = (GridItem)entry;
+ while (category == null) {
+ if (current.Parent != null && current.Parent.GridItemType == GridItemType.Category)
+ category = (GridEntry) current.Parent;
+ current = current.Parent;
+ if (current == null)
+ break;
+ }
+ return (GridEntry) category;
+ }
+
+ public void ExpandAllGridItems ()
+ {
+ ExpandItemRecursive (root_grid_item);
+ property_grid_view.UpdateView ();
+ }
+
+ private void ExpandItemRecursive (GridItem item)
+ {
+ if (item == null)
+ return;
+
+ foreach (GridItem child in item.GridItems) {
+ ExpandItemRecursive (child);
+ if (child.Expandable)
+ child.Expanded = true;
+ }
+ }
+
+ public override void Refresh ()
+ {
+ base.Refresh ();
+ // force a full reload here
+ SelectedObjects = SelectedObjects;
+ }
+
+ private void toolbar_Clicked (PropertyToolBarButton button)
+ {
+ if (button == null)
+ return;
+
+ if (button == alphabetic_toolbarbutton) {
+ this.PropertySort = PropertySort.Alphabetical;
+ alphabetic_toolbarbutton.Pushed = true;
+ categorized_toolbarbutton.Pushed = false;
+ } else if (button == categorized_toolbarbutton) {
+ this.PropertySort = PropertySort.CategorizedAlphabetical;
+ categorized_toolbarbutton.Pushed = true;
+ alphabetic_toolbarbutton.Pushed = false;
+ } else {
+ if (button.Enabled)
+ SelectPropertyTab (button.PropertyTab);
+ }
+ }
+
+ private void toolbarbutton_clicked (object o, EventArgs args)
+ {
+ toolbar_Clicked (o as PropertyToolBarButton);
+ }
+
+ private void SelectPropertyTab (PropertyTab propertyTab)
+ {
+ if (propertyTab != null && selected_tab != propertyTab) {
+ foreach (object toolbarItem in toolbar.Items) {
+ PropertyToolBarButton button = toolbarItem as PropertyToolBarButton;
+ if (button != null && button.PropertyTab != null) {
+ if (button.PropertyTab == selected_tab)
+ button.Pushed = false;
+ else if (button.PropertyTab == propertyTab)
+ button.Pushed = true;
+ }
+ }
+ selected_tab = propertyTab;
+ PopulateGrid (selected_objects);
+ SelectItemCore (null, GetDefaultPropertyItem (root_grid_item, selected_tab));
+ property_grid_view.UpdateView ();
+ }
+ }
+
+ private void UpdatePropertySortButtonsState ()
+ {
+ if (property_sort == PropertySort.NoSort) {
+ alphabetic_toolbarbutton.Pushed = false;
+ categorized_toolbarbutton.Pushed = false;
+ } else if (property_sort == PropertySort.Alphabetical) {
+ alphabetic_toolbarbutton.Pushed = true;
+ categorized_toolbarbutton.Pushed = false;
+ } else if (property_sort == PropertySort.Categorized ||
+ property_sort == PropertySort.CategorizedAlphabetical) {
+ alphabetic_toolbarbutton.Pushed = false;
+ categorized_toolbarbutton.Pushed = true;
+ }
+ }
+
+ protected void ShowEventsButton (bool value)
+ {
+ if (value && property_tabs.Contains (typeof (EventsTab)))
+ events_tab_visible = true;
+ else
+ events_tab_visible = false;
+ RefreshTabs (PropertyTabScope.Component);
+ }
+
+ public void RefreshTabs (PropertyTabScope tabScope)
+ {
+ property_tabs.Clear (tabScope);
+ if (selected_objects != null) {
+ Type[] tabTypes = null;
+ PropertyTabScope[] tabScopes = null;
+
+ if (events_tab_visible && property_tabs.Contains (typeof (EventsTab)))
+ property_tabs.InsertTab (0, properties_tab, PropertyTabScope.Component);
+
+ GetMergedPropertyTabs (selected_objects, out tabTypes, out tabScopes);
+ if (tabTypes != null && tabScopes != null && tabTypes.Length > 0) {
+ bool selectedTabPreserved = false;
+ for (int i=0; i < tabTypes.Length; i++) {
+ property_tabs.AddTabType (tabTypes[i], tabScopes[i]);
+ if (tabTypes[i] == selected_tab.GetType ())
+ selectedTabPreserved = true;
+ }
+ if (!selectedTabPreserved)
+ SelectPropertyTab (properties_tab);
+ }
+ } else {
+ SelectPropertyTab (properties_tab);
+ }
+ RefreshToolbar (property_tabs);
+ }
+
+ private void RefreshToolbar (PropertyTabCollection tabs)
+ {
+ EnsurePropertiesTab ();
+
+ toolbar.SuspendLayout ();
+ toolbar.Items.Clear ();
+
+ int imageIndex = 0;
+ toolbar.Items.Add (categorized_toolbarbutton);
+ categorized_toolbarbutton.DisplayStyle = ToolStripItemDisplayStyle.Text;
+ categorized_toolbarbutton.Text = "Categorized";
+ imageIndex++;
+ toolbar.Items.Add (alphabetic_toolbarbutton);
+ alphabetic_toolbarbutton.DisplayStyle = ToolStripItemDisplayStyle.Text;
+ alphabetic_toolbarbutton.Text = "Alphabetical";
+ imageIndex++;
+ toolbar.Items.Add (separator_toolbarbutton);
+ if (tabs != null && tabs.Count > 0) {
+ foreach (PropertyTab tab in tabs) {
+ PropertyToolBarButton button = new PropertyToolBarButton (tab);
+ toolbar.Items.Add (button);
+ if (tab == selected_tab)
+ button.Pushed = true;
+ }
+ toolbar.Items.Add (new PropertyToolBarSeparator ());
+ }
+
+ toolbar.Items.Add (propertypages_toolbarbutton);
+
+ toolbar.ResumeLayout ();
+ }
+
+ private void EnsurePropertiesTab ()
+ {
+ if (property_tabs == null)
+ return;
+
+ if (property_tabs.Count > 0 && !property_tabs.Contains (this.DefaultTabType))
+ property_tabs.InsertTab (0, properties_tab, PropertyTabScope.Component);
+ }
+
+ private void GetMergedPropertyTabs (object[] objects, out Type[] tabTypes, out PropertyTabScope[] tabScopes)
+ {
+ tabTypes = null;
+ tabScopes = null;
+ if (objects == null || objects.Length == 0)
+ return;
+
+ ArrayList intersection = null;
+ ArrayList scopes = new ArrayList ();
+ for (int i=0; i < objects.Length; i++) {
+ if (objects[i] == null)
+ continue;
+ PropertyTabAttribute tabAttribute = (PropertyTabAttribute)TypeDescriptor.GetAttributes (objects[i])[typeof (PropertyTabAttribute)];
+ if (tabAttribute == null || tabAttribute.TabClasses == null || tabAttribute.TabClasses.Length == 0)
+ return;
+
+ ArrayList new_intersection = new ArrayList ();
+ scopes.Clear ();
+ IList currentIntersection = (i == 0 ? (IList)tabAttribute.TabClasses : (IList)intersection);
+ for (int j=0; j < currentIntersection.Count; j++) {
+ if ((Type)intersection[j] == tabAttribute.TabClasses[j]) {
+ new_intersection.Add (tabAttribute.TabClasses[j]);
+ scopes.Add (tabAttribute.TabScopes[j]);
+ }
+ }
+ intersection = new_intersection;
+ }
+
+ tabTypes = new Type[intersection.Count];
+ intersection.CopyTo (tabTypes);
+ tabScopes = new PropertyTabScope[tabTypes.Length];
+ scopes.CopyTo (tabScopes);
+ }
+
+ public void ResetSelectedProperty()
+ {
+ if (selected_grid_item == null)
+ return;
+ selected_grid_item.ResetValue ();
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+
+ protected virtual PropertyTab CreatePropertyTab (Type tabType)
+ {
+ if (!typeof(PropertyTab).IsAssignableFrom (tabType))
+ return null;
+
+ PropertyTab tab = null;
+
+ ConstructorInfo ctor = tabType.GetConstructor (new Type[] { typeof (IServiceProvider) });
+ if (ctor != null)
+ tab = (PropertyTab)ctor.Invoke (new object[] { this.Site });
+ else
+ tab = (PropertyTab)Activator.CreateInstance (tabType);
+ return tab;
+ }
+
+ protected override void OnEnabledChanged (EventArgs e) {
+ base.OnEnabledChanged (e);
+ }
+
+ protected override void OnFontChanged(EventArgs e) {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnGotFocus(EventArgs e) {
+ base.OnGotFocus(e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e) {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected override void OnMouseDown (MouseEventArgs me) {
+ base.OnMouseDown (me);
+ }
+
+ protected override void OnMouseMove (MouseEventArgs me) {
+ base.OnMouseMove (me);
+ }
+
+ protected override void OnMouseUp (MouseEventArgs me) {
+ base.OnMouseUp (me);
+ }
+
+ protected void OnNotifyPropertyValueUIItemsChanged(object sender, EventArgs e)
+ {
+ property_grid_view.UpdateView ();
+ }
+
+ protected override void OnPaint (PaintEventArgs pevent) {
+ pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
+ base.OnPaint (pevent);
+ }
+
+ protected virtual void OnPropertySortChanged(EventArgs e) {
+ EventHandler eh = (EventHandler) Events [PropertySortChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnPropertyTabChanged (PropertyTabChangedEventArgs e)
+ {
+ PropertyTabChangedEventHandler eh = (PropertyTabChangedEventHandler)(Events [PropertyTabChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnPropertyValueChanged (PropertyValueChangedEventArgs e) {
+ PropertyValueChangedEventHandler eh = (PropertyValueChangedEventHandler)(Events [PropertyValueChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnResize (EventArgs e) {
+ base.OnResize (e);
+ }
+
+ protected virtual void OnSelectedGridItemChanged (SelectedGridItemChangedEventArgs e) {
+ SelectedGridItemChangedEventHandler eh = (SelectedGridItemChangedEventHandler)(Events [SelectedGridItemChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnSelectedObjectsChanged (EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [SelectedObjectsChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnSystemColorsChanged (EventArgs e) {
+ base.OnSystemColorsChanged (e);
+ }
+
+ protected override void OnVisibleChanged (EventArgs e) {
+ base.OnVisibleChanged (e);
+ }
+
+ protected override bool ProcessDialogKey (Keys keyData) {
+ return base.ProcessDialogKey (keyData);
+ }
+
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ protected override void ScaleCore (float dx, float dy) {
+ base.ScaleCore (dx, dy);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ base.WndProc (ref m);
+ }
+ #endregion
+
+ #region Events
+ static object PropertySortChangedEvent = new object ();
+ static object PropertyTabChangedEvent = new object ();
+ static object PropertyValueChangedEvent = new object ();
+ static object SelectedGridItemChangedEvent = new object ();
+ static object SelectedObjectsChangedEvent = new object ();
+
+ public event EventHandler PropertySortChanged {
+ add { Events.AddHandler (PropertySortChangedEvent, value); }
+ remove { Events.RemoveHandler (PropertySortChangedEvent, value); }
+ }
+
+ public event PropertyTabChangedEventHandler PropertyTabChanged {
+ add { Events.AddHandler (PropertyTabChangedEvent, value); }
+ remove { Events.RemoveHandler (PropertyTabChangedEvent, value); }
+ }
+
+ public event PropertyValueChangedEventHandler PropertyValueChanged {
+ add { Events.AddHandler (PropertyValueChangedEvent, value); }
+ remove { Events.RemoveHandler (PropertyValueChangedEvent, value); }
+ }
+
+ public event SelectedGridItemChangedEventHandler SelectedGridItemChanged {
+ add { Events.AddHandler (SelectedGridItemChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedGridItemChangedEvent, value); }
+ }
+
+ public event EventHandler SelectedObjectsChanged {
+ add { Events.AddHandler (SelectedObjectsChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedObjectsChangedEvent, value); }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event KeyEventHandler KeyDown {
+ add { base.KeyDown += value; }
+ remove { base.KeyDown -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event KeyPressEventHandler KeyPress {
+ add { base.KeyPress += value; }
+ remove { base.KeyPress -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event KeyEventHandler KeyUp {
+ add { base.KeyUp += value; }
+ remove { base.KeyUp -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public new event MouseEventHandler MouseDown {
+ add { base.MouseDown += value; }
+ remove { base.MouseDown -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event EventHandler MouseEnter {
+ add { base.MouseEnter += value; }
+ remove { base.MouseEnter -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event EventHandler MouseLeave {
+ add { base.MouseLeave += value; }
+ remove { base.MouseLeave -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event MouseEventHandler MouseMove {
+ add { base.MouseMove += value; }
+ remove { base.MouseMove -= value; }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ [Browsable(false)]
+ public new event MouseEventHandler MouseUp {
+ add { base.MouseUp += value; }
+ remove { base.MouseUp -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable(false)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion
+
+ #region PropertyTabCollection Class
+ public class PropertyTabCollection : ICollection, IEnumerable
+ {
+ ArrayList property_tabs;
+ ArrayList property_tabs_scopes;
+ PropertyGrid property_grid;
+
+ internal PropertyTabCollection (PropertyGrid propertyGrid)
+ {
+ property_grid = propertyGrid;
+ property_tabs = new ArrayList ();
+ property_tabs_scopes = new ArrayList ();
+ }
+
+ public PropertyTab this[int index] {
+ get { return (PropertyTab)property_tabs[index]; }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return property_tabs.IsSynchronized; }
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ property_tabs.CopyTo (dest, index);
+ }
+
+ object ICollection.SyncRoot {
+ get { return property_tabs.SyncRoot; }
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return property_tabs.GetEnumerator ();
+ }
+
+ public int Count {
+ get { return property_tabs.Count; }
+ }
+
+ public void AddTabType (Type propertyTabType)
+ {
+ AddTabType (propertyTabType, PropertyTabScope.Global);
+ }
+
+ public void AddTabType (Type propertyTabType, PropertyTabScope tabScope)
+ {
+ if (propertyTabType == null)
+ throw new ArgumentNullException ("propertyTabType");
+
+ // Avoid duplicates
+ if (this.Contains (propertyTabType))
+ return;
+ PropertyTab tab = property_grid.CreatePropertyTab (propertyTabType);
+ if (tab != null) {
+ property_tabs.Add (tab);
+ property_tabs_scopes.Add (tabScope);
+ }
+ property_grid.RefreshToolbar (this);
+ }
+
+ internal PropertyTabScope GetTabScope (PropertyTab tab)
+ {
+ if (tab == null)
+ throw new ArgumentNullException ("tab");
+
+ int index = property_tabs.IndexOf (tab);
+ if (index != -1)
+ return (PropertyTabScope)property_tabs_scopes[index];
+ return PropertyTabScope.Global;
+ }
+
+ internal void InsertTab (int index, PropertyTab propertyTab, PropertyTabScope tabScope)
+ {
+ if (propertyTab == null)
+ throw new ArgumentNullException ("propertyTab");
+
+ if (!this.Contains (propertyTab.GetType ())) {
+ property_tabs.Insert (index, propertyTab);
+ property_tabs_scopes.Insert (index, tabScope);
+ }
+ }
+
+ internal bool Contains (Type propertyType)
+ {
+ if (propertyType == null)
+ throw new ArgumentNullException ("propertyType");
+
+ foreach (PropertyTab t in property_tabs) {
+ if (t.GetType () == propertyType)
+ return true;
+ }
+ return false;
+ }
+
+ internal PropertyTab this[Type tabType] {
+ get {
+ foreach (PropertyTab tab in property_tabs) {
+ if (tabType == tab.GetType ())
+ return tab;
+ }
+ return null;
+ }
+ }
+
+ public void Clear (PropertyTabScope tabScope)
+ {
+ ArrayList toRemove = new ArrayList ();
+ for (int i=0; i < property_tabs_scopes.Count; i++) {
+ if ((PropertyTabScope)property_tabs_scopes[i] == tabScope)
+ toRemove.Add (i);
+ }
+ foreach (int indexToRemove in toRemove) {
+ property_tabs.RemoveAt (indexToRemove);
+ property_tabs_scopes.RemoveAt (indexToRemove);
+ }
+ property_grid.RefreshToolbar (this);
+ }
+
+ public void RemoveTabType (Type propertyTabType)
+ {
+ if (propertyTabType == null)
+ throw new ArgumentNullException ("propertyTabType");
+
+ ArrayList toRemove = new ArrayList ();
+ for (int i=0; i < property_tabs.Count; i++) {
+ if (property_tabs[i].GetType () == propertyTabType)
+ toRemove.Add (i);
+ }
+ foreach (int indexToRemove in toRemove) {
+ property_tabs.RemoveAt (indexToRemove);
+ property_tabs_scopes.RemoveAt (indexToRemove);
+ }
+ property_grid.RefreshToolbar (this);
+ }
+ }
+ #endregion // PropertyTabCollection Class
+
+ #region Private Helper Methods
+
+ private GridItem FindFirstPropertyItem (GridItem root)
+ {
+ if (root.GridItemType == GridItemType.Property)
+ return root;
+
+ foreach (GridItem item in root.GridItems) {
+ GridItem subitem = FindFirstPropertyItem (item);
+ if (subitem != null)
+ return subitem;
+ }
+
+ return null;
+ }
+
+ private GridEntry GetDefaultPropertyItem (GridEntry rootItem, PropertyTab propertyTab)
+ {
+ if (rootItem == null || rootItem.GridItems.Count == 0 || propertyTab == null)
+ return null;
+ object[] propertyOwners = rootItem.Values;
+ if (propertyOwners == null || propertyOwners.Length == 0 || propertyOwners[0] == null)
+ return null;
+
+ GridItem defaultSelected = null;
+ if (propertyOwners.Length > 1)
+ defaultSelected = rootItem.GridItems[0];
+ else {
+ PropertyDescriptor defaultProperty = propertyTab.GetDefaultProperty (propertyOwners[0]);
+ if (defaultProperty != null)
+ defaultSelected = FindItem (defaultProperty.Name, rootItem);
+ if (defaultSelected == null)
+ defaultSelected = FindFirstPropertyItem (rootItem);
+ }
+
+ return defaultSelected as GridEntry;
+ }
+
+ private GridEntry FindItem (string name, GridEntry rootItem)
+ {
+ if (rootItem == null || name == null)
+ return null;
+
+ if (property_sort == PropertySort.Alphabetical || property_sort == PropertySort.NoSort) {
+ foreach (GridItem item in rootItem.GridItems) {
+ if (item.Label == name) {
+ return (GridEntry)item;
+ }
+ }
+ } else if (property_sort == PropertySort.Categorized ||
+ property_sort == PropertySort.CategorizedAlphabetical) {
+ foreach (GridItem categoryItem in rootItem.GridItems) {
+ foreach (GridItem item in categoryItem.GridItems) {
+ if (item.Label == name) {
+ return (GridEntry)item;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private void OnResetPropertyClick (object sender, EventArgs e)
+ {
+ ResetSelectedProperty();
+ }
+
+ private void OnDescriptionClick (object sender, EventArgs e)
+ {
+ this.HelpVisible = !this.HelpVisible;
+ description_menuitem.Checked = this.HelpVisible;
+ }
+
+ private void PopulateGrid (object[] objects)
+ {
+ if (objects.Length > 0) {
+ root_grid_item = new RootGridEntry (this, objects);
+ root_grid_item.Expanded = true;
+ UpdateSortLayout (root_grid_item);
+ } else {
+ root_grid_item = null;
+ }
+ }
+
+ private void UpdateSortLayout (GridEntry rootItem)
+ {
+ if (rootItem == null)
+ return;
+
+ GridItemCollection reordered = new GridItemCollection ();
+
+ if (property_sort == PropertySort.Alphabetical || property_sort == PropertySort.NoSort) {
+ alphabetic_toolbarbutton.Pushed = true;
+ categorized_toolbarbutton.Pushed = false;
+ foreach (GridItem item in rootItem.GridItems) {
+ if (item.GridItemType == GridItemType.Category) {
+ foreach (GridItem categoryChild in item.GridItems) {
+ reordered.Add (categoryChild);
+ ((GridEntry)categoryChild).SetParent (rootItem);
+ }
+ } else {
+ reordered.Add (item);
+ }
+ }
+ } else if (property_sort == PropertySort.Categorized ||
+ property_sort == PropertySort.CategorizedAlphabetical) {
+ alphabetic_toolbarbutton.Pushed = false;
+ categorized_toolbarbutton.Pushed = true;
+ GridItemCollection categories = new GridItemCollection ();
+
+ foreach (GridItem item in rootItem.GridItems) {
+ if (item.GridItemType == GridItemType.Category) {
+ categories.Add (item);
+ continue;
+ }
+
+ string categoryName = item.PropertyDescriptor.Category;
+ if (categoryName == null)
+ categoryName = UNCATEGORIZED_CATEGORY_LABEL;
+ GridItem category_item = rootItem.GridItems [categoryName];
+ if (category_item == null)
+ category_item = categories [categoryName];
+
+ if (category_item == null) {
+ // Create category grid items if they already don't
+ category_item = new CategoryGridEntry (this, categoryName, rootItem);
+ category_item.Expanded = true;
+ categories.Add (category_item);
+ }
+
+ category_item.GridItems.Add (item);
+ ((GridEntry)item).SetParent (category_item);
+ }
+
+ reordered.AddRange (categories);
+ }
+
+ rootItem.GridItems.Clear ();
+ rootItem.GridItems.AddRange (reordered);
+ }
+
+ private void help_panel_Paint(object sender, PaintEventArgs e) {
+ e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(help_panel.BackColor), help_panel.ClientRectangle );
+ e.Graphics.DrawRectangle(SystemPens.ControlDark, 0,0,help_panel.Width-1,help_panel.Height-1 );
+ }
+
+ #endregion // Private Helper Methods
+
+#region Internal helper classes
+ // as we can not change the color for BorderStyle.FixedSingle and we need the correct
+ // ClientRectangle so that the ScrollBar doesn't draw over the border we need this class
+ internal class BorderHelperControl : Widget {
+
+ public BorderHelperControl ()
+ {
+ BackColor = ThemeEngine.Current.ColorWindow;
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ e.Graphics.DrawRectangle (SystemPens.ControlDark, 0 , 0 , Width - 1, Height - 1);
+ base.OnPaint (e);
+ }
+
+ protected override void OnSizeChanged (EventArgs e)
+ {
+ if (Widgets.Count == 1) {
+ Widget control = Widgets [0];
+
+ if (control.Location.X != 1 || control.Location.Y != 1)
+ control.Location = new Point (1, 1);
+
+ control.Width = ClientRectangle.Width - 2;
+ control.Height = ClientRectangle.Height - 2;
+ }
+ base.OnSizeChanged (e);
+ }
+ }
+
+ private class PropertyToolBarSeparator : ToolStripSeparator
+ {
+ public PropertyToolBarSeparator ()
+ {
+ }
+ }
+
+ private class PropertyToolBarButton : ToolStripButton
+ {
+ private PropertyTab property_tab;
+
+ public PropertyToolBarButton ()
+ {
+ }
+
+ public PropertyToolBarButton (PropertyTab propertyTab)
+ {
+ if (propertyTab == null)
+ throw new ArgumentNullException ("propertyTab");
+ property_tab = propertyTab;
+ }
+
+ public PropertyTab PropertyTab {
+ get { return property_tab; }
+ }
+
+ public bool Pushed {
+ get { return base.Checked; }
+ set { base.Checked = value; }
+ }
+
+ public ToolBarButtonStyle Style {
+ get { return ToolBarButtonStyle.PushButton; }
+ set { }
+ }
+ }
+
+ // needed! this little helper makes it possible to draw a different toolbar border
+ // and toolbar backcolor in ThemeWin32Classic
+ internal class PropertyToolBar : ToolStrip
+ {
+ ToolBarAppearance appearance;
+
+ public PropertyToolBar ()
+ {
+ SetStyle (Widgetstyles.ResizeRedraw, true);
+ GripStyle = ToolStripGripStyle.Hidden;
+ appearance = ToolBarAppearance.Normal;
+ }
+
+ public bool ShowToolTips {
+ get { return base.ShowItemToolTips; }
+ set { base.ShowItemToolTips = value; }
+ }
+
+ public ToolBarAppearance Appearance {
+ get { return appearance; }
+ set {
+ if (value == Appearance)
+ return;
+
+ switch (value) {
+ case ToolBarAppearance.Flat:
+ Renderer = new ToolStripSystemRenderer ();
+ appearance = ToolBarAppearance.Flat;
+ break;
+ case ToolBarAppearance.Normal:
+ ProfessionalColorTable table = new ProfessionalColorTable ();
+ table.UseSystemColors = true;
+ Renderer = new ToolStripProfessionalRenderer (table);
+ appearance = ToolBarAppearance.Normal;
+ break;
+ }
+ }
+ }
+ }
+
+
+ [MonoInternalNote ("not sure what this class does, but it's listed as a type converter for a property in this class, and this causes problems if it's not present")]
+ private class SelectedObjectConverter : TypeConverter
+ {
+ }
+#endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/PropertyGridCommands.cs b/source/ShiftUI/Widgets/PropertyGridCommands.cs
new file mode 100644
index 0000000..d3d376e
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertyGridCommands.cs
@@ -0,0 +1,47 @@
+// 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) 2005 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers [email protected]
+//
+
+
+// COMPLETE
+
+using System;
+using System.ComponentModel.Design;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ public class PropertyGridCommands
+ {
+ public PropertyGridCommands()
+ {
+ }
+
+ public static readonly CommandID Commands;
+ public static readonly CommandID Description;
+ public static readonly CommandID Hide;
+ public static readonly CommandID Reset;
+ protected static readonly Guid wfcMenuCommand;
+ protected static readonly Guid wfcMenuGroup;
+ }
+}
diff --git a/source/ShiftUI/Widgets/PropertyGridTextBox.cs b/source/ShiftUI/Widgets/PropertyGridTextBox.cs
new file mode 100644
index 0000000..a815f28
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertyGridTextBox.cs
@@ -0,0 +1,320 @@
+// 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.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+//
+
+// COMPLETE
+
+using System;
+using System.Drawing;
+using System.ComponentModel;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ internal class PGTextBox : TextBox
+ {
+ private bool _focusing = false;
+
+ public void FocusAt (Point location)
+ {
+ _focusing = true;
+ Point pnt = PointToClient (location);
+ XplatUI.SendMessage (Handle, Msg.WM_LBUTTONDOWN, new IntPtr ((int)MsgButtons.MK_LBUTTON), Widget.MakeParam (pnt.X, pnt.Y));
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ // To be handled by the PropertyGridView
+ if ((keyData & Keys.Alt) != 0 &&
+ (keyData & Keys.KeyCode) == Keys.Down)
+ return true;
+ return base.IsInputKey (keyData);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ // Swallow the first MOUSEMOVE after the focusing WM_LBUTTONDOWN
+ if (_focusing && m.Msg == (int)Msg.WM_MOUSEMOVE) {
+ _focusing = false;
+ return;
+ }
+ base.WndProc (ref m);
+ }
+ }
+
+ internal class PropertyGridTextBox : ShiftUI.UserWidget, IMessageFilter
+ {
+ #region Private Members
+
+ private PGTextBox textbox;
+ private Button dialog_button;
+ private Button dropdown_button;
+ private bool validating = false;
+ private bool filtering = false;
+
+ #endregion Private Members
+
+ #region Contructors
+ public PropertyGridTextBox() {
+ dialog_button = new Button();
+ dropdown_button = new Button();
+ textbox = new PGTextBox ();
+
+ SuspendLayout();
+
+ dialog_button.Dock = DockStyle.Right;
+ dialog_button.BackColor = SystemColors.Control;
+ dialog_button.Size = new Size(16, 16);
+ dialog_button.TabIndex = 1;
+ dialog_button.Visible = false;
+ dialog_button.Click += new System.EventHandler(dialog_button_Click);
+
+ dropdown_button.Dock = DockStyle.Right;
+ dropdown_button.BackColor = SystemColors.Control;
+ dropdown_button.Size = new Size(16, 16);
+ dropdown_button.TabIndex = 2;
+ dropdown_button.Visible = false;
+ dropdown_button.Click += new System.EventHandler(dropdown_button_Click);
+
+ textbox.AutoSize = false;
+ textbox.BorderStyle = BorderStyle.None;
+ textbox.Dock = DockStyle.Fill;
+ textbox.TabIndex = 3;
+
+ Widgets.Add(textbox);
+ Widgets.Add(dropdown_button);
+ Widgets.Add(dialog_button);
+
+ SetStyle (Widgetstyles.Selectable, true);
+
+ ResumeLayout(false);
+
+ dropdown_button.Paint+=new PaintEventHandler(dropdown_button_Paint);
+ dialog_button.Paint+=new PaintEventHandler(dialog_button_Paint);
+ textbox.DoubleClick+=new EventHandler(textbox_DoubleClick);
+ textbox.KeyDown+=new KeyEventHandler(textbox_KeyDown);
+ textbox.GotFocus+=new EventHandler(textbox_GotFocus);
+ }
+
+
+ #endregion Contructors
+
+ #region Protected Instance Properties
+
+ protected override void OnGotFocus (EventArgs args)
+ {
+ base.OnGotFocus (args);
+ // force-disable selection
+ textbox.has_been_focused = true;
+ textbox.Focus ();
+ textbox.SelectionLength = 0;
+ }
+
+ #endregion
+
+ #region Public Instance Properties
+
+ public bool DialogButtonVisible {
+ get{
+ return dialog_button.Visible;
+ }
+ set {
+ dialog_button.Visible = value;
+ }
+ }
+ public bool DropDownButtonVisible {
+ get{
+ return dropdown_button.Visible;
+ }
+ set {
+ dropdown_button.Visible = value;
+ }
+ }
+
+ public new Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+ set {
+ textbox.ForeColor = value;
+ dropdown_button.ForeColor = value;
+ dialog_button.ForeColor = value;
+ base.ForeColor = value;
+ }
+ }
+
+ public new Color BackColor {
+ get {
+ return base.BackColor;
+ }
+ set {
+ textbox.BackColor = value;
+ base.BackColor = value;
+ }
+ }
+ public bool ReadOnly {
+ get {
+ return textbox.ReadOnly;
+ }
+ set {
+ textbox.ReadOnly = value;
+ }
+ }
+
+ public new string Text {
+ get {
+ return textbox.Text;
+ }
+ set {
+ textbox.Text = value;
+ }
+ }
+
+ public char PasswordChar {
+ set { textbox.PasswordChar = value; }
+ }
+
+ #endregion Public Instance Properties
+
+ #region Events
+ static object DropDownButtonClickedEvent = new object ();
+ static object DialogButtonClickedEvent = new object ();
+ static object ToggleValueEvent = new object ();
+ static object KeyDownEvent = new object ();
+ static object ValidateEvent = new object ();
+
+ public event EventHandler DropDownButtonClicked {
+ add { Events.AddHandler (DropDownButtonClickedEvent, value); }
+ remove { Events.RemoveHandler (DropDownButtonClickedEvent, value); }
+ }
+
+ public event EventHandler DialogButtonClicked {
+ add { Events.AddHandler (DialogButtonClickedEvent, value); }
+ remove { Events.RemoveHandler (DialogButtonClickedEvent, value); }
+ }
+
+ public event EventHandler ToggleValue {
+ add { Events.AddHandler (ToggleValueEvent, value); }
+ remove { Events.RemoveHandler (ToggleValueEvent, value); }
+ }
+
+ public new event KeyEventHandler KeyDown {
+ add { Events.AddHandler (KeyDownEvent, value); }
+ remove { Events.RemoveHandler (KeyDownEvent, value); }
+ }
+
+ public new event CancelEventHandler Validate {
+ add { Events.AddHandler (ValidateEvent, value); }
+ remove { Events.RemoveHandler (ValidateEvent, value); }
+ }
+ #endregion Events
+
+ #region Private Helper Methods
+
+ private void dropdown_button_Paint(object sender, PaintEventArgs e)
+ {
+ ThemeEngine.Current.CPDrawComboButton(e.Graphics, dropdown_button.ClientRectangle, dropdown_button.ButtonState);
+ }
+
+ private void dialog_button_Paint(object sender, PaintEventArgs e) {
+ // best way to draw the ellipse?
+ e.Graphics.DrawString("...", new Font(Font,FontStyle.Bold), Brushes.Black, 0,0);
+ }
+
+ private void dropdown_button_Click(object sender, EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [DropDownButtonClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void dialog_button_Click(object sender, EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [DialogButtonClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ #endregion Private Helper Methods
+
+ internal void SendMouseDown (Point screenLocation)
+ {
+ Point clientLocation = PointToClient (screenLocation);
+ XplatUI.SendMessage (Handle, Msg.WM_LBUTTONDOWN, new IntPtr ((int) MsgButtons.MK_LBUTTON), Widget.MakeParam (clientLocation.X, clientLocation.Y));
+ textbox.FocusAt (screenLocation);
+ }
+
+ private void textbox_DoubleClick(object sender, EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [ToggleValueEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void textbox_KeyDown(object sender, KeyEventArgs e) {
+ KeyEventHandler eh = (KeyEventHandler)(Events [KeyDownEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ private void textbox_GotFocus(object sender, EventArgs e) {
+ if (!filtering) {
+ filtering = true;
+ Application.AddMessageFilter ((IMessageFilter)this);
+ }
+ }
+
+
+ protected override void DestroyHandle ()
+ {
+ Application.RemoveMessageFilter ((IMessageFilter)this);
+ filtering = false;
+ base.DestroyHandle ();
+ }
+
+ bool IMessageFilter.PreFilterMessage(ref Message m)
+ {
+ // validating check is to allow whatever UI code to execute
+ // without filtering messages (i.e. error dialogs, etc)
+ //
+ if (!validating && m.HWnd != textbox.Handle && textbox.Focused &&
+ (m.Msg == (int)Msg.WM_LBUTTONDOWN ||
+ m.Msg == (int)Msg.WM_MBUTTONDOWN ||
+ m.Msg == (int)Msg.WM_RBUTTONDOWN ||
+ m.Msg == (int)Msg.WM_NCLBUTTONDOWN ||
+ m.Msg == (int)Msg.WM_NCMBUTTONDOWN ||
+ m.Msg == (int)Msg.WM_NCRBUTTONDOWN)) {
+ CancelEventHandler validateHandler = (CancelEventHandler)(Events [ValidateEvent]);
+ if (validateHandler != null) {
+ CancelEventArgs args = new CancelEventArgs ();
+ validating = true;
+ validateHandler (this, args);
+ validating = false;
+ if (!args.Cancel) {
+ Application.RemoveMessageFilter ((IMessageFilter)this);
+ filtering = false;
+ }
+ return args.Cancel;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/PropertyGridView.cs b/source/ShiftUI/Widgets/PropertyGridView.cs
new file mode 100644
index 0000000..ea26d0e
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertyGridView.cs
@@ -0,0 +1,1101 @@
+
+// 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) 2005-2008 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Design;
+using System.ComponentModel;
+using System.Threading;
+using ShiftUI.Design;
+using System.Collections.Generic;
+
+namespace ShiftUI.PropertyGridInternal {
+ internal class PropertyGridView : ScrollableWidget, IWindowsFormsEditorService {
+
+ #region Private Members
+ private const char PASSWORD_PAINT_CHAR = '\u25cf'; // the dot char
+ private const char PASSWORD_TEXT_CHAR = '*';
+ private const int V_INDENT = 16;
+ private const int ENTRY_SPACING = 2;
+ private const int RESIZE_WIDTH = 3;
+ private const int BUTTON_WIDTH = 25;
+ private const int VALUE_PAINT_WIDTH = 19;
+ private const int VALUE_PAINT_INDENT = 27;
+ private double splitter_percent = .5;
+ private int row_height;
+ private int font_height_padding = 3;
+ private PropertyGridTextBox grid_textbox;
+ private PropertyGrid property_grid;
+ private bool resizing_grid;
+ private PropertyGridDropDown dropdown_form;
+ private Form dialog_form;
+ private ImplicitVScrollBar vbar;
+ private StringFormat string_format;
+ private Font bold_font;
+ private Brush inactive_text_brush;
+ private ListBox dropdown_list;
+ private Point last_click;
+ private Padding dropdown_form_padding;
+ #endregion
+
+ #region Contructors
+ public PropertyGridView (PropertyGrid propertyGrid) {
+ property_grid = propertyGrid;
+
+ string_format = new StringFormat ();
+ string_format.FormatFlags = StringFormatFlags.NoWrap;
+ string_format.Trimming = StringTrimming.None;
+
+ grid_textbox = new PropertyGridTextBox ();
+ grid_textbox.DropDownButtonClicked +=new EventHandler (DropDownButtonClicked);
+ grid_textbox.DialogButtonClicked +=new EventHandler (DialogButtonClicked);
+
+ dropdown_form = new PropertyGridDropDown ();
+ dropdown_form.FormBorderStyle = FormBorderStyle.None;
+ dropdown_form.StartPosition = FormStartPosition.Manual;
+ dropdown_form.ShowInTaskbar = false;
+
+ dialog_form = new Form ();
+ dialog_form.StartPosition = FormStartPosition.Manual;
+ dialog_form.FormBorderStyle = FormBorderStyle.None;
+ dialog_form.ShowInTaskbar = false;
+
+ dropdown_form_padding = new Padding (0, 0, 2, 2);
+
+ row_height = Font.Height + font_height_padding;
+
+ grid_textbox.Visible = false;
+ grid_textbox.Font = this.Font;
+ grid_textbox.BackColor = SystemColors.Window;
+ grid_textbox.Validate += new CancelEventHandler (grid_textbox_Validate);
+ grid_textbox.ToggleValue+=new EventHandler (grid_textbox_ToggleValue);
+ grid_textbox.KeyDown+=new KeyEventHandler (grid_textbox_KeyDown);
+ this.Widgets.Add (grid_textbox);
+
+ vbar = new ImplicitVScrollBar ();
+ vbar.Visible = false;
+ vbar.Value = 0;
+ vbar.ValueChanged+=new EventHandler (VScrollBar_HandleValueChanged);
+ vbar.Dock = DockStyle.Right;
+ this.Widgets.AddImplicit (vbar);
+
+ resizing_grid = false;
+
+ bold_font = new Font (this.Font, FontStyle.Bold);
+ inactive_text_brush = new SolidBrush (ThemeEngine.Current.ColorGrayText);
+
+ ForeColorChanged+=new EventHandler (RedrawEvent);
+ BackColorChanged+=new System.EventHandler (RedrawEvent);
+ FontChanged+=new EventHandler (RedrawEvent);
+
+ SetStyle (Widgetstyles.Selectable, true);
+ SetStyle (Widgetstyles.DoubleBuffer, true);
+ SetStyle (Widgetstyles.UserPaint, true);
+ SetStyle (Widgetstyles.AllPaintingInWmPaint, true);
+ SetStyle (Widgetstyles.ResizeRedraw, true);
+ }
+
+ #endregion
+
+ private GridEntry RootGridItem {
+ get { return (GridEntry)property_grid.RootGridItem; }
+ }
+
+ private GridEntry SelectedGridItem {
+ get { return (GridEntry)property_grid.SelectedGridItem; }
+ set { property_grid.SelectedGridItem = value; }
+ }
+
+ #region Protected Instance Methods
+
+ protected override void OnFontChanged (EventArgs e) {
+ base.OnFontChanged (e);
+
+ bold_font = new Font (this.Font, FontStyle.Bold);
+ row_height = Font.Height + font_height_padding;
+ }
+
+ private void InvalidateItemLabel (GridEntry item)
+ {
+ Invalidate (new Rectangle (0, ((GridEntry)item).Top, SplitterLocation, row_height));
+ }
+
+ private void InvalidateItem (GridEntry item)
+ {
+ if (item == null)
+ return;
+
+ Rectangle rect = new Rectangle (0, item.Top, Width, row_height);
+ Invalidate (rect);
+
+ if (item.Expanded) {
+ rect = new Rectangle (0, item.Top + row_height, Width,
+ Height - (item.Top + row_height));
+ Invalidate (rect);
+ }
+ }
+
+ // [+] expanding is handled in OnMouseDown, so in order to prevent
+ // duplicate expanding ignore it here.
+ //
+ protected override void OnDoubleClick (EventArgs e)
+ {
+ if (this.SelectedGridItem != null && this.SelectedGridItem.Expandable &&
+ !this.SelectedGridItem.PlusMinusBounds.Contains (last_click))
+ this.SelectedGridItem.Expanded = !this.SelectedGridItem.Expanded;
+ else
+ ToggleValue (this.SelectedGridItem);
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ // Background
+ e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
+
+ int yLoc = -vbar.Value*row_height;
+ if (this.RootGridItem != null)
+ DrawGridItems (this.RootGridItem.GridItems, e, 1, ref yLoc);
+
+ UpdateScrollBar ();
+ }
+
+ protected override void OnMouseWheel (MouseEventArgs e)
+ {
+ if (vbar == null || !vbar.Visible)
+ return;
+ if (e.Delta < 0)
+ vbar.Value = Math.Min (vbar.Maximum - GetVisibleRowsCount () + 1, vbar.Value + SystemInformation.MouseWheelScrollLines);
+ else
+ vbar.Value = Math.Max (0, vbar.Value - SystemInformation.MouseWheelScrollLines);
+ base.OnMouseWheel (e);
+ }
+
+
+ protected override void OnMouseMove (MouseEventArgs e) {
+ if (this.RootGridItem == null)
+ return;
+
+ if (resizing_grid) {
+ int loc = Math.Max (e.X,2*V_INDENT);
+ SplitterPercent = 1.0*loc/Width;
+ }
+ if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH)
+ this.Cursor = Cursors.SizeWE;
+ else
+ this.Cursor = Cursors.Default;
+ base.OnMouseMove (e);
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ base.OnMouseDown (e);
+ last_click = e.Location;
+ if (this.RootGridItem == null)
+ return;
+
+ if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
+ resizing_grid = true;
+ }
+ else {
+ int offset = -vbar.Value*row_height;
+ GridItem foundItem = GetSelectedGridItem (this.RootGridItem.GridItems, e.Y, ref offset);
+
+ if (foundItem != null) {
+ if (foundItem.Expandable && ((GridEntry)foundItem).PlusMinusBounds.Contains (e.X, e.Y))
+ foundItem.Expanded = !foundItem.Expanded;
+
+ this.SelectedGridItem = (GridEntry)foundItem;
+ if (!GridLabelHitTest (e.X)) {
+ // send mouse down so we get the carret under cursor
+ grid_textbox.SendMouseDown (PointToScreen (e.Location));
+ }
+ }
+ }
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e) {
+ resizing_grid = false;
+ base.OnMouseUp (e);
+ }
+
+ protected override void OnResize (EventArgs e) {
+ base.OnResize (e);
+ if (this.SelectedGridItem != null) // initialized already
+ UpdateView ();
+ }
+
+ private void UnfocusSelection ()
+ {
+ Select (this);
+ }
+
+ private void FocusSelection ()
+ {
+ Select (grid_textbox);
+ }
+
+ protected override bool ProcessDialogKey (Keys keyData) {
+ GridEntry selectedItem = this.SelectedGridItem;
+ if (selectedItem != null
+ && grid_textbox.Visible) {
+ switch (keyData) {
+ case Keys.Enter:
+ if (TrySetEntry (selectedItem, grid_textbox.Text))
+ UnfocusSelection ();
+ return true;
+ case Keys.Escape:
+ if (selectedItem.IsEditable)
+ UpdateItem (selectedItem); // reset value
+ UnfocusSelection ();
+ return true;
+ case Keys.Tab:
+ FocusSelection ();
+ return true;
+ default:
+ return false;
+ }
+ }
+ return base.ProcessDialogKey (keyData);
+ }
+
+ private bool TrySetEntry (GridEntry entry, object value)
+ {
+ if (entry == null || grid_textbox.Text.Equals (entry.ValueText))
+ return true;
+
+ if (entry.IsEditable || !entry.IsEditable && (entry.HasCustomEditor || entry.AcceptedValues != null) ||
+ !entry.IsMerged || entry.HasMergedValue ||
+ (!entry.HasMergedValue && grid_textbox.Text != String.Empty)) {
+ string error = null;
+ bool changed = entry.SetValue (value, out error);
+ if (!changed && error != null) {
+ if (property_grid.ShowError (error, MessageBoxButtons.OKCancel) == DialogResult.Cancel) {
+ UpdateItem (entry); // restore value, repaint, etc
+ UnfocusSelection ();
+ }
+ return false;
+ }
+ }
+ UpdateItem (entry); // restore value, repaint, etc
+ return true;
+ }
+
+ protected override bool IsInputKey (Keys keyData) {
+ switch (keyData) {
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.Enter:
+ case Keys.Escape:
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.PageDown:
+ case Keys.PageUp:
+ case Keys.Home:
+ case Keys.End:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private GridEntry MoveUpFromItem (GridEntry item, int up_count)
+ {
+ GridItemCollection items;
+ int index;
+
+ /* move back up the visible rows (and up the hierarchy as necessary) until
+ up_count == 0, or we reach the top of the display */
+ while (up_count > 0) {
+ items = item.Parent != null ? item.Parent.GridItems : this.RootGridItem.GridItems;
+ index = items.IndexOf (item);
+
+ if (index == 0) {
+ if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
+ return item;
+ item = (GridEntry)item.Parent;
+ up_count --;
+ }
+ else {
+ GridEntry prev_item = (GridEntry)items[index-1];
+ if (prev_item.Expandable && prev_item.Expanded) {
+ item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
+ }
+ else {
+ item = prev_item;
+ }
+ up_count --;
+ }
+ }
+ return item;
+ }
+
+ private GridEntry MoveDownFromItem (GridEntry item, int down_count)
+ {
+ while (down_count > 0) {
+ /* if we're a parent node and we're expanded, move to our first child */
+ if (item.Expandable && item.Expanded) {
+ item = (GridEntry)item.GridItems[0];
+ down_count--;
+ }
+ else {
+ GridItem searchItem = item;
+ GridItemCollection searchItems = searchItem.Parent.GridItems;
+ int searchIndex = searchItems.IndexOf (searchItem);
+
+ while (searchIndex == searchItems.Count - 1) {
+ searchItem = searchItem.Parent;
+
+ if (searchItem == null || searchItem.Parent == null)
+ break;
+
+ searchItems = searchItem.Parent.GridItems;
+ searchIndex = searchItems.IndexOf (searchItem);
+ }
+
+ if (searchIndex == searchItems.Count - 1) {
+ /* if we got all the way back to the root with no nodes after
+ us, the original item was the last one */
+ return item;
+ }
+ else {
+ item = (GridEntry)searchItems[searchIndex+1];
+ down_count--;
+ }
+ }
+ }
+
+ return item;
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ GridEntry selectedItem = this.SelectedGridItem;
+
+ if (selectedItem == null) {
+ /* XXX not sure what MS does, but at least we shouldn't crash */
+ base.OnKeyDown (e);
+ return;
+ }
+
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Left:
+ if (e.Widget) {
+ if (SplitterLocation > 2 * V_INDENT)
+ SplitterPercent -= 0.01;
+
+ e.Handled = true;
+ break;
+ }
+ else {
+ /* if the node is expandable and is expanded, collapse it.
+ otherwise, act just like the user pressed up */
+ if (selectedItem.Expandable && selectedItem.Expanded) {
+ selectedItem.Expanded = false;
+ e.Handled = true;
+ break;
+ }
+ else
+ goto case Keys.Up;
+ }
+ case Keys.Right:
+ if (e.Widget) {
+ if (SplitterLocation < Width)
+ SplitterPercent += 0.01;
+
+ e.Handled = true;
+ break;
+ }
+ else {
+ /* if the node is expandable and not expanded, expand it.
+ otherwise, act just like the user pressed down */
+ if (selectedItem.Expandable && !selectedItem.Expanded) {
+ selectedItem.Expanded = true;
+ e.Handled = true;
+ break;
+ }
+ else
+ goto case Keys.Down;
+ }
+ case Keys.Enter:
+ /* toggle the expanded state of the selected item */
+ if (selectedItem.Expandable) {
+ selectedItem.Expanded = !selectedItem.Expanded;
+ }
+ e.Handled = true;
+ break;
+ case Keys.Up:
+ this.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
+ e.Handled = true;
+ break;
+ case Keys.Down:
+ this.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
+ e.Handled = true;
+ break;
+ case Keys.PageUp:
+ this.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
+ e.Handled = true;
+ break;
+ case Keys.PageDown:
+ this.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
+ e.Handled = true;
+ break;
+ case Keys.End:
+ /* find the last, most deeply nested visible item */
+ GridEntry item = (GridEntry)this.RootGridItem.GridItems[this.RootGridItem.GridItems.Count - 1];
+ while (item.Expandable && item.Expanded)
+ item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
+ this.SelectedGridItem = item;
+ e.Handled = true;
+ break;
+ case Keys.Home:
+ this.SelectedGridItem = (GridEntry)this.RootGridItem.GridItems[0];
+ e.Handled = true;
+ break;
+ }
+
+ base.OnKeyDown (e);
+ }
+
+ #endregion
+
+ #region Private Helper Methods
+
+ private int SplitterLocation {
+ get {
+ return (int)(splitter_percent*Width);
+ }
+ }
+
+ private double SplitterPercent {
+ set {
+ int old_splitter_location = SplitterLocation;
+
+ splitter_percent = Math.Max (Math.Min (value, .9),.1);
+
+ if (old_splitter_location != SplitterLocation) {
+ int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
+ Invalidate (new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
+ UpdateItem (this.SelectedGridItem);
+ }
+ }
+ get {
+ return splitter_percent;
+ }
+ }
+
+ private bool GridLabelHitTest (int x)
+ {
+ if (0 <= x && x <= splitter_percent * this.Width)
+ return true;
+ return false;
+ }
+
+ private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
+ foreach (GridItem child_grid_item in grid_items) {
+ if (y > current && y < current + row_height) {
+ return child_grid_item;
+ }
+ current += row_height;
+ if (child_grid_item.Expanded) {
+ GridItem foundItem = GetSelectedGridItem (child_grid_item.GridItems, y, ref current);
+ if (foundItem != null)
+ return foundItem;
+ }
+ }
+ return null;
+ }
+
+ private int GetVisibleItemsCount (GridEntry entry)
+ {
+ if (entry == null)
+ return 0;
+
+ int count = 0;
+ foreach (GridEntry e in entry.GridItems) {
+ count += 1;
+ if (e.Expandable && e.Expanded)
+ count += GetVisibleItemsCount (e);
+ }
+ return count;
+ }
+
+ private int GetVisibleRowsCount ()
+ {
+ return this.Height / row_height;
+ }
+
+ private void UpdateScrollBar ()
+ {
+ if (this.RootGridItem == null)
+ return;
+
+ int visibleRows = GetVisibleRowsCount ();
+ int openedItems = GetVisibleItemsCount (this.RootGridItem);
+ if (openedItems > visibleRows) {
+ vbar.Visible = true;
+ vbar.SmallChange = 1;
+ vbar.LargeChange = visibleRows;
+ vbar.Maximum = Math.Max (0, openedItems - 1);
+ } else {
+ vbar.Value = 0;
+ vbar.Visible = false;
+ }
+ UpdateGridTextBoxBounds (this.SelectedGridItem);
+ }
+
+ // private bool GetScrollBarVisible ()
+ // {
+ // if (this.RootGridItem == null)
+ // return false;
+ //
+ // int visibleRows = GetVisibleRowsCount ();
+ // int openedItems = GetVisibleItemsCount (this.RootGridItem);
+ // if (openedItems > visibleRows)
+ // return true;
+ // return false;
+ // }
+ #region Drawing Code
+
+ private void DrawGridItems (GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
+ foreach (GridItem grid_item in grid_items) {
+ DrawGridItem ((GridEntry)grid_item, pevent, depth, ref yLoc);
+ if (grid_item.Expanded)
+ DrawGridItems (grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
+ }
+ }
+
+ private void DrawGridItemLabel (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
+ Font font = this.Font;
+ Brush brush;
+
+ if (grid_item.GridItemType == GridItemType.Category) {
+ font = bold_font;
+ brush = SystemBrushes.ControlText;
+
+ pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + ENTRY_SPACING);
+ if (grid_item == this.SelectedGridItem) {
+ SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
+ WidgetPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle (rect.X + 1, rect.Y+ENTRY_SPACING, (int)size.Width, (int)size.Height));
+ }
+ }
+ else {
+ if (grid_item == this.SelectedGridItem) {
+ Rectangle highlight = rect;
+ if (depth > 1) {
+ highlight.X -= V_INDENT;
+ highlight.Width += V_INDENT;
+ }
+ pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
+ // Label
+ brush = SystemBrushes.HighlightText;
+ }
+ else {
+ brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
+ }
+ }
+ pevent.Graphics.DrawString (grid_item.Label, font, brush,
+ new Rectangle (rect.X + 1, rect.Y + ENTRY_SPACING, rect.Width - ENTRY_SPACING, rect.Height - ENTRY_SPACING),
+ string_format);
+ }
+
+ private void DrawGridItemValue (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect)
+ {
+ if (grid_item.PropertyDescriptor == null)
+ return;
+
+ int xLoc = SplitterLocation+ENTRY_SPACING;
+ if (grid_item.PaintValueSupported) {
+ pevent.Graphics.DrawRectangle (Pens.Black, SplitterLocation + ENTRY_SPACING,
+ rect.Y + 2, VALUE_PAINT_WIDTH + 1, row_height - ENTRY_SPACING*2);
+ grid_item.PaintValue (pevent.Graphics,
+ new Rectangle (SplitterLocation + ENTRY_SPACING + 1,
+ rect.Y + ENTRY_SPACING + 1,
+ VALUE_PAINT_WIDTH, row_height - (ENTRY_SPACING*2 +1)));
+ xLoc += VALUE_PAINT_INDENT;
+ }
+
+ Font font = this.Font;
+ if (grid_item.IsResetable || !grid_item.HasDefaultValue)
+ font = bold_font;
+ Brush brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
+ string valueText = String.Empty;
+ if (!grid_item.IsMerged || grid_item.IsMerged && grid_item.HasMergedValue) {
+ if (grid_item.IsPassword)
+ valueText = new String (PASSWORD_PAINT_CHAR, grid_item.ValueText.Length);
+ else
+ valueText = grid_item.ValueText;
+ }
+ pevent.Graphics.DrawString (valueText, font,
+ brush,
+ new RectangleF (xLoc + ENTRY_SPACING, rect.Y + ENTRY_SPACING,
+ ClientRectangle.Width-(xLoc), row_height - ENTRY_SPACING*2),
+ string_format);
+ }
+
+ private void DrawGridItem (GridEntry grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
+ if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
+ // Left column
+ pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
+ 0, yLoc, V_INDENT, row_height);
+
+ if (grid_item.GridItemType == GridItemType.Category) {
+ pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.CategoryForeColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
+ }
+
+ DrawGridItemLabel (grid_item, pevent,
+ depth,
+ new Rectangle (depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
+ DrawGridItemValue (grid_item, pevent,
+ depth,
+ new Rectangle (SplitterLocation + ENTRY_SPACING , yLoc,
+ ClientRectangle.Width - SplitterLocation - ENTRY_SPACING - (vbar.Visible ? vbar.Width : 0),
+ row_height));
+
+ if (grid_item.GridItemType != GridItemType.Category) {
+ Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.LineColor);
+ // vertical divider line
+ pevent.Graphics.DrawLine (pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
+
+ // draw the horizontal line
+ pevent.Graphics.DrawLine (pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
+ }
+
+ if (grid_item.Expandable) {
+ int y = yLoc + row_height / 2 - ENTRY_SPACING + 1;
+ grid_item.PlusMinusBounds = DrawPlusMinus (pevent.Graphics, (depth - 1) * V_INDENT + ENTRY_SPACING + 1,
+ y, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
+ }
+
+ }
+ grid_item.Top = yLoc;
+ yLoc += row_height;
+ }
+
+ private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
+ Rectangle bounds = new Rectangle (x, y, 8, 8);
+ if (!category) g.FillRectangle (Brushes.White, bounds);
+ Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
+ g.DrawRectangle (pen, bounds);
+ g.DrawLine (pen, x+2, y+4, x + 6, y+4);
+ if (!expanded)
+ g.DrawLine (pen, x+4, y+2, x+4, y+6);
+
+ return bounds;
+ }
+
+ #endregion
+
+ #region Event Handling
+ private void RedrawEvent (object sender, System.EventArgs e)
+ {
+ Refresh ();
+ }
+
+ #endregion
+
+ private void listBox_MouseUp (object sender, MouseEventArgs e) {
+ AcceptListBoxSelection (sender);
+ }
+
+ private void listBox_KeyDown (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Enter:
+ AcceptListBoxSelection (sender);
+ return;
+ case Keys.Escape:
+ CloseDropDown ();
+ return;
+ }
+ }
+
+ void AcceptListBoxSelection (object sender)
+ {
+ GridEntry entry = this.SelectedGridItem as GridEntry;
+ if (entry != null) {
+ grid_textbox.Text = (string) ((ListBox) sender).SelectedItem;
+ CloseDropDown ();
+ if (TrySetEntry (entry, grid_textbox.Text))
+ UnfocusSelection ();
+ }
+ }
+
+ private void DropDownButtonClicked (object sender, EventArgs e)
+ {
+ DropDownEdit ();
+ }
+
+ private void DropDownEdit ()
+ {
+ GridEntry entry = SelectedGridItem as GridEntry;
+ if (entry == null)
+ return;
+
+ if (entry.HasCustomEditor) {
+ entry.EditValue ((IWindowsFormsEditorService) this);
+ } else {
+ if (dropdown_form.Visible) {
+ CloseDropDown ();
+ }
+ else {
+ ICollection std_values = entry.AcceptedValues;
+ if (std_values != null) {
+ if (dropdown_list == null) {
+ dropdown_list = new ListBox ();
+ dropdown_list.KeyDown += new KeyEventHandler (listBox_KeyDown);
+ dropdown_list.MouseUp += new MouseEventHandler (listBox_MouseUp);
+ }
+ dropdown_list.Items.Clear ();
+ dropdown_list.BorderStyle = BorderStyle.FixedSingle;
+ int selected_index = 0;
+ int i = 0;
+ string valueText = entry.ValueText;
+ foreach (object obj in std_values) {
+ dropdown_list.Items.Add (obj);
+ if (valueText != null && valueText.Equals (obj))
+ selected_index = i;
+ i++;
+ }
+ dropdown_list.Height = row_height * Math.Min (dropdown_list.Items.Count, 15);
+ dropdown_list.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
+ if (std_values.Count > 0)
+ dropdown_list.SelectedIndex = selected_index;
+ DropDownWidget (dropdown_list);
+ }
+ }
+ }
+ }
+
+ private void DialogButtonClicked (object sender, EventArgs e)
+ {
+ GridEntry entry = this.SelectedGridItem as GridEntry;
+ if (entry != null && entry.HasCustomEditor)
+ {
+ entry.Value = GetResultFromEditor(entry.Value);
+ }
+ }
+
+ public object GetResultFromEditor(object val)
+ {
+ var EditorDialogs = new Dictionary<Type, Dialogs.IEditorDialog>();
+ EditorDialogs.Add(typeof(string[]), new Dialogs.StringArrayDialog());
+ EditorDialogs.Add(typeof(ComboBox.ObjectCollection), new Dialogs.ComboBoxEditorDialog((val as ComboBox.ObjectCollection).Owner));
+ object retval = val;
+ try
+ {
+ var dialog = EditorDialogs[retval.GetType()];
+ dialog.ShowEditor();
+ retval = dialog.Value;
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Object {retval} is not valid for the given property. {ex.Message}", "Property Grid");
+ retval = val;
+ }
+ MessageBox.Show($"val: {val.GetType().Name}, retval: {retval.GetType().Name}", "Property Grid");
+ return retval;
+ }
+
+ private void VScrollBar_HandleValueChanged (object sender, EventArgs e)
+ {
+ UpdateView ();
+ }
+
+ private void grid_textbox_ToggleValue (object sender, EventArgs args)
+ {
+ ToggleValue (this.SelectedGridItem);
+ }
+
+ private void grid_textbox_KeyDown (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Down:
+ if (e.Alt) {
+ DropDownEdit ();
+ e.Handled = true;
+ }
+ break;
+ }
+ }
+
+ private void grid_textbox_Validate (object sender, CancelEventArgs args)
+ {
+ if (!TrySetEntry (this.SelectedGridItem, grid_textbox.Text))
+ args.Cancel = true;
+ }
+
+ private void ToggleValue (GridEntry entry)
+ {
+ if (entry != null && !entry.IsReadOnly && entry.GridItemType == GridItemType.Property)
+ entry.ToggleValue ();
+ }
+
+ internal void UpdateItem (GridEntry entry)
+ {
+ if (entry == null || entry.GridItemType == GridItemType.Category ||
+ entry.GridItemType == GridItemType.Root) {
+ grid_textbox.Visible = false;
+ InvalidateItem (entry);
+ return;
+ }
+
+ if (this.SelectedGridItem == entry) {
+ SuspendLayout ();
+ grid_textbox.Visible = false;
+ if (entry.IsResetable || !entry.HasDefaultValue)
+ grid_textbox.Font = bold_font;
+ else
+ grid_textbox.Font = this.Font;
+
+ if (entry.IsReadOnly) {
+ grid_textbox.DropDownButtonVisible = false;
+ grid_textbox.DialogButtonVisible = false;
+ grid_textbox.ReadOnly = true;
+ grid_textbox.ForeColor = SystemColors.GrayText;
+ } else {
+ grid_textbox.DropDownButtonVisible = entry.AcceptedValues != null ||
+ entry.EditorStyle == UITypeEditorEditStyle.DropDown;
+ grid_textbox.DialogButtonVisible = entry.EditorStyle == UITypeEditorEditStyle.Modal;
+ grid_textbox.ForeColor = SystemColors.ControlText;
+ grid_textbox.ReadOnly = !entry.IsEditable;
+ }
+ UpdateGridTextBoxBounds (entry);
+ grid_textbox.PasswordChar = entry.IsPassword ? PASSWORD_TEXT_CHAR : '\0';
+ grid_textbox.Text = entry.IsMerged && !entry.HasMergedValue ? String.Empty : entry.ValueText;
+ grid_textbox.Visible = true;
+ InvalidateItem (entry);
+ ResumeLayout (false);
+ } else {
+ grid_textbox.Visible = false;
+ }
+ }
+
+ private void UpdateGridTextBoxBounds (GridEntry entry)
+ {
+ if (entry == null || this.RootGridItem == null)
+ return;
+
+ int y = -vbar.Value*row_height;
+ CalculateItemY (entry, this.RootGridItem.GridItems, ref y);
+ int x = SplitterLocation + ENTRY_SPACING + (entry.PaintValueSupported ? VALUE_PAINT_INDENT : 0);
+ grid_textbox.SetBounds (x + ENTRY_SPACING, y + ENTRY_SPACING,
+ ClientRectangle.Width - ENTRY_SPACING - x - (vbar.Visible ? vbar.Width : 0),
+ row_height - ENTRY_SPACING);
+ }
+
+ // Calculates the sum of the heights of all items before the one
+ //
+ private bool CalculateItemY (GridEntry entry, GridItemCollection items, ref int y)
+ {
+ foreach (GridItem item in items) {
+ if (item == entry)
+ return true;
+ y += row_height;
+ if (item.Expandable && item.Expanded)
+ if (CalculateItemY (entry, item.GridItems, ref y))
+ return true;
+ }
+ return false;
+ }
+
+ private void ScrollToItem (GridEntry item)
+ {
+ if (item == null || this.RootGridItem == null)
+ return;
+
+ int itemY = -vbar.Value*row_height;
+ int value = vbar.Value;;
+ CalculateItemY (item, this.RootGridItem.GridItems, ref itemY);
+ if (itemY < 0) // the new item is above the viewable area
+ value += itemY / row_height;
+ else if (itemY + row_height > Height) // the new item is below the viewable area
+ value += ((itemY + row_height) - Height) / row_height + 1;
+ if (value >= vbar.Minimum && value <= vbar.Maximum)
+ vbar.Value = value;
+ }
+
+ internal void SelectItem (GridEntry oldItem, GridEntry newItem)
+ {
+ if (oldItem != null)
+ InvalidateItemLabel (oldItem);
+ if (newItem != null) {
+ UpdateItem (newItem);
+ ScrollToItem (newItem);
+ } else {
+ grid_textbox.Visible = false;
+ vbar.Visible = false;
+ }
+ }
+
+ internal void UpdateView ()
+ {
+ UpdateScrollBar ();
+ Invalidate ();
+ Update ();
+ UpdateItem (this.SelectedGridItem);
+ }
+
+ internal void ExpandItem (GridEntry item)
+ {
+ UpdateItem (this.SelectedGridItem);
+ Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
+ }
+
+ internal void CollapseItem (GridEntry item)
+ {
+ UpdateItem (this.SelectedGridItem);
+ Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
+ }
+
+ private void ShowDropDownControl (Widget control, bool resizeable)
+ {
+ dropdown_form.Size = control.Size;
+ control.Dock = DockStyle.Fill;
+
+ if (resizeable) {
+ dropdown_form.Padding = dropdown_form_padding;
+ dropdown_form.Width += dropdown_form_padding.Right;
+ dropdown_form.Height += dropdown_form_padding.Bottom;
+ dropdown_form.FormBorderStyle = FormBorderStyle.Sizable;
+ dropdown_form.SizeGripStyle = SizeGripStyle.Show;
+ } else {
+ dropdown_form.FormBorderStyle = FormBorderStyle.None;
+ dropdown_form.SizeGripStyle = SizeGripStyle.Hide;
+ dropdown_form.Padding = Padding.Empty;
+ }
+
+ dropdown_form.Widgets.Add (control);
+ dropdown_form.Width = Math.Max (ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0),
+ control.Width);
+ dropdown_form.Location = PointToScreen (new Point (grid_textbox.Right - dropdown_form.Width, grid_textbox.Location.Y + row_height));
+ RepositionInScreenWorkingArea (dropdown_form);
+ Point location = dropdown_form.Location;
+
+ Form owner = FindForm ();
+ owner.AddOwnedForm (dropdown_form);
+ dropdown_form.Show ();
+ if (dropdown_form.Location != location)
+ dropdown_form.Location = location;
+ control.Show();
+ ShiftUI.MSG msg = new MSG ();
+ object queue_id = XplatUI.StartLoop (Thread.CurrentThread);
+ control.Focus ();
+ while (dropdown_form.Visible && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
+ switch (msg.message) {
+ case Msg.WM_NCLBUTTONDOWN:
+ case Msg.WM_NCMBUTTONDOWN:
+ case Msg.WM_NCRBUTTONDOWN:
+ case Msg.WM_LBUTTONDOWN:
+ case Msg.WM_MBUTTONDOWN:
+ case Msg.WM_RBUTTONDOWN:
+ if (!HwndInControl (dropdown_form, msg.hwnd))
+ CloseDropDown ();
+ break;
+ case Msg.WM_ACTIVATE:
+ case Msg.WM_NCPAINT:
+ if (owner.window.Handle == msg.hwnd)
+ CloseDropDown ();
+ break;
+ }
+ XplatUI.TranslateMessage (ref msg);
+ XplatUI.DispatchMessage (ref msg);
+ }
+ XplatUI.EndLoop (Thread.CurrentThread);
+ }
+
+ private void RepositionInScreenWorkingArea (Form form)
+ {
+ Rectangle workingArea = Screen.FromControl (form).WorkingArea;
+ if (!workingArea.Contains (form.Bounds)) {
+ int x, y;
+ x = form.Location.X;
+ y = form.Location.Y;
+
+ if (form.Location.X < workingArea.X)
+ x = workingArea.X;
+
+ if (form.Location.Y + form.Size.Height > workingArea.Height) {
+ Point aboveTextBox = PointToScreen (new Point (grid_textbox.Right - form.Width, grid_textbox.Location.Y));
+ y = aboveTextBox.Y - form.Size.Height;
+ }
+
+ form.Location = new Point (x, y);
+ }
+ }
+
+ private bool HwndInControl (Widget c, IntPtr hwnd)
+ {
+ if (hwnd == c.window.Handle)
+ return true;
+ foreach (Widget cc in c.Widgets.GetAllWidgets ()) {
+ if (HwndInControl (cc, hwnd))
+ return true;
+ }
+ return false;
+ }
+ #endregion
+
+ #region IWindowsFormsEditorService Members
+
+
+ public void CloseDropDown ()
+ {
+ dropdown_form.Hide ();
+ dropdown_form.Widgets.Clear ();
+ }
+
+ public void DropDownWidget (Widget control)
+ {
+ bool resizeable = this.SelectedGridItem != null ? SelectedGridItem.EditorResizeable : false;
+ ShowDropDownControl (control, resizeable);
+ }
+
+ public ShiftUI.DialogResult ShowDialog (Form dialog) {
+ return dialog.ShowDialog (this);
+ }
+
+ #endregion
+
+ internal class PropertyGridDropDown : Form
+ {
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+ cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
+ cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOPMOST);
+ return cp;
+ }
+ }
+
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/RadioButton.cs b/source/ShiftUI/Widgets/RadioButton.cs
new file mode 100644
index 0000000..83a4f7c
--- /dev/null
+++ b/source/ShiftUI/Widgets/RadioButton.cs
@@ -0,0 +1,391 @@
+// 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.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Text;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI {
+ [DefaultProperty("Checked")]
+ [DefaultEvent("CheckedChanged")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [DefaultBindingProperty ("Checked")]
+ [ToolboxItem ("ShiftUI.Design.AutoSizeToolboxItem," + Consts.AssemblySystem_Design)]
+ //[Designer ("ShiftUI.Design.RadioButtonDesigner, " + Consts.AssemblySystem_Design)]
+ [ToolboxWidget]
+ public class RadioButton : ButtonBase {
+ #region Local Variables
+ internal Appearance appearance;
+ internal bool auto_check;
+ internal ContentAlignment radiobutton_alignment;
+ internal CheckState check_state;
+ #endregion // Local Variables
+
+ #region RadioButtonAccessibleObject Subclass
+ [ComVisible(true)]
+ public class RadioButtonAccessibleObject : ButtonBaseAccessibleObject {
+ #region RadioButtonAccessibleObject Local Variables
+ private new RadioButton owner;
+ #endregion // RadioButtonAccessibleObject Local Variables
+
+ #region RadioButtonAccessibleObject Constructors
+ public RadioButtonAccessibleObject(RadioButton owner) : base(owner) {
+ this.owner = owner;
+ }
+ #endregion // RadioButtonAccessibleObject Constructors
+
+ #region RadioButtonAccessibleObject Properties
+ public override string DefaultAction {
+ get {
+ return "Select";
+ }
+ }
+
+ public override AccessibleRole Role {
+ get {
+ return AccessibleRole.RadioButton;
+ }
+ }
+
+ public override AccessibleStates State {
+ get {
+ AccessibleStates retval;
+
+ retval = AccessibleStates.Default;
+
+ if (owner.check_state == CheckState.Checked) {
+ retval |= AccessibleStates.Checked;
+ }
+
+ if (owner.Focused) {
+ retval |= AccessibleStates.Focused;
+ }
+
+ if (owner.CanFocus) {
+ retval |= AccessibleStates.Focusable;
+ }
+
+ return retval;
+ }
+ }
+ #endregion // RadioButtonAccessibleObject Properties
+
+ #region RadioButtonAccessibleObject Methods
+ public override void DoDefaultAction() {
+ owner.PerformClick();
+ }
+ #endregion // RadioButtonAccessibleObject Methods
+ }
+ #endregion // RadioButtonAccessibleObject Sub-class
+
+ #region Public Constructors
+ public RadioButton() {
+ appearance = Appearance.Normal;
+ auto_check = true;
+ radiobutton_alignment = ContentAlignment.MiddleLeft;
+ TextAlign = ContentAlignment.MiddleLeft;
+ TabStop = false;
+ }
+ #endregion // Public Constructors
+
+ #region Private Methods
+
+ // When getting OnEnter we need to set Checked as true in case none of the sibling radio
+ // buttons is checked.
+ private void PerformDefaultCheck ()
+ {
+ // if we are already checked, no need to check the other Widgets
+ if (!auto_check || Checked)
+ return;
+
+ bool is_any_selected = false;
+ Widget c = Parent;
+ if (c != null) {
+ for (int i = 0; i < c.Widgets.Count; i++) {
+ RadioButton rb = c.Widgets [i] as RadioButton;
+ if (rb == null || !rb.auto_check)
+ continue;
+
+ if (rb.check_state == CheckState.Checked) {
+ is_any_selected = true;
+ break;
+ }
+ }
+ }
+
+ if (!is_any_selected)
+ Checked = true;
+ }
+
+ private void UpdateSiblings() {
+ Widget c;
+
+ if (auto_check == false) {
+ return;
+ }
+
+ // Remove tabstop property from and uncheck our radio-button siblings
+ c = this.Parent;
+ if (c != null) {
+ for (int i = 0; i < c.Widgets.Count; i++) {
+ if ((this != c.Widgets[i]) && (c.Widgets[i] is RadioButton)) {
+ if (((RadioButton)(c.Widgets[i])).auto_check) {
+ c.Widgets[i].TabStop = false;
+ ((RadioButton)(c.Widgets[i])).Checked = false;
+ }
+ }
+ }
+ }
+
+ this.TabStop = true;
+ }
+
+ internal override void Draw (PaintEventArgs pe) {
+ // FIXME: This should be called every time something that can affect it
+ // is changed, not every paint. Can only change so many things at a time.
+
+ // Figure out where our text and image should go
+ Rectangle glyph_rectangle;
+ Rectangle text_rectangle;
+ Rectangle image_rectangle;
+
+ ThemeEngine.Current.CalculateRadioButtonTextAndImageLayout (this, Point.Empty, out glyph_rectangle, out text_rectangle, out image_rectangle);
+
+ // Draw our button
+ if (FlatStyle != FlatStyle.System && Appearance != Appearance.Button)
+ ThemeEngine.Current.DrawRadioButton (pe.Graphics, this, glyph_rectangle, text_rectangle, image_rectangle, pe.ClipRectangle);
+ else
+ ThemeEngine.Current.DrawRadioButton (pe.Graphics, this.ClientRectangle, this);
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ if (this.AutoSize)
+ return ThemeEngine.Current.CalculateRadioButtonAutoSize (this);
+
+ return base.GetPreferredSizeCore (proposedSize);
+ }
+ #endregion // Private Methods
+
+ #region Public Instance Properties
+ [DefaultValue(Appearance.Normal)]
+ [Localizable(true)]
+ public Appearance Appearance {
+ get {
+ return appearance;
+ }
+
+ set {
+ if (value != appearance) {
+ appearance = value;
+ EventHandler eh = (EventHandler)(Events [AppearanceChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ if (Parent != null)
+ Parent.PerformLayout (this, "Appearance");
+ Invalidate();
+ }
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool AutoCheck {
+ get {
+ return auto_check;
+ }
+
+ set {
+ auto_check = value;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(ContentAlignment.MiddleLeft)]
+ public ContentAlignment CheckAlign {
+ get {
+ return radiobutton_alignment;
+ }
+
+ set {
+ if (value != radiobutton_alignment) {
+ radiobutton_alignment = value;
+
+ Invalidate();
+ }
+ }
+ }
+
+ [DefaultValue(false)]
+ [SettingsBindable (true)]
+ [Bindable (true, BindingDirection.OneWay)]
+ public bool Checked {
+ get {
+ if (check_state != CheckState.Unchecked) {
+ return true;
+ }
+ return false;
+ }
+
+ set {
+ if (value && (check_state != CheckState.Checked)) {
+ check_state = CheckState.Checked;
+ Invalidate();
+ UpdateSiblings();
+ OnCheckedChanged(EventArgs.Empty);
+ } else if (!value && (check_state != CheckState.Unchecked)) {
+ TabStop = false;
+ check_state = CheckState.Unchecked;
+ Invalidate();
+ OnCheckedChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue(false)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [DefaultValue(ContentAlignment.MiddleLeft)]
+ [Localizable(true)]
+ public override ContentAlignment TextAlign {
+ get { return base.TextAlign; }
+ set { base.TextAlign = value; }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ SetStyle(Widgetstyles.AllPaintingInWmPaint, true);
+ SetStyle(Widgetstyles.UserPaint, true);
+
+ return base.CreateParams;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return ThemeEngine.Current.RadioButtonDefaultSize;
+ }
+ }
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public void PerformClick() {
+ OnClick(EventArgs.Empty);
+ }
+
+ public override string ToString() {
+ return base.ToString() + ", Checked: " + this.Checked;
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override AccessibleObject CreateAccessibilityInstance() {
+ AccessibleObject ao;
+
+ ao = base.CreateAccessibilityInstance ();
+ ao.role = AccessibleRole.RadioButton;
+
+ return ao;
+ }
+
+ protected virtual void OnCheckedChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [CheckedChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnClick(EventArgs e) {
+ if (auto_check) {
+ if (!Checked) {
+ Checked = true;
+ }
+ } else {
+ Checked = !Checked;
+ }
+
+ base.OnClick (e);
+ }
+
+ protected override void OnEnter(EventArgs e) {
+ PerformDefaultCheck ();
+ base.OnEnter(e);
+ }
+
+ protected override void OnHandleCreated(EventArgs e) {
+ base.OnHandleCreated(e);
+ }
+
+ protected override void OnMouseUp(MouseEventArgs mevent) {
+ base.OnMouseUp(mevent);
+ }
+
+ protected override bool ProcessMnemonic(char charCode) {
+ if (IsMnemonic(charCode, Text) == true) {
+ Select();
+ PerformClick();
+ return true;
+ }
+
+ return base.ProcessMnemonic(charCode);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object AppearanceChangedEvent = new object ();
+ static object CheckedChangedEvent = new object ();
+
+ public event EventHandler AppearanceChanged {
+ add { Events.AddHandler (AppearanceChangedEvent, value); }
+ remove { Events.RemoveHandler (AppearanceChangedEvent, value); }
+ }
+
+ public event EventHandler CheckedChanged {
+ add { Events.AddHandler (CheckedChangedEvent, value); }
+ remove { Events.RemoveHandler (CheckedChangedEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+ #endregion // Events
+ }
+}
diff --git a/source/ShiftUI/Widgets/RichTextBox.cs b/source/ShiftUI/Widgets/RichTextBox.cs
new file mode 100644
index 0000000..c1d2325
--- /dev/null
+++ b/source/ShiftUI/Widgets/RichTextBox.cs
@@ -0,0 +1,2089 @@
+// 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) 2005-2006 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok <[email protected]>
+//
+//
+
+// #define DEBUG
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using RTF=ShiftUI.RTF;
+
+namespace ShiftUI {
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [Docking (DockingBehavior.Ask)]
+ [ComVisible (true)]
+ [Designer ("ShiftUI.Design.RichTextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class RichTextBox : TextBoxBase {
+ #region Local Variables
+ internal bool auto_word_select;
+ internal int bullet_indent;
+ internal bool detect_urls;
+ private bool reuse_line; // Sometimes we are loading text with already available lines
+ internal int margin_right;
+ internal float zoom;
+ private StringBuilder rtf_line;
+
+ private RtfSectionStyle rtf_style; // Replaces individual style
+ // properties so we can revert
+ private Stack rtf_section_stack;
+
+ private RTF.TextMap rtf_text_map;
+ private int rtf_skip_count;
+ private int rtf_cursor_x;
+ private int rtf_cursor_y;
+ private int rtf_chars;
+
+ private bool enable_auto_drag_drop;
+ private RichTextBoxLanguageOptions language_option;
+ private bool rich_text_shortcuts_enabled;
+ private Color selection_back_color;
+ #endregion // Local Variables
+
+ #region Public Constructors
+ public RichTextBox() {
+ accepts_return = true;
+ auto_size = false;
+ auto_word_select = false;
+ bullet_indent = 0;
+ base.MaxLength = Int32.MaxValue;
+ margin_right = 0;
+ zoom = 1;
+ base.Multiline = true;
+ document.CRLFSize = 1;
+ shortcuts_enabled = true;
+ base.EnableLinks = true;
+ richtext = true;
+
+ rtf_style = new RtfSectionStyle ();
+ rtf_section_stack = null;
+
+ scrollbars = RichTextBoxScrollBars.Both;
+ alignment = HorizontalAlignment.Left;
+ LostFocus += new EventHandler(RichTextBox_LostFocus);
+ GotFocus += new EventHandler(RichTextBox_GotFocus);
+ BackColor = ThemeEngine.Current.ColorWindow;
+ backcolor_set = false;
+ language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
+ rich_text_shortcuts_enabled = true;
+ selection_back_color = DefaultBackColor;
+ ForeColor = ThemeEngine.Current.ColorWindowText;
+
+ base.HScrolled += new EventHandler(RichTextBox_HScrolled);
+ base.VScrolled += new EventHandler(RichTextBox_VScrolled);
+
+ SetStyle (Widgetstyles.StandardDoubleClick, false);
+ }
+ #endregion // Public Constructors
+
+ #region Private & Internal Methods
+
+ internal override void HandleLinkClicked (LinkRectangle link)
+ {
+ OnLinkClicked (new LinkClickedEventArgs (link.LinkTag.LinkText));
+ }
+
+ internal override Color ChangeBackColor (Color backColor)
+ {
+ if (backColor == Color.Empty) {
+ backcolor_set = false;
+ if (!ReadOnly) {
+ backColor = SystemColors.Window;
+ }
+ }
+ return backColor;
+ }
+
+ internal override void RaiseSelectionChanged()
+ {
+ OnSelectionChanged (EventArgs.Empty);
+ }
+
+ private void RichTextBox_LostFocus(object sender, EventArgs e) {
+ Invalidate();
+ }
+
+ private void RichTextBox_GotFocus(object sender, EventArgs e) {
+ Invalidate();
+ }
+ #endregion // Private & Internal Methods
+
+ #region Public Instance Properties
+ [Browsable (false)]
+ public override bool AllowDrop {
+ get {
+ return base.AllowDrop;
+ }
+
+ set {
+ base.AllowDrop = value;
+ }
+ }
+
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override bool AutoSize {
+ get {
+ return auto_size;
+ }
+
+ set {
+ base.AutoSize = value;
+ }
+ }
+
+ [MonoTODO ("Value not respected, always true")]
+ [DefaultValue(false)]
+ public bool AutoWordSelection {
+ get { return auto_word_select; }
+ set { auto_word_select = value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override System.Drawing.Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue(0)]
+ [Localizable(true)]
+ public int BulletIndent {
+ get {
+ return bullet_indent;
+ }
+
+ set {
+ bullet_indent = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public bool CanRedo {
+ get {
+ return document.undo.CanRedo;
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool DetectUrls {
+ get { return base.EnableLinks; }
+ set { base.EnableLinks = value; }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [DefaultValue (false)]
+ public bool EnableAutoDragDrop {
+ get { return enable_auto_drag_drop; }
+ set { enable_auto_drag_drop = value; }
+ }
+
+ public override Font Font {
+ get {
+ return base.Font;
+ }
+
+ set {
+ if (font != value) {
+ Line start;
+ Line end;
+
+ if (auto_size) {
+ if (PreferredHeight != Height) {
+ Height = PreferredHeight;
+ }
+ }
+
+ base.Font = value;
+
+ // Font changes always set the whole doc to that font
+ start = document.GetLine(1);
+ end = document.GetLine(document.Lines);
+ document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
+ }
+ }
+ }
+
+ public override Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+
+ set {
+ base.ForeColor = value;
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public RichTextBoxLanguageOptions LanguageOption {
+ get { return language_option; }
+ set { language_option = value; }
+ }
+
+ [DefaultValue(Int32.MaxValue)]
+ public override int MaxLength {
+ get { return base.MaxLength; }
+ set { base.MaxLength = value; }
+ }
+
+ [DefaultValue(true)]
+ public override bool Multiline {
+ get {
+ return base.Multiline;
+ }
+
+ set {
+ base.Multiline = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string RedoActionName {
+ get {
+ return document.undo.RedoActionName;
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DefaultValue (true)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public bool RichTextShortcutsEnabled {
+ get { return rich_text_shortcuts_enabled; }
+ set { rich_text_shortcuts_enabled = value; }
+ }
+
+ [DefaultValue(0)]
+ [Localizable(true)]
+ [MonoTODO ("Stub, does nothing")]
+ [MonoInternalNote ("Teach TextControl.RecalculateLine to consider the right margin as well")]
+ public int RightMargin {
+ get {
+ return margin_right;
+ }
+
+ set {
+ margin_right = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [RefreshProperties (RefreshProperties.All)]
+ public string Rtf {
+ get {
+ Line start_line;
+ Line end_line;
+
+ start_line = document.GetLine(1);
+ end_line = document.GetLine(document.Lines);
+ return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
+ }
+
+ set {
+ MemoryStream data;
+
+ document.Empty();
+ data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
+
+ InsertRTFFromStream(data, 0, 1);
+
+ data.Close();
+
+ Invalidate();
+ }
+ }
+
+ [DefaultValue(RichTextBoxScrollBars.Both)]
+ [Localizable(true)]
+ public RichTextBoxScrollBars ScrollBars {
+ get {
+ return scrollbars;
+ }
+
+ set {
+ if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (RichTextBoxScrollBars));
+
+ if (value != scrollbars) {
+ scrollbars = value;
+ CalculateDocument ();
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string SelectedRtf {
+ get {
+ return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
+ }
+
+ set {
+ MemoryStream data;
+ int x;
+ int y;
+ int sel_start;
+ int chars;
+ Line line;
+ LineTag tag;
+
+ if (document.selection_visible) {
+ document.ReplaceSelection("", false);
+ }
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+
+ data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
+ int cursor_x = document.selection_start.pos;
+ int cursor_y = document.selection_start.line.line_no;
+
+ // The RFT parser by default, when finds our x cursor in 0, it thinks if needs to
+ // add a new line; but in *this* scenario the line is already created, so force it to reuse it.
+ // Hackish, but works without touching the heart of the buggy parser.
+ if (cursor_x == 0)
+ reuse_line = true;
+
+ InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
+ data.Close();
+
+ int nl_length = document.LineEndingLength (XplatUI.RunningOnUnix ? LineEnding.Rich : LineEnding.Hard);
+ document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * nl_length,
+ out line, out tag, out sel_start);
+ if (sel_start >= line.text.Length)
+ sel_start = line.text.Length -1;
+
+ document.SetSelection(line, sel_start);
+ document.PositionCaret(line, sel_start);
+ document.DisplayCaret();
+ ScrollToCaret();
+ OnTextChanged(EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public override string SelectedText {
+ get {
+ return base.SelectedText;
+ }
+
+ set {
+ // TextBox/TextBoxBase don't set Modified in this same property
+ Modified = true;
+ base.SelectedText = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(HorizontalAlignment.Left)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public HorizontalAlignment SelectionAlignment {
+ get {
+ HorizontalAlignment align;
+ Line start;
+ Line end;
+ Line line;
+
+ start = document.ParagraphStart(document.selection_start.line);
+ align = start.alignment;
+
+ end = document.ParagraphEnd(document.selection_end.line);
+
+ line = start;
+
+ while (true) {
+ if (line.alignment != align) {
+ return HorizontalAlignment.Left;
+ }
+
+ if (line == end) {
+ break;
+ }
+ line = document.GetLine(line.line_no + 1);
+ }
+
+ return align;
+ }
+
+ set {
+ Line start;
+ Line end;
+ Line line;
+
+ start = document.ParagraphStart(document.selection_start.line);
+
+ end = document.ParagraphEnd(document.selection_end.line);
+
+ line = start;
+
+ while (true) {
+ line.alignment = value;
+
+ if (line == end) {
+ break;
+ }
+ line = document.GetLine(line.line_no + 1);
+ }
+ this.CalculateDocument();
+ }
+ }
+
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Color SelectionBackColor {
+ get { return selection_back_color; }
+ set { selection_back_color = value; }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool SelectionBullet {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionCharOffset {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Color SelectionColor {
+ get {
+ Color color;
+ LineTag start;
+ LineTag end;
+ LineTag tag;
+
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ color = start.Color;
+
+ tag = start;
+ while (tag != null) {
+
+ if (!color.Equals (tag.Color))
+ return Color.Empty;
+
+ if (tag == end)
+ break;
+
+ tag = document.NextTag (tag);
+ }
+
+ return color;
+ }
+
+ set {
+ if (value == Color.Empty)
+ value = DefaultForeColor;
+
+ int sel_start;
+ int sel_end;
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+ sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
+
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, null,
+ value, Color.Empty, FormatSpecified.Color);
+
+ document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
+ document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
+
+ document.UpdateView(document.selection_start.line, 0);
+
+ //Re-Align the caret in case its changed size or position
+ //probably not necessary here
+ document.AlignCaret(false);
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Font SelectionFont {
+ get {
+ Font font;
+ LineTag start;
+ LineTag end;
+ LineTag tag;
+
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ font = start.Font;
+
+ if (selection_length > 1) {
+ tag = start;
+ while (tag != null) {
+
+ if (!font.Equals(tag.Font))
+ return null;
+
+ if (tag == end)
+ break;
+
+ tag = document.NextTag (tag);
+ }
+ }
+
+ return font;
+ }
+
+ set {
+ int sel_start;
+ int sel_end;
+
+ sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
+ sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
+
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, value,
+ Color.Empty, Color.Empty, FormatSpecified.Font);
+
+ document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
+ document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
+
+ document.UpdateView(document.selection_start.line, 0);
+ //Re-Align the caret in case its changed size or position
+ Document.AlignCaret (false);
+
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionHangingIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public override int SelectionLength {
+ get {
+ return base.SelectionLength;
+ }
+
+ set {
+ base.SelectionLength = value;
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool SelectionProtected {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DefaultValue(0)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int SelectionRightIndent {
+ get {
+ return 0;
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MonoTODO ("Stub, does nothing")]
+ public int[] SelectionTabs {
+ get {
+ return new int[0];
+ }
+
+ set {
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public RichTextBoxSelectionTypes SelectionType {
+ get {
+ if (document.selection_start == document.selection_end) {
+ return RichTextBoxSelectionTypes.Empty;
+ }
+
+ // Lazy, but works
+ if (SelectedText.Length > 1) {
+ return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
+ }
+
+ return RichTextBoxSelectionTypes.Text;
+ }
+ }
+
+ [DefaultValue(false)]
+ [MonoTODO ("Stub, does nothing")]
+ public bool ShowSelectionMargin {
+ get {
+ return false;
+ }
+
+ set {
+ }
+ }
+
+ [Localizable(true)]
+ [RefreshProperties (RefreshProperties.All)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+
+ set {
+ base.Text = value;
+ }
+ }
+
+ [Browsable(false)]
+ public override int TextLength {
+ get {
+ return base.TextLength;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string UndoActionName {
+ get {
+ return document.undo.UndoActionName;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(1)]
+ public float ZoomFactor {
+ get {
+ return zoom;
+ }
+
+ set {
+ zoom = value;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return new Size(100, 96);
+ }
+ }
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public bool CanPaste(DataFormats.Format clipFormat) {
+ if ((clipFormat.Name == DataFormats.Rtf) ||
+ (clipFormat.Name == DataFormats.Text) ||
+ (clipFormat.Name == DataFormats.UnicodeText)) {
+ return true;
+ }
+ return false;
+ }
+
+ public int Find(char[] characterSet) {
+ return Find(characterSet, -1, -1);
+ }
+
+ public int Find(char[] characterSet, int start) {
+ return Find(characterSet, start, -1);
+ }
+
+ public int Find(char[] characterSet, int start, int end) {
+ Document.Marker start_mark;
+ Document.Marker end_mark;
+ Document.Marker result;
+
+ if (start == -1) {
+ document.GetMarker(out start_mark, true);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ start_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(start, out line, out tag, out pos);
+ start_mark.line = line;
+ start_mark.tag = tag;
+ start_mark.pos = pos;
+ }
+
+ if (end == -1) {
+ document.GetMarker(out end_mark, false);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ end_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(end, out line, out tag, out pos);
+ end_mark.line = line;
+ end_mark.tag = tag;
+ end_mark.pos = pos;
+ }
+
+ if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
+ return document.LineTagToCharIndex(result.line, result.pos);
+ }
+
+ return -1;
+ }
+
+ public int Find(string str) {
+ return Find(str, -1, -1, RichTextBoxFinds.None);
+ }
+
+ public int Find(string str, int start, int end, RichTextBoxFinds options) {
+ Document.Marker start_mark;
+ Document.Marker end_mark;
+ Document.Marker result;
+
+ if (start == -1) {
+ document.GetMarker(out start_mark, true);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ start_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(start, out line, out tag, out pos);
+
+ start_mark.line = line;
+ start_mark.tag = tag;
+ start_mark.pos = pos;
+ }
+
+ if (end == -1) {
+ document.GetMarker(out end_mark, false);
+ } else {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ end_mark = new Document.Marker();
+
+ document.CharIndexToLineTag(end, out line, out tag, out pos);
+
+ end_mark.line = line;
+ end_mark.tag = tag;
+ end_mark.pos = pos;
+ }
+
+ if (document.Find(str, start_mark, end_mark, out result, options)) {
+ return document.LineTagToCharIndex(result.line, result.pos);
+ }
+
+ return -1;
+ }
+
+ public int Find(string str, int start, RichTextBoxFinds options) {
+ return Find(str, start, -1, options);
+ }
+
+ public int Find(string str, RichTextBoxFinds options) {
+ return Find(str, -1, -1, options);
+ }
+
+
+ internal override char GetCharFromPositionInternal (Point p)
+ {
+ LineTag tag;
+ int pos;
+
+ PointToTagPos (p, out tag, out pos);
+
+ if (pos >= tag.Line.text.Length)
+ return '\n';
+
+ return tag.Line.text[pos];
+ }
+
+ public override int GetCharIndexFromPosition(Point pt) {
+ LineTag tag;
+ int pos;
+
+ PointToTagPos(pt, out tag, out pos);
+
+ return document.LineTagToCharIndex(tag.Line, pos);
+ }
+
+ public override int GetLineFromCharIndex(int index) {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ document.CharIndexToLineTag(index, out line, out tag, out pos);
+
+ return line.LineNo - 1;
+ }
+
+ public override Point GetPositionFromCharIndex(int index) {
+ Line line;
+ LineTag tag;
+ int pos;
+
+ document.CharIndexToLineTag(index, out line, out tag, out pos);
+ return new Point(line.X + (int)line.widths[pos] + document.OffsetX - document.ViewPortX,
+ line.Y + document.OffsetY - document.ViewPortY);
+ }
+
+ public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
+ document.Empty();
+
+
+ // FIXME - ignoring unicode
+ if (fileType == RichTextBoxStreamType.PlainText) {
+ StringBuilder sb;
+ char[] buffer;
+
+ try {
+ sb = new StringBuilder ((int) data.Length);
+ buffer = new char [1024];
+ } catch {
+ throw new IOException("Not enough memory to load document");
+ }
+
+ StreamReader sr = new StreamReader (data, Encoding.Default, true);
+ int charsRead = sr.Read (buffer, 0, buffer.Length);
+ while (charsRead > 0) {
+ sb.Append (buffer, 0, charsRead);
+ charsRead = sr.Read (buffer, 0, buffer.Length);
+ }
+
+ // Remove the EOF converted to an extra EOL by the StreamReader
+ if (sb.Length > 0 && sb [sb.Length - 1] == '\n')
+ sb.Remove (sb.Length - 1, 1);
+
+ base.Text = sb.ToString();
+ return;
+ }
+
+ InsertRTFFromStream(data, 0, 1);
+
+ document.PositionCaret (document.GetLine (1), 0);
+ document.SetSelectionToCaret (true);
+ ScrollToCaret ();
+ }
+
+ public void LoadFile(string path) {
+ LoadFile (path, RichTextBoxStreamType.RichText);
+ }
+
+ public void LoadFile(string path, RichTextBoxStreamType fileType) {
+ FileStream data;
+
+ data = null;
+
+
+ try {
+ data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
+
+ LoadFile(data, fileType);
+ }
+#if !DEBUG
+ catch (Exception ex) {
+ throw new IOException("Could not open file " + path, ex);
+ }
+#endif
+ finally {
+ if (data != null) {
+ data.Close();
+ }
+ }
+ }
+
+ public void Paste(DataFormats.Format clipFormat) {
+ base.Paste(Clipboard.GetDataObject(), clipFormat, false);
+ }
+
+ public void Redo()
+ {
+ if (document.undo.Redo ())
+ OnTextChanged (EventArgs.Empty);
+ }
+
+ public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
+ Encoding encoding;
+ int i;
+ Byte[] bytes;
+
+
+ if (fileType == RichTextBoxStreamType.UnicodePlainText) {
+ encoding = Encoding.Unicode;
+ } else {
+ encoding = Encoding.ASCII;
+ }
+
+ switch(fileType) {
+ case RichTextBoxStreamType.PlainText:
+ case RichTextBoxStreamType.TextTextOleObjs:
+ case RichTextBoxStreamType.UnicodePlainText: {
+ if (!Multiline) {
+ bytes = encoding.GetBytes(document.Root.text.ToString());
+ data.Write(bytes, 0, bytes.Length);
+ return;
+ }
+
+ for (i = 1; i < document.Lines; i++) {
+ // Normalize the new lines to the system ones
+ string line_text = document.GetLine (i).TextWithoutEnding () + Environment.NewLine;
+ bytes = encoding.GetBytes(line_text);
+ data.Write(bytes, 0, bytes.Length);
+ }
+ bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
+ data.Write(bytes, 0, bytes.Length);
+ return;
+ }
+ }
+
+ // If we're here we're saving RTF
+ Line start_line;
+ Line end_line;
+ StringBuilder rtf;
+ int current;
+ int total;
+
+ start_line = document.GetLine(1);
+ end_line = document.GetLine(document.Lines);
+ rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
+ total = rtf.Length;
+ bytes = new Byte[4096];
+
+ // Let's chunk it so we don't use up all memory...
+ for (i = 0; i < total; i += 1024) {
+ if ((i + 1024) < total) {
+ current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
+ } else {
+ current = total - i;
+ current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
+ }
+ data.Write(bytes, 0, current);
+ }
+ }
+
+ public void SaveFile(string path) {
+ if (path.EndsWith(".rtf")) {
+ SaveFile(path, RichTextBoxStreamType.RichText);
+ } else {
+ SaveFile(path, RichTextBoxStreamType.PlainText);
+ }
+ }
+
+ public void SaveFile(string path, RichTextBoxStreamType fileType) {
+ FileStream data;
+
+ data = null;
+
+// try {
+ data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
+ SaveFile(data, fileType);
+// }
+
+// catch {
+// throw new IOException("Could not write document to file " + path);
+// }
+
+// finally {
+ if (data != null) {
+ data.Close();
+ }
+// }
+ }
+
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new void DrawToBitmap (Bitmap bitmap, Rectangle targetBounds)
+ {
+ using (Graphics dc = Graphics.FromImage (bitmap))
+ Draw (dc, targetBounds);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected virtual object CreateRichEditOleCallback()
+ {
+ throw new NotImplementedException ();
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected virtual void OnContentsResized (ContentsResizedEventArgs e)
+ {
+ ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnContextMenuChanged (EventArgs e)
+ {
+ base.OnContextMenuChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnHScroll(EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [HScrollEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ [MonoTODO ("Stub, never called")]
+ protected virtual void OnImeChange(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
+ LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnProtected(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnRightToLeftChanged(EventArgs e) {
+ base.OnRightToLeftChanged (e);
+ }
+
+ protected virtual void OnSelectionChanged(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnVScroll(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [VScrollEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void WndProc(ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ protected override bool ProcessCmdKey (ref Message m, Keys keyData)
+ {
+ return base.ProcessCmdKey (ref m, keyData);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object ContentsResizedEvent = new object ();
+ static object HScrollEvent = new object ();
+ static object ImeChangeEvent = new object ();
+ static object LinkClickedEvent = new object ();
+ static object ProtectedEvent = new object ();
+ static object SelectionChangedEvent = new object ();
+ static object VScrollEvent = new object ();
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ public event ContentsResizedEventHandler ContentsResized {
+ add { Events.AddHandler (ContentsResizedEvent, value); }
+ remove { Events.RemoveHandler (ContentsResizedEvent, value); }
+ }
+
+ [Browsable(false)]
+ public new event DragEventHandler DragDrop {
+ add { base.DragDrop += value; }
+ remove { base.DragDrop -= value; }
+ }
+
+ [Browsable(false)]
+ public new event DragEventHandler DragEnter {
+ add { base.DragEnter += value; }
+ remove { base.DragEnter -= value; }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler DragLeave {
+ add { base.DragLeave += value; }
+ remove { base.DragLeave -= value; }
+ }
+
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event DragEventHandler DragOver {
+ add { base.DragOver += value; }
+ remove { base.DragOver -= value; }
+ }
+
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event GiveFeedbackEventHandler GiveFeedback {
+ add { base.GiveFeedback += value; }
+ remove { base.GiveFeedback -= value; }
+ }
+
+ public event EventHandler HScroll {
+ add { Events.AddHandler (HScrollEvent, value); }
+ remove { Events.RemoveHandler (HScrollEvent, value); }
+ }
+
+ public event EventHandler ImeChange {
+ add { Events.AddHandler (ImeChangeEvent, value); }
+ remove { Events.RemoveHandler (ImeChangeEvent, value); }
+ }
+
+ public event LinkClickedEventHandler LinkClicked {
+ add { Events.AddHandler (LinkClickedEvent, value); }
+ remove { Events.RemoveHandler (LinkClickedEvent, value); }
+ }
+
+ public event EventHandler Protected {
+ add { Events.AddHandler (ProtectedEvent, value); }
+ remove { Events.RemoveHandler (ProtectedEvent, value); }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event QueryContinueDragEventHandler QueryContinueDrag {
+ add { base.QueryContinueDrag += value; }
+ remove { base.QueryContinueDrag -= value; }
+ }
+
+ [MonoTODO ("Event never raised")]
+ public event EventHandler SelectionChanged {
+ add { Events.AddHandler (SelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectionChangedEvent, value); }
+ }
+
+ public event EventHandler VScroll {
+ add { Events.AddHandler (VScrollEvent, value); }
+ remove { Events.RemoveHandler (VScrollEvent, value); }
+ }
+ #endregion // Events
+
+ #region Private Methods
+
+ internal override void SelectWord ()
+ {
+ document.ExpandSelection(CaretSelection.Word, false);
+ }
+
+ private class RtfSectionStyle : ICloneable {
+ internal Color rtf_color;
+ internal RTF.Font rtf_rtffont;
+ internal int rtf_rtffont_size;
+ internal FontStyle rtf_rtfstyle;
+ internal HorizontalAlignment rtf_rtfalign;
+ internal int rtf_par_line_left_indent;
+ internal bool rtf_visible;
+ internal int rtf_skip_width;
+
+ public object Clone ()
+ {
+ RtfSectionStyle new_style = new RtfSectionStyle ();
+
+ new_style.rtf_color = rtf_color;
+ new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
+ new_style.rtf_rtfalign = rtf_rtfalign;
+ new_style.rtf_rtffont = rtf_rtffont;
+ new_style.rtf_rtffont_size = rtf_rtffont_size;
+ new_style.rtf_rtfstyle = rtf_rtfstyle;
+ new_style.rtf_visible = rtf_visible;
+ new_style.rtf_skip_width = rtf_skip_width;
+
+ return new_style;
+ }
+ }
+
+ // To allow us to keep track of the sections and revert formatting
+ // as we go in and out of sections of the document.
+ private void HandleGroup (RTF.RTF rtf)
+ {
+ //start group - save the current formatting on to a stack
+ //end group - go back to the formatting at the current group
+ if (rtf_section_stack == null) {
+ rtf_section_stack = new Stack ();
+ }
+
+ if (rtf.Major == RTF.Major.BeginGroup) {
+ rtf_section_stack.Push (rtf_style.Clone ());
+ //spec specifies resetting unicode ignore at begin group as an attempt at error
+ //recovery.
+ rtf_skip_count = 0;
+ } else if (rtf.Major == RTF.Major.EndGroup) {
+ if (rtf_section_stack.Count > 0) {
+ FlushText (rtf, false);
+
+ rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
+ }
+ }
+ }
+
+ [MonoInternalNote ("Add QuadJust support for justified alignment")]
+ private void HandleControl(RTF.RTF rtf) {
+ switch(rtf.Major) {
+ case RTF.Major.Unicode: {
+ switch(rtf.Minor) {
+ case RTF.Minor.UnicodeCharBytes: {
+ rtf_style.rtf_skip_width = rtf.Param;
+ break;
+ }
+
+ case RTF.Minor.UnicodeChar: {
+ FlushText (rtf, false);
+ rtf_skip_count += rtf_style.rtf_skip_width;
+ rtf_line.Append((char)rtf.Param);
+ break;
+ }
+ }
+ break;
+ }
+
+ case RTF.Major.Destination: {
+// Console.Write("[Got Destination control {0}]", rtf.Minor);
+ rtf.SkipGroup();
+ break;
+ }
+
+ case RTF.Major.PictAttr:
+ if (rtf.Picture != null && rtf.Picture.IsValid ()) {
+ Line line = document.GetLine (rtf_cursor_y);
+ document.InsertPicture (line, 0, rtf.Picture);
+ rtf_cursor_x++;
+
+ FlushText (rtf, true);
+ rtf.Picture = null;
+ }
+ break;
+
+ case RTF.Major.CharAttr: {
+ switch(rtf.Minor) {
+ case RTF.Minor.ForeColor: {
+ ShiftUI.RTF.Color color;
+
+ color = ShiftUI.RTF.Color.GetColor(rtf, rtf.Param);
+
+ if (color != null) {
+ FlushText(rtf, false);
+ if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
+ this.rtf_style.rtf_color = ForeColor;
+ } else {
+ this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
+ }
+ FlushText (rtf, false);
+ }
+ break;
+ }
+
+ case RTF.Minor.FontSize: {
+ FlushText(rtf, false);
+ this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
+ break;
+ }
+
+ case RTF.Minor.FontNum: {
+ ShiftUI.RTF.Font font;
+
+ font = ShiftUI.RTF.Font.GetFont(rtf, rtf.Param);
+ if (font != null) {
+ FlushText(rtf, false);
+ this.rtf_style.rtf_rtffont = font;
+ }
+ break;
+ }
+
+ case RTF.Minor.Plain: {
+ FlushText(rtf, false);
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
+ break;
+ }
+
+ case RTF.Minor.Bold: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Bold;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
+ }
+ break;
+ }
+
+ case RTF.Minor.Italic: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Italic;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
+ }
+ break;
+ }
+
+ case RTF.Minor.StrikeThru: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
+ } else {
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
+ }
+ break;
+ }
+
+ case RTF.Minor.Underline: {
+ FlushText(rtf, false);
+ if (rtf.Param == RTF.RTF.NoParam) {
+ rtf_style.rtf_rtfstyle |= FontStyle.Underline;
+ } else {
+ rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
+ }
+ break;
+ }
+
+ case RTF.Minor.Invisible: {
+ FlushText (rtf, false);
+ rtf_style.rtf_visible = false;
+ break;
+ }
+
+ case RTF.Minor.NoUnderline: {
+ FlushText(rtf, false);
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
+ break;
+ }
+ }
+ break;
+ }
+
+ case RTF.Major.ParAttr: {
+ switch (rtf.Minor) {
+
+ case RTF.Minor.ParDef:
+ FlushText (rtf, false);
+ rtf_style.rtf_par_line_left_indent = 0;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.LeftIndent:
+ using (Graphics g = CreateGraphics ())
+ rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * g.DpiX + 0.5F);
+ break;
+
+ case RTF.Minor.QuadCenter:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadJust:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadLeft:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.QuadRight:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
+ break;
+ }
+ break;
+ }
+
+ case RTF.Major.SpecialChar: {
+ //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
+ SpecialChar (rtf);
+ break;
+ }
+ }
+ }
+
+ private void SpecialChar(RTF.RTF rtf) {
+ switch(rtf.Minor) {
+ case RTF.Minor.Page:
+ case RTF.Minor.Sect:
+ case RTF.Minor.Row:
+ case RTF.Minor.Line:
+ case RTF.Minor.Par: {
+ FlushText(rtf, true);
+ break;
+ }
+
+ case RTF.Minor.Cell: {
+ Console.Write(" ");
+ break;
+ }
+
+ case RTF.Minor.NoBrkSpace: {
+ Console.Write(" ");
+ break;
+ }
+
+ case RTF.Minor.Tab: {
+ rtf_line.Append ("\t");
+// FlushText (rtf, false);
+ break;
+ }
+
+ case RTF.Minor.NoReqHyphen:
+ case RTF.Minor.NoBrkHyphen: {
+ rtf_line.Append ("-");
+// FlushText (rtf, false);
+ break;
+ }
+
+ case RTF.Minor.Bullet: {
+ Console.WriteLine("*");
+ break;
+ }
+
+ case RTF.Minor.WidowCtrl:
+ break;
+
+ case RTF.Minor.EmDash: {
+ rtf_line.Append ("\u2014");
+ break;
+ }
+
+ case RTF.Minor.EnDash: {
+ rtf_line.Append ("\u2013");
+ break;
+ }
+/*
+ case RTF.Minor.LQuote: {
+ Console.Write("\u2018");
+ break;
+ }
+
+ case RTF.Minor.RQuote: {
+ Console.Write("\u2019");
+ break;
+ }
+
+ case RTF.Minor.LDblQuote: {
+ Console.Write("\u201C");
+ break;
+ }
+
+ case RTF.Minor.RDblQuote: {
+ Console.Write("\u201D");
+ break;
+ }
+*/
+ default: {
+// Console.WriteLine ("skipped special char: {0}", rtf.Minor);
+// rtf.SkipGroup();
+ break;
+ }
+ }
+ }
+
+ private void HandleText(RTF.RTF rtf) {
+ string str = rtf.EncodedText;
+
+ //todo - simplistically skips characters, should skip bytes?
+ if (rtf_skip_count > 0 && str.Length > 0) {
+ int iToRemove = Math.Min (rtf_skip_count, str.Length);
+
+ str = str.Substring (iToRemove);
+ rtf_skip_count-=iToRemove;
+ }
+
+ /*
+ if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
+ rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
+ } else {
+ if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
+ rtf_line.Append((char)rtf.Major);
+ } else {
+ //rtf_line.Append((char)rtf.Major);
+ Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
+ }
+ }
+ */
+
+ if (rtf_style.rtf_visible)
+ rtf_line.Append (str);
+ }
+
+ private void FlushText(RTF.RTF rtf, bool newline) {
+ int length;
+ Font font;
+
+ length = rtf_line.Length;
+ if (!newline && (length == 0)) {
+ return;
+ }
+
+ if (rtf_style.rtf_rtffont == null) {
+ // First font in table is default
+ rtf_style.rtf_rtffont = ShiftUI.RTF.Font.GetFont (rtf, 0);
+ }
+
+ font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
+
+ if (rtf_style.rtf_color == Color.Empty) {
+ ShiftUI.RTF.Color color;
+
+ // First color in table is default
+ color = ShiftUI.RTF.Color.GetColor (rtf, 0);
+
+ if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
+ rtf_style.rtf_color = ForeColor;
+ } else {
+ rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
+ }
+
+ }
+
+ rtf_chars += rtf_line.Length;
+
+ // Try to re-use if we are told so - this usually happens when we are inserting a flow of rtf text
+ // with an already alive line.
+ if (rtf_cursor_x == 0 && !reuse_line) {
+ if (newline && rtf_line.ToString ().EndsWith (Environment.NewLine) == false)
+ rtf_line.Append (Environment.NewLine);
+
+ document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
+ newline ? LineEnding.Rich : LineEnding.Wrap);
+ if (rtf_style.rtf_par_line_left_indent != 0) {
+ Line line = document.GetLine (rtf_cursor_y);
+ line.indent = rtf_style.rtf_par_line_left_indent;
+ }
+ } else {
+ Line line;
+
+ line = document.GetLine (rtf_cursor_y);
+ line.indent = rtf_style.rtf_par_line_left_indent;
+ if (rtf_line.Length > 0) {
+ document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
+ document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
+ font, rtf_style.rtf_color, Color.Empty,
+ FormatSpecified.Font | FormatSpecified.Color);
+ }
+ if (newline) {
+ line = document.GetLine (rtf_cursor_y);
+ line.ending = LineEnding.Rich;
+
+ if (line.Text.EndsWith (Environment.NewLine) == false)
+ line.Text += Environment.NewLine;
+ }
+
+ reuse_line = false; // sanity assignment - in this case we have already re-used one line.
+ }
+
+ if (newline) {
+ rtf_cursor_x = 0;
+ rtf_cursor_y++;
+ } else {
+ rtf_cursor_x += length;
+ }
+ rtf_line.Length = 0; // Empty line
+ }
+
+ private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
+ int x;
+ int y;
+ int chars;
+
+ InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
+ }
+
+ private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
+ RTF.RTF rtf;
+
+ rtf = new RTF.RTF(data);
+
+ // Prepare
+ rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
+ rtf.ClassCallback[RTF.TokenClass.Widget] = new RTF.ClassDelegate(HandleControl);
+ rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
+
+ rtf_skip_count = 0;
+ rtf_line = new StringBuilder();
+ rtf_style.rtf_color = Color.Empty;
+ rtf_style.rtf_rtffont_size = (int)this.Font.Size;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
+ rtf_style.rtf_rtffont = null;
+ rtf_style.rtf_visible = true;
+ rtf_style.rtf_skip_width = 1;
+ rtf_cursor_x = cursor_x;
+ rtf_cursor_y = cursor_y;
+ rtf_chars = 0;
+ rtf.DefaultFont(this.Font.Name);
+
+ rtf_text_map = new RTF.TextMap();
+ RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
+
+ document.SuspendRecalc ();
+
+ try {
+ rtf.Read(); // That's it
+ FlushText(rtf, false);
+
+ }
+
+
+ catch (RTF.RTFException e) {
+#if DEBUG
+ throw e;
+#endif
+ // Seems to be plain text or broken RTF
+ }
+
+ to_x = rtf_cursor_x;
+ to_y = rtf_cursor_y;
+ chars = rtf_chars;
+
+ // clear the section stack if it was used
+ if (rtf_section_stack != null)
+ rtf_section_stack.Clear();
+
+ document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
+ document.ResumeRecalc (true);
+
+ document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
+ }
+
+ private void RichTextBox_HScrolled(object sender, EventArgs e) {
+ OnHScroll(e);
+ }
+
+ private void RichTextBox_VScrolled(object sender, EventArgs e) {
+ OnVScroll(e);
+ }
+
+ private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
+ Point p;
+
+ p = pt;
+
+ if (p.X >= document.ViewPortWidth) {
+ p.X = document.ViewPortWidth - 1;
+ } else if (p.X < 0) {
+ p.X = 0;
+ }
+
+ if (p.Y >= document.ViewPortHeight) {
+ p.Y = document.ViewPortHeight - 1;
+ } else if (p.Y < 0) {
+ p.Y = 0;
+ }
+
+ tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
+ }
+
+ private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
+ if (prev_index != font_index) {
+ rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
+ }
+
+ if ((prev_font == null) || (prev_font.Size != font.Size)) {
+ rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
+ }
+
+ if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
+ if (font.Bold) {
+ rtf.Append("\\b");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\b0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
+ if (font.Italic) {
+ rtf.Append("\\i");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\i0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
+ if (font.Strikeout) {
+ rtf.Append("\\strike");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\strike0");
+ }
+ }
+ }
+
+ if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
+ if (font.Underline) {
+ rtf.Append("\\ul");
+ } else {
+ if (prev_font != null) {
+ rtf.Append("\\ul0");
+ }
+ }
+ }
+ }
+
+ static readonly char [] ReservedRTFChars = new char [] { '\\', '{', '}' };
+
+ private void EmitRTFText(StringBuilder rtf, string text) {
+ int start = rtf.Length;
+ int count = text.Length;
+
+ // First emit simple unicode chars as escaped
+ EmitEscapedUnicode (rtf, text);
+
+ // This method emits user text *only*, so it's safe to escape any reserved rtf chars
+ // Escape '\' first, since it is used later to escape the other chars
+ if (text.IndexOfAny (ReservedRTFChars) > -1) {
+ rtf.Replace ("\\", "\\\\", start, count);
+ rtf.Replace ("{", "\\{", start, count);
+ rtf.Replace ("}", "\\}", start, count);
+ }
+ }
+
+ // The chars to be escaped use "\'" + its hexadecimal value.
+ private void EmitEscapedUnicode (StringBuilder sb, string text)
+ {
+ int pos;
+ int start = 0;
+
+ while ((pos = IndexOfNonAscii (text, start)) > -1) {
+ sb.Append (text, start, pos - start);
+
+ int n = (int)text [pos];
+ sb.Append ("\\'");
+ sb.Append (n.ToString ("X"));
+
+ start = pos + 1;
+ }
+
+ // Append remaining (maybe all) the text value.
+ if (start < text.Length)
+ sb.Append (text, start, text.Length - start);
+ }
+
+ // MS seems to be escaping values larger than 0x80
+ private int IndexOfNonAscii (string text, int startIndex)
+ {
+ for (int i = startIndex; i < text.Length; i++) {
+ int n = (int)text [i];
+ if (n < 0 || n >= 0x80)
+ return i;
+ }
+
+ return -1;
+ }
+
+ // start_pos and end_pos are 0-based
+ private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
+ StringBuilder sb;
+ ArrayList fonts;
+ ArrayList colors;
+ Color color;
+ Font font;
+ Line line;
+ LineTag tag;
+ int pos;
+ int line_no;
+ int line_len;
+ int i;
+ int length;
+
+ sb = new StringBuilder();
+ fonts = new ArrayList(10);
+ colors = new ArrayList(10);
+
+ // Two runs, first we parse to determine tables;
+ // and unlike most of our processing here we work on tags
+
+ line = start_line;
+ line_no = start_line.line_no;
+ pos = start_pos;
+
+ // Add default font and color; to optimize document content we don't
+ // use this.Font and this.ForeColor but the font/color from the first tag
+ tag = LineTag.FindTag(start_line, pos);
+ font = tag.Font;
+ color = tag.Color;
+ fonts.Add(font.Name);
+ colors.Add(color);
+
+ while (line_no <= end_line.line_no) {
+ line = document.GetLine(line_no);
+ tag = LineTag.FindTag(line, pos);
+
+ if (line_no != end_line.line_no) {
+ line_len = line.text.Length;
+ } else {
+ line_len = end_pos;
+ }
+
+ while (pos < line_len) {
+ if (tag.Font.Name != font.Name) {
+ font = tag.Font;
+ if (!fonts.Contains(font.Name)) {
+ fonts.Add(font.Name);
+ }
+ }
+
+ if (tag.Color != color) {
+ color = tag.Color;
+ if (!colors.Contains(color)) {
+ colors.Add(color);
+ }
+ }
+
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
+ }
+ pos = 0;
+ line_no++;
+ }
+
+ // We have the tables, emit the header
+ sb.Append("{\\rtf1\\ansi");
+ sb.Append("\\ansicpg1252"); // FIXME - is this correct?
+
+ // Default Font
+ sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
+
+ // Default Language
+ sb.Append("\\deflang1033" + Environment.NewLine); // FIXME - always 1033?
+
+ // Emit the font table
+ sb.Append("{\\fonttbl");
+ for (i = 0; i < fonts.Count; i++) {
+ sb.Append(String.Format("{{\\f{0}", i)); // {Font
+ sb.Append("\\fnil"); // Family
+ sb.Append("\\fcharset0 "); // Charset ANSI<space>
+ sb.Append((string)fonts[i]); // Font name
+ sb.Append(";}"); // }
+ }
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+
+ // Emit the color table (if needed)
+ if ((colors.Count > 1) || ((((Color)colors[0]).R != this.ForeColor.R) || (((Color)colors[0]).G != this.ForeColor.G) || (((Color)colors[0]).B != this.ForeColor.B))) {
+ sb.Append("{\\colortbl "); // Header and NO! default color
+ for (i = 0; i < colors.Count; i++) {
+ sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
+ sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
+ sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
+ sb.Append(";");
+ }
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+ }
+
+ sb.Append("{\\*\\generator Mono RichTextBox;}");
+ // Emit initial paragraph settings
+ tag = LineTag.FindTag(start_line, start_pos);
+ sb.Append("\\pard"); // Reset to default paragraph properties
+ EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
+ sb.Append(" "); // Space separator
+
+ font = tag.Font;
+ color = (Color)colors[0];
+ line = start_line;
+ line_no = start_line.line_no;
+ pos = start_pos;
+
+ while (line_no <= end_line.line_no) {
+ line = document.GetLine(line_no);
+ tag = LineTag.FindTag(line, pos);
+
+ if (line_no != end_line.line_no) {
+ line_len = line.text.Length;
+ } else {
+ line_len = end_pos;
+ }
+
+ while (pos < line_len) {
+ length = sb.Length;
+
+ if (tag.Font != font) {
+ EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
+ font = tag.Font;
+ }
+
+ if (tag.Color != color) {
+ color = tag.Color;
+ sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
+ }
+ if (length != sb.Length) {
+ sb.Append(" "); // Emit space to separate keywords from text
+ }
+
+ // Emit the string itself
+ if (line_no != end_line.line_no) {
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
+ } else {
+ if (end_pos < (tag.Start + tag.Length - 1)) {
+ // Emit partial tag only, end_pos is inside this tag
+ EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
+ } else {
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
+ }
+ }
+
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
+ }
+ if (pos >= line.text.Length) {
+ if (line.ending != LineEnding.Wrap) {
+ sb.Append("\\par");
+ sb.Append(Environment.NewLine);
+ }
+ }
+ pos = 0;
+ line_no++;
+ }
+
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
+
+ return sb;
+ }
+ #endregion // Private Methods
+ }
+}
diff --git a/source/ShiftUI/Widgets/RootGridEntry.cs b/source/ShiftUI/Widgets/RootGridEntry.cs
new file mode 100644
index 0000000..534e166
--- /dev/null
+++ b/source/ShiftUI/Widgets/RootGridEntry.cs
@@ -0,0 +1,88 @@
+// 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) 2006 Novell, Inc.
+//
+// Authors:
+// Chris Toshok ([email protected])
+//
+
+using System;
+using System.Drawing;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ /// <summary>
+ /// Summary description for PropertyGridRootGridItem
+ /// </summary>
+ [MonoInternalNote ("needs to implement IRootGridEntry")]
+ internal class RootGridEntry : GridEntry /*, IRootGridEntry */
+ {
+ object[] val;
+
+ public RootGridEntry (PropertyGrid owner, object[] obj)
+ : base (owner, null)
+ {
+ if (obj == null || obj.Length == 0)
+ throw new ArgumentNullException ("obj");
+ val = obj;
+ }
+
+ public override bool Expandable {
+ get { return true; }
+ }
+
+ public override GridItemType GridItemType {
+ get { return GridItemType.Root; }
+ }
+
+ public override string Label {
+ get { return val.Length > 1 ? val.GetType().ToString() : val[0].GetType().ToString(); }
+ }
+
+ public override object Value {
+ get { return val.Length > 1 ? val : val[0]; }
+ }
+
+ public override object[] Values {
+ get { return val; }
+ }
+
+ public override bool Select ()
+ {
+ return false; /* root entries aren't selectable */
+ }
+
+ public override bool IsReadOnly {
+ get { return true; }
+ }
+
+ public override bool IsEditable {
+ get { return false; }
+ }
+
+ public override bool IsResetable {
+ get { return false; }
+ }
+
+ public override bool IsMerged {
+ get { return val.Length > 1; }
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/ScrollBar.cs b/source/ShiftUI/Widgets/ScrollBar.cs
new file mode 100644
index 0000000..9fceb0b
--- /dev/null
+++ b/source/ShiftUI/Widgets/ScrollBar.cs
@@ -0,0 +1,1624 @@
+//
+// ShiftUI.ScrollBar.cs
+//
+// 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.
+//
+// Authors:
+// Jordi Mas i Hernandez [email protected]
+//
+//
+
+// COMPLETE
+
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Drawing.Drawing2D;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI
+{
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent ("Scroll")]
+ [DefaultProperty ("Value")]
+ public abstract class ScrollBar : Widget
+ {
+ #region Local Variables
+ private int position;
+ private int minimum;
+ private int maximum;
+ private int large_change;
+ private int small_change;
+ internal int scrollbutton_height;
+ internal int scrollbutton_width;
+ private Rectangle first_arrow_area = new Rectangle (); // up or left
+ private Rectangle second_arrow_area = new Rectangle (); // down or right
+ private Rectangle thumb_pos = new Rectangle ();
+ private Rectangle thumb_area = new Rectangle ();
+ internal ButtonState firstbutton_state = ButtonState.Normal;
+ internal ButtonState secondbutton_state = ButtonState.Normal;
+ private bool firstbutton_pressed = false;
+ private bool secondbutton_pressed = false;
+ private bool thumb_pressed = false;
+ private float pixel_per_pos = 0;
+ private Timer timer = new Timer ();
+ private TimerType timer_type;
+ private int thumb_size = 40;
+ private const int thumb_min_size = 8;
+ private const int thumb_notshown_size = 40;
+ internal bool use_manual_thumb_size;
+ internal int manual_thumb_size;
+ internal bool vert;
+ internal bool implicit_Widget;
+ private int lastclick_pos; // Position of the last button-down event
+ private int thumbclick_offset; // Position of the last button-down event relative to the thumb edge
+ private Rectangle dirty;
+
+ internal ThumbMoving thumb_moving = ThumbMoving.None;
+ bool first_button_entered;
+ bool second_button_entered;
+ bool thumb_entered;
+ #endregion // Local Variables
+
+ private enum TimerType
+ {
+ HoldButton,
+ RepeatButton,
+ HoldThumbArea,
+ RepeatThumbArea
+ }
+
+ internal enum ThumbMoving
+ {
+ None,
+ Forward,
+ Backwards,
+ }
+
+ #region events
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler AutoSizeChanged {
+ add { base.AutoSizeChanged += value; }
+ remove { base.AutoSizeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackColorChanged {
+ add { base.BackColorChanged += value; }
+ remove { base.BackColorChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler FontChanged {
+ add { base.FontChanged += value; }
+ remove { base.FontChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDown {
+ add { base.MouseDown += value; }
+ remove { base.MouseDown -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseMove {
+ add { base.MouseMove += value; }
+ remove { base.MouseMove -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseUp {
+ add { base.MouseUp += value; }
+ remove { base.MouseUp -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ static object ScrollEvent = new object ();
+ static object ValueChangedEvent = new object ();
+
+ public event ScrollEventHandler Scroll {
+ add { Events.AddHandler (ScrollEvent, value); }
+ remove { Events.RemoveHandler (ScrollEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ public event EventHandler ValueChanged {
+ add { Events.AddHandler (ValueChangedEvent, value); }
+ remove { Events.RemoveHandler (ValueChangedEvent, value); }
+ }
+ #endregion Events
+
+ public ScrollBar ()
+ {
+ position = 0;
+ minimum = 0;
+ maximum = 100;
+ large_change = 10;
+ small_change = 1;
+
+ timer.Tick += new EventHandler (OnTimer);
+ MouseEnter += new EventHandler (OnMouseEnter);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ base.KeyDown += new KeyEventHandler (OnKeyDownSB);
+ base.MouseDown += new MouseEventHandler (OnMouseDownSB);
+ base.MouseUp += new MouseEventHandler (OnMouseUpSB);
+ base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
+ base.Resize += new EventHandler (OnResizeSB);
+ base.TabStop = false;
+ base.Cursor = Cursors.Default;
+
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.StandardClick | Widgetstyles.UseTextForAccessibility, false);
+ }
+
+ #region Internal & Private Properties
+ internal Rectangle FirstArrowArea {
+ get {
+ return this.first_arrow_area;
+ }
+
+ set {
+ this.first_arrow_area = value;
+ }
+ }
+
+ internal Rectangle SecondArrowArea {
+ get {
+ return this.second_arrow_area;
+ }
+
+ set {
+ this.second_arrow_area = value;
+ }
+ }
+
+ int MaximumAllowed {
+ get {
+ return use_manual_thumb_size ? maximum - manual_thumb_size + 1 :
+ maximum - LargeChange + 1;
+ }
+ }
+
+ internal Rectangle ThumbPos {
+ get {
+ return thumb_pos;
+ }
+
+ set {
+ thumb_pos = value;
+ }
+ }
+
+ internal bool FirstButtonEntered {
+ get { return first_button_entered; }
+ private set {
+ if (first_button_entered == value)
+ return;
+ first_button_entered = value;
+ if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
+ Invalidate (first_arrow_area);
+ }
+ }
+
+ internal bool SecondButtonEntered {
+ get { return second_button_entered; }
+ private set {
+ if (second_button_entered == value)
+ return;
+ second_button_entered = value;
+ if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
+ Invalidate (second_arrow_area);
+ }
+ }
+
+ internal bool ThumbEntered {
+ get { return thumb_entered; }
+ private set {
+ if (thumb_entered == value)
+ return;
+ thumb_entered = value;
+ if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
+ Invalidate (thumb_pos);
+ }
+ }
+
+ internal bool ThumbPressed {
+ get { return thumb_pressed; }
+ private set {
+ if (thumb_pressed == value)
+ return;
+ thumb_pressed = value;
+ if (ThemeEngine.Current.ScrollBarHasPressedThumbStyle)
+ Invalidate (thumb_pos);
+ }
+ }
+
+ #endregion // Internal & Private Properties
+
+ #region Public Properties
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override bool AutoSize {
+ get { return base.AutoSize; }
+ set { base.AutoSize = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Color BackColor
+ {
+ get { return base.BackColor; }
+ set {
+ if (base.BackColor == value)
+ return;
+ base.BackColor = value;
+ Refresh ();
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Image BackgroundImage
+ {
+ get { return base.BackgroundImage; }
+ set {
+ if (base.BackgroundImage == value)
+ return;
+
+ base.BackgroundImage = value;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ protected override CreateParams CreateParams
+ {
+ get { return base.CreateParams; }
+ }
+
+ protected override Padding DefaultMargin {
+ get { return Padding.Empty; }
+ }
+
+ protected override ImeMode DefaultImeMode
+ {
+ get { return ImeMode.Disable; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Font Font
+ {
+ get { return base.Font; }
+ set {
+ if (base.Font.Equals (value))
+ return;
+
+ base.Font = value;
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Color ForeColor
+ {
+ get { return base.ForeColor; }
+ set {
+ if (base.ForeColor == value)
+ return;
+
+ base.ForeColor = value;
+ Refresh ();
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new ImeMode ImeMode
+ {
+ get { return base.ImeMode; }
+ set {
+ if (base.ImeMode == value)
+ return;
+
+ base.ImeMode = value;
+ }
+ }
+
+ [DefaultValue (10)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
+ public int LargeChange {
+ get { return Math.Min (large_change, maximum - minimum + 1); }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
+
+ if (large_change != value) {
+ large_change = value;
+
+ // thumb area depends on large change value,
+ // so we need to recalculate it.
+ CalcThumbArea ();
+ UpdatePos (Value, true);
+ InvalidateDirty ();
+
+ // UIA Framework: Generate UIA Event to indicate LargeChange change
+ OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.LargeIncrement, value));
+ }
+ }
+ }
+
+ [DefaultValue (100)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
+ public int Maximum {
+ get { return maximum; }
+ set {
+ if (maximum == value)
+ return;
+
+ maximum = value;
+
+ // UIA Framework: Generate UIA Event to indicate Maximum change
+ OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.Last, value));
+
+ if (maximum < minimum)
+ minimum = maximum;
+ if (Value > maximum)
+ Value = maximum;
+
+ // thumb area depends on maximum value,
+ // so we need to recalculate it.
+ CalcThumbArea ();
+ UpdatePos (Value, true);
+ InvalidateDirty ();
+ }
+ }
+
+ internal void SetValues (int maximum, int large_change)
+ {
+ SetValues (-1, maximum, -1, large_change);
+ }
+
+ internal void SetValues (int minimum, int maximum, int small_change, int large_change)
+ {
+ bool update = false;
+
+ if (-1 != minimum && this.minimum != minimum) {
+ this.minimum = minimum;
+
+ if (minimum > this.maximum)
+ this.maximum = minimum;
+ update = true;
+
+ // change the position if it is out of range now
+ position = Math.Max (position, minimum);
+ }
+
+ if (-1 != maximum && this.maximum != maximum) {
+ this.maximum = maximum;
+
+ if (maximum < this.minimum)
+ this.minimum = maximum;
+ update = true;
+
+ // change the position if it is out of range now
+ position = Math.Min (position, maximum);
+ }
+
+ if (-1 != small_change && this.small_change != small_change) {
+ this.small_change = small_change;
+ }
+
+ if (this.large_change != large_change) {
+ this.large_change = large_change;
+ update = true;
+ }
+
+ if (update) {
+ CalcThumbArea ();
+ UpdatePos (Value, true);
+ InvalidateDirty ();
+ }
+ }
+
+ [DefaultValue (0)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
+ public int Minimum {
+ get { return minimum; }
+ set {
+ if (minimum == value)
+ return;
+
+ minimum = value;
+
+ // UIA Framework: Generate UIA Event to indicate Minimum change
+ OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.First, value));
+
+ if (minimum > maximum)
+ maximum = minimum;
+
+ // thumb area depends on minimum value,
+ // so we need to recalculate it.
+ CalcThumbArea ();
+ UpdatePos (Value, true);
+ InvalidateDirty ();
+ }
+ }
+
+ [DefaultValue (1)]
+ [MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
+ public int SmallChange {
+ get { return small_change > LargeChange ? LargeChange : small_change; }
+ set {
+ if ( value < 0 )
+ throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
+
+ if (small_change != value) {
+ small_change = value;
+ UpdatePos (Value, true);
+ InvalidateDirty ();
+
+ // UIA Framework: Generate UIA Event to indicate SmallChange change
+ OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.SmallIncrement, value));
+ }
+ }
+ }
+
+ [DefaultValue (false)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Bindable (false)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ [Bindable(true)]
+ [DefaultValue (0)]
+ [MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
+ public int Value {
+ get { return position; }
+ set {
+ if ( value < minimum || value > maximum )
+ throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
+
+ if (position != value){
+ position = value;
+
+ OnValueChanged (EventArgs.Empty);
+
+ if (IsHandleCreated) {
+ Rectangle thumb_rect = thumb_pos;
+
+ UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
+
+ MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
+ }
+ }
+ }
+ }
+
+ #endregion //Public Properties
+
+ #region Public Methods
+ protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
+ {
+ // Basically, we want to keep our small edge and scale the long edge
+ // ie: if we are vertical, don't scale our width
+ if (vert)
+ return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
+ else
+ return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
+ }
+
+ protected override void OnEnabledChanged (EventArgs e)
+ {
+ base.OnEnabledChanged (e);
+
+ if (Enabled)
+ firstbutton_state = secondbutton_state = ButtonState.Normal;
+ else
+ firstbutton_state = secondbutton_state = ButtonState.Inactive;
+
+ Refresh ();
+ }
+
+ protected override void OnHandleCreated (System.EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ CalcButtonSizes ();
+ CalcThumbArea ();
+ UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
+ }
+
+ protected virtual void OnScroll (ScrollEventArgs se)
+ {
+ ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
+ if (eh == null)
+ return;
+
+ if (se.NewValue < Minimum) {
+ se.NewValue = Minimum;
+ }
+
+ if (se.NewValue > Maximum) {
+ se.NewValue = Maximum;
+ }
+
+ eh (this, se);
+ }
+
+ private void SendWMScroll(ScrollBarCommands cmd) {
+ if ((Parent != null) && Parent.IsHandleCreated) {
+ if (vert) {
+ XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_Widget ? IntPtr.Zero : Handle);
+ } else {
+ XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_Widget ? IntPtr.Zero : Handle);
+ }
+ }
+ }
+
+ protected virtual void OnValueChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
+ GetType( ).FullName, minimum, maximum, position);
+ }
+
+ protected void UpdateScrollInfo ()
+ {
+ Refresh ();
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ base.WndProc (ref m);
+ }
+
+ #endregion //Public Methods
+
+ #region Private Methods
+
+ private void CalcButtonSizes ()
+ {
+ if (vert) {
+ if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
+ scrollbutton_height = Height /2;
+ else
+ scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
+
+ } else {
+ if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
+ scrollbutton_width = Width /2;
+ else
+ scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
+ }
+ }
+
+ private void CalcThumbArea ()
+ {
+ int lchange = use_manual_thumb_size ? manual_thumb_size : LargeChange;
+
+ // Thumb area
+ if (vert) {
+
+ thumb_area.Height = Height - scrollbutton_height - scrollbutton_height;
+ thumb_area.X = 0;
+ thumb_area.Y = scrollbutton_height;
+ thumb_area.Width = Width;
+
+ if (Height < thumb_notshown_size)
+ thumb_size = 0;
+ else {
+ double per = ((double) lchange / (double)((1 + maximum - minimum)));
+ thumb_size = 1 + (int) (thumb_area.Height * per);
+
+ if (thumb_size < thumb_min_size)
+ thumb_size = thumb_min_size;
+
+ // Give the user something to drag if LargeChange is zero
+ if (LargeChange == 0)
+ thumb_size = 17;
+ }
+
+ pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
+
+ } else {
+
+ thumb_area.Y = 0;
+ thumb_area.X = scrollbutton_width;
+ thumb_area.Height = Height;
+ thumb_area.Width = Width - scrollbutton_width - scrollbutton_width;
+
+ if (Width < thumb_notshown_size)
+ thumb_size = 0;
+ else {
+ double per = ((double) lchange / (double)((1 + maximum - minimum)));
+ thumb_size = 1 + (int) (thumb_area.Width * per);
+
+ if (thumb_size < thumb_min_size)
+ thumb_size = thumb_min_size;
+
+ // Give the user something to drag if LargeChange is zero
+ if (LargeChange == 0)
+ thumb_size = 17;
+ }
+
+ pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
+ }
+ }
+
+ private void LargeIncrement ()
+ {
+ ScrollEventArgs event_args;
+ int pos = Math.Min (MaximumAllowed, position + large_change);
+
+ event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ // UIA Framework event invoked when the "LargeIncrement
+ // Button" is "clicked" either by using the Invoke Pattern
+ // or the space between the thumb and the bottom/right button
+ OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeIncrement, Value));
+ }
+
+ private void LargeDecrement ()
+ {
+ ScrollEventArgs event_args;
+ int pos = Math.Max (Minimum, position - large_change);
+
+ event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ // UIA Framework event invoked when the "LargeDecrement
+ // Button" is "clicked" either by using the Invoke Pattern
+ // or the space between the thumb and the top/left button
+ OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeDecrement, Value));
+ }
+
+ private void OnResizeSB (Object o, EventArgs e)
+ {
+ if (Width <= 0 || Height <= 0)
+ return;
+
+ CalcButtonSizes ();
+ CalcThumbArea ();
+ UpdatePos (position, true);
+
+ Refresh ();
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
+ }
+
+ private void OnTimer (Object source, EventArgs e)
+ {
+ ClearDirty ();
+
+ switch (timer_type) {
+
+ case TimerType.HoldButton:
+ SetRepeatButtonTimer ();
+ break;
+
+ case TimerType.RepeatButton:
+ {
+ if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
+ SmallDecrement();
+ SendWMScroll(ScrollBarCommands.SB_LINEUP);
+ }
+
+ if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
+ SmallIncrement();
+ SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
+ }
+
+ break;
+ }
+
+ case TimerType.HoldThumbArea:
+ SetRepeatThumbAreaTimer ();
+ break;
+
+ case TimerType.RepeatThumbArea:
+ {
+ Point pnt, pnt_screen;
+ Rectangle thumb_area_screen = thumb_area;
+
+ pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
+ thumb_area_screen.X = pnt_screen.X;
+ thumb_area_screen.Y = pnt_screen.Y;
+
+ if (thumb_area_screen.Contains (MousePosition) == false) {
+ timer.Enabled = false;
+ thumb_moving = ThumbMoving.None;
+ DirtyThumbArea ();
+ InvalidateDirty ();
+ }
+
+ pnt = PointToClient (MousePosition);
+
+ if (vert)
+ lastclick_pos = pnt.Y;
+ else
+ lastclick_pos = pnt.X;
+
+ if (thumb_moving == ThumbMoving.Forward) {
+ if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
+ (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
+ (thumb_area.Contains (pnt) == false)) {
+ timer.Enabled = false;
+ thumb_moving = ThumbMoving.None;
+ Refresh ();
+ return;
+ } else {
+ LargeIncrement ();
+ SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
+ }
+ } else {
+ if ((vert && (thumb_pos.Y < lastclick_pos)) ||
+ (!vert && (thumb_pos.X < lastclick_pos))){
+ timer.Enabled = false;
+ thumb_moving = ThumbMoving.None;
+ SendWMScroll(ScrollBarCommands.SB_PAGEUP);
+ Refresh ();
+ } else {
+ LargeDecrement ();
+ SendWMScroll(ScrollBarCommands.SB_PAGEUP);
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ InvalidateDirty ();
+ }
+
+ private void MoveThumb (Rectangle original_thumbpos, int value)
+ {
+ /* so, the reason this works can best be
+ * described by the following 1 dimensional
+ * pictures
+ *
+ * say you have a scrollbar thumb positioned
+ * thusly:
+ *
+ * <---------------------| |------------------------------>
+ *
+ * and you want it to end up looking like this:
+ *
+ * <-----------------------------| |---------------------->
+ *
+ * that can be done with the scrolling api by
+ * extending the rectangle to encompass both
+ * positions:
+ *
+ * start of range end of range
+ * \ /
+ * <---------------------| |-------|---------------------->
+ *
+ * so, we end up scrolling just this little region:
+ *
+ * | |-------|
+ *
+ * and end up with ********| |
+ *
+ * where ****** is space that is automatically
+ * redrawn.
+ *
+ * It's clear that in both cases (left to
+ * right, right to left) we need to extend the
+ * size of the scroll rectangle to encompass
+ * both. In the right to left case, we also
+ * need to decrement the X coordinate.
+ *
+ * We call Update after scrolling to make sure
+ * there's no garbage left in the window to be
+ * copied again if we're called before the
+ * paint events have been handled.
+ *
+ */
+ int delta;
+
+ if (vert) {
+ delta = value - original_thumbpos.Y;
+
+ if (delta < 0) {
+ original_thumbpos.Y += delta;
+ original_thumbpos.Height -= delta;
+ }
+ else {
+ original_thumbpos.Height += delta;
+ }
+
+ XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
+ }
+ else {
+ delta = value - original_thumbpos.X;
+
+ if (delta < 0) {
+ original_thumbpos.X += delta;
+ original_thumbpos.Width -= delta;
+ }
+ else {
+ original_thumbpos.Width += delta;
+ }
+
+ XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
+ }
+
+ Update ();
+ }
+
+ private void OnMouseMoveSB (object sender, MouseEventArgs e)
+ {
+ if (Enabled == false)
+ return;
+
+ FirstButtonEntered = first_arrow_area.Contains (e.Location);
+ SecondButtonEntered = second_arrow_area.Contains (e.Location);
+
+ if (thumb_size == 0)
+ return;
+
+ ThumbEntered = thumb_pos.Contains (e.Location);
+
+ if (firstbutton_pressed) {
+ if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
+ firstbutton_state = ButtonState.Normal;
+ Invalidate (first_arrow_area);
+ Update();
+ return;
+ } else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
+ firstbutton_state = ButtonState.Pushed;
+ Invalidate (first_arrow_area);
+ Update();
+ return;
+ }
+ } else if (secondbutton_pressed) {
+ if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
+ secondbutton_state = ButtonState.Normal;
+ Invalidate (second_arrow_area);
+ Update();
+ return;
+ } else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
+ secondbutton_state = ButtonState.Pushed;
+ Invalidate (second_arrow_area);
+ Update();
+ return;
+ }
+ } else if (thumb_pressed == true) {
+ if (vert) {
+ int thumb_edge = e.Y - thumbclick_offset;
+
+ if (thumb_edge < thumb_area.Y)
+ thumb_edge = thumb_area.Y;
+ else if (thumb_edge > thumb_area.Bottom - thumb_size)
+ thumb_edge = thumb_area.Bottom - thumb_size;
+
+ if (thumb_edge != thumb_pos.Y) {
+ Rectangle thumb_rect = thumb_pos;
+
+ UpdateThumbPos (thumb_edge, false, true);
+
+ MoveThumb (thumb_rect, thumb_pos.Y);
+
+ OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
+ }
+ SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
+ } else {
+ int thumb_edge = e.X - thumbclick_offset;
+
+ if (thumb_edge < thumb_area.X)
+ thumb_edge = thumb_area.X;
+ else if (thumb_edge > thumb_area.Right - thumb_size)
+ thumb_edge = thumb_area.Right - thumb_size;
+
+ if (thumb_edge != thumb_pos.X) {
+ Rectangle thumb_rect = thumb_pos;
+
+ UpdateThumbPos (thumb_edge, false, true);
+
+ MoveThumb (thumb_rect, thumb_pos.X);
+
+ OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
+ }
+ SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
+ }
+
+ }
+
+ }
+
+ private void OnMouseDownSB (object sender, MouseEventArgs e)
+ {
+ ClearDirty ();
+
+ if (Enabled == false || (e.Button & MouseButtons.Left) == 0)
+ return;
+
+ if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
+ SendWMScroll(ScrollBarCommands.SB_LINEUP);
+ firstbutton_state = ButtonState.Pushed;
+ firstbutton_pressed = true;
+ Invalidate (first_arrow_area);
+ Update();
+ if (!timer.Enabled) {
+ SetHoldButtonClickTimer ();
+ timer.Enabled = true;
+ }
+ }
+
+ if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
+ SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
+ secondbutton_state = ButtonState.Pushed;
+ secondbutton_pressed = true;
+ Invalidate (second_arrow_area);
+ Update();
+ if (!timer.Enabled) {
+ SetHoldButtonClickTimer ();
+ timer.Enabled = true;
+ }
+ }
+
+ if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
+ ThumbPressed = true;
+ SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
+ if (vert) {
+ thumbclick_offset = e.Y - thumb_pos.Y;
+ lastclick_pos = e.Y;
+ }
+ else {
+ thumbclick_offset = e.X - thumb_pos.X;
+ lastclick_pos = e.X;
+ }
+ } else {
+ if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
+
+ if (vert) {
+ lastclick_pos = e.Y;
+
+ if (e.Y > thumb_pos.Y + thumb_pos.Height) {
+ SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
+ LargeIncrement ();
+ thumb_moving = ThumbMoving.Forward;
+ Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
+ ClientRectangle.Width,
+ ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
+ scrollbutton_height));
+ } else {
+ SendWMScroll(ScrollBarCommands.SB_PAGEUP);
+ LargeDecrement ();
+ thumb_moving = ThumbMoving.Backwards;
+ Dirty (new Rectangle (0, scrollbutton_height,
+ ClientRectangle.Width,
+ thumb_pos.Y - scrollbutton_height));
+ }
+ } else {
+
+ lastclick_pos = e.X;
+
+ if (e.X > thumb_pos.X + thumb_pos.Width) {
+ SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
+ thumb_moving = ThumbMoving.Forward;
+ LargeIncrement ();
+ Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
+ ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
+ scrollbutton_width,
+ ClientRectangle.Height));
+ } else {
+ SendWMScroll(ScrollBarCommands.SB_PAGEUP);
+ thumb_moving = ThumbMoving.Backwards;
+ LargeDecrement ();
+ Dirty (new Rectangle (scrollbutton_width, 0,
+ thumb_pos.X - scrollbutton_width,
+ ClientRectangle.Height));
+ }
+ }
+
+ SetHoldThumbAreaTimer ();
+ timer.Enabled = true;
+ InvalidateDirty ();
+ }
+ }
+ }
+
+ private void OnMouseUpSB (object sender, MouseEventArgs e)
+ {
+ ClearDirty ();
+
+ if (Enabled == false)
+ return;
+
+ timer.Enabled = false;
+ if (thumb_moving != ThumbMoving.None) {
+ DirtyThumbArea ();
+ thumb_moving = ThumbMoving.None;
+ }
+
+ if (firstbutton_pressed) {
+ firstbutton_state = ButtonState.Normal;
+ if (first_arrow_area.Contains (e.X, e.Y)) {
+ SmallDecrement ();
+ }
+ SendWMScroll(ScrollBarCommands.SB_LINEUP);
+ firstbutton_pressed = false;
+ Dirty (first_arrow_area);
+ } else if (secondbutton_pressed) {
+ secondbutton_state = ButtonState.Normal;
+ if (second_arrow_area.Contains (e.X, e.Y)) {
+ SmallIncrement ();
+ }
+ SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
+ Dirty (second_arrow_area);
+ secondbutton_pressed = false;
+ } else if (thumb_pressed == true) {
+ OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
+ OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
+ SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
+ ThumbPressed = false;
+ return;
+ }
+
+ InvalidateDirty ();
+ }
+
+ private void OnKeyDownSB (Object o, KeyEventArgs key)
+ {
+ if (Enabled == false)
+ return;
+
+ ClearDirty ();
+
+ switch (key.KeyCode){
+ case Keys.Up:
+ {
+ SmallDecrement ();
+ break;
+ }
+ case Keys.Down:
+ {
+ SmallIncrement ();
+ break;
+ }
+ case Keys.PageUp:
+ {
+ LargeDecrement ();
+ break;
+ }
+ case Keys.PageDown:
+ {
+ LargeIncrement ();
+ break;
+ }
+ case Keys.Home:
+ {
+ SetHomePosition ();
+ break;
+ }
+ case Keys.End:
+ {
+ SetEndPosition ();
+ break;
+ }
+ default:
+ break;
+ }
+
+ InvalidateDirty ();
+ }
+
+ // I hate to do this, but we don't have the resources to track
+ // down everything internal that is setting a value outside the
+ // correct range, so we'll clamp it to the acceptable values.
+ internal void SafeValueSet (int value)
+ {
+ value = Math.Min (value, maximum);
+ value = Math.Max (value, minimum);
+
+ Value = value;
+ }
+
+ private void SetEndPosition ()
+ {
+ ScrollEventArgs event_args;
+ int pos = MaximumAllowed;
+
+ event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
+ OnScroll (event_args);
+ pos = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
+ OnScroll (event_args);
+ pos = event_args.NewValue;
+
+ SetValue (pos);
+ }
+
+ private void SetHomePosition ()
+ {
+ ScrollEventArgs event_args;
+ int pos = Minimum;
+
+ event_args = new ScrollEventArgs (ScrollEventType.First, pos);
+ OnScroll (event_args);
+ pos = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
+ OnScroll (event_args);
+ pos = event_args.NewValue;
+
+ SetValue (pos);
+ }
+
+ private void SmallIncrement ()
+ {
+ ScrollEventArgs event_args;
+ int pos = Math.Min (MaximumAllowed, position + SmallChange);
+
+ event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ // UIA Framework event invoked when the "SmallIncrement
+ // Button" (a.k.a bottom/right button) is "clicked" either
+ // by using the Invoke Pattern or the button itself
+ OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallIncrement, Value));
+ }
+
+ private void SmallDecrement ()
+ {
+ ScrollEventArgs event_args;
+ int pos = Math.Max (Minimum, position - SmallChange);
+
+ event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
+ OnScroll (event_args);
+ Value = event_args.NewValue;
+
+ // UIA Framework event invoked when the "SmallDecrement
+ // Button" (a.k.a top/left button) is "clicked" either
+ // by using the Invoke Pattern or the button itself
+ OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallDecrement, Value));
+ }
+
+ private void SetHoldButtonClickTimer ()
+ {
+ timer.Enabled = false;
+ timer.Interval = 200;
+ timer_type = TimerType.HoldButton;
+ timer.Enabled = true;
+ }
+
+ private void SetRepeatButtonTimer ()
+ {
+ timer.Enabled = false;
+ timer.Interval = 50;
+ timer_type = TimerType.RepeatButton;
+ timer.Enabled = true;
+ }
+
+ private void SetHoldThumbAreaTimer ()
+ {
+ timer.Enabled = false;
+ timer.Interval = 200;
+ timer_type = TimerType.HoldThumbArea;
+ timer.Enabled = true;
+ }
+
+ private void SetRepeatThumbAreaTimer ()
+ {
+ timer.Enabled = false;
+ timer.Interval = 50;
+ timer_type = TimerType.RepeatThumbArea;
+ timer.Enabled = true;
+ }
+
+ private void UpdatePos (int newPos, bool update_thumbpos)
+ {
+ int pos;
+
+ if (newPos < minimum)
+ pos = minimum;
+ else
+ if (newPos > MaximumAllowed)
+ pos = MaximumAllowed;
+ else
+ pos = newPos;
+
+ // pos can't be less than minimum or greater than maximum
+ if (pos < minimum)
+ pos = minimum;
+ if (pos > maximum)
+ pos = maximum;
+
+ if (update_thumbpos) {
+ if (vert)
+ UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
+ else
+ UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
+ SetValue (pos);
+ }
+ else {
+ position = pos; // Updates directly the value to avoid thumb pos update
+
+
+ // XXX some reason we don't call OnValueChanged?
+ EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+ }
+
+ private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
+ {
+ float new_pos = 0;
+
+ if (vert) {
+ if (dirty)
+ Dirty (thumb_pos);
+ if (pixel < thumb_area.Y)
+ thumb_pos.Y = thumb_area.Y;
+ else if (pixel > thumb_area.Bottom - thumb_size)
+ thumb_pos.Y = thumb_area.Bottom - thumb_size;
+ else
+ thumb_pos.Y = pixel;
+
+ thumb_pos.X = 0;
+ thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
+ thumb_pos.Height = thumb_size;
+ new_pos = (float) (thumb_pos.Y - thumb_area.Y);
+ new_pos = new_pos / pixel_per_pos;
+ if (dirty)
+ Dirty (thumb_pos);
+ } else {
+ if (dirty)
+ Dirty (thumb_pos);
+ if (pixel < thumb_area.X)
+ thumb_pos.X = thumb_area.X;
+ else if (pixel > thumb_area.Right - thumb_size)
+ thumb_pos.X = thumb_area.Right - thumb_size;
+ else
+ thumb_pos.X = pixel;
+
+ thumb_pos.Y = 0;
+ thumb_pos.Width = thumb_size;
+ thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
+ new_pos = (float) (thumb_pos.X - thumb_area.X);
+ new_pos = new_pos / pixel_per_pos;
+
+ if (dirty)
+ Dirty (thumb_pos);
+ }
+
+ if (update_value)
+ UpdatePos ((int) new_pos + minimum, false);
+ }
+
+ private void SetValue (int value)
+ {
+ if ( value < minimum || value > maximum )
+ throw new ArgumentException(
+ String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
+
+ if (position != value){
+ position = value;
+
+ OnValueChanged (EventArgs.Empty);
+ UpdatePos (value, true);
+ }
+ }
+
+ private void ClearDirty ()
+ {
+ dirty = Rectangle.Empty;
+ }
+
+ private void Dirty (Rectangle r)
+ {
+ if (dirty == Rectangle.Empty) {
+ dirty = r;
+ return;
+ }
+ dirty = Rectangle.Union (dirty, r);
+ }
+
+ private void DirtyThumbArea ()
+ {
+ if (thumb_moving == ThumbMoving.Forward) {
+ if (vert) {
+ Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
+ ClientRectangle.Width,
+ ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
+ scrollbutton_height));
+ } else {
+ Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
+ ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
+ scrollbutton_width,
+ ClientRectangle.Height));
+ }
+ } else if (thumb_moving == ThumbMoving.Backwards) {
+ if (vert) {
+ Dirty(new Rectangle (0, scrollbutton_height,
+ ClientRectangle.Width,
+ thumb_pos.Y - scrollbutton_height));
+ } else {
+ Dirty (new Rectangle (scrollbutton_width, 0,
+ thumb_pos.X - scrollbutton_width,
+ ClientRectangle.Height));
+ }
+ }
+ }
+
+ private void InvalidateDirty ()
+ {
+ Invalidate (dirty);
+ Update();
+ dirty = Rectangle.Empty;
+ }
+
+ void OnMouseEnter (object sender, EventArgs e)
+ {
+ if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
+ Region region_to_invalidate = new Region (first_arrow_area);
+ region_to_invalidate.Union (second_arrow_area);
+ Invalidate (region_to_invalidate);
+ }
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ Region region_to_invalidate = new Region ();
+ region_to_invalidate.MakeEmpty ();
+ bool dirty = false;
+ if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
+ region_to_invalidate.Union (first_arrow_area);
+ region_to_invalidate.Union (second_arrow_area);
+ dirty = true;
+ } else
+ if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
+ if (first_button_entered) {
+ region_to_invalidate.Union (first_arrow_area);
+ dirty = true;
+ } else if (second_button_entered) {
+ region_to_invalidate.Union (second_arrow_area);
+ dirty = true;
+ }
+ if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
+ if (thumb_entered) {
+ region_to_invalidate.Union (thumb_pos);
+ dirty = true;
+ }
+ first_button_entered = false;
+ second_button_entered = false;
+ thumb_entered = false;
+ if (dirty)
+ Invalidate (region_to_invalidate);
+ region_to_invalidate.Dispose ();
+ }
+ #endregion //Private Methods
+ protected override void OnMouseWheel (MouseEventArgs e)
+ {
+ base.OnMouseWheel (e);
+ }
+
+ #region UIA Framework Section: Events, Methods and Properties.
+
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ScrollBarButtonInvokePatternInvokeEvent uses the events.
+ //
+ // Types used to generate UIA InvokedEvent
+ // * args.Type = ScrollEventType.LargeIncrement. Space between Thumb and bottom/right Button
+ // * args.Type = ScrollEventType.LargeDecrement. Space between Thumb and top/left Button
+ // * args.Type = ScrollEventType.SmallIncrement. Small increment UIA Button (bottom/right Button)
+ // * args.Type = ScrollEventType.SmallDecrement. Small decrement UIA Button (top/left Button)
+ // Types used to generate RangeValue-related events
+ // * args.Type = ScrollEventType.LargeIncrement. LargeChange event
+ // * args.Type = ScrollEventType.Last. Maximum event
+ // * args.Type = ScrollEventType.First. Minimum event
+ // * args.Type = ScrollEventType.SmallIncrement. SmallChange event
+ static object UIAScrollEvent = new object ();
+ static object UIAValueChangeEvent = new object ();
+
+ internal event ScrollEventHandler UIAScroll {
+ add { Events.AddHandler (UIAScrollEvent, value); }
+ remove { Events.RemoveHandler (UIAScrollEvent, value); }
+ }
+
+ internal event ScrollEventHandler UIAValueChanged {
+ add { Events.AddHandler (UIAValueChangeEvent, value); }
+ remove { Events.RemoveHandler (UIAValueChangeEvent, value); }
+ }
+
+ internal void OnUIAScroll (ScrollEventArgs args)
+ {
+ ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
+ if (eh != null)
+ eh (this, args);
+ }
+
+ internal void OnUIAValueChanged (ScrollEventArgs args)
+ {
+ ScrollEventHandler eh = (ScrollEventHandler) Events [UIAValueChangeEvent];
+ if (eh != null)
+ eh (this, args);
+ }
+
+ //NOTE:
+ // Wrapper methods used by the Reflection.
+ // Class ScrollBarButtonInvokeProviderBehavior uses the events.
+ //
+ internal void UIALargeIncrement ()
+ {
+ LargeIncrement ();
+ }
+
+ internal void UIALargeDecrement ()
+ {
+ LargeDecrement ();
+ }
+
+ internal void UIASmallIncrement ()
+ {
+ SmallIncrement ();
+ }
+
+ internal void UIASmallDecrement ()
+ {
+ SmallDecrement ();
+ }
+
+ internal Rectangle UIAThumbArea {
+ get { return thumb_area; }
+ }
+
+ internal Rectangle UIAThumbPosition {
+ get { return thumb_pos; }
+ }
+
+ #endregion UIA Framework Section: Events, Methods and Properties.
+
+ }
+}
+
+
+
diff --git a/source/ShiftUI/Widgets/ScrollBarRenderer.cs b/source/ShiftUI/Widgets/ScrollBarRenderer.cs
new file mode 100644
index 0000000..3030c72
--- /dev/null
+++ b/source/ShiftUI/Widgets/ScrollBarRenderer.cs
@@ -0,0 +1,326 @@
+//
+// ScrollBarRenderer.cs
+//
+// 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) 2006 Novell, Inc.
+//
+// Authors:
+// Jonathan Pobst ([email protected])
+//
+
+using System.Drawing;
+using System.Windows.Forms.VisualStyles;
+
+namespace ShiftUI
+{
+ public sealed class ScrollBarRenderer
+ {
+ #region Private Constructor
+ private ScrollBarRenderer () { }
+ #endregion
+
+ #region Public Static Methods
+ public static void DrawArrowButton (Graphics g, Rectangle bounds, ScrollBarArrowButtonState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarArrowButtonState.DownDisabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.DownDisabled);
+ break;
+ case ScrollBarArrowButtonState.DownHot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.DownHot);
+ break;
+ case ScrollBarArrowButtonState.DownNormal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.DownNormal);
+ break;
+ case ScrollBarArrowButtonState.DownPressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.DownPressed);
+ break;
+ case ScrollBarArrowButtonState.LeftDisabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.LeftDisabled);
+ break;
+ case ScrollBarArrowButtonState.LeftHot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.LeftHot);
+ break;
+ case ScrollBarArrowButtonState.LeftNormal:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.LeftNormal);
+ break;
+ case ScrollBarArrowButtonState.LeftPressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.LeftPressed);
+ break;
+ case ScrollBarArrowButtonState.RightDisabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.RightDisabled);
+ break;
+ case ScrollBarArrowButtonState.RightHot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.RightHot);
+ break;
+ case ScrollBarArrowButtonState.RightNormal:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.RightNormal);
+ break;
+ case ScrollBarArrowButtonState.RightPressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.RightPressed);
+ break;
+ case ScrollBarArrowButtonState.UpDisabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.UpDisabled);
+ break;
+ case ScrollBarArrowButtonState.UpHot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.UpHot);
+ break;
+ case ScrollBarArrowButtonState.UpNormal:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.UpNormal);
+ break;
+ case ScrollBarArrowButtonState.UpPressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ArrowButton.UpPressed);
+ break;
+ }
+
+ vsr.DrawBackground(g, bounds);
+ }
+
+ public static void DrawHorizontalThumb (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonHorizontal.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawHorizontalThumbGrip (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.GripperHorizontal.Normal);
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawLeftHorizontalTrack (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LeftTrackHorizontal.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LeftTrackHorizontal.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LeftTrackHorizontal.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LeftTrackHorizontal.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawLowerVerticalTrack (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LowerTrackVertical.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LowerTrackVertical.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LowerTrackVertical.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.LowerTrackVertical.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawRightHorizontalTrack (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.RightTrackHorizontal.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.RightTrackHorizontal.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.RightTrackHorizontal.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.RightTrackHorizontal.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawSizeBox (Graphics g, Rectangle bounds, ScrollBarSizeBoxState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarSizeBoxState.LeftAlign:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.SizeBox.LeftAlign);
+ break;
+ case ScrollBarSizeBoxState.RightAlign:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.SizeBox.RightAlign);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawUpperVerticalTrack (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.UpperTrackVertical.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.UpperTrackVertical.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.UpperTrackVertical.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.UpperTrackVertical.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawVerticalThumb (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr;
+
+ switch (state) {
+ case ScrollBarState.Disabled:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonVertical.Disabled);
+ break;
+ case ScrollBarState.Hot:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonVertical.Hot);
+ break;
+ case ScrollBarState.Normal:
+ default:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonVertical.Normal);
+ break;
+ case ScrollBarState.Pressed:
+ vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.ThumbButtonVertical.Pressed);
+ break;
+ }
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static void DrawVerticalThumbGrip (Graphics g, Rectangle bounds, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.GripperVertical.Normal); ;
+
+ vsr.DrawBackground (g, bounds);
+ }
+
+ public static Size GetSizeBoxSize (Graphics g, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.SizeBox.LeftAlign);
+
+ return vsr.GetPartSize(g, ThemeSizeType.Draw);
+ }
+
+ public static Size GetThumbGripSize (Graphics g, ScrollBarState state)
+ {
+ if (!IsSupported)
+ throw new InvalidOperationException ();
+
+ VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.ScrollBar.GripperVertical.Normal);
+
+ return vsr.GetPartSize (g, ThemeSizeType.Draw);
+ }
+ #endregion
+
+ #region Public Static Properties
+ public static bool IsSupported {
+ get { return VisualStyleInformation.IsEnabledByUser && (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled || Application.VisualStyleState == VisualStyleState.ClientAreaEnabled); }
+ }
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/ScrollButton.cs b/source/ShiftUI/Widgets/ScrollButton.cs
new file mode 100644
index 0000000..2c95da8
--- /dev/null
+++ b/source/ShiftUI/Widgets/ScrollButton.cs
@@ -0,0 +1,39 @@
+// 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 Novell, Inc.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+//
+
+
+// COMPLETE
+
+namespace ShiftUI {
+ public enum ScrollButton {
+ Min = 0,
+ Up = 0,
+ Down = 1,
+ Left = 2,
+ Right = 3,
+ Max = 3
+ }
+}
diff --git a/source/ShiftUI/Widgets/SizeGrip.cs b/source/ShiftUI/Widgets/SizeGrip.cs
new file mode 100644
index 0000000..3e33462
--- /dev/null
+++ b/source/ShiftUI/Widgets/SizeGrip.cs
@@ -0,0 +1,293 @@
+// 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) 2005 Novell, Inc.
+//
+// Authors:
+// Jackson Harper ([email protected])
+
+using System;
+using System.Drawing;
+
+namespace ShiftUI {
+
+ internal class SizeGrip : Widget {
+ #region Local Variables
+ private Point capture_point;
+ private Widget captured_Widget;
+ private int window_w;
+ private int window_h;
+ private bool hide_pending;
+ private bool captured;
+ private bool is_virtual; // If virtual the size grip is painted directly on the captured Widgets' surface.
+ private bool enabled;
+ private bool fill_background;
+ private Rectangle last_painted_area; // The last area that was painted (to know which area to invalidate when resizing).
+ #endregion // Local Variables
+
+ #region Constructors
+ public SizeGrip (Widget CapturedWidget)
+ {
+ this.Cursor = Cursors.SizeNWSE;
+ enabled = true;
+ fill_background = true;
+ this.Size = GetDefaultSize ();
+ this.CapturedWidget = CapturedWidget;
+ }
+ #endregion // Constructors
+
+ #region Properties
+ public bool FillBackground {
+ get {
+ return fill_background;
+ }
+ set {
+ fill_background = value;
+ }
+ }
+
+ public bool Virtual {
+ get {
+ return is_virtual;
+ }
+ set {
+ if (is_virtual == value)
+ return;
+
+ is_virtual = value;
+ if (is_virtual) {
+ CapturedWidget.MouseMove += new MouseEventHandler(HandleMouseMove);
+ CapturedWidget.MouseUp += new MouseEventHandler(HandleMouseUp);
+ CapturedWidget.MouseDown += new MouseEventHandler(HandleMouseDown);
+ CapturedWidget.EnabledChanged += new EventHandler(HandleEnabledChanged);
+ CapturedWidget.Resize += new EventHandler(HandleResize);
+ } else {
+ CapturedWidget.MouseMove -= new MouseEventHandler (HandleMouseMove);
+ CapturedWidget.MouseUp -= new MouseEventHandler (HandleMouseUp);
+ CapturedWidget.MouseDown -= new MouseEventHandler (HandleMouseDown);
+ CapturedWidget.EnabledChanged -= new EventHandler (HandleEnabledChanged);
+ CapturedWidget.Resize -= new EventHandler (HandleResize);
+ }
+ }
+ }
+
+ public Widget CapturedWidget {
+ get {
+ return captured_Widget;
+ }
+ set {
+ captured_Widget = value;
+ }
+ }
+
+ #endregion // Properties
+
+ #region Methods
+ static internal Size GetDefaultSize () {
+ return new Size (SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight);
+ }
+
+ static internal Rectangle GetDefaultRectangle (Widget Parent)
+ {
+ Size size = GetDefaultSize ();
+ return new Rectangle (Parent.ClientSize.Width - size.Width, Parent.ClientSize.Height - size.Height, size.Width, size.Height);
+ }
+
+ private void HandleResize (object sender, EventArgs e)
+ {
+ Widget ctrl = (Widget) sender;
+ ctrl.Invalidate (last_painted_area);
+ }
+
+ private void HandleEnabledChanged (object sender, EventArgs e)
+ {
+ Widget ctrl = (Widget) sender;
+ enabled = ctrl.Enabled;
+ Cursor cursor;
+ if (enabled) {
+ cursor = Cursors.SizeNWSE;
+ } else {
+ cursor = Cursors.Default;
+ }
+ if (is_virtual) {
+ if (CapturedWidget != null)
+ CapturedWidget.Cursor = cursor;
+ } else {
+ this.Cursor = cursor;
+ }
+ ctrl.Invalidate (GetDefaultRectangle (ctrl));
+
+ }
+
+ // This method needs to be internal, since the captured Widget must be able to call
+ // it. We can't use events to hook it up, since then the paint ordering won't be correct
+ internal void HandlePaint (object sender, PaintEventArgs e)
+ {
+ if (Visible) {
+ Widget destination = (Widget) sender;
+ Graphics gr = e.Graphics;
+ Rectangle rect = GetDefaultRectangle (destination);
+
+ if (!is_virtual || fill_background) {
+ gr.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl), rect);
+ }
+ if (enabled) {
+ WidgetPaint.DrawSizeGrip (gr, BackColor, rect);
+ }
+ last_painted_area = rect;
+ }
+ }
+
+ private void HandleMouseCaptureChanged (object sender, EventArgs e)
+ {
+ Widget ctrl = (Widget) sender;
+ if (captured && !ctrl.Capture) {
+ captured = false;
+ CapturedWidget.Size = new Size (window_w, window_h);
+ }
+ }
+
+ internal void HandleMouseDown (object sender, MouseEventArgs e)
+ {
+ if (enabled) {
+ Widget ctrl = (Widget)sender;
+ if (!GetDefaultRectangle (ctrl).Contains (e.X, e.Y)) {
+ return;
+ }
+
+ ctrl.Capture = true;
+ captured = true;
+ capture_point = Widget.MousePosition;
+
+ window_w = CapturedWidget.Width;
+ window_h = CapturedWidget.Height;
+ }
+ }
+
+ internal void HandleMouseMove (object sender, MouseEventArgs e)
+ {
+ Widget ctrl = (Widget) sender;
+ Rectangle rect = GetDefaultRectangle (ctrl);
+
+ if (rect.Contains (e.X, e.Y)) {
+ ctrl.Cursor = Cursors.SizeNWSE;
+ } else {
+ ctrl.Cursor = Cursors.Default;
+ }
+
+ if (captured) {
+ int delta_x;
+ int delta_y;
+ Point current_point;
+
+ current_point = Widget.MousePosition;
+
+ delta_x = current_point.X - capture_point.X;
+ delta_y = current_point.Y - capture_point.Y;
+
+ Widget parent = CapturedWidget;
+ Form form_parent = parent as Form;
+ Size new_size = new Size (window_w + delta_x, window_h + delta_y);
+ Size max_size = form_parent != null ? form_parent.MaximumSize : Size.Empty;
+ Size min_size = form_parent != null ? form_parent.MinimumSize : Size.Empty;
+
+ if (new_size.Width > max_size.Width && max_size.Width > 0)
+ new_size.Width = max_size.Width;
+ else if (new_size.Width < min_size.Width)
+ new_size.Width = min_size.Width;
+
+ if (new_size.Height > max_size.Height && max_size.Height > 0)
+ new_size.Height = max_size.Height;
+ else if (new_size.Height < min_size.Height)
+ new_size.Height = min_size.Height;
+
+ if (new_size != parent.Size) {
+ parent.Size = new_size;
+ }
+ }
+ }
+
+ internal void HandleMouseUp (object sender, MouseEventArgs e)
+ {
+ if (captured) {
+ Widget ctrl = (Widget) sender;
+ captured = false;
+ ctrl.Capture = false;
+ ctrl.Invalidate (last_painted_area);
+
+ if (Parent is ScrollableWidget) {
+ ((ScrollableWidget)Parent).UpdateSizeGripVisible ();
+ }
+ if (hide_pending) {
+ Hide();
+ hide_pending = false;
+ }
+ }
+ }
+
+
+ protected override void SetVisibleCore(bool value) {
+ if (Capture) {
+ if (value == false) {
+ hide_pending = true;
+ } else {
+ hide_pending = false;
+ }
+ return;
+ }
+ base.SetVisibleCore (value);
+ }
+
+ protected override void OnPaint (PaintEventArgs pe)
+ {
+ HandlePaint (this, pe);
+ base.OnPaint (pe);
+ }
+
+ protected override void OnMouseCaptureChanged (EventArgs e)
+ {
+ base.OnMouseCaptureChanged (e);
+ HandleMouseCaptureChanged (this, e);
+ }
+
+ protected override void OnEnabledChanged (EventArgs e)
+ {
+ base.OnEnabledChanged (e);
+ HandleEnabledChanged (this, e);
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ HandleMouseDown (this, e);
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ HandleMouseMove (this, e);
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e)
+ {
+ HandleMouseUp (this, e);
+ }
+ #endregion // Methods
+ }
+}
+
+
diff --git a/source/ShiftUI/Widgets/SizeGripStyle.cs b/source/ShiftUI/Widgets/SizeGripStyle.cs
new file mode 100644
index 0000000..cdfc82c
--- /dev/null
+++ b/source/ShiftUI/Widgets/SizeGripStyle.cs
@@ -0,0 +1,37 @@
+// 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 Novell, Inc. (http://www.novell.com)
+//
+// Author:
+// Ravindra ([email protected])
+//
+
+// COMPLETE
+
+
+namespace ShiftUI
+{
+ public enum SizeGripStyle
+ {
+ Auto = 0,
+ Show = 1,
+ Hide = 2
+ }
+}
diff --git a/source/ShiftUI/Widgets/StatusBar.cs b/source/ShiftUI/Widgets/StatusBar.cs
new file mode 100644
index 0000000..2e99958
--- /dev/null
+++ b/source/ShiftUI/Widgets/StatusBar.cs
@@ -0,0 +1,806 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+
+//
+// TODO:
+// - Change cursor when mouse is over grip
+//
+
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Text;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI {
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent("PanelClick")]
+ //[Designer("ShiftUI.Design.StatusBarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultProperty("Text")]
+ public class StatusBar : Widget {
+ #region Fields
+ private StatusBarPanelCollection panels;
+
+ private bool show_panels = false;
+ private bool sizing_grip = true;
+
+ // Stuff for panel Tooltips
+ private Timer tooltip_timer;
+ private ToolTip tooltip_window;
+ private StatusBarPanel tooltip_currently_showing;
+ #endregion // Fields
+
+ #region Public Constructors
+ public StatusBar ()
+ {
+ Dock = DockStyle.Bottom;
+ this.TabStop = false;
+ this.SetStyle(Widgetstyles.UserPaint | Widgetstyles.Selectable, false);
+
+ // For displaying/hiding tooltips
+ MouseMove += new MouseEventHandler (StatusBar_MouseMove);
+ MouseLeave += new EventHandler (StatusBar_MouseLeave);
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color BackColor {
+ get { return base.BackColor; }
+ set { base.BackColor = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get {
+ return base.BackgroundImageLayout;
+ }
+ set {
+ base.BackgroundImageLayout = value;
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue(DockStyle.Bottom)]
+ public override DockStyle Dock {
+ get { return base.Dock; }
+ set { base.Dock = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get {
+ return base.DoubleBuffered;
+ }
+ set {
+ base.DoubleBuffered = value;
+ }
+ }
+
+ [Localizable(true)]
+ public override Font Font {
+ get { return base.Font; }
+ set {
+ if (value == Font)
+ return;
+ base.Font = value;
+ UpdateStatusBar ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set { base.ForeColor = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new ImeMode ImeMode {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ [MergableProperty(false)]
+ [Localizable(true)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+ public StatusBarPanelCollection Panels {
+ get {
+ if (panels == null)
+ panels = new StatusBarPanelCollection (this);
+ return panels;
+ }
+ }
+
+ [DefaultValue(false)]
+ public bool ShowPanels {
+ get { return show_panels; }
+ set {
+ if (show_panels == value)
+ return;
+ show_panels = value;
+ UpdateStatusBar ();
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool SizingGrip {
+ get { return sizing_grip; }
+ set {
+ if (sizing_grip == value)
+ return;
+ sizing_grip = value;
+ UpdateStatusBar ();
+ }
+ }
+
+ [DefaultValue(false)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ [Localizable(true)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (value == Text)
+ return;
+ base.Text = value;
+ UpdateStatusBar ();
+ }
+
+ }
+
+ #endregion Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override ImeMode DefaultImeMode {
+ get { return ImeMode.Disable; }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.StatusBarDefaultSize; }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public override string ToString () {
+ return base.ToString () + ", Panels.Count: " + Panels.Count +
+ (Panels.Count > 0 ? ", Panels[0]: " + Panels [0] : String.Empty);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ }
+
+ protected override void Dispose (bool disposing) {
+ base.Dispose (disposing);
+ }
+
+ protected virtual void OnDrawItem (StatusBarDrawItemEventArgs sbdievent) {
+ StatusBarDrawItemEventHandler eh = (StatusBarDrawItemEventHandler)(Events [DrawItemEvent]);
+ if (eh != null)
+ eh (this, sbdievent);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated (e);
+ CalcPanelSizes ();
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e) {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected override void OnLayout (LayoutEventArgs levent) {
+ base.OnLayout (levent);
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e) {
+ if (panels == null)
+ return;
+
+ float prev_x = 0;
+ float gap = ThemeEngine.Current.StatusBarHorzGapWidth;
+ for (int i = 0; i < panels.Count; i++) {
+ float x = panels [i].Width + prev_x + (i == panels.Count - 1 ? gap : gap / 2);
+ if (e.X >= prev_x && e.X <= x) {
+ OnPanelClick (new StatusBarPanelClickEventArgs (panels [i],
+ e.Button, e.Clicks, e.X, e.Y));
+ break;
+ }
+ prev_x = x;
+ }
+
+ base.OnMouseDown (e);
+ }
+
+ protected virtual void OnPanelClick (StatusBarPanelClickEventArgs e) {
+ StatusBarPanelClickEventHandler eh = (StatusBarPanelClickEventHandler)(Events [PanelClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+
+ if (Width <= 0 || Height <= 0)
+ return;
+
+ UpdateStatusBar ();
+ }
+
+ protected override void WndProc(ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ #endregion // Methods
+
+
+ #region Internal Methods
+ internal void OnDrawItemInternal (StatusBarDrawItemEventArgs e)
+ {
+ OnDrawItem (e);
+ }
+
+ internal void UpdatePanel (StatusBarPanel panel)
+ {
+ if (panel.AutoSize == StatusBarPanelAutoSize.Contents) {
+ UpdateStatusBar ();
+ return;
+ }
+
+ UpdateStatusBar ();
+ }
+
+ internal void UpdatePanelContents (StatusBarPanel panel)
+ {
+ if (panel.AutoSize == StatusBarPanelAutoSize.Contents) {
+ UpdateStatusBar ();
+ Invalidate ();
+ return;
+ }
+
+ Invalidate (new Rectangle (panel.X + 2, 2, panel.Width - 4, bounds.Height - 4));
+ }
+
+ void UpdateStatusBar ()
+ {
+ CalcPanelSizes ();
+ Refresh ();
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ Draw (pevent.Graphics, pevent.ClipRectangle);
+ }
+
+ private void CalcPanelSizes ()
+ {
+ if (panels == null || !show_panels)
+ return;
+
+ if (Width == 0 || Height == 0)
+ return;
+
+ int border = 2;
+ int gap = ThemeEngine.Current.StatusBarHorzGapWidth;
+ int taken = 0;
+ ArrayList springs = null;
+
+ taken = border;
+ for (int i = 0; i < panels.Count; i++) {
+ StatusBarPanel p = panels [i];
+
+ if (p.AutoSize == StatusBarPanelAutoSize.None) {
+ taken += p.Width;
+ taken += gap;
+ continue;
+ }
+ if (p.AutoSize == StatusBarPanelAutoSize.Contents) {
+ int len = (int)(TextRenderer.MeasureString (p.Text, Font).Width + 0.5F);
+ if (p.Icon != null) {
+ len += 21;
+ }
+ p.SetWidth (len + 8);
+ taken += p.Width;
+ taken += gap;
+ continue;
+ }
+ if (p.AutoSize == StatusBarPanelAutoSize.Spring) {
+ if (springs == null)
+ springs = new ArrayList ();
+ springs.Add (p);
+ taken += gap;
+ continue;
+ }
+ }
+
+ if (springs != null) {
+ int spring_total = springs.Count;
+ int total_width = Width - taken - (SizingGrip ? ThemeEngine.Current.StatusBarSizeGripWidth : 0);
+ for (int i = 0; i < spring_total; i++) {
+ StatusBarPanel p = (StatusBarPanel)springs[i];
+ int width = total_width / spring_total;
+ p.SetWidth(width >= p.MinWidth ? width : p.MinWidth);
+ }
+ }
+
+ taken = border;
+ for (int i = 0; i < panels.Count; i++) {
+ StatusBarPanel p = panels [i];
+ p.X = taken;
+ taken += p.Width + gap;
+ }
+ }
+
+ private void Draw (Graphics dc, Rectangle clip)
+ {
+ ThemeEngine.Current.DrawStatusBar (dc, clip, this);
+
+ }
+ #endregion // Internal Methods
+
+ #region Stuff for ToolTips
+ private void StatusBar_MouseMove (object sender, MouseEventArgs e)
+ {
+ if (!show_panels)
+ return;
+
+ StatusBarPanel p = GetPanelAtPoint (e.Location);
+
+ if (p != tooltip_currently_showing)
+ MouseLeftPanel (tooltip_currently_showing);
+
+ if (p != null && tooltip_currently_showing == null)
+ MouseEnteredPanel (p);
+ }
+
+ private void StatusBar_MouseLeave (object sender, EventArgs e)
+ {
+ if (tooltip_currently_showing != null)
+ MouseLeftPanel (tooltip_currently_showing);
+ }
+
+ private StatusBarPanel GetPanelAtPoint (Point point)
+ {
+ foreach (StatusBarPanel p in Panels)
+ if (point.X >= p.X && point.X <= (p.X + p.Width))
+ return p;
+
+ return null;
+ }
+
+ private void MouseEnteredPanel (StatusBarPanel item)
+ {
+ tooltip_currently_showing = item;
+ ToolTipTimer.Start ();
+ }
+
+ private void MouseLeftPanel (StatusBarPanel item)
+ {
+ ToolTipTimer.Stop ();
+ ToolTipWindow.Hide (this);
+ tooltip_currently_showing = null;
+ }
+
+ private Timer ToolTipTimer {
+ get {
+ if (tooltip_timer == null) {
+ tooltip_timer = new Timer ();
+ tooltip_timer.Enabled = false;
+ tooltip_timer.Interval = 500;
+ tooltip_timer.Tick += new EventHandler (ToolTipTimer_Tick);
+ }
+
+ return tooltip_timer;
+ }
+ }
+
+ private ToolTip ToolTipWindow {
+ get {
+ if (tooltip_window == null)
+ tooltip_window = new ToolTip ();
+
+ return tooltip_window;
+ }
+ }
+
+ private void ToolTipTimer_Tick (object o, EventArgs args)
+ {
+ string tooltip = tooltip_currently_showing.ToolTipText;
+
+ if (tooltip != null && tooltip.Length > 0)
+ ToolTipWindow.Present (this, tooltip);
+
+ ToolTipTimer.Stop ();
+ }
+ #endregion
+
+ #region Events
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackColorChanged {
+ add { base.BackColorChanged += value; }
+ remove { base.BackColorChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged
+ {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ static object DrawItemEvent = new object ();
+ static object PanelClickEvent = new object ();
+
+ public event StatusBarDrawItemEventHandler DrawItem {
+ add { Events.AddHandler (DrawItemEvent, value); }
+ remove { Events.RemoveHandler (DrawItemEvent, value); }
+ }
+
+ public event StatusBarPanelClickEventHandler PanelClick {
+ add { Events.AddHandler (PanelClickEvent, value); }
+ remove { Events.RemoveHandler (PanelClickEvent, value); }
+ }
+ #endregion // Events
+
+
+ #region Subclass StatusBarPanelCollection
+ [ListBindable (false)]
+ public class StatusBarPanelCollection : IList, ICollection, IEnumerable {
+ #region Fields
+ private StatusBar owner;
+ private ArrayList panels = new ArrayList ();
+ private int last_index_by_key;
+ #endregion // Fields
+
+ #region UIA Framework Events
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChanged (CollectionChangeEventArgs e)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, e);
+ }
+ #endregion
+
+ #region Public Constructors
+ public StatusBarPanelCollection (StatusBar owner)
+ {
+ this.owner = owner;
+ }
+
+ #endregion // Public Constructors
+
+ #region Private & Internal Methods
+ private int AddInternal (StatusBarPanel p, bool refresh) {
+ if (p == null)
+ throw new ArgumentNullException ("value");
+
+ p.SetParent (owner);
+ int res = panels.Add (p);
+
+ if (refresh) {
+ owner.CalcPanelSizes ();
+ owner.Refresh ();
+ }
+
+ // UIA Framework Event: Panel Added
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, res));
+
+ return res;
+ }
+
+ #endregion // Private & Internal Methods
+
+ #region Public Instance Properties
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public int Count {
+ get { return panels.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual StatusBarPanel this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+ return (StatusBarPanel) panels [index];
+ }
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("index");
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("index");
+
+ // UIA Framework Event: Panel Removed
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
+
+ value.SetParent (owner);
+
+ panels [index] = value;
+
+ // UIA Framework Event: Panel Added
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
+ }
+ }
+
+ public virtual StatusBarPanel this [string key] {
+ get {
+ int index = IndexOfKey (key);
+ if (index >= 0 && index < Count) {
+ return (StatusBarPanel) panels [index];
+ }
+ return null;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Public Instance Methods
+ public virtual int Add (StatusBarPanel value) {
+ return AddInternal (value, true);
+ }
+
+ public virtual StatusBarPanel Add (string text) {
+ StatusBarPanel res = new StatusBarPanel ();
+ res.Text = text;
+ Add (res);
+ return res;
+ }
+
+ public virtual void AddRange (StatusBarPanel [] panels) {
+ if (panels == null)
+ throw new ArgumentNullException ("panels");
+ if (panels.Length == 0)
+ return;
+
+ for (int i = 0; i < panels.Length; i++)
+ AddInternal (panels [i], false);
+ owner.Refresh ();
+ }
+
+ public virtual void Clear () {
+ panels.Clear ();
+
+ owner.Refresh ();
+
+ // UIA Framework Event: Panel Cleared
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
+ }
+
+ public bool Contains (StatusBarPanel panel) {
+ return panels.Contains (panel);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ int index = IndexOfKey (key);
+ return index >= 0 && index < Count;
+ }
+
+ public IEnumerator GetEnumerator () {
+ return panels.GetEnumerator ();
+ }
+
+ public int IndexOf (StatusBarPanel panel) {
+ return panels.IndexOf (panel);
+ }
+
+ public virtual int IndexOfKey (string key)
+ {
+ if (key == null || key == string.Empty)
+ return -1;
+
+ if (last_index_by_key >= 0 && last_index_by_key < Count &&
+ String.Compare (((StatusBarPanel)panels [last_index_by_key]).Name, key, StringComparison.OrdinalIgnoreCase) == 0) {
+ return last_index_by_key;
+ }
+
+ for (int i = 0; i < Count; i++) {
+ StatusBarPanel item;
+ item = panels [i] as StatusBarPanel;
+ if (item != null && String.Compare (item.Name, key, StringComparison.OrdinalIgnoreCase) == 0) {
+ last_index_by_key = i;
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public virtual void Insert (int index, StatusBarPanel value) {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ if (index > Count)
+ throw new ArgumentOutOfRangeException ("index");
+ // TODO: InvalidArgumentException for bad AutoSize values
+ // although it seems impossible to set it to a bad value
+ value.SetParent (owner);
+
+ panels.Insert(index, value);
+ owner.Refresh ();
+
+ // UIA Framework Event: Panel Added
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
+ }
+
+ public virtual void Remove (StatusBarPanel value) {
+ int index = IndexOf (value);
+ panels.Remove (value);
+
+ // UIA Framework Event: Panel Removed
+ if (index >= 0)
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
+ }
+
+ public virtual void RemoveAt (int index) {
+ panels.RemoveAt (index);
+
+ // UIA Framework Event: Panel Removed
+ OnUIACollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ int index = IndexOfKey (key);
+ if (index >= 0 && index < Count)
+ RemoveAt (index);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region IList & ICollection Interfaces
+ bool ICollection.IsSynchronized {
+ get { return panels.IsSynchronized; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return panels.SyncRoot; }
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ panels.CopyTo (dest, index);
+ }
+
+
+ object IList.this [int index] {
+ get { return this[index]; }
+ set {
+ if (!(value is StatusBarPanel))
+ throw new ArgumentException ("Value must be of type StatusBarPanel.", "value");
+
+ this[index] = (StatusBarPanel)value;
+ }
+ }
+
+ int IList.Add (object value) {
+ if (!(value is StatusBarPanel))
+ throw new ArgumentException ("Value must be of type StatusBarPanel.", "value");
+
+ return AddInternal ((StatusBarPanel)value, true);
+ }
+
+ bool IList.Contains (object panel) {
+ return panels.Contains (panel);
+ }
+
+ int IList.IndexOf (object panel)
+ {
+ return panels.IndexOf (panel);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ if (!(value is StatusBarPanel))
+ throw new ArgumentException ("Value must be of type StatusBarPanel.", "value");
+
+ Insert (index, (StatusBarPanel)value);
+ }
+
+ bool IList.IsFixedSize {
+ get { return false; }
+ }
+
+ void IList.Remove (object value)
+ {
+ StatusBarPanel s = value as StatusBarPanel;
+ Remove (s);
+ }
+ #endregion // IList & ICollection Interfaces
+ }
+ #endregion // Subclass StatusBarPanelCollection
+ }
+
+}
+
diff --git a/source/ShiftUI/Widgets/StatusBarPanel.cs b/source/ShiftUI/Widgets/StatusBarPanel.cs
new file mode 100644
index 0000000..bdbfa64
--- /dev/null
+++ b/source/ShiftUI/Widgets/StatusBarPanel.cs
@@ -0,0 +1,272 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+
+// COMPLETE
+
+using System;
+using System.Drawing;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [ToolboxItem (false)]
+ [DefaultProperty("Text")]
+ [DesignTimeVisible(false)]
+ public class StatusBarPanel : Component, ISupportInitialize {
+ #region Local Variables
+ private StatusBar parent;
+
+ private bool initializing;
+ private string text = String.Empty;
+ private string tool_tip_text = String.Empty;
+
+ private Icon icon;
+ private HorizontalAlignment alignment = HorizontalAlignment.Left;
+ private StatusBarPanelAutoSize auto_size = StatusBarPanelAutoSize.None;
+ private StatusBarPanelBorderStyle border_style = StatusBarPanelBorderStyle.Sunken;
+ private StatusBarPanelStyle style = StatusBarPanelStyle.Text;
+ private int width = 100;
+ private int min_width = 10;
+ internal int X;
+
+ private string name;
+ private object tag;
+ #endregion // Local Variables
+
+ #region UIA Framework Events
+ static object UIATextChangedEvent = new object ();
+
+ internal event EventHandler UIATextChanged {
+ add { Events.AddHandler (UIATextChangedEvent, value); }
+ remove { Events.RemoveHandler (UIATextChangedEvent, value); }
+ }
+
+ internal void OnUIATextChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [UIATextChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+ #endregion
+
+ #region Constructors
+ public StatusBarPanel ()
+ {
+ }
+ #endregion // Constructors
+
+ [DefaultValue(HorizontalAlignment.Left)]
+ [Localizable(true)]
+ public HorizontalAlignment Alignment {
+ get { return alignment; }
+ set {
+ alignment = value;
+ InvalidateContents ();
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.All)]
+ [DefaultValue(StatusBarPanelAutoSize.None)]
+ public StatusBarPanelAutoSize AutoSize {
+ get { return auto_size; }
+ set {
+ auto_size = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue(StatusBarPanelBorderStyle.Sunken)]
+ [DispId(-504)]
+ public StatusBarPanelBorderStyle BorderStyle {
+ get { return border_style; }
+ set {
+ border_style = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue(null)]
+ [Localizable(true)]
+ public Icon Icon {
+ get { return icon; }
+ set {
+ icon = value;
+ InvalidateContents ();
+ }
+ }
+
+ [DefaultValue(10)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ public int MinWidth {
+ get {
+ /*
+ MSDN says that when AutoSize = None then MinWidth is automatically
+ set to Width, but neither v1.1 nor v2.0 behave that way.
+ */
+ return min_width;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("value");
+
+ min_width = value;
+ if (min_width > width)
+ width = min_width;
+
+ Invalidate ();
+ }
+ }
+
+ [Localizable (true)]
+ public string Name {
+ get {
+ if (name == null)
+ return string.Empty;
+ return name;
+ }
+ set {
+ name = value;
+ }
+ }
+
+ [DefaultValue(100)]
+ [Localizable(true)]
+ public int Width {
+ get { return width; }
+ set {
+ if (value < 0)
+ throw new ArgumentException ("value");
+
+ if (initializing)
+ width = value;
+ else
+ SetWidth(value);
+
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue(StatusBarPanelStyle.Text)]
+ public StatusBarPanelStyle Style {
+ get { return style; }
+ set {
+ style = value;
+ Invalidate ();
+ }
+ }
+
+ [TypeConverter (typeof (StringConverter))]
+ [Localizable (false)]
+ [Bindable (true)]
+ [DefaultValue (null)]
+ public object Tag {
+ get {
+ return tag;
+ }
+ set {
+ tag = value;
+ }
+ }
+
+ [DefaultValue("")]
+ [Localizable(true)]
+ public string Text {
+ get { return text; }
+ set {
+ text = value;
+ InvalidateContents ();
+
+ // UIA Framework Event: Text Changed
+ OnUIATextChanged (EventArgs.Empty);
+ }
+ }
+
+ [DefaultValue("")]
+ [Localizable(true)]
+ public string ToolTipText {
+ get { return tool_tip_text; }
+ set { tool_tip_text = value; }
+ }
+
+ [Browsable(false)]
+ public StatusBar Parent {
+ get { return parent; }
+ }
+
+ private void Invalidate ()
+ {
+ if (parent == null)
+ return;
+ parent.UpdatePanel (this);
+ }
+
+ private void InvalidateContents ()
+ {
+ if (parent == null)
+ return;
+ parent.UpdatePanelContents (this);
+ }
+
+ internal void SetParent (StatusBar parent)
+ {
+ this.parent = parent;
+ }
+
+ internal void SetWidth (int width)
+ {
+ this.width = width;
+ if (min_width > this.width)
+ this.width = min_width;
+ }
+
+ public override string ToString ()
+ {
+ return "StatusBarPanel: {" + Text +"}";
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ }
+
+ public void BeginInit ()
+ {
+ initializing = true;
+ }
+
+ public void EndInit ()
+ {
+ if (!initializing)
+ return;
+
+ if (min_width > width)
+ width = min_width;
+
+ initializing = false;
+ }
+ }
+}
+
+
diff --git a/source/ShiftUI/Widgets/StatusStrip.cs b/source/ShiftUI/Widgets/StatusStrip.cs
new file mode 100644
index 0000000..3ac3600
--- /dev/null
+++ b/source/ShiftUI/Widgets/StatusStrip.cs
@@ -0,0 +1,313 @@
+//
+// StatusStrip.cs
+//
+// 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) 2006 Jonathan Pobst
+//
+// Authors:
+// Jonathan Pobst ([email protected])
+//
+
+using System;
+using System.Drawing;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI
+{
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ public class StatusStrip : ToolStrip
+ {
+ private bool sizing_grip;
+
+ public StatusStrip ()
+ {
+ SetStyle (Widgetstyles.ResizeRedraw, true);
+
+ base.CanOverflow = false;
+ this.GripStyle = ToolStripGripStyle.Hidden;
+ base.LayoutStyle = ToolStripLayoutStyle.Table;
+ base.RenderMode = ToolStripRenderMode.System;
+ this.sizing_grip = true;
+ base.Stretch = true;
+ }
+
+ #region Public Properties
+ [DefaultValue (DockStyle.Bottom)]
+ public override DockStyle Dock {
+ get { return base.Dock; }
+ set { base.Dock = value; }
+ }
+
+ [Browsable (false)]
+ [DefaultValue (false)]
+ public new bool CanOverflow {
+ get { return base.CanOverflow; }
+ set { base.CanOverflow = value; }
+ }
+
+ [DefaultValue (ToolStripGripStyle.Hidden)]
+ public new ToolStripGripStyle GripStyle {
+ get { return base.GripStyle; }
+ set { base.GripStyle = value; }
+ }
+
+ [DefaultValue (ToolStripLayoutStyle.Table)]
+ public new ToolStripLayoutStyle LayoutStyle {
+ get { return base.LayoutStyle; }
+ set { base.LayoutStyle = value; }
+ }
+
+ [Browsable (false)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ [DefaultValue (false)]
+ public new bool ShowItemToolTips {
+ get { return base.ShowItemToolTips; }
+ set { base.ShowItemToolTips = value; }
+ }
+
+ [Browsable (false)]
+ public Rectangle SizeGripBounds {
+ get { return new Rectangle (this.Width - 12, 0, 12, this.Height); }
+ }
+
+ [DefaultValue (true)]
+ public bool SizingGrip {
+ get { return this.sizing_grip; }
+ set { this.sizing_grip = value; }
+ }
+
+ [DefaultValue (true)]
+ public new bool Stretch {
+ get { return base.Stretch; }
+ set { base.Stretch = value; }
+ }
+ #endregion
+
+ #region Protected Properties
+ protected override DockStyle DefaultDock {
+ get { return DockStyle.Bottom; }
+ }
+
+ protected override Padding DefaultPadding {
+ get { return new Padding (1, 0, 14, 0); }
+ }
+
+ protected override bool DefaultShowItemToolTips {
+ get { return false; }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (200, 22); }
+ }
+ #endregion
+
+ #region Protected Methods
+ protected override AccessibleObject CreateAccessibilityInstance ()
+ {
+ return new StatusStripAccessibleObject ();
+ }
+
+ protected internal override ToolStripItem CreateDefaultItem (string text, Image image, EventHandler onClick)
+ {
+ if (text == "-")
+ return new ToolStripSeparator ();
+
+ return new ToolStripLabel (text, image, false, onClick);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ base.Dispose (disposing);
+ }
+
+ protected override void OnLayout (LayoutEventArgs levent)
+ {
+ this.OnSpringTableLayoutCore ();
+ this.Invalidate ();
+ }
+
+ protected override void OnPaintBackground (PaintEventArgs e)
+ {
+ base.OnPaintBackground (e);
+
+ if (this.sizing_grip)
+ this.Renderer.DrawStatusStripSizingGrip (new ToolStripRenderEventArgs (e.Graphics, this, Bounds, SystemColors.Control));
+ }
+
+ protected virtual void OnSpringTableLayoutCore ()
+ {
+ if (!this.Created)
+ return;
+
+ ToolStripItemOverflow[] overflow = new ToolStripItemOverflow[this.Items.Count];
+ ToolStripItemPlacement[] placement = new ToolStripItemPlacement[this.Items.Count];
+ Size proposedSize = new Size (0, Bounds.Height);
+ int[] widths = new int[this.Items.Count];
+ int total_width = 0;
+ int toolstrip_width = DisplayRectangle.Width;
+ int i = 0;
+ int spring_count = 0;
+
+ foreach (ToolStripItem tsi in this.Items) {
+ overflow[i] = tsi.Overflow;
+ widths[i] = tsi.GetPreferredSize (proposedSize).Width + tsi.Margin.Horizontal;
+ placement[i] = tsi.Overflow == ToolStripItemOverflow.Always ? ToolStripItemPlacement.None : ToolStripItemPlacement.Main;
+ placement[i] = tsi.Available && tsi.InternalVisible ? placement[i] : ToolStripItemPlacement.None;
+ total_width += placement[i] == ToolStripItemPlacement.Main ? widths[i] : 0;
+ if (tsi is ToolStripStatusLabel && (tsi as ToolStripStatusLabel).Spring)
+ spring_count++;
+
+ i++;
+ }
+
+ while (total_width > toolstrip_width) {
+ bool removed_one = false;
+
+ // Start at the right, removing Overflow.AsNeeded first
+ for (int j = widths.Length - 1; j >= 0; j--)
+ if (overflow[j] == ToolStripItemOverflow.AsNeeded && placement[j] == ToolStripItemPlacement.Main) {
+ placement[j] = ToolStripItemPlacement.None;
+ total_width -= widths[j];
+ removed_one = true;
+ break;
+ }
+
+ // If we didn't remove any AsNeeded ones, we have to start removing Never ones
+ // These are not put on the Overflow, they are simply not shown
+ if (!removed_one)
+ for (int j = widths.Length - 1; j >= 0; j--)
+ if (overflow[j] == ToolStripItemOverflow.Never && placement[j] == ToolStripItemPlacement.Main) {
+ placement[j] = ToolStripItemPlacement.None;
+ total_width -= widths[j];
+ removed_one = true;
+ break;
+ }
+
+ // There's nothing left to remove, break or we will loop forever
+ if (!removed_one)
+ break;
+ }
+
+ if (spring_count > 0) {
+ int per_item = (toolstrip_width - total_width) / spring_count;
+ i = 0;
+
+ foreach (ToolStripItem tsi in this.Items) {
+ if (tsi is ToolStripStatusLabel && (tsi as ToolStripStatusLabel).Spring)
+ widths[i] += per_item;
+
+ i++;
+ }
+ }
+
+ i = 0;
+ Point layout_pointer = new Point (this.DisplayRectangle.Left, this.DisplayRectangle.Top);
+ int button_height = this.DisplayRectangle.Height;
+
+ // Now we should know where everything goes, so lay everything out
+ foreach (ToolStripItem tsi in this.Items) {
+ tsi.SetPlacement (placement[i]);
+
+ if (placement[i] == ToolStripItemPlacement.Main) {
+ tsi.SetBounds (new Rectangle (layout_pointer.X + tsi.Margin.Left, layout_pointer.Y + tsi.Margin.Top, widths[i] - tsi.Margin.Horizontal, button_height - tsi.Margin.Vertical));
+ layout_pointer.X += widths[i];
+ }
+
+ i++;
+ }
+
+ this.SetDisplayedItems ();
+ }
+
+ protected override void SetDisplayedItems ()
+ {
+ // Only clean the internal collection, without modifying Owner/Parent on items.
+ this.displayed_items.ClearInternal ();
+
+ foreach (ToolStripItem tsi in this.Items)
+ if (tsi.Placement == ToolStripItemPlacement.Main && tsi.Available) {
+ this.displayed_items.AddNoOwnerOrLayout (tsi);
+ tsi.Parent = this;
+ }
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ // If the mouse is over the size grip, change the cursor
+ case Msg.WM_MOUSEMOVE: {
+ if (FromParamToMouseButtons ((int) m.WParam.ToInt32()) == MouseButtons.None) {
+ Point p = new Point (LowOrder ((int) m.LParam.ToInt32 ()), HighOrder ((int) m.LParam.ToInt32 ()));
+
+ if (this.SizingGrip && this.SizeGripBounds.Contains (p)) {
+ this.Cursor = Cursors.SizeNWSE;
+ return;
+ } else
+ this.Cursor = Cursors.Default;
+ }
+
+ break;
+ }
+ // If the left mouse button is pushed over the size grip,
+ // send the WM a message to begin a window resize operation
+ case Msg.WM_LBUTTONDOWN: {
+ Point p = new Point (LowOrder ((int)m.LParam.ToInt32 ()), HighOrder ((int)m.LParam.ToInt32 ()));
+ Form form = FindForm ();
+
+ if (this.SizingGrip && this.SizeGripBounds.Contains (p)) {
+ // For top level forms it's not enoug to send a NCLBUTTONDOWN message, so
+ // we make a direct call to our XplatUI engine.
+ if (!form.IsMdiChild)
+ XplatUI.BeginMoveResize (form.Handle);
+
+ XplatUI.SendMessage (form.Handle, Msg.WM_NCLBUTTONDOWN, (IntPtr) HitTest.HTBOTTOMRIGHT, IntPtr.Zero);
+ return;
+ }
+
+ break;
+ }
+ }
+
+ base.WndProc (ref m);
+ }
+ #endregion
+
+ #region Public Events
+ [Browsable (false)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+ #endregion
+
+ #region StatusStripAccessibleObject
+ private class StatusStripAccessibleObject : AccessibleObject
+ {
+ }
+ #endregion
+ }
+}
diff --git a/source/ShiftUI/Widgets/TabControl.cs b/source/ShiftUI/Widgets/TabControl.cs
new file mode 100644
index 0000000..0404510
--- /dev/null
+++ b/source/ShiftUI/Widgets/TabControl.cs
@@ -0,0 +1,2008 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using ShiftUI.Theming;
+using ShiftUI.VisualStyles;
+
+namespace ShiftUI {
+ [ComVisibleAttribute (true)]
+ [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent("SelectedIndexChanged")]
+ [DefaultProperty("TabPages")]
+ //[Designer("ShiftUI.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class TabWidget : Widget {
+ #region Fields
+ private int selected_index = -1;
+ private TabAlignment alignment;
+ private TabAppearance appearance;
+ private TabDrawMode draw_mode;
+ private bool multiline;
+ private ImageList image_list;
+ private Size item_size = Size.Empty;
+ private bool item_size_manual;
+ private Point padding;
+ private int row_count = 0;
+ private bool hottrack;
+ private TabPageCollection tab_pages;
+ private bool show_tool_tips;
+ private TabSizeMode size_mode;
+ private bool show_slider = false;
+ private PushButtonState right_slider_state = PushButtonState.Normal;
+ private PushButtonState left_slider_state = PushButtonState.Normal;
+ private int slider_pos = 0;
+ TabPage entered_tab_page;
+ bool mouse_down_on_a_tab_page;
+ ToolTip tooltip;
+ ToolTip.TipState tooltip_state = ToolTip.TipState.Down;
+ Timer tooltip_timer;
+
+ private bool rightToLeftLayout;
+ #endregion // Fields
+
+ #region UIA Framework Events
+ static object UIAHorizontallyScrollableChangedEvent = new object ();
+
+ internal event EventHandler UIAHorizontallyScrollableChanged {
+ add { Events.AddHandler (UIAHorizontallyScrollableChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAHorizontallyScrollableChangedEvent, value); }
+ }
+
+ internal void OnUIAHorizontallyScrollableChanged (EventArgs e)
+ {
+ EventHandler eh
+ = (EventHandler) Events [UIAHorizontallyScrollableChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ static object UIAHorizontallyScrolledEvent = new object ();
+
+ internal event EventHandler UIAHorizontallyScrolled {
+ add { Events.AddHandler (UIAHorizontallyScrolledEvent, value); }
+ remove { Events.RemoveHandler (UIAHorizontallyScrolledEvent, value); }
+ }
+
+ internal void OnUIAHorizontallyScrolled (EventArgs e)
+ {
+ EventHandler eh
+ = (EventHandler) Events [UIAHorizontallyScrolledEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+ #endregion
+
+ #region UIA Framework Property
+ internal double UIAHorizontalViewSize {
+ get { return LeftScrollButtonArea.Left * 100 / TabPages [TabCount - 1].TabBounds.Right; }
+ }
+ #endregion
+
+ #region Public Constructors
+ public TabWidget ()
+ {
+ tab_pages = new TabPageCollection (this);
+ SetStyle (Widgetstyles.UserPaint, false);
+ padding = ThemeEngine.Current.TabControlDefaultPadding;
+
+ MouseDown += new MouseEventHandler (MouseDownHandler);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ MouseMove += new MouseEventHandler (OnMouseMove);
+ MouseUp += new MouseEventHandler (MouseUpHandler);
+ SizeChanged += new EventHandler (SizeChangedHandler);
+ }
+
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ [DefaultValue(TabAlignment.Top)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ public TabAlignment Alignment {
+ get { return alignment; }
+ set {
+ if (alignment == value)
+ return;
+ alignment = value;
+ if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
+ multiline = true;
+ Redraw ();
+ }
+ }
+
+ [DefaultValue(TabAppearance.Normal)]
+ [Localizable(true)]
+ public TabAppearance Appearance {
+ get { return appearance; }
+ set {
+ if (appearance == value)
+ return;
+ appearance = value;
+ Redraw ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color BackColor {
+ get { return ThemeEngine.Current.ColorControl; }
+ set { /* nothing happens on set on MS */ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ public override Rectangle DisplayRectangle {
+ get {
+ return ThemeEngine.Current.TabControlGetDisplayRectangle (this);
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get { return base.DoubleBuffered; }
+ set { base.DoubleBuffered = value; }
+ }
+
+ [DefaultValue(TabDrawMode.Normal)]
+ public TabDrawMode DrawMode {
+ get { return draw_mode; }
+ set {
+ if (draw_mode == value)
+ return;
+ draw_mode = value;
+ Redraw ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set { base.ForeColor = value; }
+ }
+
+ [DefaultValue(false)]
+ public bool HotTrack {
+ get { return hottrack; }
+ set {
+ if (hottrack == value)
+ return;
+ hottrack = value;
+ Redraw ();
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [DefaultValue(null)]
+ public ImageList ImageList {
+ get { return image_list; }
+ set {
+ image_list = value;
+ Redraw ();
+ }
+ }
+
+ [Localizable(true)]
+ public Size ItemSize {
+ get {
+ if (item_size_manual)
+ return item_size;
+
+ if (!IsHandleCreated)
+ return Size.Empty;
+
+ Size size = item_size;
+ if (SizeMode != TabSizeMode.Fixed) {
+ size.Width += padding.X * 2;
+ size.Height += padding.Y * 2;
+ }
+
+ if (tab_pages.Count == 0)
+ size.Width = 0;
+
+ return size;
+ }
+ set {
+ if (value.Height < 0 || value.Width < 0)
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
+ item_size = value;
+ item_size_manual = true;
+ Redraw ();
+ }
+ }
+
+ [DefaultValue(false)]
+ public bool Multiline {
+ get { return multiline; }
+ set {
+ if (multiline == value)
+ return;
+ multiline = value;
+ if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
+ alignment = TabAlignment.Top;
+ Redraw ();
+ }
+ }
+
+ [Localizable(true)]
+ public
+ new
+ Point Padding {
+ get { return padding; }
+ set {
+ if (value.X < 0 || value.Y < 0)
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
+ if (padding == value)
+ return;
+ padding = value;
+ Redraw ();
+ }
+
+ }
+
+ [MonoTODO ("RTL not supported")]
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public virtual bool RightToLeftLayout {
+ get { return this.rightToLeftLayout; }
+ set {
+ if (value != this.rightToLeftLayout) {
+ this.rightToLeftLayout = value;
+ this.OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int RowCount {
+ get { return row_count; }
+ }
+
+ [DefaultValue(-1)]
+ [Browsable(false)]
+ public int SelectedIndex {
+ get { return selected_index; }
+ set {
+
+ if (value < -1) {
+ throw new ArgumentOutOfRangeException ("SelectedIndex", "Value of '" + value + "' is valid for 'SelectedIndex'. " +
+ "'SelectedIndex' must be greater than or equal to -1.");
+ }
+ if (!this.IsHandleCreated) {
+ if (selected_index != value) {
+ selected_index = value;
+ }
+ return;
+ }
+
+ if (value >= TabCount) {
+ if (value != selected_index)
+ OnSelectedIndexChanged (EventArgs.Empty);
+ return;
+ }
+
+ if (value == selected_index) {
+ if (selected_index > -1)
+ Invalidate(GetTabRect (selected_index));
+ return;
+ }
+
+ TabControlCancelEventArgs ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Deselecting);
+ OnDeselecting (ret);
+ if (ret.Cancel)
+ return;
+
+ Focus ();
+ int old_index = selected_index;
+ int new_index = value;
+
+ selected_index = new_index;
+
+ ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Selecting);
+ OnSelecting (ret);
+ if (ret.Cancel) {
+ selected_index = old_index;
+ return;
+ }
+
+ SuspendLayout ();
+
+ Rectangle invalid = Rectangle.Empty;
+ bool refresh = false;
+
+ if (new_index != -1 && show_slider && new_index < slider_pos) {
+ slider_pos = new_index;
+ refresh = true;
+ }
+
+ if (new_index != -1) {
+ int le = TabPages[new_index].TabBounds.Right;
+ int re = LeftScrollButtonArea.Left;
+ if (show_slider && le > re) {
+ int i = 0;
+ for (i = 0; i < new_index; i++) {
+ if (TabPages [i].TabBounds.Left < 0) // tab scrolled off the visible area, ignore
+ continue;
+
+ if (TabPages [new_index].TabBounds.Right - TabPages[i].TabBounds.Right < re) {
+ i++;
+ break;
+ }
+ }
+ slider_pos = i;
+ refresh = true;
+ }
+ }
+
+ if (old_index != -1 && new_index != -1) {
+ if (!refresh)
+ invalid = GetTabRect (old_index);
+ ((TabPage) Widgets[old_index]).SetVisible (false);
+ }
+
+ TabPage selected = null;
+
+ if (new_index != -1) {
+ selected = (TabPage) Widgets[new_index];
+ invalid = Rectangle.Union (invalid, GetTabRect (new_index));
+ selected.SetVisible (true);
+ }
+
+ OnSelectedIndexChanged (EventArgs.Empty);
+
+ ResumeLayout ();
+
+ if (refresh) {
+ SizeTabs ();
+ Refresh ();
+ } else if (new_index != -1 && selected.Row != BottomRow) {
+ DropRow (TabPages[new_index].Row);
+ // calculating what to invalidate here seems to be slower then just
+ // refreshing the whole thing
+ SizeTabs ();
+ Refresh ();
+ } else {
+ SizeTabs ();
+ // The lines are drawn on the edges of the tabs so the invalid area should
+ // needs to include the extra pixels of line width (but should not
+ // overflow the control bounds).
+ if (appearance == TabAppearance.Normal) {
+ invalid.Inflate (6, 4);
+ invalid.Intersect (ClientRectangle);
+ }
+ Invalidate (invalid);
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public TabPage SelectedTab {
+ get {
+ if (selected_index == -1)
+ return null;
+ return tab_pages [selected_index];
+ }
+ set {
+ int index = IndexForTabPage (value);
+ if (index == selected_index)
+ return;
+ SelectedIndex = index;
+ }
+ }
+
+ [DefaultValue(false)]
+ [Localizable(true)]
+ public bool ShowToolTips {
+ get { return show_tool_tips; }
+ set {
+ if (show_tool_tips == value)
+ return;
+ show_tool_tips = value;
+ }
+ }
+
+ [DefaultValue(TabSizeMode.Normal)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public TabSizeMode SizeMode {
+ get { return size_mode; }
+ set {
+ if (size_mode == value)
+ return;
+ size_mode = value;
+ Redraw ();
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int TabCount {
+ get {
+ return tab_pages.Count;
+ }
+ }
+
+ //[Editor ("ShiftUI.Design.TabPageCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [MergableProperty(false)]
+ public TabPageCollection TabPages {
+ get { return tab_pages; }
+ }
+
+ [Browsable(false)]
+ [Bindable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+ #endregion // Public Instance Properties
+
+ #region Internal Properties
+ internal bool ShowSlider {
+ get { return show_slider; }
+ set {
+ show_slider = value;
+
+ // UIA Framework Event: HorizontallyScrollable Changed
+ OnUIAHorizontallyScrollableChanged (EventArgs.Empty);
+ }
+ }
+
+ internal int SliderPos {
+ get { return slider_pos; }
+ }
+
+ internal PushButtonState RightSliderState {
+ get { return right_slider_state; }
+ private set {
+ if (right_slider_state == value)
+ return;
+ PushButtonState old_value = right_slider_state;
+ right_slider_state = value;
+ if (NeedsToInvalidateScrollButton (old_value, value))
+ Invalidate (RightScrollButtonArea);
+ }
+ }
+
+ internal PushButtonState LeftSliderState {
+ get { return left_slider_state; }
+ set {
+ if (left_slider_state == value)
+ return;
+ PushButtonState old_value = left_slider_state;
+ left_slider_state = value;
+ if (NeedsToInvalidateScrollButton (old_value, value))
+ Invalidate (LeftScrollButtonArea);
+ }
+ }
+
+ bool NeedsToInvalidateScrollButton (PushButtonState oldState, PushButtonState newState)
+ {
+ if ((oldState == PushButtonState.Hot && newState == PushButtonState.Normal) ||
+ (oldState == PushButtonState.Normal && newState == PushButtonState.Hot))
+ return HasHotElementStyles;
+ return true;
+ }
+
+ internal TabPage EnteredTabPage {
+ get { return entered_tab_page; }
+ private set {
+ if (entered_tab_page == value)
+ return;
+ if (HasHotElementStyles) {
+ Region area_to_invalidate = new Region ();
+ area_to_invalidate.MakeEmpty ();
+ if (entered_tab_page != null)
+ area_to_invalidate.Union (entered_tab_page.TabBounds);
+ entered_tab_page = value;
+ if (entered_tab_page != null)
+ area_to_invalidate.Union (entered_tab_page.TabBounds);
+ Invalidate (area_to_invalidate);
+ area_to_invalidate.Dispose ();
+ } else
+ entered_tab_page = value;
+
+ if (value == null)
+ CloseToolTip ();
+ else
+ SetToolTip (GetToolTipText (value));
+ }
+ }
+ #endregion // Internal Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams c = base.CreateParams;
+ return c;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (200, 100); }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public Rectangle GetTabRect (int index)
+ {
+ TabPage page = GetTab (index);
+ return page.TabBounds;
+ }
+
+ public Widget GetControl (int index)
+ {
+ return GetTab (index);
+ }
+
+ public void SelectTab (TabPage tabPage)
+ {
+ if (tabPage == null)
+ throw new ArgumentNullException ("tabPage");
+
+ SelectTab (this.tab_pages [tabPage]);
+ }
+
+ public void SelectTab (string tabPageName)
+ {
+ if (tabPageName == null)
+ throw new ArgumentNullException ("tabPageName");
+
+ SelectTab (this.tab_pages [tabPageName]);
+ }
+
+ public void SelectTab (int index)
+ {
+ if (index < 0 || index > this.tab_pages.Count - 1)
+ throw new ArgumentOutOfRangeException ("index");
+
+ SelectedIndex = index;
+ }
+
+ public void DeselectTab (TabPage tabPage)
+ {
+ if (tabPage == null)
+ throw new ArgumentNullException ("tabPage");
+
+ DeselectTab (this.tab_pages [tabPage]);
+ }
+
+ public void DeselectTab (string tabPageName)
+ {
+ if (tabPageName == null)
+ throw new ArgumentNullException ("tabPageName");
+
+ DeselectTab (this.tab_pages [tabPageName]);
+ }
+
+ public void DeselectTab (int index)
+ {
+ if (index == SelectedIndex) {
+ if (index >= 0 && index < this.tab_pages.Count - 1)
+ SelectedIndex = ++index;
+ else
+ SelectedIndex = 0;
+ }
+ }
+
+
+ public override string ToString ()
+ {
+ string res = String.Concat (base.ToString (),
+ ", TabPages.Count: ",
+ TabCount);
+ if (TabCount > 0)
+ res = String.Concat (res, ", TabPages[0]: ",
+ TabPages [0]);
+ return res;
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+
+ #region Handles
+ protected override Widget.WidgetCollection CreateWidgetsInstance ()
+ {
+ return new TabWidget.WidgetCollection (this);
+ }
+
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ selected_index = (selected_index >= TabCount ? (TabCount > 0 ? 0 : -1) : selected_index);
+
+ if (TabCount > 0) {
+ if (selected_index > -1)
+ this.SelectedTab.SetVisible(true);
+ else
+ tab_pages[0].SetVisible(true);
+ }
+ ResizeTabPages ();
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ CloseToolTip ();
+ base.Dispose (disposing);
+ }
+
+ #endregion
+
+ #region Events
+ protected virtual void OnDrawItem (DrawItemEventArgs e)
+ {
+ if (DrawMode != TabDrawMode.OwnerDrawFixed)
+ return;
+
+ DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ internal void OnDrawItemInternal (DrawItemEventArgs e)
+ {
+ OnDrawItem (e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ ResizeTabPages ();
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+ }
+
+ protected override void OnStyleChanged (EventArgs e)
+ {
+ base.OnStyleChanged (e);
+ }
+
+ protected virtual void OnSelectedIndexChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) (Events[SelectedIndexChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pe)
+ {
+ if (GetStyle (Widgetstyles.UserPaint))
+ return;
+
+ Draw (pe.Graphics, pe.ClipRectangle);
+ pe.Handled = true;
+ }
+
+ protected override void OnEnter (EventArgs e)
+ {
+ base.OnEnter (e);
+ if (SelectedTab != null)
+ SelectedTab.FireEnter ();
+ }
+
+ protected override void OnLeave (EventArgs e)
+ {
+ if (SelectedTab != null)
+ SelectedTab.FireLeave ();
+ base.OnLeave (e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) (Events[RightToLeftLayoutChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override void ScaleCore (float dx, float dy)
+ {
+ base.ScaleCore (dx, dy);
+ }
+
+ protected virtual void OnDeselecting (TabControlCancelEventArgs e)
+ {
+ TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[DeselectingEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ if (!e.Cancel)
+ OnDeselected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Deselected));
+ }
+
+ protected virtual void OnDeselected (TabControlEventArgs e)
+ {
+ TabControlEventHandler eh = (TabControlEventHandler) (Events[DeselectedEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ if (this.SelectedTab != null)
+ this.SelectedTab.FireLeave ();
+ }
+
+ protected virtual void OnSelecting (TabControlCancelEventArgs e)
+ {
+ TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[SelectingEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ if (!e.Cancel)
+ OnSelected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Selected));
+ }
+
+ protected virtual void OnSelected (TabControlEventArgs e)
+ {
+ TabControlEventHandler eh = (TabControlEventHandler) (Events[SelectedEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ if (this.SelectedTab != null)
+ this.SelectedTab.FireEnter ();
+ }
+
+ #endregion
+
+ #region Keys
+ protected override bool ProcessKeyPreview (ref Message m)
+ {
+ return base.ProcessKeyPreview (ref m);
+ }
+
+ protected override void OnKeyDown (KeyEventArgs ke)
+ {
+ base.OnKeyDown (ke);
+ if (ke.Handled)
+ return;
+
+ if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Widget) != 0) {
+ if ((ke.KeyData & Keys.Shift) == 0)
+ SelectedIndex = (SelectedIndex + 1) % TabCount;
+ else
+ SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
+ ke.Handled = true;
+ } else if (ke.KeyCode == Keys.PageUp && (ke.KeyData & Keys.Widget) != 0) {
+ SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
+ ke.Handled = true;
+ } else if (ke.KeyCode == Keys.PageDown && (ke.KeyData & Keys.Widget) != 0) {
+ SelectedIndex = (SelectedIndex + 1) % TabCount;
+ ke.Handled = true;
+ } else if (ke.KeyCode == Keys.Home) {
+ SelectedIndex = 0;
+ ke.Handled = true;
+ } else if (ke.KeyCode == Keys.End) {
+ SelectedIndex = TabCount - 1;
+ ke.Handled = true;
+ } else if (NavigateTabs (ke.KeyCode))
+ ke.Handled = true;
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ switch (keyData & Keys.KeyCode) {
+ case Keys.Home:
+ case Keys.End:
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.Up:
+ case Keys.Down:
+ return true;
+ }
+ return base.IsInputKey (keyData);
+ }
+
+ private bool NavigateTabs (Keys keycode)
+ {
+ bool move_left = false;
+ bool move_right = false;
+
+ if (alignment == TabAlignment.Bottom || alignment == TabAlignment.Top) {
+ if (keycode == Keys.Left)
+ move_left = true;
+ else if (keycode == Keys.Right)
+ move_right = true;
+ } else {
+ if (keycode == Keys.Up)
+ move_left = true;
+ else if (keycode == Keys.Down)
+ move_right = true;
+ }
+
+ if (move_left) {
+ if (SelectedIndex > 0) {
+ SelectedIndex--;
+ return true;
+ }
+ }
+
+ if (move_right) {
+ if (SelectedIndex < TabCount - 1) {
+ SelectedIndex++;
+ return true;
+ }
+ }
+
+ return false;
+ }
+ #endregion
+
+ #region Pages Collection
+ protected void RemoveAll ()
+ {
+ Widgets.Clear ();
+ }
+
+ protected virtual object [] GetItems ()
+ {
+ TabPage [] pages = new TabPage [Widgets.Count];
+ Widgets.CopyTo (pages, 0);
+ return pages;
+ }
+
+ protected virtual object [] GetItems (Type baseType)
+ {
+ object[] pages = (object[])Array.CreateInstance (baseType, Widgets.Count);
+ Widgets.CopyTo (pages, 0);
+ return pages;
+ }
+ #endregion
+
+ protected void UpdateTabSelection (bool updateFocus)
+ {
+ ResizeTabPages ();
+ }
+
+ protected string GetToolTipText (object item)
+ {
+ TabPage page = (TabPage) item;
+ return page.ToolTipText;
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_SETFOCUS:
+ if (selected_index != -1)
+ Invalidate(GetTabRect(selected_index));
+ base.WndProc (ref m);
+ break;
+ case Msg.WM_KILLFOCUS:
+ if (selected_index != -1)
+ Invalidate(GetTabRect(selected_index));
+ base.WndProc (ref m);
+ break;
+ default:
+ base.WndProc (ref m);
+ break;
+ }
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region Internal & Private Methods
+ private bool CanScrollRight {
+ get {
+ return (slider_pos < TabCount - 1);
+ }
+ }
+
+ private bool CanScrollLeft {
+ get { return slider_pos > 0; }
+ }
+
+ private void MouseDownHandler (object sender, MouseEventArgs e)
+ {
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ if (ShowSlider) {
+ Rectangle right = RightScrollButtonArea;
+ Rectangle left = LeftScrollButtonArea;
+ if (right.Contains (e.X, e.Y)) {
+ right_slider_state = PushButtonState.Pressed;
+ if (CanScrollRight) {
+ slider_pos++;
+ SizeTabs ();
+
+ // UIA Framework Event: Horizontally Scrolled
+ OnUIAHorizontallyScrolled (EventArgs.Empty);
+
+ switch (this.Alignment) {
+ case TabAlignment.Top:
+ Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
+ break;
+ case TabAlignment.Bottom:
+ Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
+ break;
+ case TabAlignment.Left:
+ Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
+ break;
+ case TabAlignment.Right:
+ Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
+ break;
+ }
+
+ } else {
+ Invalidate (right);
+ }
+ return;
+ } else if (left.Contains (e.X, e.Y)) {
+ left_slider_state = PushButtonState.Pressed;
+ if (CanScrollLeft) {
+ slider_pos--;
+ SizeTabs ();
+
+ // UIA Framework Event: Horizontally Scrolled
+ OnUIAHorizontallyScrolled (EventArgs.Empty);
+
+ switch (this.Alignment) {
+ case TabAlignment.Top:
+ Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
+ break;
+ case TabAlignment.Bottom:
+ Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
+ break;
+ case TabAlignment.Left:
+ Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
+ break;
+ case TabAlignment.Right:
+ Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
+ break;
+ }
+ } else {
+ Invalidate (left);
+ }
+ return;
+ }
+ }
+
+ int count = Widgets.Count;
+ for (int i = SliderPos; i < count; i++) {
+ if (!GetTabRect (i).Contains (e.X, e.Y))
+ continue;
+ SelectedIndex = i;
+ mouse_down_on_a_tab_page = true;
+ break;
+ }
+ }
+
+ private void MouseUpHandler (object sender, MouseEventArgs e)
+ {
+ mouse_down_on_a_tab_page = false;
+ if (ShowSlider && (left_slider_state == PushButtonState.Pressed || right_slider_state == PushButtonState.Pressed)) {
+ Rectangle invalid;
+ if (left_slider_state == PushButtonState.Pressed) {
+ invalid = LeftScrollButtonArea;
+ left_slider_state = GetScrollButtonState (invalid, e.Location);
+ } else {
+ invalid = RightScrollButtonArea;
+ right_slider_state = GetScrollButtonState (invalid, e.Location);
+ }
+ Invalidate (invalid);
+ }
+ }
+
+ bool HasHotElementStyles {
+ get {
+ return ThemeElements.CurrentTheme.TabWidgetPainter.HasHotElementStyles (this);
+ }
+ }
+
+ Rectangle LeftScrollButtonArea {
+ get {
+ return ThemeElements.CurrentTheme.TabWidgetPainter.GetLeftScrollRect (this);
+ }
+ }
+
+ Rectangle RightScrollButtonArea {
+ get {
+ return ThemeElements.CurrentTheme.TabWidgetPainter.GetRightScrollRect (this);
+ }
+ }
+
+ static PushButtonState GetScrollButtonState (Rectangle scrollButtonArea, Point cursorLocation)
+ {
+ return scrollButtonArea.Contains (cursorLocation) ? PushButtonState.Hot : PushButtonState.Normal;
+ }
+
+ private void SizeChangedHandler (object sender, EventArgs e)
+ {
+ Redraw ();
+ }
+
+ internal int IndexForTabPage (TabPage page)
+ {
+ for (int i = 0; i < tab_pages.Count; i++) {
+ if (page == tab_pages [i])
+ return i;
+ }
+ return -1;
+ }
+
+ private void ResizeTabPages ()
+ {
+ CalcTabRows ();
+ SizeTabs ();
+ Rectangle r = DisplayRectangle;
+ foreach (TabPage page in Widgets) {
+ page.Bounds = r;
+ }
+ }
+
+ private int MinimumTabWidth {
+ get {
+ return ThemeEngine.Current.TabControlMinimumTabWidth;
+ }
+ }
+
+ private Size TabSpacing {
+ get {
+ return ThemeEngine.Current.TabControlGetSpacing (this);
+ }
+ }
+
+ private void CalcTabRows ()
+ {
+ switch (Alignment) {
+ case TabAlignment.Right:
+ case TabAlignment.Left:
+ CalcTabRows (Height);
+ break;
+ default:
+ CalcTabRows (Width);
+ break;
+ }
+ }
+
+ private void CalcTabRows (int row_width)
+ {
+ int xpos = 0;
+ int ypos = 0;
+ Size spacing = TabSpacing;
+
+ if (TabPages.Count > 0)
+ row_count = 1;
+ show_slider = false;
+
+ CalculateItemSize ();
+
+ for (int i = 0; i < TabPages.Count; i++) {
+ TabPage page = TabPages [i];
+ int aux = 0;
+ SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, 0, ref aux, true);
+ }
+
+ if (SelectedIndex != -1 && TabPages.Count > SelectedIndex && TabPages[SelectedIndex].Row != BottomRow)
+ DropRow (TabPages [SelectedIndex].Row);
+ }
+
+ // ItemSize per-se is used mostly only to retrieve the Height,
+ // since the actual Width of the tabs is computed individually,
+ // except when SizeMode is TabSizeMode.Fixed, where Width is used as well.
+ private void CalculateItemSize ()
+ {
+ if (item_size_manual)
+ return;
+
+ SizeF size;
+ if (tab_pages.Count > 0) {
+ // .Net uses the first tab page if available.
+ size = TextRenderer.MeasureString (tab_pages [0].Text, Font);
+
+ } else {
+ size = TextRenderer.MeasureString ("a", Font);
+ size.Width = 0;
+ }
+
+ if (size_mode == TabSizeMode.Fixed)
+ size.Width = 96;
+ if (size.Width < MinimumTabWidth)
+ size.Width = MinimumTabWidth;
+ if (image_list != null && image_list.ImageSize.Height > size.Height)
+ size.Height = image_list.ImageSize.Height;
+
+ item_size = size.ToSize ();
+ }
+
+ private int BottomRow {
+ get { return 1; }
+ }
+
+ private int Direction
+ {
+ get {
+ return 1;
+ }
+ }
+
+ private void DropRow (int row)
+ {
+ if (Appearance != TabAppearance.Normal)
+ return;
+
+ int bottom = BottomRow;
+ int direction = Direction;
+
+ foreach (TabPage page in TabPages) {
+ if (page.Row == row) {
+ page.Row = bottom;
+ } else if (direction == 1 && page.Row < row) {
+ page.Row += direction;
+ } else if (direction == -1 && page.Row > row) {
+ page.Row += direction;
+ }
+ }
+ }
+
+ private int CalcYPos ()
+ {
+ if (Alignment == TabAlignment.Bottom || Alignment == TabAlignment.Left)
+ return ThemeEngine.Current.TabControlGetPanelRect (this).Bottom;
+
+ if (Appearance == TabAppearance.Normal)
+ return this.ClientRectangle.Y + ThemeEngine.Current.TabWidgetselectedDelta.Y;
+
+ return this.ClientRectangle.Y;
+
+ }
+
+ private int CalcXPos ()
+ {
+ if (Alignment == TabAlignment.Right)
+ return ThemeEngine.Current.TabControlGetPanelRect (this).Right;
+
+ if (Appearance == TabAppearance.Normal)
+ return this.ClientRectangle.X + ThemeEngine.Current.TabWidgetselectedDelta.X;
+
+ return this.ClientRectangle.X;
+ }
+
+ private void SizeTabs ()
+ {
+ switch (Alignment) {
+ case TabAlignment.Right:
+ case TabAlignment.Left:
+ SizeTabs (Height, true);
+ break;
+ default:
+ SizeTabs (Width, false);
+ break;
+ }
+ }
+
+ private void SizeTabs (int row_width, bool vertical)
+ {
+ int ypos = 0;
+ int xpos = 0;
+ int prev_row = 1;
+ Size spacing = TabSpacing;
+ int begin_prev = 0;
+
+ if (TabPages.Count == 0)
+ return;
+
+ prev_row = TabPages [0].Row;
+
+ // Reset the slider position if the slider isn't needed
+ // anymore (ie window size was increased so all tabs are visible)
+ if (!show_slider)
+ slider_pos = 0;
+ else {
+ // set X = -1 for marking tabs that are not visible due to scrolling
+ for (int i = 0; i < slider_pos; i++) {
+ TabPage page = TabPages[i];
+ Rectangle x = page.TabBounds;
+ x.X = -1;
+ page.TabBounds = x;
+ }
+ }
+
+ for (int i = slider_pos; i < TabPages.Count; i++) {
+ TabPage page = TabPages[i];
+ SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, prev_row, ref begin_prev, false);
+ prev_row = page.Row;
+ }
+
+ if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
+ FillRow (begin_prev, TabPages.Count - 1,
+ ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)),
+ spacing, vertical);
+ }
+
+ if (SelectedIndex != -1) {
+ ExpandSelected (TabPages [SelectedIndex], 0, row_width - 1);
+ }
+ }
+
+ private void SizeTab (TabPage page, int i, int row_width, ref int xpos, ref int ypos,
+ Size spacing, int prev_row, ref int begin_prev, bool widthOnly)
+ {
+ int width, height = 0;
+
+ if (SizeMode == TabSizeMode.Fixed) {
+ width = item_size.Width;
+ } else {
+ width = MeasureStringWidth (DeviceContext, page.Text, Font);
+ width += (Padding.X * 2) + 2;
+
+ if (ImageList != null && page.ImageIndex >= 0) {
+ width += ImageList.ImageSize.Width + ThemeEngine.Current.TabControlImagePadding.X;
+
+ int image_size = ImageList.ImageSize.Height + ThemeEngine.Current.TabControlImagePadding.Y;
+ if (item_size.Height < image_size)
+ item_size.Height = image_size;
+ }
+
+ if (width < MinimumTabWidth)
+ width = MinimumTabWidth;
+ }
+
+ // Use ItemSize property to recover the padding info as well.
+ height = ItemSize.Height - ThemeEngine.Current.TabWidgetselectedDelta.Height; // full height only for selected tab
+
+ if (i == SelectedIndex)
+ width += ThemeEngine.Current.TabWidgetselectedSpacing;
+
+ if (widthOnly) {
+ page.TabBounds = new Rectangle (xpos, 0, width, 0);
+ page.Row = row_count;
+ if (xpos + width > row_width && multiline) {
+ xpos = 0;
+ row_count++;
+ } else if (xpos + width > row_width) {
+ show_slider = true;
+ }
+ if (i == selected_index && show_slider) {
+ for (int j = i-1; j >= 0; j--) {
+ if (TabPages [j].TabBounds.Left < xpos + width - row_width) {
+ slider_pos = j+1;
+ break;
+ }
+ }
+ }
+ } else {
+ if (page.Row != prev_row) {
+ xpos = 0;
+ }
+
+ switch (Alignment) {
+ case TabAlignment.Top:
+ case TabAlignment.Bottom:
+ page.TabBounds = new Rectangle (
+ xpos + CalcXPos (),
+ ypos + (height + spacing.Height) * (row_count - page.Row) + CalcYPos (),
+ width,
+ height);
+ break;
+ case TabAlignment.Left:
+ if (Appearance == TabAppearance.Normal) {
+ // tab rows are positioned right to left
+ page.TabBounds = new Rectangle (
+ ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
+ xpos,
+ height,
+ width);
+ } else {
+ // tab rows are positioned left to right
+ page.TabBounds = new Rectangle (
+ ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
+ xpos,
+ height,
+ width);
+ }
+
+ break;
+ case TabAlignment.Right:
+ if (Appearance == TabAppearance.Normal) {
+ // tab rows are positioned left to right
+ page.TabBounds = new Rectangle (
+ ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
+ xpos,
+ height,
+ width);
+ } else {
+ // tab rows are positioned right to left
+ page.TabBounds = new Rectangle (
+ ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
+ xpos,
+ height,
+ width);
+ }
+
+ break;
+ }
+
+ if (page.Row != prev_row) {
+ if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
+ bool vertical = alignment == TabAlignment.Right || alignment == TabAlignment.Left;
+ int offset = vertical ? TabPages [i - 1].TabBounds.Bottom : TabPages [i - 1].TabBounds.Right;
+ FillRow (begin_prev, i - 1, ((row_width - offset) / (i - begin_prev)), spacing,
+ vertical);
+ }
+ begin_prev = i;
+ }
+ }
+
+ xpos += width + spacing.Width + ThemeEngine.Current.TabControlColSpacing;
+ }
+
+ private void FillRow (int start, int end, int amount, Size spacing, bool vertical)
+ {
+ if (vertical)
+ FillRowV (start, end, amount, spacing);
+ else
+ FillRow (start, end, amount, spacing);
+ }
+
+ private void FillRow (int start, int end, int amount, Size spacing)
+ {
+ int xpos = TabPages [start].TabBounds.Left;
+ for (int i = start; i <= end; i++) {
+ TabPage page = TabPages [i];
+ int left = xpos;
+ int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
+
+ page.TabBounds = new Rectangle (left, page.TabBounds.Top,
+ width, page.TabBounds.Height);
+ xpos = page.TabBounds.Right + 1 + spacing.Width;
+ }
+ }
+
+ private void FillRowV (int start, int end, int amount, Size spacing)
+ {
+ int ypos = TabPages [start].TabBounds.Top;
+ for (int i = start; i <= end; i++) {
+ TabPage page = TabPages [i];
+ int top = ypos;
+ int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
+
+ page.TabBounds = new Rectangle (page.TabBounds.Left, top,
+ page.TabBounds.Width, height);
+ ypos = page.TabBounds.Bottom + 1;
+ }
+ }
+
+ private void ExpandSelected (TabPage page, int left_edge, int right_edge)
+ {
+ if (Appearance != TabAppearance.Normal)
+ return;
+
+ Rectangle r = page.TabBounds;
+ r.Y -= ThemeEngine.Current.TabWidgetselectedDelta.Y;
+ r.X -= ThemeEngine.Current.TabWidgetselectedDelta.X;
+
+ r.Width += ThemeEngine.Current.TabWidgetselectedDelta.Width;
+ r.Height += ThemeEngine.Current.TabWidgetselectedDelta.Height;
+ if (r.Left < left_edge)
+ r.X = left_edge;
+ // Adjustment can't be used for right alignment, since it is
+ // the only one that has a different X origin than 0
+ if (r.Right > right_edge && SizeMode != TabSizeMode.Normal &&
+ alignment != TabAlignment.Right)
+ r.Width = right_edge - r.X;
+ page.TabBounds = r;
+ }
+
+ private void Draw (Graphics dc, Rectangle clip)
+ {
+ ThemeEngine.Current.DrawTabControl (dc, clip, this);
+ }
+
+ private TabPage GetTab (int index)
+ {
+ return Widgets [index] as TabPage;
+ }
+
+ private void SetTab (int index, TabPage value)
+ {
+ if (!tab_pages.Contains (value)) {
+ this.Widgets.Add (value);
+ }
+ this.Widgets.RemoveAt (index);
+ this.Widgets.SetChildIndex (value, index);
+ Redraw ();
+ }
+ private void InsertTab (int index, TabPage value)
+ {
+ if (!tab_pages.Contains (value)) {
+ this.Widgets.Add (value);
+ }
+ this.Widgets.SetChildIndex (value, index);
+ Redraw ();
+ }
+ internal void Redraw ()
+ {
+ if (!IsHandleCreated)
+ return;
+
+ ResizeTabPages ();
+ Refresh ();
+ }
+
+ private int MeasureStringWidth (Graphics graphics, string text, Font font)
+ {
+ if (text == String.Empty)
+ return 0;
+ StringFormat format = new StringFormat();
+ RectangleF rect = new RectangleF(0, 0, 1000, 1000);
+ CharacterRange[] ranges = { new CharacterRange(0, text.Length) };
+ Region[] regions = new Region[1];
+
+ format.SetMeasurableCharacterRanges(ranges);
+ format.FormatFlags = StringFormatFlags.NoClip;
+ format.FormatFlags |= StringFormatFlags.NoWrap;
+ regions = graphics.MeasureCharacterRanges(text + "I", font, rect, format);
+ rect = regions[0].GetBounds(graphics);
+
+ return (int)(rect.Width);
+ }
+
+ void SetToolTip (string text)
+ {
+ if (!show_tool_tips)
+ return;
+
+ if (text == null || text.Length == 0) {
+ CloseToolTip ();
+ return;
+ }
+
+ if (tooltip == null) {
+ tooltip = new ToolTip ();
+ tooltip_timer = new Timer ();
+ tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
+ }
+
+ CloseToolTip ();
+
+ tooltip_state = ToolTip.TipState.Initial;
+ tooltip_timer.Interval = 500;
+ tooltip_timer.Start ();
+ }
+
+ void CloseToolTip ()
+ {
+ if (tooltip == null)
+ return;
+
+ tooltip.Hide (this);
+ tooltip_timer.Stop ();
+ tooltip_state = ToolTip.TipState.Down;
+ }
+
+ void ToolTipTimerTick (object o, EventArgs args)
+ {
+ switch (tooltip_state) {
+ case ToolTip.TipState.Initial:
+ tooltip_timer.Stop ();
+ tooltip_timer.Interval = 5000;
+ tooltip_timer.Start ();
+ tooltip_state = ToolTip.TipState.Show;
+ tooltip.Present (this, GetToolTipText (EnteredTabPage));
+ break;
+ case ToolTip.TipState.Show:
+ CloseToolTip ();
+ break;
+ }
+ }
+
+ void OnMouseMove (object sender, MouseEventArgs e)
+ {
+ if (!mouse_down_on_a_tab_page && ShowSlider) {
+ if (LeftSliderState == PushButtonState.Pressed ||
+ RightSliderState == PushButtonState.Pressed)
+ return;
+ if (LeftScrollButtonArea.Contains (e.Location)) {
+ LeftSliderState = PushButtonState.Hot;
+ RightSliderState = PushButtonState.Normal;
+ EnteredTabPage = null;
+ return;
+ }
+ if (RightScrollButtonArea.Contains (e.Location)) {
+ RightSliderState = PushButtonState.Hot;
+ LeftSliderState = PushButtonState.Normal;
+ EnteredTabPage = null;
+ return;
+ }
+ LeftSliderState = PushButtonState.Normal;
+ RightSliderState = PushButtonState.Normal;
+ }
+ if (EnteredTabPage != null && EnteredTabPage.TabBounds.Contains (e.Location))
+ return;
+ for (int index = 0; index < TabCount; index++) {
+ TabPage tab_page = TabPages[index];
+ if (tab_page.TabBounds.Contains (e.Location)) {
+ EnteredTabPage = tab_page;
+ return;
+ }
+ }
+ EnteredTabPage = null;
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ if (ShowSlider) {
+ LeftSliderState = PushButtonState.Normal;
+ RightSliderState = PushButtonState.Normal;
+ }
+ EnteredTabPage = null;
+ }
+ #endregion // Internal & Private Methods
+
+ #region Events
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackColorChanged {
+ add { base.BackColorChanged += value; }
+ remove { base.BackColorChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged
+ {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ static object DrawItemEvent = new object ();
+ static object SelectedIndexChangedEvent = new object ();
+
+ public event DrawItemEventHandler DrawItem {
+ add { Events.AddHandler (DrawItemEvent, value); }
+ remove { Events.RemoveHandler (DrawItemEvent, value); }
+ }
+
+ public event EventHandler SelectedIndexChanged {
+ add { Events.AddHandler (SelectedIndexChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
+ }
+
+ static object SelectedEvent = new object ();
+
+ public event TabControlEventHandler Selected {
+ add { Events.AddHandler (SelectedEvent, value); }
+ remove { Events.RemoveHandler (SelectedEvent, value); }
+ }
+
+ static object DeselectedEvent = new object ();
+
+ public event TabControlEventHandler Deselected
+ {
+ add { Events.AddHandler (DeselectedEvent, value); }
+ remove { Events.RemoveHandler (DeselectedEvent, value); }
+ }
+
+ static object SelectingEvent = new object ();
+
+ public event TabControlCancelEventHandler Selecting
+ {
+ add { Events.AddHandler (SelectingEvent, value); }
+ remove { Events.RemoveHandler (SelectingEvent, value); }
+ }
+
+ static object DeselectingEvent = new object ();
+
+ public event TabControlCancelEventHandler Deselecting
+ {
+ add { Events.AddHandler (DeselectingEvent, value); }
+ remove { Events.RemoveHandler (DeselectingEvent, value); }
+ }
+
+ static object RightToLeftLayoutChangedEvent = new object ();
+ public event EventHandler RightToLeftLayoutChanged
+ {
+ add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
+ remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
+ }
+ #endregion // Events
+
+
+ #region Class TaControl.ControlCollection
+ [ComVisible (false)]
+ public new class ControlCollection : Widget.WidgetCollection {
+
+ private TabWidget owner;
+
+ public ControlCollection (TabWidget owner) : base (owner)
+ {
+ this.owner = owner;
+ }
+
+ public override void Add (Widget value)
+ {
+ TabPage page = value as TabPage;
+ if (page == null)
+ throw new ArgumentException ("Cannot add " +
+ value.GetType ().Name + " to TabControl. " +
+ "Only TabPages can be directly added to TabWidgets.");
+
+ page.SetVisible (false);
+ base.Add (value);
+ if (owner.TabCount == 1 && owner.selected_index < 0)
+ owner.SelectedIndex = 0;
+ owner.Redraw ();
+ }
+
+ public override void Remove (Widget value)
+ {
+ bool change_index = false;
+
+ TabPage page = value as TabPage;
+ if (page != null && owner.Widgets.Contains (page)) {
+ int index = owner.IndexForTabPage (page);
+ if (index < owner.SelectedIndex || owner.SelectedIndex == Count - 1)
+ change_index = true;
+ }
+
+ base.Remove (value);
+
+ // We don't want to raise SelectedIndexChanged until after we
+ // have removed from the collection, so TabCount will be
+ // correct for the user.
+ if (change_index && Count > 0) {
+ // Clear the selected index internally, to avoid trying to access the previous
+ // selected tab when setting the new one - this is what .net seems to do
+ int prev_selected_index = owner.SelectedIndex;
+ owner.selected_index = -1;
+
+ owner.SelectedIndex = --prev_selected_index;
+ owner.Invalidate ();
+ } else if (change_index) {
+ owner.selected_index = -1;
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.Invalidate ();
+ } else
+ owner.Redraw ();
+ }
+ }
+ #endregion // Class TabControl.ControlCollection
+
+ #region Class TabPage.TabPageCollection
+ public class TabPageCollection : IList, ICollection, IEnumerable {
+
+ private TabWidget owner;
+
+ public TabPageCollection (TabWidget owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException ("Value cannot be null.");
+ this.owner = owner;
+ }
+
+ [Browsable(false)]
+ public int Count {
+ get { return owner.Widgets.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public virtual TabPage this [int index] {
+ get {
+ return owner.GetTab (index);
+ }
+ set {
+ owner.SetTab (index, value);
+ }
+ }
+ public virtual TabPage this [string key] {
+ get {
+ if (string.IsNullOrEmpty (key))
+ return null;
+
+ int index = this.IndexOfKey (key);
+ if (index < 0 || index >= this.Count)
+ return null;
+
+ return this[index];
+ }
+ }
+
+ internal int this[TabPage tabPage] {
+ get {
+ if (tabPage == null)
+ return -1;
+
+ for (int i = 0; i < this.Count; i++)
+ if (this[i].Equals (tabPage))
+ return i;
+
+ return -1;
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return false; }
+ }
+
+ object IList.this [int index] {
+ get {
+ return owner.GetTab (index);
+ }
+ set {
+ owner.SetTab (index, (TabPage) value);
+ }
+ }
+
+ public void Add (TabPage value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("Value cannot be null.");
+ owner.Widgets.Add (value);
+ }
+
+ public void Add (string text)
+ {
+ TabPage page = new TabPage (text);
+ this.Add (page);
+ }
+
+ public void Add (string key, string text)
+ {
+ TabPage page = new TabPage (text);
+ page.Name = key;
+ this.Add (page);
+ }
+
+ public void Add (string key, string text, int imageIndex)
+ {
+ TabPage page = new TabPage (text);
+ page.Name = key;
+ page.ImageIndex = imageIndex;
+ this.Add (page);
+ }
+
+ // .Net sets the ImageKey, but does not show the image when this is used
+ public void Add (string key, string text, string imageKey)
+ {
+ TabPage page = new TabPage (text);
+ page.Name = key;
+ page.ImageKey = imageKey;
+ this.Add (page);
+ }
+
+ public void AddRange (TabPage [] pages)
+ {
+ if (pages == null)
+ throw new ArgumentNullException ("Value cannot be null.");
+ owner.Widgets.AddRange (pages);
+ }
+
+ public virtual void Clear ()
+ {
+ owner.Widgets.Clear ();
+ owner.Invalidate ();
+ }
+
+ public bool Contains (TabPage page)
+ {
+ if (page == null)
+ throw new ArgumentNullException ("Value cannot be null.");
+ return owner.Widgets.Contains (page);
+ }
+
+ public virtual bool ContainsKey (string key)
+ {
+ int index = this.IndexOfKey (key);
+ return (index >= 0 && index < this.Count);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return owner.Widgets.GetEnumerator ();
+ }
+
+ public int IndexOf (TabPage page)
+ {
+ return owner.Widgets.IndexOf (page);
+ }
+
+ public virtual int IndexOfKey(string key)
+ {
+ if (string.IsNullOrEmpty (key))
+ return -1;
+
+ for (int i = 0; i < this.Count; i++) {
+ if (string.Compare (this[i].Name, key, true,
+ System.Globalization.CultureInfo.InvariantCulture) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public void Remove (TabPage value)
+ {
+ owner.Widgets.Remove (value);
+ owner.Invalidate ();
+ }
+
+ public void RemoveAt (int index)
+ {
+ owner.Widgets.RemoveAt (index);
+ owner.Invalidate ();
+ }
+
+ public virtual void RemoveByKey (string key)
+ {
+ int index = this.IndexOfKey (key);
+ if (index >= 0 && index < this.Count)
+ this.RemoveAt (index);
+ }
+
+ void ICollection.CopyTo (Array dest, int index)
+ {
+ owner.Widgets.CopyTo (dest, index);
+ }
+
+ int IList.Add (object value)
+ {
+ TabPage page = value as TabPage;
+ if (value == null)
+ throw new ArgumentException ("value");
+ owner.Widgets.Add (page);
+ return owner.Widgets.IndexOf (page);
+ }
+
+ bool IList.Contains (object page)
+ {
+ TabPage tabPage = page as TabPage;
+ if (tabPage == null)
+ return false;
+ return Contains (tabPage);
+ }
+
+ int IList.IndexOf (object page)
+ {
+ TabPage tabPage = page as TabPage;
+ if (tabPage == null)
+ return -1;
+ return IndexOf (tabPage);
+ }
+
+ void IList.Insert (int index, object tabPage)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public void Insert (int index, string text)
+ {
+ owner.InsertTab (index, new TabPage (text));
+ }
+
+ public void Insert (int index, TabPage tabPage)
+ {
+ owner.InsertTab (index, tabPage);
+ }
+
+ public void Insert (int index, string key, string text)
+ {
+ TabPage page = new TabPage(text);
+ page.Name = key;
+ owner.InsertTab (index, page);
+ }
+
+ public void Insert (int index, string key, string text, int imageIndex)
+ {
+ TabPage page = new TabPage(text);
+ page.Name = key;
+ owner.InsertTab (index, page);
+ page.ImageIndex = imageIndex;
+ }
+
+ public void Insert (int index, string key, string text, string imageKey)
+ {
+ TabPage page = new TabPage(text);
+ page.Name = key;
+ owner.InsertTab (index, page);
+ page.ImageKey = imageKey;
+ }
+ void IList.Remove (object value)
+ {
+ TabPage page = value as TabPage;
+ if (page == null)
+ return;
+ Remove ((TabPage) value);
+ }
+ }
+ #endregion // Class TabPage.TabPageCollection
+ }
+}
+
+
diff --git a/source/ShiftUI/Widgets/TabPage.cs b/source/ShiftUI/Widgets/TabPage.cs
new file mode 100644
index 0000000..7ae0165
--- /dev/null
+++ b/source/ShiftUI/Widgets/TabPage.cs
@@ -0,0 +1,399 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent("Click")]
+ [DesignTimeVisible(false)]
+ [DefaultProperty("Text")]
+ //[Designer("ShiftUI.Design.TabPageDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxItem(false)]
+ public class TabPage : Panel {
+ #region Fields
+ private int imageIndex = -1;
+ private string imageKey;
+ private string tooltip_text = String.Empty;
+ private Rectangle tab_bounds;
+ private int row;
+ private bool use_visual_style_back_color;
+ #endregion // Fields
+
+ #region Public Constructors
+ public TabPage ()
+ {
+ Visible = true;
+
+ SetStyle (Widgetstyles.CacheText, true);
+ }
+
+ public TabPage (string text) : base ()
+ {
+ base.Text = text;
+ }
+
+ #endregion // Public Constructors
+
+ #region .NET 2.0 Public Instance Properties
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override bool AutoSize {
+ get { return base.AutoSize; }
+ set { base.AutoSize = value; }
+ }
+
+ [Browsable (false)]
+ [Localizable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override AutoSizeMode AutoSizeMode {
+ get { return base.AutoSizeMode; }
+ set { base.AutoSizeMode = value; }
+ }
+
+ [Browsable (false)]
+ [DefaultValue ("{Width=0, Height=0}")]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Size MaximumSize {
+ get { return base.MaximumSize; }
+ set { base.MaximumSize = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Size MinimumSize {
+ get { return base.MinimumSize; }
+ set { base.MinimumSize = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new Size PreferredSize {
+ get { return base.PreferredSize; }
+ }
+
+ [DefaultValue (false)]
+ public bool UseVisualStyleBackColor {
+ get { return use_visual_style_back_color; }
+ set { use_visual_style_back_color = value; }
+ }
+
+ public override Color BackColor {
+ get { return base.BackColor; }
+ set { use_visual_style_back_color = false; base.BackColor = value; }
+ }
+ #endregion
+
+ #region Public Instance Properties
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override AnchorStyles Anchor {
+ get { return base.Anchor; }
+ set { base.Anchor = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override DockStyle Dock {
+ get { return base.Dock; }
+ set { base.Dock = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool Enabled {
+ get { return base.Enabled; }
+ set { base.Enabled = value; }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [DefaultValue(-1)]
+ //[Editor("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Localizable(true)]
+ [TypeConverter(typeof(ImageIndexConverter))]
+ public int ImageIndex {
+ get { return imageIndex; }
+ set {
+ if (imageIndex == value)
+ return;
+ imageIndex = value;
+ UpdateOwner ();
+ }
+ }
+
+ [Localizable (true)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [DefaultValue ("")]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
+ //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ public string ImageKey
+ {
+ get { return imageKey; }
+ set {
+ imageKey = value;
+ TabWidget control = this.Parent as TabWidget;
+ if (control != null) {
+ ImageIndex = control.ImageList.Images.IndexOfKey (imageKey);
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new int TabIndex {
+ get { return base.TabIndex; }
+ set { base.TabIndex = value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool TabStop {
+ get { return base.TabStop; }
+ set { base.TabStop = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [Browsable(true)]
+ [Localizable(true)]
+ public override string Text {
+ get { return base.Text; }
+ set {
+ if (value == base.Text)
+ return;
+ base.Text = value;
+ UpdateOwner ();
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue("")]
+ public string ToolTipText {
+ get { return tooltip_text; }
+ set {
+ if (value == null)
+ value = String.Empty;
+ tooltip_text = value;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new bool Visible {
+ get { return base.Visible; }
+ set { /* according to MS docs we can ignore this */ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Public Static Methods
+ public static TabPage GetTabPageOfComponent (object comp)
+ {
+ Widget control = comp as Widget;
+ if (control == null)
+ return null;
+ control = control.Parent;
+ while (control != null) {
+ if (control is TabPage)
+ break;
+ control = control.Parent;
+ }
+ return control as TabPage;
+ }
+
+ #endregion // Public Static Methods
+
+ #region Public Instance Methods
+ public override string ToString ()
+ {
+ return "TabPage: {" + Text + "}";
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Internal & Private Methods and Properties
+ internal Rectangle TabBounds {
+ get { return tab_bounds; }
+ set { tab_bounds = value; }
+ }
+
+ internal int Row {
+ get { return row; }
+ set { row = value; }
+ }
+
+ private void UpdateOwner ()
+ {
+ if (Owner != null) {
+ Owner.Redraw ();
+ }
+ }
+
+ private TabWidget Owner {
+ get { return base.Parent as TabWidget; }
+ }
+
+ internal void SetVisible (bool value)
+ {
+ base.Visible = value;
+ }
+
+ #endregion // Internal & Private Methods and Properties
+
+ #region Protected Instance Methods
+ protected override WidgetCollection CreateWidgetsInstance ()
+ {
+ return new TabPageControlCollection (this);
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ if (Owner != null && Owner.IsHandleCreated) {
+ Rectangle display = Owner.DisplayRectangle;
+
+ base.SetBoundsCore (display.X, display.Y,
+ display.Width, display.Height,
+ BoundsSpecified.All);
+ } else {
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+ }
+
+ protected override void OnEnter (EventArgs e)
+ {
+ base.OnEnter (e);
+ }
+
+ protected override void OnLeave (EventArgs e)
+ {
+ base.OnLeave (e);
+ }
+
+ protected override void OnPaintBackground (PaintEventArgs e)
+ {
+ base.OnPaintBackground (e);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler AutoSizeChanged {
+ add { base.AutoSizeChanged += value; }
+ remove { base.AutoSizeChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler DockChanged {
+ add { base.DockChanged += value; }
+ remove { base.DockChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler EnabledChanged {
+ add { base.EnabledChanged += value; }
+ remove { base.EnabledChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler LocationChanged {
+ add { base.LocationChanged += value; }
+ remove { base.LocationChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabIndexChanged {
+ add { base.TabIndexChanged += value; }
+ remove { base.TabIndexChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TabStopChanged {
+ add { base.TabStopChanged += value; }
+ remove { base.TabStopChanged -= value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler VisibleChanged {
+ add { base.VisibleChanged += value; }
+ remove { base.VisibleChanged -= value; }
+ }
+
+ #endregion // Events
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new Point Location {
+ get {
+ return base.Location;
+ }
+
+ set {
+ base.Location = value;
+ }
+ }
+
+ #region Class TabPageControlCollection
+ [ComVisible (false)]
+ public class TabPageControlCollection : WidgetCollection {
+
+ //private TabPage owner;
+
+ public TabPageControlCollection (TabPage owner) : base (owner)
+ {
+ //this.owner = owner;
+ }
+
+ public override void Add (Widget value)
+ {
+ base.Add (value);
+ }
+ }
+ #endregion // Class TabPageControlCollection
+
+ }
+
+
+}
diff --git a/source/ShiftUI/Widgets/TableLayoutPanel.cs b/source/ShiftUI/Widgets/TableLayoutPanel.cs
new file mode 100644
index 0000000..e327795
--- /dev/null
+++ b/source/ShiftUI/Widgets/TableLayoutPanel.cs
@@ -0,0 +1,788 @@
+//
+// TableLayoutPanel.cs
+//
+// 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) 2006 Jonathan Pobst
+//
+// Authors:
+// Jonathan Pobst ([email protected])
+//
+
+using System;
+using System.Drawing;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using ShiftUI.Layout;
+using System.ComponentModel.Design.Serialization;
+
+namespace ShiftUI
+{
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ProvideProperty ("CellPosition", typeof (Widget))]
+ [ProvideProperty ("Column", typeof (Widget))]
+ [ProvideProperty ("ColumnSpan", typeof (Widget))]
+ [ProvideProperty ("Row", typeof (Widget))]
+ [ProvideProperty ("RowSpan", typeof (Widget))]
+ [DefaultProperty ("ColumnCount")]
+ [Docking (DockingBehavior.Never)]
+ //[Designer ("ShiftUI.Design.TableLayoutPanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ //[DesignerSerializer ("ShiftUI.Design.TableLayoutPanelCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
+ [ToolboxWidget]
+ public class TableLayoutPanel : Panel, IExtenderProvider
+ {
+ private TableLayoutSettings settings;
+ private static TableLayout layout_engine = new TableLayout ();
+ private TableLayoutPanelCellBorderStyle cell_border_style;
+
+ // This is the row/column the Widget actually got placed
+ internal Widget[,] actual_positions;
+
+ // Widths and heights of each column/row
+ internal int[] column_widths;
+ internal int[] row_heights;
+
+ #region Public Constructor
+ public TableLayoutPanel ()
+ {
+ settings = new TableLayoutSettings(this);
+ cell_border_style = TableLayoutPanelCellBorderStyle.None;
+ column_widths = new int[0];
+ row_heights = new int[0];
+ CreateDockPadding ();
+ }
+ #endregion
+
+ #region Public Properties
+ [Localizable (true)]
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ new public BorderStyle BorderStyle {
+ get { return base.BorderStyle; }
+ set { base.BorderStyle = value; }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (TableLayoutPanelCellBorderStyle.None)]
+ public TableLayoutPanelCellBorderStyle CellBorderStyle {
+ get { return this.cell_border_style; }
+ set {
+ if (this.cell_border_style != value) {
+ this.cell_border_style = value;
+ this.PerformLayout (this, "CellBorderStyle");
+ this.Invalidate ();
+ }
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (0)]
+ public int ColumnCount {
+ get { return settings.ColumnCount; }
+ set { settings.ColumnCount = value; }
+ }
+
+ [Browsable (false)]
+ [DisplayName ("Columns")]
+ [MergableProperty (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+ public TableLayoutColumnStyleCollection ColumnStyles {
+ get { return settings.ColumnStyles; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ new public TableLayoutControlCollection Widgets {
+ get { return (TableLayoutControlCollection) base.Widgets; }
+ }
+
+ [DefaultValue (TableLayoutPanelGrowStyle.AddRows)]
+ public TableLayoutPanelGrowStyle GrowStyle {
+ get { return settings.GrowStyle; }
+ set { settings.GrowStyle = value; }
+ }
+
+ public override ShiftUI.Layout.LayoutEngine LayoutEngine {
+ get { return TableLayoutPanel.layout_engine; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public TableLayoutSettings LayoutSettings {
+ get { return this.settings; }
+ set {
+ if (value.isSerialized) {
+ // Serialized version doesn't calculate these.
+ value.ColumnCount = value.ColumnStyles.Count;
+ value.RowCount = value.RowStyles.Count;
+ value.panel = this;
+
+ this.settings = value;
+ } else
+ throw new NotSupportedException ("LayoutSettings value cannot be set directly.");
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (0)]
+ public int RowCount {
+ get { return settings.RowCount; }
+ set { settings.RowCount = value; }
+ }
+
+ [Browsable (false)]
+ [DisplayName ("Rows")]
+ [MergableProperty (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ public TableLayoutRowStyleCollection RowStyles {
+ get { return settings.RowStyles; }
+ }
+ #endregion
+
+ #region Public Methods
+ [DefaultValue (-1)]
+ [DisplayName ("Cell")]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public TableLayoutPanelCellPosition GetCellPosition (Widget control)
+ {
+ return settings.GetCellPosition (control);
+ }
+
+ [DisplayName ("Column")]
+ [DefaultValue (-1)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int GetColumn (Widget control)
+ {
+ return settings.GetColumn (control);
+ }
+
+ [DisplayName ("ColumnSpan")]
+ [DefaultValue (1)]
+ public int GetColumnSpan (Widget control)
+ {
+ return settings.GetColumnSpan (control);
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public int[] GetColumnWidths ()
+ {
+ return this.column_widths;
+ }
+
+ public Widget GetControlFromPosition (int column, int row)
+ {
+ if (column < 0 || row < 0)
+ throw new ArgumentException ();
+
+ TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row);
+
+ foreach (Widget c in this.Widgets)
+ if (settings.GetCellPosition (c) == pos)
+ return c;
+
+ return null;
+ }
+
+ public TableLayoutPanelCellPosition GetPositionFromControl (Widget control)
+ {
+ for (int x = 0; x < this.actual_positions.GetLength (0); x++)
+ for (int y = 0; y < this.actual_positions.GetLength (1); y++)
+ if (this.actual_positions[x, y] == control)
+ return new TableLayoutPanelCellPosition (x, y);
+
+ return new TableLayoutPanelCellPosition (-1, -1);
+ }
+
+ [DisplayName ("Row")]
+ [DefaultValue ("-1")]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int GetRow (Widget control)
+ {
+ return settings.GetRow (control);
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public int[] GetRowHeights ()
+ {
+ return this.row_heights;
+ }
+
+ [DisplayName ("RowSpan")]
+ [DefaultValue (1)]
+ public int GetRowSpan (Widget control)
+ {
+ return settings.GetRowSpan (control);
+ }
+
+ public void SetCellPosition (Widget control, TableLayoutPanelCellPosition position)
+ {
+ settings.SetCellPosition (control, position);
+ }
+
+ public void SetColumn (Widget control, int column)
+ {
+ settings.SetColumn (control, column);
+ }
+
+ public void SetColumnSpan (Widget control, int value)
+ {
+ settings.SetColumnSpan (control, value);
+ }
+
+ public void SetRow (Widget control, int row)
+ {
+ settings.SetRow (control, row);
+ }
+
+ public void SetRowSpan (Widget control, int value)
+ {
+ settings.SetRowSpan (control, value);
+ }
+ #endregion
+
+ #region Protected Methods
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override WidgetCollection CreateWidgetsInstance ()
+ {
+ return new TableLayoutControlCollection (this);
+ }
+
+ protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e)
+ {
+ TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnLayout (LayoutEventArgs levent)
+ {
+ base.OnLayout (levent);
+ Invalidate ();
+ }
+
+ protected override void OnPaintBackground (PaintEventArgs e)
+ {
+ base.OnPaintBackground (e);
+
+ DrawCellBorders (e);
+
+ int border_width = GetCellBorderWidth (CellBorderStyle);
+
+ int x = border_width;
+ int y = border_width;
+
+ for (int i = 0; i < column_widths.Length; i++) {
+ for (int j = 0; j < row_heights.Length; j++) {
+ this.OnCellPaint (new TableLayoutCellPaintEventArgs (e.Graphics, e.ClipRectangle, new Rectangle (x, y, column_widths[i] + border_width, row_heights[j] + border_width), i, j));
+ y += row_heights[j] + border_width;
+ }
+
+ x += column_widths[i] + border_width;
+ y = border_width;
+ }
+ }
+
+ protected override void ScaleWidget (SizeF factor, BoundsSpecified specified)
+ {
+ base.ScaleWidget (factor, specified);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override void ScaleCore (float dx, float dy)
+ {
+ base.ScaleCore (dx, dy);
+ }
+ #endregion
+
+ #region Internal Methods
+ internal static int GetCellBorderWidth (TableLayoutPanelCellBorderStyle style)
+ {
+ switch (style) {
+ case TableLayoutPanelCellBorderStyle.Single:
+ return 1;
+ case TableLayoutPanelCellBorderStyle.Inset:
+ case TableLayoutPanelCellBorderStyle.Outset:
+ return 2;
+ case TableLayoutPanelCellBorderStyle.InsetDouble:
+ case TableLayoutPanelCellBorderStyle.OutsetPartial:
+ case TableLayoutPanelCellBorderStyle.OutsetDouble:
+ return 3;
+ }
+
+ return 0;
+ }
+
+ private void DrawCellBorders (PaintEventArgs e)
+ {
+ Rectangle paint_here = new Rectangle (Point.Empty, this.Size);
+
+ switch (CellBorderStyle) {
+ case TableLayoutPanelCellBorderStyle.Single:
+ DrawSingleBorder (e.Graphics, paint_here);
+ break;
+ case TableLayoutPanelCellBorderStyle.Inset:
+ DrawInsetBorder (e.Graphics, paint_here);
+ break;
+ case TableLayoutPanelCellBorderStyle.InsetDouble:
+ DrawInsetDoubleBorder (e.Graphics, paint_here);
+ break;
+ case TableLayoutPanelCellBorderStyle.Outset:
+ DrawOutsetBorder (e.Graphics, paint_here);
+ break;
+ case TableLayoutPanelCellBorderStyle.OutsetDouble:
+ case TableLayoutPanelCellBorderStyle.OutsetPartial:
+ DrawOutsetDoubleBorder (e.Graphics, paint_here);
+ break;
+ }
+ }
+
+ private void DrawSingleBorder (Graphics g, Rectangle rect)
+ {
+ WidgetPaint.DrawBorder (g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid);
+
+ int x = DisplayRectangle.X;
+ int y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 1;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 2));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 1;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 2, y));
+ }
+ }
+
+ private void DrawInsetBorder (Graphics g, Rectangle rect)
+ {
+ WidgetPaint.DrawBorder3D (g, rect, Border3DStyle.Etched);
+
+ int x = DisplayRectangle.X;
+ int y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 2;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 3));
+ g.DrawLine (Pens.White, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 2;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 3, y));
+ g.DrawLine (Pens.White, new Point (1, y + 1), new Point (Right - 3, y + 1));
+ }
+ }
+
+ private void DrawOutsetBorder (Graphics g, Rectangle rect)
+ {
+ g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 1, rect.Top + 1, rect.Width - 2, rect.Height - 2));
+ g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
+
+ int x = DisplayRectangle.X;
+ int y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 2;
+
+ g.DrawLine (Pens.White, new Point (x, 1), new Point (x, Bottom - 3));
+ g.DrawLine (SystemPens.ControlDark, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 2;
+
+ g.DrawLine (Pens.White, new Point (1, y), new Point (Right - 3, y));
+ g.DrawLine (SystemPens.ControlDark, new Point (1, y + 1), new Point (Right - 3, y + 1));
+ }
+ }
+
+ private void DrawOutsetDoubleBorder (Graphics g, Rectangle rect)
+ {
+ rect.Width -= 1;
+ rect.Height -= 1;
+
+ g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
+ g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
+
+ int x = DisplayRectangle.X;
+ int y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 3;
+
+ g.DrawLine (Pens.White, new Point (x, 3), new Point (x, Bottom - 5));
+ g.DrawLine (SystemPens.ControlDark, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 3;
+
+ g.DrawLine (Pens.White, new Point (3, y), new Point (Right - 4, y));
+ g.DrawLine (SystemPens.ControlDark, new Point (3, y + 2), new Point (Right - 4, y + 2));
+ }
+
+ x = DisplayRectangle.X;
+ y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 3;
+
+ g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 3;
+
+ g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
+ }
+ }
+
+ private void DrawInsetDoubleBorder (Graphics g, Rectangle rect)
+ {
+ rect.Width -= 1;
+ rect.Height -= 1;
+
+ g.DrawRectangle (Pens.White, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
+ g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
+
+ int x = DisplayRectangle.X;
+ int y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 3;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (x, 3), new Point (x, Bottom - 5));
+ g.DrawLine (Pens.White, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 3;
+
+ g.DrawLine (SystemPens.ControlDark, new Point (3, y), new Point (Right - 4, y));
+ g.DrawLine (Pens.White, new Point (3, y + 2), new Point (Right - 4, y + 2));
+ }
+
+ x = DisplayRectangle.X;
+ y = DisplayRectangle.Y;
+
+ for (int i = 0; i < column_widths.Length - 1; i++) {
+ x += column_widths[i] + 3;
+
+ g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
+ }
+
+ for (int j = 0; j < row_heights.Length - 1; j++) {
+ y += row_heights[j] + 3;
+
+ g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
+ }
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ // If the tablelayoutowner is autosize, we have to make sure it is big enough
+ // to hold every non-autosize control
+ actual_positions = (LayoutEngine as TableLayout).CalculateWidgetPositions (this, Math.Max (ColumnCount, 1), Math.Max (RowCount, 1));
+
+ // Use actual row/column counts, not user set ones
+ int actual_cols = actual_positions.GetLength (0);
+ int actual_rows = actual_positions.GetLength (1);
+
+ // Find the largest column-span/row-span values. A table entry that spans more than one
+ // column (row) should not be treated as though it's width (height) all belongs to the
+ // first column (row), but should be spread out across all the columns (rows) that are
+ // spanned. So we need to keep track of the widths (heights) of spans as well as
+ // individual columns (rows).
+ int max_colspan = 1, max_rowspan = 1;
+ foreach (Widget c in Widgets)
+ {
+ max_colspan = Math.Max(max_colspan, GetColumnSpan(c));
+ max_rowspan = Math.Max(max_rowspan, GetRowSpan(c));
+ }
+
+ // Figure out how wide the owner needs to be
+ int[] column_widths = new int[actual_cols];
+ // Keep track of widths for spans as well as columns. column_span_widths[i,j] stores
+ // the maximum width for items column i than have a span of j+1 (ie, covers columns
+ // i through i+j).
+ int[,] column_span_widths = new int[actual_cols, max_colspan];
+ int[] biggest = new int[max_colspan];
+ float total_column_percentage = 0f;
+
+ // Figure out how wide each column wants to be
+ for (int i = 0; i < actual_cols; i++) {
+ if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
+ total_column_percentage += ColumnStyles[i].Width;
+ int absolute_width = -1;
+ if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Absolute)
+ absolute_width = (int)ColumnStyles[i].Width; // use the absolute width if it's absolute!
+
+ for (int s = 0; s < max_colspan; ++s)
+ biggest[s] = 0;
+
+ for (int j = 0; j < actual_rows; j++) {
+ Widget c = actual_positions[i, j];
+
+ if (c != null) {
+ int colspan = GetColumnSpan (c);
+ if (colspan == 0)
+ continue;
+ if (colspan == 1 && absolute_width > -1)
+ biggest[0] = absolute_width; // use the absolute width if the column has absolute width assigned!
+ else if (!c.AutoSize)
+ biggest[colspan-1] = Math.Max (biggest[colspan-1], c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
+ else
+ biggest[colspan-1] = Math.Max (biggest[colspan-1], c.PreferredSize.Width + c.Margin.Horizontal + Padding.Horizontal);
+ }
+ else if (absolute_width > -1) {
+ biggest[0] = absolute_width;
+ }
+ }
+
+ for (int s = 0; s < max_colspan; ++s)
+ column_span_widths[i,s] = biggest[s];
+ }
+
+ for (int i = 0; i < actual_cols; ++i) {
+ for (int s = 1; s < max_colspan; ++s) {
+ if (column_span_widths[i,s] > 0)
+ AdjustWidthsForSpans (column_span_widths, i, s);
+ }
+ column_widths[i] = column_span_widths[i,0];
+ }
+
+ // Because percentage based rows divy up the remaining space,
+ // we have to make the owner big enough so that all the rows
+ // get bigger, even if we only need one to be bigger.
+ int non_percent_total_width = 0;
+ int percent_total_width = 0;
+
+ for (int i = 0; i < actual_cols; i++) {
+ if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
+ percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage)));
+ else
+ non_percent_total_width += column_widths[i];
+ }
+
+ int border_width = GetCellBorderWidth (CellBorderStyle);
+ int needed_width = non_percent_total_width + percent_total_width + (border_width * (actual_cols + 1));
+
+ // Figure out how tall the owner needs to be
+ int[] row_heights = new int[actual_rows];
+ int[,] row_span_heights = new int[actual_rows, max_rowspan];
+ biggest = new int[max_rowspan];
+ float total_row_percentage = 0f;
+
+ // Figure out how tall each row wants to be
+ for (int j = 0; j < actual_rows; j++) {
+ if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
+ total_row_percentage += RowStyles[j].Height;
+ int absolute_height = -1;
+ if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Absolute)
+ absolute_height = (int)RowStyles[j].Height; // use the absolute height if it's absolute!
+
+ for (int s = 0; s < max_rowspan; ++s)
+ biggest[s] = 0;
+
+ for (int i = 0; i < actual_cols; i++) {
+ Widget c = actual_positions[i, j];
+
+ if (c != null) {
+ int rowspan = GetRowSpan (c);
+ if (rowspan == 0)
+ continue;
+ if (rowspan == 1 && absolute_height > -1)
+ biggest[0] = absolute_height; // use the absolute height if the row has absolute height assigned!
+ else if (!c.AutoSize)
+ biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical);
+ else
+ biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical);
+ }
+ else if (absolute_height > -1) {
+ biggest[0] = absolute_height;
+ }
+ }
+
+ for (int s = 0; s < max_rowspan; ++s)
+ row_span_heights[j,s] = biggest[s];
+ }
+
+ for (int j = 0; j < actual_rows; ++j) {
+ for (int s = 1; s < max_rowspan; ++s) {
+ if (row_span_heights[j,s] > 0)
+ AdjustHeightsForSpans (row_span_heights, j, s);
+ }
+ row_heights[j] = row_span_heights[j,0];
+ }
+
+ // Because percentage based rows divy up the remaining space,
+ // we have to make the owner big enough so that all the rows
+ // get bigger, even if we only need one to be bigger.
+ int non_percent_total_height = 0;
+ int percent_total_height = 0;
+
+ for (int j = 0; j < actual_rows; j++) {
+ if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
+ percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage)));
+ else
+ non_percent_total_height += row_heights[j];
+ }
+
+ int needed_height = non_percent_total_height + percent_total_height + (border_width * (actual_rows + 1));
+
+ return new Size (needed_width, needed_height);
+ }
+
+ /// <summary>
+ /// Adjust the widths of the columns underlying a span if necessary.
+ /// </summary>
+ private void AdjustWidthsForSpans (int[,] widths, int col, int span)
+ {
+ // Get the combined width of the columns underlying the span.
+ int existing_width = 0;
+ for (int i = col; i <= col+span; ++i)
+ existing_width += widths[i,0];
+ if (widths[col,span] > existing_width)
+ {
+ // We need to expand one or more of the underlying columns to fit the span,
+ // preferably ones that are not Absolute style.
+ int excess = widths[col,span] - existing_width;
+ int remaining = excess;
+ List<int> adjusting = new List<int>();
+ List<float> adjusting_widths = new List<float>();
+ for (int i = col; i <= col+span; ++i) {
+ if (i < ColumnStyles.Count && ColumnStyles[i].SizeType != SizeType.Absolute) {
+ adjusting.Add(i);
+ adjusting_widths.Add((float)widths[i,0]);
+ }
+ }
+ if (adjusting.Count == 0) {
+ // if every column is Absolute, spread the gain across every column
+ for (int i = col; i <= col+span; ++i) {
+ adjusting.Add(i);
+ adjusting_widths.Add((float)widths[i,0]);
+ }
+ }
+ float original_total = 0f;
+ foreach (var w in adjusting_widths)
+ original_total += w;
+ // Divide up the needed additional width proportionally.
+ for (int i = 0; i < adjusting.Count; ++i) {
+ var idx = adjusting[i];
+ var percent = adjusting_widths[i] / original_total;
+ var adjust = (int)(percent * excess);
+ widths[idx,0] += adjust;
+ remaining -= adjust;
+ }
+ // Any remaining fragment (1 or 2 pixels?) is divided evenly.
+ while (remaining > 0) {
+ for (int i = 0; i < adjusting.Count && remaining > 0; ++i) {
+ ++widths[adjusting[i],0];
+ --remaining;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Adjust the heights of the rows underlying a span if necessary.
+ /// </summary>
+ private void AdjustHeightsForSpans (int[,] heights, int row, int span)
+ {
+ // Get the combined height of the rows underlying the span.
+ int existing_height = 0;
+ for (int i = row; i <= row+span; ++i)
+ existing_height += heights[i,0];
+ if (heights[row,span] > existing_height)
+ {
+ // We need to expand one or more of the underlying rows to fit the span,
+ // preferably ones that are not Absolute style.
+ int excess = heights[row,span] - existing_height;
+ int remaining = excess;
+ List<int> adjusting = new List<int>();
+ List<float> adjusting_heights = new List<float>();
+ for (int i = row; i <= row+span; ++i) {
+ if (i < RowStyles.Count && RowStyles[i].SizeType != SizeType.Absolute) {
+ adjusting.Add(i);
+ adjusting_heights.Add((float)heights[i,0]);
+ }
+ }
+ if (adjusting.Count == 0) {
+ // if every row is Absolute, spread the gain across every row
+ for (int i = row; i <= row+span; ++i) {
+ adjusting.Add(i);
+ adjusting_heights.Add((float)heights[i,0]);
+ }
+ }
+ float original_total = 0f;
+ foreach (var w in adjusting_heights)
+ original_total += w;
+ // Divide up the needed additional height proportionally.
+ for (int i = 0; i < adjusting.Count; ++i) {
+ var idx = adjusting[i];
+ var percent = adjusting_heights[i] / original_total;
+ var adjust = (int)(percent * excess);
+ heights[idx,0] += adjust;
+ remaining -= adjust;
+ }
+ // Any remaining fragment (1 or 2 pixels?) is divided evenly.
+ while (remaining > 0) {
+ for (int i = 0; i < adjusting.Count && remaining > 0; ++i) {
+ ++heights[adjusting[i],0];
+ --remaining;
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Public Events
+ static object CellPaintEvent = new object ();
+
+ public event TableLayoutCellPaintEventHandler CellPaint {
+ add { Events.AddHandler (CellPaintEvent, value); }
+ remove { Events.RemoveHandler (CellPaintEvent, value); }
+ }
+ #endregion
+
+ #region IExtenderProvider
+ bool IExtenderProvider.CanExtend (object obj)
+ {
+ if (obj is Widget)
+ if ((obj as Widget).Parent == this)
+ return true;
+
+ return false;
+ }
+ #endregion
+
+ }
+}
diff --git a/source/ShiftUI/Widgets/TextBox.cs b/source/ShiftUI/Widgets/TextBox.cs
new file mode 100644
index 0000000..1ef880d
--- /dev/null
+++ b/source/ShiftUI/Widgets/TextBox.cs
@@ -0,0 +1,1025 @@
+// 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-2006 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok [email protected]
+// Daniel Nauck (dna(at)mono-project(dot)de)
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+
+ [ComVisible(true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ //[Designer ("ShiftUI.Design.TextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class TextBox : TextBoxBase {
+ #region Variables
+ private MenuItem undo;
+ private MenuItem cut;
+ private MenuItem copy;
+ private MenuItem paste;
+ private MenuItem delete;
+ private MenuItem select_all;
+
+ private bool use_system_password_char;
+ private AutoCompleteStringCollection auto_complete_custom_source;
+ private AutoCompleteMode auto_complete_mode = AutoCompleteMode.None;
+ private AutoCompleteSource auto_complete_source = AutoCompleteSource.None;
+ private AutoCompleteListBox auto_complete_listbox;
+ private string auto_complete_original_text;
+ private int auto_complete_selected_index = -1;
+ private List<string> auto_complete_matches;
+ private ComboBox auto_complete_cb_source;
+ #endregion // Variables
+
+ #region Public Constructors
+ public TextBox() {
+
+ scrollbars = RichTextBoxScrollBars.None;
+ alignment = HorizontalAlignment.Left;
+ this.LostFocus +=new EventHandler(TextBox_LostFocus);
+ this.RightToLeftChanged += new EventHandler (TextBox_RightToLeftChanged);
+ MouseWheel += new MouseEventHandler (TextBox_MouseWheel);
+
+ BackColor = ThemeEngine.Current.ColorControl;
+ ForeColor = ThemeEngine.Current.ColorControlText;
+ backcolor_set = false;
+
+ SetStyle (Widgetstyles.StandardClick | Widgetstyles.StandardDoubleClick, false);
+ SetStyle (Widgetstyles.FixedHeight, true);
+
+
+ document.multiline = false;
+ }
+
+ #endregion // Public Constructors
+
+ #region Private & Internal Methods
+
+ void TextBox_RightToLeftChanged (object sender, EventArgs e)
+ {
+ UpdateAlignment ();
+ }
+
+ private void TextBox_LostFocus (object sender, EventArgs e) {
+ if (hide_selection)
+ document.InvalidateSelectionArea ();
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ }
+
+ private void TextBox_MouseWheel (object o, MouseEventArgs args)
+ {
+ if (auto_complete_listbox == null || !auto_complete_listbox.Visible)
+ return;
+
+ int lines = args.Delta / 120;
+ auto_complete_listbox.Scroll (-lines);
+ }
+
+ // Receives either WM_KEYDOWN or WM_CHAR that will likely need the generation/lookup
+ // of new matches
+ private void ProcessAutoCompleteInput (ref Message m, bool deleting_chars)
+ {
+ // Need to call base.WndProc before to have access to
+ // the updated Text property value
+ base.WndProc (ref m);
+ auto_complete_original_text = Text;
+ ShowAutoCompleteListBox (deleting_chars);
+ }
+
+ private void ShowAutoCompleteListBox (bool deleting_chars)
+ {
+ //
+ // We only support CustomSource by now
+ //
+
+ IList source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items;
+
+ bool append = auto_complete_mode == AutoCompleteMode.Append || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+ bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+
+ if (Text.Length == 0) {
+ if (auto_complete_listbox != null)
+ auto_complete_listbox.HideListBox (false);
+ return;
+ }
+
+ if (auto_complete_matches == null)
+ auto_complete_matches = new List<string> ();
+
+ string text = Text;
+ auto_complete_matches.Clear ();
+
+ for (int i = 0; i < source.Count; i++) {
+ string item_text = auto_complete_cb_source == null ? auto_complete_custom_source [i] :
+ auto_complete_cb_source.GetItemText (auto_complete_cb_source.Items [i]);
+ if (item_text.StartsWith (text, StringComparison.CurrentCultureIgnoreCase))
+ auto_complete_matches.Add (item_text);
+ }
+
+ auto_complete_matches.Sort ();
+
+ // Return if we have a single exact match
+ if ((auto_complete_matches.Count == 0) || (auto_complete_matches.Count == 1 &&
+ auto_complete_matches [0].Equals (text, StringComparison.CurrentCultureIgnoreCase))) {
+
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ return;
+ }
+
+ auto_complete_selected_index = suggest ? -1 : 0;
+
+ if (suggest) {
+ if (auto_complete_listbox == null)
+ auto_complete_listbox = new AutoCompleteListBox (this);
+
+ // Show or update auto complete listbox contents
+ auto_complete_listbox.Location = PointToScreen (new Point (0, Height));
+ auto_complete_listbox.ShowListBox ();
+ }
+
+ if (append && !deleting_chars)
+ AppendAutoCompleteMatch (0);
+
+ document.MoveCaret (CaretDirection.End);
+ }
+
+ internal void HideAutoCompleteList ()
+ {
+ if (auto_complete_listbox != null)
+ auto_complete_listbox.HideListBox (false);
+ }
+
+ internal bool IsAutoCompleteAvailable {
+ get {
+ if (auto_complete_source == AutoCompleteSource.None || auto_complete_mode == AutoCompleteMode.None)
+ return false;
+
+ // We only support CustomSource by now, as well as an internal custom source used by ComboBox
+ if (auto_complete_source != AutoCompleteSource.CustomSource)
+ return false;
+ IList custom_source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items;
+ if (custom_source == null || custom_source.Count == 0)
+ return false;
+
+ return true;
+ }
+ }
+
+ internal ComboBox AutoCompleteInternalSource {
+ get {
+ return auto_complete_cb_source;
+ }
+ set {
+ auto_complete_cb_source = value;
+ }
+ }
+
+ internal bool CanNavigateAutoCompleteList {
+ get {
+ if (auto_complete_mode == AutoCompleteMode.None)
+ return false;
+ if (auto_complete_matches == null || auto_complete_matches.Count == 0)
+ return false;
+
+ bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
+ if (auto_complete_mode == AutoCompleteMode.Suggest && !suggest_window_visible)
+ return false;
+
+ return true;
+ }
+ }
+
+ bool NavigateAutoCompleteList (Keys key)
+ {
+ if (auto_complete_matches == null || auto_complete_matches.Count == 0)
+ return false;
+
+ bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
+ if (!suggest_window_visible && auto_complete_mode == AutoCompleteMode.Suggest)
+ return false;
+
+ int index = auto_complete_selected_index;
+
+ switch (key) {
+ case Keys.Up:
+ index -= 1;
+ if (index < -1)
+ index = auto_complete_matches.Count - 1;
+ break;
+ case Keys.Down:
+ index += 1;
+ if (index >= auto_complete_matches.Count)
+ index = -1;
+ break;
+ case Keys.PageUp:
+ if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
+ goto case Keys.Up;
+
+ if (index == -1)
+ index = auto_complete_matches.Count - 1;
+ else if (index == 0)
+ index = -1;
+ else {
+ index -= auto_complete_listbox.page_size - 1;
+ if (index < 0)
+ index = 0;
+ }
+ break;
+ case Keys.PageDown:
+ if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
+ goto case Keys.Down;
+
+ if (index == -1)
+ index = 0;
+ else if (index == auto_complete_matches.Count - 1)
+ index = -1;
+ else {
+ index += auto_complete_listbox.page_size - 1;
+ if (index >= auto_complete_matches.Count)
+ index = auto_complete_matches.Count - 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // In SuggestAppend mode the navigation mode depends on the visibility of the suggest lb.
+ bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+ if (suggest && suggest_window_visible) {
+ Text = index == -1 ? auto_complete_original_text : auto_complete_matches [index];
+ auto_complete_listbox.HighlightedIndex = index;
+ } else
+ // Append only, not suggest at all
+ AppendAutoCompleteMatch (index < 0 ? 0 : index);
+
+ auto_complete_selected_index = index;
+ document.MoveCaret (CaretDirection.End);
+
+ return true;
+ }
+
+ void AppendAutoCompleteMatch (int index)
+ {
+ Text = auto_complete_original_text + auto_complete_matches [index].Substring (auto_complete_original_text.Length);
+ SelectionStart = auto_complete_original_text.Length;
+ SelectionLength = auto_complete_matches [index].Length - auto_complete_original_text.Length;
+ }
+
+ // this is called when the user selects a value from the autocomplete list
+ // *with* the mouse
+ internal virtual void OnAutoCompleteValueSelected (EventArgs args)
+ {
+ }
+
+ private void UpdateAlignment ()
+ {
+ HorizontalAlignment new_alignment = alignment;
+ RightToLeft rtol = GetInheritedRtoL ();
+
+ if (rtol == RightToLeft.Yes) {
+ if (new_alignment == HorizontalAlignment.Left)
+ new_alignment = HorizontalAlignment.Right;
+ else if (new_alignment == HorizontalAlignment.Right)
+ new_alignment = HorizontalAlignment.Left;
+ }
+
+ document.alignment = new_alignment;
+
+ // MS word-wraps if alignment isn't left
+ if (Multiline) {
+ if (alignment != HorizontalAlignment.Left) {
+ document.Wrap = true;
+ } else {
+ document.Wrap = word_wrap;
+ }
+ }
+
+ for (int i = 1; i <= document.Lines; i++) {
+ document.GetLine (i).Alignment = new_alignment;
+ }
+
+ document.RecalculateDocument (CreateGraphicsInternal ());
+
+ Invalidate (); // Make sure we refresh
+ }
+
+ internal override Color ChangeBackColor (Color backColor)
+ {
+ if (backColor == Color.Empty) {
+ if (!ReadOnly)
+ backColor = SystemColors.Window;
+
+ backcolor_set = false;
+ }
+
+ return backColor;
+ }
+
+ void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) {
+ if(auto_complete_source == AutoCompleteSource.CustomSource) {
+ //FIXME: handle add, remove and refresh events in AutoComplete algorithm.
+ }
+ }
+ #endregion // Private & Internal Methods
+
+ #region Public Instance Properties
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetStringCollectionEditor, " + Consts.AssemblySystem_Design,
+ //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+ public AutoCompleteStringCollection AutoCompleteCustomSource {
+ get {
+ if(auto_complete_custom_source == null) {
+ auto_complete_custom_source = new AutoCompleteStringCollection ();
+ auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+ }
+ return auto_complete_custom_source;
+ }
+ set {
+ if(auto_complete_custom_source == value)
+ return;
+
+ if(auto_complete_custom_source != null) //remove eventhandler from old collection
+ auto_complete_custom_source.CollectionChanged -= new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+
+ auto_complete_custom_source = value;
+
+ if(auto_complete_custom_source != null)
+ auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
+ }
+ }
+
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [DefaultValue (AutoCompleteMode.None)]
+ public AutoCompleteMode AutoCompleteMode {
+ get { return auto_complete_mode; }
+ set {
+ if(auto_complete_mode == value)
+ return;
+
+ if((value < AutoCompleteMode.None) || (value > AutoCompleteMode.SuggestAppend))
+ throw new InvalidEnumArgumentException (String.Format ("Enum argument value '{0}' is not valid for AutoCompleteMode", value));
+
+ auto_complete_mode = value;
+ }
+ }
+
+ [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [DefaultValue (AutoCompleteSource.None)]
+ [TypeConverter (typeof (TextBoxAutoCompleteSourceConverter))]
+ public AutoCompleteSource AutoCompleteSource {
+ get { return auto_complete_source; }
+ set {
+ if(auto_complete_source == value)
+ return;
+
+ if(!Enum.IsDefined (typeof (AutoCompleteSource), value))
+ throw new InvalidEnumArgumentException (String.Format ("Enum argument value '{0}' is not valid for AutoCompleteSource", value));
+
+ auto_complete_source = value;
+ }
+ }
+
+ [DefaultValue(false)]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public bool UseSystemPasswordChar {
+ get {
+ return use_system_password_char;
+ }
+
+ set {
+ if (use_system_password_char != value) {
+ use_system_password_char = value;
+
+ if (!Multiline)
+ document.PasswordChar = PasswordChar.ToString ();
+ else
+ document.PasswordChar = string.Empty;
+ Invalidate ();
+ }
+ }
+ }
+
+ [DefaultValue(false)]
+ [MWFCategory("Behavior")]
+ public bool AcceptsReturn {
+ get {
+ return accepts_return;
+ }
+
+ set {
+ if (value != accepts_return) {
+ accepts_return = value;
+ }
+ }
+ }
+
+ [DefaultValue(CharacterCasing.Normal)]
+ [MWFCategory("Behavior")]
+ public CharacterCasing CharacterCasing {
+ get {
+ return character_casing;
+ }
+
+ set {
+ if (value != character_casing) {
+ character_casing = value;
+ }
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue('\0')]
+ [MWFCategory("Behavior")]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ public char PasswordChar {
+ get {
+ if (use_system_password_char) {
+ return '*';
+ }
+ return password_char;
+ }
+
+ set {
+ if (value != password_char) {
+ password_char = value;
+ if (!Multiline) {
+ document.PasswordChar = PasswordChar.ToString ();
+ } else {
+ document.PasswordChar = string.Empty;
+ }
+ this.CalculateDocument();
+ }
+ }
+ }
+
+ [DefaultValue(ScrollBars.None)]
+ [Localizable(true)]
+ [MWFCategory("Appearance")]
+ public ScrollBars ScrollBars {
+ get {
+ return (ScrollBars)scrollbars;
+ }
+
+ set {
+ if (!Enum.IsDefined (typeof (ScrollBars), value))
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (ScrollBars));
+
+ if (value != (ScrollBars)scrollbars) {
+ scrollbars = (RichTextBoxScrollBars)value;
+ base.CalculateScrollBars();
+ }
+ }
+ }
+
+ public override string Text {
+ get {
+ return base.Text;
+ }
+
+ set {
+ base.Text = value;
+ }
+ }
+
+ [DefaultValue(HorizontalAlignment.Left)]
+ [Localizable(true)]
+ [MWFCategory("Appearance")]
+ public HorizontalAlignment TextAlign {
+ get {
+ return alignment;
+ }
+
+ set {
+ if (value != alignment) {
+ alignment = value;
+
+ UpdateAlignment ();
+
+ OnTextAlignChanged(EventArgs.Empty);
+ }
+ }
+ }
+ #endregion // Public Instance Properties
+
+ public void Paste (string text)
+ {
+ document.ReplaceSelection (CaseAdjust (text), false);
+
+ ScrollToCaret();
+ OnTextChanged(EventArgs.Empty);
+ }
+ #region Protected Instance Methods
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ base.Dispose (disposing);
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ return base.IsInputKey (keyData);
+ }
+
+ protected override void OnGotFocus (EventArgs e)
+ {
+ base.OnGotFocus (e);
+ if (selection_length == -1 && !has_been_focused)
+ SelectAllNoScroll ();
+ has_been_focused = true;
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ }
+
+ protected virtual void OnTextAlignChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [TextAlignChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_KEYDOWN:
+ if (!IsAutoCompleteAvailable)
+ break;
+
+ Keys key_data = (Keys)m.WParam.ToInt32 ();
+ switch (key_data) {
+ case Keys.Down:
+ case Keys.Up:
+ case Keys.PageDown:
+ case Keys.PageUp:
+ if (NavigateAutoCompleteList (key_data)) {
+ m.Result = IntPtr.Zero;
+ return;
+ }
+ break;
+ case Keys.Enter:
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ SelectAll ();
+ break;
+ case Keys.Escape:
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ break;
+ case Keys.Delete:
+ ProcessAutoCompleteInput (ref m, true);
+ return;
+ default:
+ break;
+ }
+ break;
+ case Msg.WM_CHAR:
+ if (!IsAutoCompleteAvailable)
+ break;
+
+ // Don't handle either Enter or Esc - they are handled in the WM_KEYDOWN case
+ int char_value = m.WParam.ToInt32 ();
+ if (char_value == 13 || char_value == 27)
+ break;
+
+ ProcessAutoCompleteInput (ref m, char_value == 8);
+ return;
+ case Msg.WM_LBUTTONDOWN:
+ // When the textbox gets focus by LBUTTON (but not by middle or right)
+ // it does not do the select all / scroll thing.
+ has_been_focused = true;
+ FocusInternal (true);
+ break;
+ }
+
+ base.WndProc(ref m);
+ }
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object TextAlignChangedEvent = new object ();
+
+ public event EventHandler TextAlignChanged {
+ add { Events.AddHandler (TextAlignChangedEvent, value); }
+ remove { Events.RemoveHandler (TextAlignChangedEvent, value); }
+ }
+ #endregion // Events
+
+ #region Private Methods
+
+
+
+ internal void RestoreContextMenu ()
+ {
+ //ContextMenuInternal = menu;
+ }
+
+ private void menu_Popup(object sender, EventArgs e) {
+ if (SelectionLength == 0) {
+ cut.Enabled = false;
+ copy.Enabled = false;
+ } else {
+ cut.Enabled = true;
+ copy.Enabled = true;
+ }
+
+ if (SelectionLength == TextLength) {
+ select_all.Enabled = false;
+ } else {
+ select_all.Enabled = true;
+ }
+
+ if (!CanUndo) {
+ undo.Enabled = false;
+ } else {
+ undo.Enabled = true;
+ }
+
+ if (ReadOnly) {
+ undo.Enabled = cut.Enabled = paste.Enabled = delete.Enabled = false;
+ }
+ }
+
+ private void undo_Click(object sender, EventArgs e) {
+ Undo();
+ }
+
+ private void cut_Click(object sender, EventArgs e) {
+ Cut();
+ }
+
+ private void copy_Click(object sender, EventArgs e) {
+ Copy();
+ }
+
+ private void paste_Click(object sender, EventArgs e) {
+ Paste();
+ }
+
+ private void delete_Click(object sender, EventArgs e) {
+ SelectedText = string.Empty;
+ }
+
+ private void select_all_Click(object sender, EventArgs e) {
+ SelectAll();
+ }
+ #endregion // Private Methods
+
+ public override bool Multiline {
+ get {
+ return base.Multiline;
+ }
+
+ set {
+ base.Multiline = value;
+ }
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ class AutoCompleteListBox : Widget
+ {
+ TextBox owner;
+ VScrollBar vscroll;
+ int top_item;
+ int last_item;
+ internal int page_size;
+ int item_height;
+ int highlighted_index = -1;
+ bool user_defined_size;
+ bool resizing;
+ Rectangle resizer_bounds;
+
+ const int DefaultDropDownItems = 7;
+
+ public AutoCompleteListBox (TextBox tb)
+ {
+ owner = tb;
+ item_height = FontHeight + 2;
+
+ vscroll = new VScrollBar ();
+ vscroll.ValueChanged += VScrollValueChanged;
+ Widgets.Add (vscroll);
+
+ is_visible = false;
+ InternalBorderStyle = BorderStyle.FixedSingle;
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+
+ cp.Style ^= (int)WindowStyles.WS_CHILD;
+ cp.Style ^= (int)WindowStyles.WS_VISIBLE;
+ cp.Style |= (int)WindowStyles.WS_POPUP;
+ cp.ExStyle |= (int)WindowExStyles.WS_EX_TOPMOST | (int)WindowExStyles.WS_EX_TOOLWINDOW;
+ return cp;
+ }
+ }
+
+ public int HighlightedIndex {
+ get {
+ return highlighted_index;
+ }
+ set {
+ if (value == highlighted_index)
+ return;
+
+ if (highlighted_index != -1)
+ Invalidate (GetItemBounds (highlighted_index));
+ highlighted_index = value;
+ if (highlighted_index != -1)
+ Invalidate (GetItemBounds (highlighted_index));
+
+ if (highlighted_index != -1)
+ EnsureVisible (highlighted_index);
+ }
+ }
+
+ public void Scroll (int lines)
+ {
+ int max = vscroll.Maximum - page_size + 1;
+ int val = vscroll.Value + lines;
+ if (val > max)
+ val = max;
+ else if (val < vscroll.Minimum)
+ val = vscroll.Minimum;
+
+ vscroll.Value = val;
+ }
+
+ public void EnsureVisible (int index)
+ {
+ if (index < top_item) {
+ vscroll.Value = index;
+ } else {
+ int max = vscroll.Maximum - page_size + 1;
+ int rows = Height / item_height;
+ if (index > top_item + rows - 1) {
+ index = index - rows + 1;
+ vscroll.Value = index > max ? max : index;
+ }
+ }
+ }
+
+ internal override bool ActivateOnShow {
+ get {
+ return false;
+ }
+ }
+
+ void VScrollValueChanged (object o, EventArgs args)
+ {
+ if (top_item == vscroll.Value)
+ return;
+
+ top_item = vscroll.Value;
+ last_item = GetLastVisibleItem ();
+ Invalidate ();
+ }
+
+ int GetLastVisibleItem ()
+ {
+ int top_y = Height;
+
+ for (int i = top_item; i < owner.auto_complete_matches.Count; i++) {
+ int pos = i - top_item; // relative to visible area
+ if ((pos * item_height) + item_height >= top_y)
+ return i;
+ }
+
+ return owner.auto_complete_matches.Count - 1;
+ }
+
+ Rectangle GetItemBounds (int index)
+ {
+ int pos = index - top_item;
+ Rectangle bounds = new Rectangle (0, pos * item_height, Width, item_height);
+ if (vscroll.Visible)
+ bounds.Width -= vscroll.Width;
+
+ return bounds;
+ }
+
+ int GetItemAt (Point loc)
+ {
+ if (loc.Y > (last_item - top_item) * item_height + item_height)
+ return -1;
+
+ int retval = loc.Y / item_height;
+ retval += top_item;
+
+ return retval;
+ }
+
+ void LayoutListBox ()
+ {
+ int total_height = owner.auto_complete_matches.Count * item_height;
+ page_size = Math.Max (Height / item_height, 1);
+ last_item = GetLastVisibleItem ();
+
+ if (Height < total_height) {
+ vscroll.Visible = true;
+ vscroll.Maximum = owner.auto_complete_matches.Count - 1;
+ vscroll.LargeChange = page_size;
+ vscroll.Location = new Point (Width - vscroll.Width, 0);
+ vscroll.Height = Height - item_height;
+ } else
+ vscroll.Visible = false;
+
+ resizer_bounds = new Rectangle (Width - item_height, Height - item_height,
+ item_height, item_height);
+ }
+
+ public void HideListBox (bool set_text)
+ {
+ if (set_text)
+ owner.Text = owner.auto_complete_matches [HighlightedIndex];
+
+ Capture = false;
+ Hide ();
+ }
+
+ public void ShowListBox ()
+ {
+ if (!user_defined_size) {
+ // This should call the Layout routine for us
+ int height = owner.auto_complete_matches.Count > DefaultDropDownItems ?
+ DefaultDropDownItems * item_height : (owner.auto_complete_matches.Count + 1) * item_height;
+ Size = new Size (owner.Width, height);
+ } else
+ LayoutListBox ();
+
+ vscroll.Value = 0;
+ HighlightedIndex = -1;
+
+ Show ();
+ // make sure we are on top - call the raw routine, since we are parentless
+ XplatUI.SetZOrder (Handle, IntPtr.Zero, true, false);
+ Invalidate ();
+ }
+
+ protected override void OnResize (EventArgs args)
+ {
+ base.OnResize (args);
+
+ LayoutListBox ();
+ Refresh ();
+ }
+
+ protected override void OnMouseDown (MouseEventArgs args)
+ {
+ base.OnMouseDown (args);
+
+ if (!resizer_bounds.Contains (args.Location))
+ return;
+
+ user_defined_size = true;
+ resizing = true;
+ Capture = true;
+ }
+
+ protected override void OnMouseMove (MouseEventArgs args)
+ {
+ base.OnMouseMove (args);
+
+ if (resizing) {
+ Point mouse_loc = Widget.MousePosition;
+ Point ctrl_loc = PointToScreen (Point.Empty);
+
+ Size new_size = new Size (mouse_loc.X - ctrl_loc.X, mouse_loc.Y - ctrl_loc.Y);
+ if (new_size.Height < item_height)
+ new_size.Height = item_height;
+ if (new_size.Width < item_height)
+ new_size.Width = item_height;
+
+ Size = new_size;
+ return;
+ }
+
+ Cursor = resizer_bounds.Contains (args.Location) ? Cursors.SizeNWSE : Cursors.Default;
+
+ int item_idx = GetItemAt (args.Location);
+ if (item_idx != -1)
+ HighlightedIndex = item_idx;
+ }
+
+ protected override void OnMouseUp (MouseEventArgs args)
+ {
+ base.OnMouseUp (args);
+
+ int item_idx = GetItemAt (args.Location);
+ if (item_idx != -1 && !resizing)
+ HideListBox (true);
+
+ owner.OnAutoCompleteValueSelected (EventArgs.Empty); // internal
+ resizing = false;
+ Capture = false;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs args)
+ {
+ Graphics g = args.Graphics;
+ Brush brush = ThemeEngine.Current.ResPool.GetSolidBrush (ForeColor);
+
+ int highlighted_idx = HighlightedIndex;
+
+ int y = 0;
+ int last = GetLastVisibleItem ();
+ for (int i = top_item; i <= last; i++) {
+ Rectangle item_bounds = GetItemBounds (i);
+ if (!item_bounds.IntersectsWith (args.ClipRectangle))
+ continue;
+
+ if (i == highlighted_idx) {
+ g.FillRectangle (SystemBrushes.Highlight, item_bounds);
+ g.DrawString (owner.auto_complete_matches [i], Font, SystemBrushes.HighlightText, item_bounds);
+ } else
+ g.DrawString (owner.auto_complete_matches [i], Font, brush, item_bounds);
+
+ y += item_height;
+ }
+
+ ThemeEngine.Current.CPDrawSizeGrip (g, SystemColors.Control, resizer_bounds);
+ }
+ }
+ }
+
+ internal class TextBoxAutoCompleteSourceConverter : EnumConverter
+ {
+ public TextBoxAutoCompleteSourceConverter(Type type)
+ : base(type)
+ { }
+
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
+ {
+ StandardValuesCollection stdv = base.GetStandardValues(context);
+ AutoCompleteSource[] arr = new AutoCompleteSource[stdv.Count];
+ stdv.CopyTo(arr, 0);
+ AutoCompleteSource[] arr2 = Array.FindAll(arr, delegate (AutoCompleteSource value) {
+ // No "ListItems" in a TextBox.
+ return value != AutoCompleteSource.ListItems;
+ });
+ return new StandardValuesCollection(arr2);
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/TextBoxBase.cs b/source/ShiftUI/Widgets/TextBoxBase.cs
new file mode 100644
index 0000000..aca2dee
--- /dev/null
+++ b/source/ShiftUI/Widgets/TextBoxBase.cs
@@ -0,0 +1,2493 @@
+// 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:c
+//
+// 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-2006 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+//
+
+// NOT COMPLETE
+
+
+#undef Debug
+#undef DebugClick
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Text;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Collections;
+
+namespace ShiftUI
+{
+ [ComVisible (true)]
+ [DefaultBindingProperty ("Text")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultEvent("TextChanged")]
+ //[Designer("ShiftUI.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
+ public abstract class TextBoxBase : Widget
+ {
+ #region Local Variables
+ internal HorizontalAlignment alignment;
+ internal bool accepts_tab;
+ internal bool accepts_return;
+ internal bool auto_size;
+ internal bool backcolor_set;
+ internal CharacterCasing character_casing;
+ internal bool hide_selection;
+ int max_length;
+ internal bool modified;
+ internal char password_char;
+ internal bool read_only;
+ internal bool word_wrap;
+ internal Document document;
+ internal LineTag caret_tag; // tag our cursor is in
+ internal int caret_pos; // position on the line our cursor is in (can be 0 = beginning of line)
+ internal ImplicitHScrollBar hscroll;
+ internal ImplicitVScrollBar vscroll;
+ internal RichTextBoxScrollBars scrollbars;
+ internal Timer scroll_timer;
+ internal bool richtext;
+ internal bool show_selection; // set to true to always show selection, even if no focus is set
+ internal ArrayList list_links; // currently showing links
+ private LinkRectangle current_link; // currently hovering link
+ private bool enable_links; // whether links are enabled
+
+ internal bool has_been_focused;
+
+ internal int selection_length = -1; // set to the user-specified selection length, or -1 if none
+ internal bool show_caret_w_selection; // TextBox shows the caret when the selection is visible
+ internal int canvas_width;
+ internal int canvas_height;
+ static internal int track_width = 2; //
+ static internal int track_border = 5; //
+ internal DateTime click_last;
+ internal int click_point_x;
+ internal int click_point_y;
+ internal CaretSelection click_mode;
+ internal BorderStyle actual_border_style;
+ internal bool shortcuts_enabled = true;
+ #if Debug
+ internal static bool draw_lines = false;
+ #endif
+
+ #endregion // Local Variables
+
+ #region Internal Constructor
+ // Constructor will go when complete, only for testing - pdb
+ internal TextBoxBase ()
+ {
+ alignment = HorizontalAlignment.Left;
+ accepts_return = false;
+ accepts_tab = false;
+ auto_size = true;
+ InternalBorderStyle = BorderStyle.Fixed3D;
+ actual_border_style = BorderStyle.Fixed3D;
+ character_casing = CharacterCasing.Normal;
+ hide_selection = true;
+ max_length = short.MaxValue;
+ password_char = '\0';
+ read_only = false;
+ word_wrap = true;
+ richtext = false;
+ show_selection = false;
+ enable_links = false;
+ list_links = new ArrayList ();
+ current_link = null;
+ show_caret_w_selection = (this is TextBox);
+ document = new Document(this);
+ document.WidthChanged += new EventHandler(document_WidthChanged);
+ document.HeightChanged += new EventHandler(document_HeightChanged);
+ //document.CaretMoved += new EventHandler(CaretMoved);
+ document.Wrap = false;
+ click_last = DateTime.Now;
+ click_mode = CaretSelection.Position;
+
+ MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
+ MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
+ MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
+ SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
+ FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
+ ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
+ MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
+ RightToLeftChanged += new EventHandler (TextBoxBase_RightToLeftChanged);
+
+ scrollbars = RichTextBoxScrollBars.None;
+
+ hscroll = new ImplicitHScrollBar();
+ hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
+ hscroll.SetStyle (Widgetstyles.Selectable, false);
+ hscroll.Enabled = false;
+ hscroll.Visible = false;
+ hscroll.Maximum = Int32.MaxValue;
+
+ vscroll = new ImplicitVScrollBar();
+ vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
+ vscroll.SetStyle (Widgetstyles.Selectable, false);
+ vscroll.Enabled = false;
+ vscroll.Visible = false;
+ vscroll.Maximum = Int32.MaxValue;
+
+ SuspendLayout ();
+ this.Widgets.AddImplicit (hscroll);
+ this.Widgets.AddImplicit (vscroll);
+ ResumeLayout ();
+
+ SetStyle(Widgetstyles.UserPaint | Widgetstyles.StandardClick, false);
+ SetStyle(Widgetstyles.UseTextForAccessibility, false);
+
+ base.SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
+
+ canvas_width = ClientSize.Width;
+ canvas_height = ClientSize.Height;
+ document.ViewPortWidth = canvas_width;
+ document.ViewPortHeight = canvas_height;
+
+ Cursor = Cursors.IBeam;
+ }
+ #endregion // Internal Constructor
+
+ #region Private and Internal Methods
+ internal string CaseAdjust (string s)
+ {
+ if (character_casing == CharacterCasing.Normal)
+ return s;
+ if (character_casing == CharacterCasing.Lower)
+ return s.ToLower();
+ return s.ToUpper();
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ return new Size (Width, Height);
+ }
+
+ internal override void HandleClick (int clicks, MouseEventArgs me)
+ {
+ // MS seems to fire the click event in spite of the styles they set
+ bool click_set = GetStyle (Widgetstyles.StandardClick);
+ bool doubleclick_set = GetStyle (Widgetstyles.StandardDoubleClick);
+
+ // so explicitly set them to true first
+ SetStyle (Widgetstyles.StandardClick | Widgetstyles.StandardDoubleClick, true);
+
+ base.HandleClick (clicks, me);
+
+ // then revert to our previous state
+ if (!click_set)
+ SetStyle (Widgetstyles.StandardClick, false);
+ if (!doubleclick_set)
+ SetStyle (Widgetstyles.StandardDoubleClick, false);
+ }
+
+ internal override void PaintWidgetBackground (PaintEventArgs pevent)
+ {
+ if (!ThemeEngine.Current.TextBoxBaseShouldPaintBackground (this))
+ return;
+ base.PaintWidgetBackground (pevent);
+ }
+ #endregion // Private and Internal Methods
+
+ #region Public Instance Properties
+ [DefaultValue(false)]
+ [MWFCategory("Behavior")]
+ public bool AcceptsTab {
+ get {
+ return accepts_tab;
+ }
+
+ set {
+ if (value != accepts_tab) {
+ accepts_tab = value;
+ OnAcceptsTabChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [DefaultValue(true)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [MWFCategory("Behavior")]
+ public override bool AutoSize {
+ get {
+ return auto_size;
+ }
+
+ set {
+ if (value != auto_size) {
+ auto_size = value;
+ if (auto_size) {
+ if (PreferredHeight != Height) {
+ Height = PreferredHeight;
+ }
+ }
+ }
+ }
+ }
+
+ [DispId(-501)]
+ public override System.Drawing.Color BackColor {
+ get {
+ return base.BackColor;
+ }
+ set {
+ backcolor_set = true;
+ base.BackColor = ChangeBackColor (value);
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override System.Drawing.Image BackgroundImage {
+ get {
+ return base.BackgroundImage;
+ }
+ set {
+ base.BackgroundImage = value;
+ }
+ }
+
+ [DefaultValue(BorderStyle.Fixed3D)]
+ [DispId(-504)]
+ [MWFCategory("Appearance")]
+ public BorderStyle BorderStyle {
+ get { return actual_border_style; }
+ set {
+ if (value == actual_border_style)
+ return;
+
+ if (actual_border_style != BorderStyle.Fixed3D || value != BorderStyle.Fixed3D)
+ Invalidate ();
+
+ actual_border_style = value;
+ document.UpdateMargins ();
+
+ if (value != BorderStyle.Fixed3D)
+ value = BorderStyle.None;
+
+ InternalBorderStyle = value;
+ OnBorderStyleChanged(EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public bool CanUndo {
+ get {
+ return document.undo.CanUndo;
+ }
+ }
+
+ [DispId(-513)]
+ public override System.Drawing.Color ForeColor {
+ get {
+ return base.ForeColor;
+ }
+ set {
+ base.ForeColor = value;
+ }
+ }
+
+ [DefaultValue(true)]
+ [MWFCategory("Behavior")]
+ public bool HideSelection {
+ get {
+ return hide_selection;
+ }
+
+ set {
+ if (value != hide_selection) {
+ hide_selection = value;
+ OnHideSelectionChanged(EventArgs.Empty);
+ }
+ document.selection_visible = !hide_selection;
+ document.InvalidateSelectionArea();
+ }
+ }
+
+ [MergableProperty (false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ //[Editor("ShiftUI.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Localizable(true)]
+ [MWFCategory("Appearance")]
+ public string[] Lines {
+ get {
+ int count;
+ ArrayList lines;
+
+ count = document.Lines;
+
+ // Handle empty document
+ if ((count == 1) && (document.GetLine (1).text.Length == 0)) {
+ return new string [0];
+ }
+
+ lines = new ArrayList ();
+
+ int i = 1;
+ while (i <= count) {
+ Line line;
+ StringBuilder lt = new StringBuilder ();
+
+ do {
+ line = document.GetLine (i++);
+ lt.Append (line.TextWithoutEnding ());
+ } while (line.ending == LineEnding.Wrap && i <= count);
+
+ lines.Add (lt.ToString ());
+ }
+
+ return (string []) lines.ToArray (typeof (string));
+ }
+
+ set {
+ StringBuilder sb = new StringBuilder ();
+
+ for (int i = 0; i < value.Length; i++) {
+ // Don't add the last line if it is just an empty line feed
+ // the line feed is reflected in the previous line's ending
+ if (i == value.Length - 1 && value[i].Length == 0)
+ break;
+
+ sb.Append (value[i] + Environment.NewLine);
+ }
+
+ int newline_length = Environment.NewLine.Length;
+
+ // We want to remove the final new line character
+ if (sb.Length >= newline_length)
+ sb.Remove (sb.Length - newline_length, newline_length);
+
+ Text = sb.ToString ();
+ }
+ }
+
+ [DefaultValue(32767)]
+ [Localizable(true)]
+ [MWFCategory("Behavior")]
+ public virtual int MaxLength {
+ get {
+ if (max_length == (int.MaxValue - 1)) { // We don't distinguish between single and multi-line limits
+ return 0;
+ }
+ return max_length;
+ }
+
+ set {
+ if (value != max_length) {
+ if (value == 0)
+ value = int.MaxValue - 1;
+
+ max_length = value;
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public bool Modified {
+ get {
+ return modified;
+ }
+
+ set {
+ if (value != modified) {
+ modified = value;
+ OnModifiedChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue(false)]
+ [Localizable(true)]
+ [RefreshProperties(RefreshProperties.All)]
+ [MWFCategory("Behavior")]
+ public virtual bool Multiline {
+ get {
+ return document.multiline;
+ }
+
+ set {
+ if (value != document.multiline) {
+ document.multiline = value;
+
+ if (this is TextBox)
+ SetStyle (Widgetstyles.FixedHeight, !value);
+
+ // SetBoundsCore overrides the Height for multiline if it needs to,
+ // so we don't need to worry about it here.
+ SetBoundsCore (Left, Top, Width, ExplicitBounds.Height, BoundsSpecified.None);
+
+ if (Parent != null)
+ Parent.PerformLayout ();
+
+ OnMultilineChanged(EventArgs.Empty);
+ }
+
+ if (document.multiline) {
+ document.Wrap = word_wrap;
+ document.PasswordChar = "";
+
+ } else {
+ document.Wrap = false;
+ if (this.password_char != '\0') {
+ if (this is TextBox)
+ document.PasswordChar = (this as TextBox).PasswordChar.ToString ();
+ } else {
+ document.PasswordChar = "";
+ }
+ }
+
+ if (IsHandleCreated)
+ CalculateDocument ();
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable(EditorBrowsableState.Advanced)]
+ // This returns the preferred outer height, not the client height.
+ public int PreferredHeight {
+ get {
+ int clientDelta = Height - ClientSize.Height;
+ if (BorderStyle != BorderStyle.None)
+ return Font.Height + 7 + clientDelta;
+
+ // usually in borderless mode the top margin is 0, but
+ // try to access it, in case it was set manually, as ToolStrip* Widgets do
+ return Font.Height + TopMargin + clientDelta;
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [DefaultValue(false)]
+ [MWFCategory("Behavior")]
+ public bool ReadOnly {
+ get {
+ return read_only;
+ }
+
+ set {
+ if (value != read_only) {
+ read_only = value;
+ if (!backcolor_set) {
+ if (read_only)
+ background_color = SystemColors.Control;
+ else
+ background_color = SystemColors.Window;
+ }
+ OnReadOnlyChanged(EventArgs.Empty);
+ Invalidate ();
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public virtual string SelectedText {
+ get {
+ string retval = document.GetSelection ();
+
+ return retval;
+ }
+
+ set {
+ if (value == null)
+ value = String.Empty;
+
+ document.ReplaceSelection(CaseAdjust(value), false);
+
+ ScrollToCaret();
+ OnTextChanged(EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public virtual int SelectionLength {
+ get {
+ int res = document.SelectionLength ();
+
+ return res;
+ }
+
+ set {
+ if (value < 0) {
+ string msg = String.Format ("'{0}' is not a valid value for 'SelectionLength'", value);
+ throw new ArgumentOutOfRangeException ("SelectionLength", msg);
+ }
+
+ document.InvalidateSelectionArea ();
+ if (value != 0) {
+ int start;
+ Line line;
+ LineTag tag;
+ int pos;
+
+ selection_length = value;
+ start = document.LineTagToCharIndex (document.selection_start.line, document.selection_start.pos);
+ document.CharIndexToLineTag (start + value, out line, out tag, out pos);
+ document.SetSelectionEnd (line, pos, true);
+ document.PositionCaret (line, pos);
+ } else {
+ selection_length = -1;
+ document.SetSelectionEnd (document.selection_start.line, document.selection_start.pos, true);
+ document.PositionCaret (document.selection_start.line, document.selection_start.pos);
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int SelectionStart {
+ get {
+ return document.LineTagToCharIndex(document.selection_start.line,
+ document.selection_start.pos);
+ }
+
+ set {
+ if (value < 0) {
+ string msg = String.Format ("'{0}' is not a valid value for 'SelectionStart'", value);
+ throw new ArgumentOutOfRangeException ("SelectionStart", msg);
+ }
+
+ // If SelectionStart has been used, we don't highlight on focus
+ has_been_focused = true;
+
+ document.InvalidateSelectionArea ();
+ document.SetSelectionStart (value, false);
+ if (selection_length > -1)
+ document.SetSelectionEnd (value + selection_length, true);
+ else
+ document.SetSelectionEnd (value, true);
+ document.PositionCaret (document.selection_start.line, document.selection_start.pos);
+ ScrollToCaret ();
+ }
+ }
+
+ [DefaultValue (true)]
+ public virtual bool ShortcutsEnabled {
+ get { return shortcuts_enabled; }
+ set { shortcuts_enabled = value; }
+ }
+
+ //[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
+ //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+ [Localizable(true)]
+ public override string Text {
+ get {
+ if (document == null || document.Root == null || document.Root.text == null)
+ return string.Empty;
+
+ StringBuilder sb = new StringBuilder();
+
+ Line line = null;
+ for (int i = 1; i <= document.Lines; i++) {
+ line = document.GetLine (i);
+ sb.Append(line.text.ToString ());
+ }
+
+ return sb.ToString();
+ }
+
+ set {
+ // reset to force a select all next time the box gets focus
+ has_been_focused = false;
+
+ if (value == Text)
+ return;
+
+ document.Empty ();
+ if ((value != null) && (value != "")) {
+ document.Insert (document.GetLine (1), 0, false, value);
+ } else {
+ if (IsHandleCreated) {
+ document.SetSelectionToCaret (true);
+ CalculateDocument ();
+ }
+ }
+
+ document.PositionCaret (document.GetLine (1), 0);
+ document.SetSelectionToCaret (true);
+
+ ScrollToCaret ();
+
+ OnTextChanged(EventArgs.Empty);
+ }
+ }
+
+ [Browsable(false)]
+ public virtual int TextLength {
+ get {
+ if (document == null || document.Root == null || document.Root.text == null)
+ return 0;
+ return Text.Length;
+ }
+ }
+
+ [DefaultValue(true)]
+ [Localizable(true)]
+ [MWFCategory("Behavior")]
+ public bool WordWrap {
+ get {
+ return word_wrap;
+ }
+
+ set {
+ if (value != word_wrap) {
+ if (document.multiline) {
+ word_wrap = value;
+ document.Wrap = value;
+ }
+ CalculateDocument ();
+ }
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ protected override Cursor DefaultCursor {
+ get { return Cursors.IBeam; }
+ }
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+ protected override bool CanEnableIme {
+ get {
+ if (ReadOnly || password_char != '\0')
+ return false;
+
+ return true;
+ }
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override System.Drawing.Size DefaultSize {
+ get {
+ return new Size(100, 20);
+ }
+ }
+
+ // Currently our double buffering breaks our scrolling, so don't let people enable this
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get { return false; }
+ set { }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public void AppendText (string text)
+ {
+ // Save some cycles and only check the Text if we are one line
+ bool is_empty = document.Lines == 1 && Text == String.Empty;
+
+ // make sure the caret begins at the end
+ if (document.caret.line.line_no != document.Lines ||
+ (document.caret.pos) != document.caret.line.TextLengthWithoutEnding ()) {
+ document.MoveCaret (CaretDirection.CtrlEnd);
+ }
+ document.Insert (document.caret.line, document.caret.pos, false, text, document.CaretTag);
+ document.MoveCaret (CaretDirection.CtrlEnd);
+ document.SetSelectionToCaret (true);
+
+ if (!is_empty)
+ ScrollToCaret ();
+
+ //
+ // Avoid the initial focus selecting all when append text is used
+ //
+ has_been_focused = true;
+
+ Modified = false;
+ OnTextChanged(EventArgs.Empty);
+ }
+
+ public void Clear ()
+ {
+ Modified = false;
+ Text = string.Empty;
+ }
+
+ public void ClearUndo ()
+ {
+ document.undo.Clear();
+ }
+
+ public void Copy ()
+ {
+ DataObject o;
+
+ o = new DataObject(DataFormats.Text, SelectedText);
+ if (this is RichTextBox)
+ o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
+ Clipboard.SetDataObject(o);
+ }
+
+ public void Cut ()
+ {
+ DataObject o;
+
+ o = new DataObject(DataFormats.Text, SelectedText);
+ if (this is RichTextBox)
+ o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
+ Clipboard.SetDataObject (o);
+
+ document.undo.BeginUserAction (String.Format ("Cut"));
+ document.ReplaceSelection (String.Empty, false);
+ document.undo.EndUserAction ();
+
+ Modified = true;
+ OnTextChanged (EventArgs.Empty);
+ }
+
+ public void Paste ()
+ {
+ Paste (Clipboard.GetDataObject(), null, false);
+ }
+
+ public void ScrollToCaret ()
+ {
+ if (IsHandleCreated)
+ CaretMoved (this, EventArgs.Empty);
+ }
+
+ public void Select(int start, int length)
+ {
+ SelectionStart = start;
+ SelectionLength = length;
+ }
+
+ public void SelectAll ()
+ {
+ Line last;
+
+ last = document.GetLine(document.Lines);
+ document.SetSelectionStart(document.GetLine(1), 0, false);
+ document.SetSelectionEnd(last, last.text.Length, true);
+ document.PositionCaret (document.selection_end.line, document.selection_end.pos);
+ selection_length = -1;
+
+ CaretMoved (this, null);
+
+ document.DisplayCaret ();
+ }
+
+ internal void SelectAllNoScroll ()
+ {
+ Line last;
+
+ last = document.GetLine(document.Lines);
+ document.SetSelectionStart(document.GetLine(1), 0, false);
+ document.SetSelectionEnd(last, last.text.Length, false);
+ document.PositionCaret (document.selection_end.line, document.selection_end.pos);
+ selection_length = -1;
+
+ document.DisplayCaret ();
+ }
+
+ public override string ToString ()
+ {
+ return String.Concat (base.ToString (), ", Text: ", Text);
+ }
+
+ [MonoInternalNote ("Deleting is classed as Typing, instead of its own Undo event")]
+ public void Undo ()
+ {
+ if (document.undo.Undo ()) {
+ Modified = true;
+ OnTextChanged (EventArgs.Empty);
+ }
+ }
+
+ public void DeselectAll ()
+ {
+ SelectionLength = 0;
+ }
+
+ public virtual char GetCharFromPosition (Point pt)
+ {
+ return GetCharFromPositionInternal (pt);
+ }
+
+ internal virtual char GetCharFromPositionInternal (Point p)
+ {
+ int index;
+ LineTag tag = document.FindCursor (p.X, p.Y, out index);
+ if (tag == null)
+ return (char) 0; // Shouldn't happen
+
+ if (index >= tag.Line.text.Length) {
+
+ if (tag.Line.ending == LineEnding.Wrap) {
+ // If we have wrapped text, we return the first char of the next line
+ Line line = document.GetLine (tag.Line.line_no + 1);
+ if (line != null)
+ return line.text [0];
+
+ }
+
+ if (tag.Line.line_no == document.Lines) {
+ // Last line returns the last char
+ return tag.Line.text [tag.Line.text.Length - 1];
+ }
+
+ // This really shouldn't happen
+ return (char) 0;
+ }
+ return tag.Line.text [index];
+ }
+
+ public virtual int GetCharIndexFromPosition (Point pt)
+ {
+ int line_index;
+ LineTag tag = document.FindCursor (pt.X, pt.Y, out line_index);
+ if (tag == null)
+ return 0;
+
+ if (line_index >= tag.Line.text.Length) {
+
+ if (tag.Line.ending == LineEnding.Wrap) {
+ // If we have wrapped text, we return the first char of the next line
+ Line line = document.GetLine (tag.Line.line_no + 1);
+ if (line != null)
+ return document.LineTagToCharIndex (line, 0);
+ }
+
+ if (tag.Line.line_no == document.Lines) {
+ // Last line returns the last char
+ return document.LineTagToCharIndex (tag.Line, tag.Line.text.Length - 1);
+ }
+
+ return 0;
+ }
+
+ return document.LineTagToCharIndex (tag.Line, line_index);
+ }
+
+ public virtual Point GetPositionFromCharIndex (int index)
+ {
+ int pos;
+ Line line;
+ LineTag tag;
+
+ document.CharIndexToLineTag (index, out line, out tag, out pos);
+
+ return new Point ((int) (line.widths [pos] +
+ line.X + document.viewport_x),
+ line.Y + document.viewport_y + tag.Shift);
+ }
+
+ public int GetFirstCharIndexFromLine (int lineNumber)
+ {
+ Line line = document.GetLine (lineNumber + 1);
+ if (line == null)
+ return -1;
+
+ return document.LineTagToCharIndex (line, 0);
+ }
+
+ public int GetFirstCharIndexOfCurrentLine ()
+ {
+ return document.LineTagToCharIndex (document.caret.line, 0);
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override void CreateHandle ()
+ {
+ CalculateDocument ();
+ base.CreateHandle ();
+ document.AlignCaret();
+ ScrollToCaret();
+ }
+
+ internal virtual void HandleLinkClicked (LinkRectangle link_clicked)
+ {
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ if ((keyData & Keys.Alt) != 0)
+ return base.IsInputKey(keyData);
+
+ switch (keyData & Keys.KeyCode) {
+ case Keys.Enter: {
+ return (accepts_return && document.multiline);
+ }
+
+ case Keys.Tab: {
+ if (accepts_tab && document.multiline)
+ if ((keyData & Keys.Widget) == 0)
+ return true;
+ return false;
+ }
+
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.PageUp:
+ case Keys.PageDown:
+ case Keys.Home:
+ case Keys.End: {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected virtual void OnAcceptsTabChanged(EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [AcceptsTabChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnBorderStyleChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [BorderStyleChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+
+ if (auto_size && !document.multiline) {
+ if (PreferredHeight != Height) {
+ Height = PreferredHeight;
+ }
+ }
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+ FixupHeight ();
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnHideSelectionChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [HideSelectionChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnModifiedChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ModifiedChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnMultilineChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [MultilineChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnPaddingChanged (EventArgs e)
+ {
+ base.OnPaddingChanged (e);
+ }
+
+ protected virtual void OnReadOnlyChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ReadOnlyChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
+ {
+ return base.ProcessCmdKey (ref msg, keyData);
+ }
+ protected override bool ProcessDialogKey (Keys keyData)
+ {
+ // The user can use Ctrl-Tab or Ctrl-Shift-Tab to move Widget focus
+ // instead of inserting a Tab. However, the focus-moving-tab-stuffs
+ // doesn't work if Ctrl is pushed, so we remove it before sending it.
+ if (accepts_tab && (keyData & (Keys.Widget | Keys.Tab)) == (Keys.Widget | Keys.Tab))
+ keyData ^= Keys.Widget;
+
+ return base.ProcessDialogKey(keyData);
+ }
+
+ private bool ProcessKey (Keys keyData)
+ {
+ bool control;
+ bool shift;
+
+ control = (Widget.ModifierKeys & Keys.Widget) != 0;
+ shift = (Widget.ModifierKeys & Keys.Shift) != 0;
+
+ if (shortcuts_enabled) {
+ switch (keyData & Keys.KeyCode) {
+ case Keys.X:
+ if (control && read_only == false) {
+ Cut();
+ return true;
+ }
+ return false;
+
+ case Keys.C:
+ if (control) {
+ Copy();
+ return true;
+ }
+ return false;
+
+ case Keys.V:
+ if (control && read_only == false) {
+ return Paste(Clipboard.GetDataObject(), null, true);
+ }
+ return false;
+
+ case Keys.Z:
+ if (control && read_only == false) {
+ Undo ();
+ return true;
+ }
+ return false;
+
+ case Keys.A:
+ if (control) {
+ SelectAll();
+ return true;
+ }
+ return false;
+
+ case Keys.Insert:
+
+ if (read_only == false) {
+ if (shift) {
+ Paste (Clipboard.GetDataObject (), null, true);
+ return true;
+ }
+
+ if (control) {
+ Copy ();
+ return true;
+ }
+ }
+
+ return false;
+
+ case Keys.Delete:
+
+ if (read_only)
+ break;
+
+ if (shift && read_only == false) {
+ Cut ();
+ return true;
+ }
+
+ if (document.selection_visible) {
+ document.ReplaceSelection("", false);
+ } else {
+ // DeleteChar only deletes on the line, doesn't do the combine
+ if (document.CaretPosition >= document.CaretLine.TextLengthWithoutEnding ()) {
+ if (document.CaretLine.LineNo < document.Lines) {
+ Line line;
+
+ line = document.GetLine(document.CaretLine.LineNo + 1);
+
+ // this line needs to be invalidated before it is combined
+ // because once it is combined, all it's coordinates will
+ // have changed
+ document.Invalidate (line, 0, line, line.text.Length);
+ document.Combine(document.CaretLine, line);
+
+ document.UpdateView(document.CaretLine,
+ document.Lines, 0);
+
+ }
+ } else {
+ if (!control) {
+ document.DeleteChar(document.CaretTag.Line, document.CaretPosition, true);
+ } else {
+ int end_pos;
+
+ end_pos = document.CaretPosition;
+
+ while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
+ end_pos++;
+ }
+
+ if (end_pos < document.CaretLine.Text.Length) {
+ end_pos++;
+ }
+ document.DeleteChars(document.CaretTag.Line, document.CaretPosition, end_pos - document.CaretPosition);
+ }
+ }
+ }
+
+ document.AlignCaret();
+ document.UpdateCaret();
+ CaretMoved(this, null);
+
+ Modified = true;
+ OnTextChanged (EventArgs.Empty);
+
+ return true;
+ }
+ }
+
+ switch (keyData & Keys.KeyCode) {
+ case Keys.Left: {
+ if (control) {
+ document.MoveCaret(CaretDirection.WordBack);
+ } else {
+ if (!document.selection_visible || shift) {
+ document.MoveCaret(CaretDirection.CharBack);
+ } else {
+ document.MoveCaret(CaretDirection.SelectionStart);
+ }
+ }
+
+ if (!shift) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ case Keys.Right: {
+ if (control) {
+ document.MoveCaret(CaretDirection.WordForward);
+ } else {
+ if (!document.selection_visible || shift) {
+ document.MoveCaret(CaretDirection.CharForward);
+ } else {
+ document.MoveCaret(CaretDirection.SelectionEnd);
+ }
+ }
+ if (!shift) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ case Keys.Up: {
+ if (control) {
+ if (document.CaretPosition == 0) {
+ document.MoveCaret(CaretDirection.LineUp);
+ } else {
+ document.MoveCaret(CaretDirection.Home);
+ }
+ } else {
+ document.MoveCaret(CaretDirection.LineUp);
+ }
+
+ if ((Widget.ModifierKeys & Keys.Shift) == 0) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ case Keys.Down: {
+ if (control) {
+ if (document.CaretPosition == document.CaretLine.Text.Length) {
+ document.MoveCaret(CaretDirection.LineDown);
+ } else {
+ document.MoveCaret(CaretDirection.End);
+ }
+ } else {
+ document.MoveCaret(CaretDirection.LineDown);
+ }
+
+ if ((Widget.ModifierKeys & Keys.Shift) == 0) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ case Keys.Home: {
+ if ((Widget.ModifierKeys & Keys.Widget) != 0) {
+ document.MoveCaret(CaretDirection.CtrlHome);
+ } else {
+ document.MoveCaret(CaretDirection.Home);
+ }
+
+ if ((Widget.ModifierKeys & Keys.Shift) == 0) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ case Keys.End: {
+ if ((Widget.ModifierKeys & Keys.Widget) != 0) {
+ document.MoveCaret(CaretDirection.CtrlEnd);
+ } else {
+ document.MoveCaret(CaretDirection.End);
+ }
+
+ if ((Widget.ModifierKeys & Keys.Shift) == 0) {
+ document.SetSelectionToCaret(true);
+ } else {
+ document.SetSelectionToCaret(false);
+ }
+
+ CaretMoved(this, null);
+ return true;
+ }
+
+ //case Keys.Enter: {
+ // // ignoring accepts_return, fixes bug #76355
+ // if (!read_only && document.multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Widget.ModifierKeys & Keys.Widget) != 0))) {
+ // Line line;
+
+ // if (document.selection_visible) {
+ // document.ReplaceSelection("\n", false);
+ // }
+
+ // line = document.CaretLine;
+
+ // document.Split (document.CaretLine, document.CaretTag, document.CaretPosition);
+ // line.ending = LineEnding.Rich;
+ // document.InsertString (line, line.text.Length,
+ // document.LineEndingToString (line.ending));
+ // OnTextChanged(EventArgs.Empty);
+
+ // document.UpdateView (line, document.Lines - line.line_no, 0);
+ // CaretMoved(this, null);
+ // return true;
+ // }
+ // break;
+ //}
+
+ case Keys.Tab: {
+ if (!read_only && accepts_tab && document.multiline) {
+ document.InsertCharAtCaret ('\t', true);
+
+ CaretMoved(this, null);
+ Modified = true;
+ OnTextChanged (EventArgs.Empty);
+
+ return true;
+ }
+ break;
+ }
+
+ case Keys.PageUp: {
+ if ((Widget.ModifierKeys & Keys.Widget) != 0) {
+ document.MoveCaret(CaretDirection.CtrlPgUp);
+ } else {
+ document.MoveCaret(CaretDirection.PgUp);
+ }
+ document.DisplayCaret ();
+ return true;
+ }
+
+ case Keys.PageDown: {
+ if ((Widget.ModifierKeys & Keys.Widget) != 0) {
+ document.MoveCaret(CaretDirection.CtrlPgDn);
+ } else {
+ document.MoveCaret(CaretDirection.PgDn);
+ }
+ document.DisplayCaret ();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ internal virtual void RaiseSelectionChanged ()
+ {
+ // Do nothing, overridden in RTB
+ }
+
+ private void HandleBackspace (bool Widget)
+ {
+ bool fire_changed;
+
+ fire_changed = false;
+
+ // delete only deletes on the line, doesn't do the combine
+ if (document.selection_visible) {
+ document.undo.BeginUserAction (String.Format ("Delete"));
+ document.ReplaceSelection("", false);
+ document.undo.EndUserAction ();
+ fire_changed = true;
+ document.SetSelectionToCaret (true);
+ } else {
+ document.SetSelectionToCaret (true);
+
+ if (document.CaretPosition == 0) {
+ if (document.CaretLine.LineNo > 1) {
+ Line line;
+ int new_caret_pos;
+
+ line = document.GetLine(document.CaretLine.LineNo - 1);
+ new_caret_pos = line.TextLengthWithoutEnding ();
+
+ // Invalidate the old line position before we do the combine
+ document.Invalidate (line, 0, line, line.text.Length);
+ document.Combine(line, document.CaretLine);
+
+ document.UpdateView(line, document.Lines - line.LineNo, 0);
+ document.PositionCaret(line, new_caret_pos);
+ document.SetSelectionToCaret (true);
+ document.UpdateCaret();
+ fire_changed = true;
+ }
+ } else {
+ if (!Widget || document.CaretPosition == 0) {
+
+ // Move before we delete because the delete will change positions around
+ // if we cross a wrap border
+ LineTag tag = document.CaretTag;
+ int pos = document.CaretPosition;
+ document.MoveCaret (CaretDirection.CharBack);
+ document.DeleteChar (tag.Line, pos, false);
+ document.SetSelectionToCaret (true);
+ } else {
+ int start_pos;
+
+
+ start_pos = document.CaretPosition - 1;
+ while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
+ start_pos--;
+ }
+
+ document.undo.BeginUserAction (String.Format ("Delete"));
+ document.DeleteChars(document.CaretTag.Line, start_pos, document.CaretPosition - start_pos);
+ document.undo.EndUserAction ();
+ document.PositionCaret(document.CaretLine, start_pos);
+ document.SetSelectionToCaret (true);
+ }
+ document.UpdateCaret();
+ fire_changed = true;
+ }
+ }
+
+ CaretMoved (this, null);
+
+ if (fire_changed) {
+ Modified = true;
+ OnTextChanged(EventArgs.Empty);
+ }
+ }
+
+ private void HandleEnter ()
+ {
+ // ignoring accepts_return, fixes bug #76355
+ if (!read_only && document.multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Widget.ModifierKeys & Keys.Widget) != 0))) {
+ Line line;
+
+ if (document.selection_visible)
+ document.ReplaceSelection ("", false);
+
+ line = document.CaretLine;
+
+ document.Split (document.CaretLine, document.CaretTag, document.CaretPosition);
+ line.ending = document.StringToLineEnding (Environment.NewLine);
+ document.InsertString (line, line.text.Length, document.LineEndingToString (line.ending));
+
+ document.UpdateView (line, document.Lines - line.line_no, 0);
+ CaretMoved (this, null);
+ Modified = true;
+ OnTextChanged (EventArgs.Empty);
+ }
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ // Make sure we don't get sized bigger than we want to be
+
+ if (!richtext) {
+ if (!document.multiline) {
+ if (height != PreferredHeight) {
+ // If the specified has Height, we need to store that in the
+ // ExplicitBounds because we are going to override it
+ if ((specified & BoundsSpecified.Height) != 0) {
+ Rectangle r = ExplicitBounds;
+ r.Height = height;
+ ExplicitBounds = r;
+ specified &= ~BoundsSpecified.Height;
+ }
+
+ height = PreferredHeight;
+ }
+ }
+ }
+
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ switch ((Msg)m.Msg) {
+ case Msg.WM_KEYDOWN: {
+ if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
+ m.Result = IntPtr.Zero;
+ return;
+ }
+ DefWndProc (ref m);
+ return;
+ }
+
+ case Msg.WM_CHAR: {
+ int ch;
+
+ if (ProcessKeyMessage(ref m)) {
+ m.Result = IntPtr.Zero;
+ return;
+ }
+
+ if (read_only) {
+ return;
+ }
+
+ m.Result = IntPtr.Zero;
+
+ ch = m.WParam.ToInt32();
+
+ if (ch == 127) {
+ HandleBackspace(true);
+ } else if (ch >= 32) {
+ if (document.selection_visible) {
+ document.ReplaceSelection("", false);
+ }
+
+ char c = (char)m.WParam;
+ switch (character_casing) {
+ case CharacterCasing.Upper:
+ c = Char.ToUpper((char) m.WParam);
+ break;
+ case CharacterCasing.Lower:
+ c = Char.ToLower((char) m.WParam);
+ break;
+ }
+
+ if (document.Length < max_length) {
+ document.InsertCharAtCaret(c, true);
+ OnTextUpdate ();
+ CaretMoved (this, null);
+ Modified = true;
+ OnTextChanged(EventArgs.Empty);
+
+ } else {
+ XplatUI.AudibleAlert(AlertType.Default);
+ }
+ return;
+ } else if (ch == 8) {
+ HandleBackspace(false);
+ } else if (ch == 13)
+ HandleEnter ();
+
+ return;
+ }
+
+ case Msg.WM_SETFOCUS:
+ base.WndProc(ref m);
+ document.CaretHasFocus ();
+ break;
+
+ case Msg.WM_KILLFOCUS:
+ base.WndProc(ref m);
+ document.CaretLostFocus ();
+ break;
+
+ case Msg.WM_NCPAINT:
+ if (!ThemeEngine.Current.TextBoxBaseHandleWmNcPaint (this, ref m))
+ base.WndProc(ref m);
+ break;
+
+ default:
+ base.WndProc(ref m);
+ return;
+ }
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region Events
+ static object AcceptsTabChangedEvent = new object ();
+ static object AutoSizeChangedEvent = new object ();
+ static object BorderStyleChangedEvent = new object ();
+ static object HideSelectionChangedEvent = new object ();
+ static object ModifiedChangedEvent = new object ();
+ static object MultilineChangedEvent = new object ();
+ static object ReadOnlyChangedEvent = new object ();
+ static object HScrolledEvent = new object ();
+ static object VScrolledEvent = new object ();
+
+ public event EventHandler AcceptsTabChanged {
+ add { Events.AddHandler (AcceptsTabChangedEvent, value); }
+ remove { Events.RemoveHandler (AcceptsTabChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler AutoSizeChanged {
+ add { Events.AddHandler (AutoSizeChangedEvent, value); }
+ remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
+ }
+
+ public event EventHandler BorderStyleChanged {
+ add { Events.AddHandler (BorderStyleChangedEvent, value); }
+ remove { Events.RemoveHandler (BorderStyleChangedEvent, value); }
+ }
+
+ public event EventHandler HideSelectionChanged {
+ add { Events.AddHandler (HideSelectionChangedEvent, value); }
+ remove { Events.RemoveHandler (HideSelectionChangedEvent, value); }
+ }
+
+ public event EventHandler ModifiedChanged {
+ add { Events.AddHandler (ModifiedChangedEvent, value); }
+ remove { Events.RemoveHandler (ModifiedChangedEvent, value); }
+ }
+
+ public event EventHandler MultilineChanged {
+ add { Events.AddHandler (MultilineChangedEvent, value); }
+ remove { Events.RemoveHandler (MultilineChangedEvent, value); }
+ }
+
+ public event EventHandler ReadOnlyChanged {
+ add { Events.AddHandler (ReadOnlyChangedEvent, value); }
+ remove { Events.RemoveHandler (ReadOnlyChangedEvent, value); }
+ }
+
+ internal event EventHandler HScrolled {
+ add { Events.AddHandler (HScrolledEvent, value); }
+ remove { Events.RemoveHandler (HScrolledEvent, value); }
+ }
+
+ internal event EventHandler VScrolled {
+ add { Events.AddHandler (VScrolledEvent, value); }
+ remove { Events.RemoveHandler (VScrolledEvent, value); }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ // XXX should this not manipulate base.Paint?
+#pragma warning disable 0067
+ [MonoTODO]
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint;
+#pragma warning restore 0067
+
+ #endregion // Events
+
+ #region Private Methods
+ internal Document Document {
+ get {
+ return document;
+ }
+
+ set {
+ document = value;
+ }
+ }
+
+ internal bool EnableLinks {
+ get { return enable_links; }
+ set {
+ enable_links = value;
+
+ document.EnableLinks = value;
+ }
+ }
+
+ internal override bool ScaleChildrenInternal {
+ get { return false; }
+ }
+
+ internal bool ShowSelection {
+ get {
+ if (show_selection || !hide_selection) {
+ return true;
+ }
+
+ return has_focus;
+ }
+
+ set {
+ if (show_selection == value)
+ return;
+
+ show_selection = value;
+ // Currently InvalidateSelectionArea is commented out so do a full invalidate
+ document.InvalidateSelectionArea();
+ }
+ }
+
+ internal int TopMargin {
+ get {
+ return document.top_margin;
+ }
+ set {
+ document.top_margin = value;
+ }
+ }
+
+ #region UIA Framework Properties
+
+ internal ScrollBar UIAHScrollBar {
+ get { return hscroll; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return vscroll; }
+ }
+
+ #endregion UIA Framework Properties
+
+ internal Graphics CreateGraphicsInternal ()
+ {
+ if (IsHandleCreated)
+ return base.CreateGraphics();
+
+ return DeviceContext;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ Draw (pevent.Graphics, pevent.ClipRectangle);
+
+ //
+ // OnPaint does not get raised on MS (see bug #80639)
+ //
+ pevent.Handled = true;
+ }
+
+ internal void Draw (Graphics g, Rectangle clippingArea)
+ {
+ ThemeEngine.Current.TextBoxBaseFillBackground (this, g, clippingArea);
+
+ // Draw the viewable document
+ document.Draw(g, clippingArea);
+ }
+
+ private void FixupHeight ()
+ {
+ if (!richtext) {
+ if (!document.multiline) {
+ if (PreferredHeight != Height) {
+ Height = PreferredHeight;
+ }
+ }
+ }
+ }
+
+ private bool IsDoubleClick (MouseEventArgs e)
+ {
+ TimeSpan interval = DateTime.Now - click_last;
+ if (interval.TotalMilliseconds > SystemInformation.DoubleClickTime)
+ return false;
+ Size dcs = SystemInformation.DoubleClickSize;
+ if (e.X < click_point_x - dcs.Width / 2 || e.X > click_point_x + dcs.Width / 2)
+ return false;
+ if (e.Y < click_point_y - dcs.Height / 2 || e.Y > click_point_y + dcs.Height / 2)
+ return false;
+ return true;
+ }
+
+ private void TextBoxBase_MouseDown (object sender, MouseEventArgs e)
+ {
+ bool dbliclick = false;
+
+ if (e.Button == MouseButtons.Left) {
+
+ // Special case when shift key is pressed and
+ // left mouse is clicked.. set selection from
+ // current cursor to mouse
+ if ((Widget.ModifierKeys & Keys.Shift) > 0) {
+ document.PositionCaret (e.X + document.ViewPortX, e.Y + document.ViewPortY);
+ document.SetSelectionToCaret (false);
+ document.DisplayCaret ();
+ return;
+ }
+
+ dbliclick = IsDoubleClick (e);
+
+ if (current_link != null) {
+ HandleLinkClicked (current_link);
+ return;
+ }
+
+ //ensure nothing is selected anymore BEFORE we
+ //position the caret, so the caret is recreated
+ //(caret is only visible when nothing is selected)
+ if (document.selection_visible && dbliclick == false) {
+ document.SetSelectionToCaret (true);
+ click_mode = CaretSelection.Position;
+ }
+
+ document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
+
+ if (dbliclick) {
+ switch (click_mode) {
+ case CaretSelection.Position:
+ SelectWord ();
+ click_mode = CaretSelection.Word;
+ break;
+ case CaretSelection.Word:
+
+ if (this is TextBox) {
+ document.SetSelectionToCaret (true);
+ click_mode = CaretSelection.Position;
+ break;
+ }
+
+ document.ExpandSelection (CaretSelection.Line, false);
+ click_mode = CaretSelection.Line;
+ break;
+ case CaretSelection.Line:
+
+ // Gotta do this first because Exanding to a word
+ // from a line doesn't really work
+ document.SetSelectionToCaret (true);
+
+ SelectWord ();
+ click_mode = CaretSelection.Word;
+ break;
+ }
+ } else {
+ document.SetSelectionToCaret (true);
+ click_mode = CaretSelection.Position;
+ }
+
+ click_point_x = e.X;
+ click_point_y = e.Y;
+ click_last = DateTime.Now;
+ }
+
+ if ((e.Button == MouseButtons.Middle) && XplatUI.RunningOnUnix) {
+ Document.Marker marker;
+
+ marker.tag = document.FindCursor(e.X + document.ViewPortX, e.Y + document.ViewPortY, out marker.pos);
+ marker.line = marker.tag.Line;
+ marker.height = marker.tag.Height;
+
+ document.SetSelection(marker.line, marker.pos, marker.line, marker.pos);
+ Paste (Clipboard.GetDataObject (true), null, true);
+ }
+ }
+
+ private void TextBoxBase_MouseUp (object sender, MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left) {
+ if (click_mode == CaretSelection.Position) {
+ document.SetSelectionToCaret(false);
+ document.DisplayCaret();
+
+ // Only raise if there is text.
+ if (Text.Length > 0)
+ RaiseSelectionChanged ();
+ }
+
+ if (scroll_timer != null) {
+ scroll_timer.Enabled = false;
+ }
+ return;
+ }
+ }
+
+ private void SizeWidgets ()
+ {
+ if (hscroll.Visible) {
+ //vscroll.Maximum += hscroll.Height;
+ canvas_height = ClientSize.Height - hscroll.Height;
+ } else {
+ canvas_height = ClientSize.Height;
+ }
+
+ if (vscroll.Visible) {
+ //hscroll.Maximum += vscroll.Width;
+ canvas_width = ClientSize.Width - vscroll.Width;
+
+ if (GetInheritedRtoL () == RightToLeft.Yes) {
+ document.OffsetX = vscroll.Width;
+ } else {
+ document.OffsetX = 0;
+ }
+
+ } else {
+ canvas_width = ClientSize.Width;
+ document.OffsetX = 0;
+ }
+
+ document.ViewPortWidth = canvas_width;
+ document.ViewPortHeight = canvas_height;
+ }
+
+ private void PositionWidgets ()
+ {
+ if (canvas_height < 1 || canvas_width < 1)
+ return;
+
+ int hmod = vscroll.Visible ? vscroll.Width : 0;
+ int vmod = hscroll.Visible ? hscroll.Height : 0;
+
+ if (GetInheritedRtoL () == RightToLeft.Yes) {
+ hscroll.Bounds = new Rectangle (ClientRectangle.Left + hmod,
+ Math.Max(0, ClientRectangle.Height - hscroll.Height),
+ ClientSize.Width,
+ hscroll.Height);
+
+ vscroll.Bounds = new Rectangle (ClientRectangle.Left,
+ ClientRectangle.Top,
+ vscroll.Width,
+ Math.Max(0, ClientSize.Height - (vmod)));
+ } else {
+ hscroll.Bounds = new Rectangle (ClientRectangle.Left,
+ Math.Max(0, ClientRectangle.Height - hscroll.Height),
+ Math.Max(0, ClientSize.Width - hmod),
+ hscroll.Height);
+
+ vscroll.Bounds = new Rectangle (
+ Math.Max(0, ClientRectangle.Right - vscroll.Width),
+ ClientRectangle.Top,
+ vscroll.Width,
+ Math.Max(0, ClientSize.Height - vmod));
+ }
+ }
+
+ internal RightToLeft GetInheritedRtoL ()
+ {
+ for (Widget c = this; c != null; c = c.Parent)
+ if (c.RightToLeft != RightToLeft.Inherit)
+ return c.RightToLeft;
+ return RightToLeft.No;
+ }
+
+ private void TextBoxBase_SizeChanged (object sender, EventArgs e)
+ {
+ if (IsHandleCreated)
+ CalculateDocument ();
+ }
+
+ private void TextBoxBase_RightToLeftChanged (object o, EventArgs e)
+ {
+ if (IsHandleCreated)
+ CalculateDocument ();
+ }
+
+ private void TextBoxBase_MouseWheel (object sender, MouseEventArgs e)
+ {
+ if (!vscroll.Enabled)
+ return;
+
+ if (e.Delta < 0)
+ vscroll.Value = Math.Min (vscroll.Value + SystemInformation.MouseWheelScrollLines * 5,
+ Math.Max (0, vscroll.Maximum - document.ViewPortHeight + 1));
+ else
+ vscroll.Value = Math.Max (0, vscroll.Value - SystemInformation.MouseWheelScrollLines * 5);
+ }
+
+ internal virtual void SelectWord ()
+ {
+ StringBuilder s = document.caret.line.text;
+ int start = document.caret.pos;
+ int end = document.caret.pos;
+
+ if (s.Length < 1) {
+ if (document.caret.line.line_no >= document.Lines)
+ return;
+ Line line = document.GetLine (document.caret.line.line_no + 1);
+ document.PositionCaret (line, 0);
+ return;
+ }
+
+ if (start > 0) {
+ start--;
+ end--;
+ }
+
+ // skip whitespace until we hit a word
+ while (start > 0 && s [start] == ' ')
+ start--;
+ if (start > 0) {
+ while (start > 0 && (s [start] != ' '))
+ start--;
+ if (s [start] == ' ')
+ start++;
+ }
+
+ if (s [end] == ' ') {
+ while (end < s.Length && s [end] == ' ')
+ end++;
+ } else {
+ while (end < s.Length && s [end] != ' ')
+ end++;
+ while (end < s.Length && s [end] == ' ')
+ end++;
+ }
+
+ document.SetSelection (document.caret.line, start, document.caret.line, end);
+ document.PositionCaret (document.selection_end.line, document.selection_end.pos);
+ document.DisplayCaret();
+ }
+
+ internal void CalculateDocument()
+ {
+ CalculateScrollBars ();
+ document.RecalculateDocument (CreateGraphicsInternal ());
+
+
+ if (document.caret.line != null && document.caret.line.Y < document.ViewPortHeight) {
+ // The window has probably been resized, making the entire thing visible, so
+ // we need to set the scroll position back to zero.
+ vscroll.Value = 0;
+ }
+
+ Invalidate();
+ }
+
+ internal void CalculateScrollBars ()
+ {
+ // FIXME - need separate calculations for center and right alignment
+ SizeWidgets ();
+
+ if (document.Width >= document.ViewPortWidth) {
+ hscroll.SetValues (0, Math.Max (1, document.Width), -1,
+ document.ViewPortWidth < 0 ? 0 : document.ViewPortWidth);
+ if (document.multiline)
+ hscroll.Enabled = true;
+ } else {
+ hscroll.Enabled = false;
+ hscroll.Maximum = document.ViewPortWidth;
+ }
+
+ if (document.Height >= document.ViewPortHeight) {
+ vscroll.SetValues (0, Math.Max (1, document.Height), -1,
+ document.ViewPortHeight < 0 ? 0 : document.ViewPortHeight);
+ if (document.multiline)
+ vscroll.Enabled = true;
+ } else {
+ vscroll.Enabled = false;
+ vscroll.Maximum = document.ViewPortHeight;
+ }
+
+ if (!WordWrap) {
+ switch (scrollbars) {
+ case RichTextBoxScrollBars.Both:
+ case RichTextBoxScrollBars.Horizontal:
+ if (richtext)
+ hscroll.Visible = hscroll.Enabled;
+ else
+ hscroll.Visible = this.Multiline;
+ break;
+ case RichTextBoxScrollBars.ForcedBoth:
+ case RichTextBoxScrollBars.ForcedHorizontal:
+ hscroll.Visible = true;
+ break;
+ default:
+ hscroll.Visible = false;
+ break;
+ }
+ } else {
+ hscroll.Visible = false;
+ }
+
+ switch (scrollbars) {
+ case RichTextBoxScrollBars.Both:
+ case RichTextBoxScrollBars.Vertical:
+ if (richtext)
+ vscroll.Visible = vscroll.Enabled;
+ else
+ vscroll.Visible = this.Multiline;
+ break;
+ case RichTextBoxScrollBars.ForcedBoth:
+ case RichTextBoxScrollBars.ForcedVertical:
+ vscroll.Visible = true;
+ break;
+ default:
+ vscroll.Visible = false;
+ break;
+ }
+
+ PositionWidgets ();
+
+ SizeWidgets (); //Update sizings now we've decided whats visible
+ }
+
+ private void document_WidthChanged (object sender, EventArgs e)
+ {
+ CalculateScrollBars();
+ }
+
+ private void document_HeightChanged (object sender, EventArgs e)
+ {
+ CalculateScrollBars();
+ }
+
+ private void ScrollLinks (int xChange, int yChange)
+ {
+ foreach (LinkRectangle link in list_links)
+ link.Scroll (xChange, yChange);
+ }
+
+ private void hscroll_ValueChanged (object sender, EventArgs e)
+ {
+ int old_viewport_x;
+
+ old_viewport_x = document.ViewPortX;
+ document.ViewPortX = this.hscroll.Value;
+
+ //
+ // Before scrolling we want to destroy the caret, then draw a new one after the scroll
+ // the reason for this is that scrolling changes the coordinates of the caret, and we
+ // will get tracers if we don't
+ //
+ if (Focused)
+ document.CaretLostFocus ();
+
+ if (vscroll.Visible) {
+ if (GetInheritedRtoL () == RightToLeft.Yes) {
+ XplatUI.ScrollWindow (this.Handle, new Rectangle (vscroll.Width, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
+ } else {
+ XplatUI.ScrollWindow (this.Handle, new Rectangle (0, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
+ }
+ } else {
+ XplatUI.ScrollWindow(this.Handle, ClientRectangle, old_viewport_x - this.hscroll.Value, 0, false);
+ }
+
+ ScrollLinks (old_viewport_x - this.hscroll.Value, 0);
+
+ if (Focused)
+ document.CaretHasFocus ();
+
+ EventHandler eh = (EventHandler)(Events [HScrolledEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void vscroll_ValueChanged (object sender, EventArgs e)
+ {
+ int old_viewport_y;
+
+ old_viewport_y = document.ViewPortY;
+ document.ViewPortY = this.vscroll.Value;
+
+ //
+ // Before scrolling we want to destroy the caret, then draw a new one after the scroll
+ // the reason for this is that scrolling changes the coordinates of the caret, and we
+ // will get tracers if we don't
+ //
+ if (Focused)
+ document.CaretLostFocus ();
+
+ if (hscroll.Visible) {
+ XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height - hscroll.Height), 0, old_viewport_y - this.vscroll.Value, false);
+ } else {
+ XplatUI.ScrollWindow(this.Handle, ClientRectangle, 0, old_viewport_y - this.vscroll.Value, false);
+ }
+
+ ScrollLinks (0, old_viewport_y - this.vscroll.Value);
+
+ if (Focused)
+ document.CaretHasFocus ();
+
+ EventHandler eh = (EventHandler)(Events [VScrolledEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ private void TextBoxBase_MouseMove (object sender, MouseEventArgs e)
+ {
+ // FIXME - handle auto-scrolling if mouse is to the right/left of the window
+ if (e.Button == MouseButtons.Left && Capture) {
+ if (!ClientRectangle.Contains (e.X, e.Y)) {
+ if (scroll_timer == null) {
+ scroll_timer = new Timer ();
+ scroll_timer.Interval = 100;
+ scroll_timer.Tick += new EventHandler (ScrollTimerTickHandler);
+ }
+
+ if (!scroll_timer.Enabled) {
+ scroll_timer.Start ();
+
+ // Force the first tick
+ ScrollTimerTickHandler (null, EventArgs.Empty);
+ }
+ }
+
+ document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
+ if (click_mode == CaretSelection.Position) {
+ document.SetSelectionToCaret(false);
+ document.DisplayCaret();
+ }
+ }
+
+ //search through link boxes to see if the mouse is over one of them
+
+ bool found_link = false;
+ foreach (LinkRectangle link in list_links) {
+ if (link.LinkAreaRectangle.Contains (e.X, e.Y)) {
+ XplatUI.SetCursor (window.Handle, Cursors.Hand.handle);
+
+ found_link = true;
+ current_link = link;
+ break;
+ }
+ }
+
+ if (found_link == false) {
+ XplatUI.SetCursor (window.Handle, DefaultCursor.handle);
+ current_link = null;
+ }
+ }
+
+ private void TextBoxBase_FontOrColorChanged (object sender, EventArgs e)
+ {
+ Line line;
+
+ document.SuspendRecalc ();
+ // Font changes apply to the whole document
+ for (int i = 1; i <= document.Lines; i++) {
+ line = document.GetLine(i);
+ if (LineTag.FormatText(line, 1, line.text.Length, Font, ForeColor,
+ Color.Empty, FormatSpecified.Font | FormatSpecified.Color))
+ document.RecalculateDocument (CreateGraphicsInternal (), line.LineNo, line.LineNo, false);
+ }
+ document.ResumeRecalc (false);
+
+ // Make sure the caret height is matching the new font height
+ document.AlignCaret();
+ }
+
+ private void ScrollTimerTickHandler (object sender, EventArgs e)
+ {
+ Point pt = Cursor.Position;
+
+ pt = PointToClient (pt);
+
+ if (pt.X < ClientRectangle.Left) {
+ document.MoveCaret(CaretDirection.CharBackNoWrap);
+ document.SetSelectionToCaret(false);
+
+ CaretMoved(this, null);
+ } else if (pt.X > ClientRectangle.Right) {
+ document.MoveCaret(CaretDirection.CharForwardNoWrap);
+ document.SetSelectionToCaret(false);
+
+ CaretMoved(this, null);
+ } else if (pt.Y > ClientRectangle.Bottom) {
+ document.MoveCaret(CaretDirection.LineDown);
+ document.SetSelectionToCaret(false);
+
+ CaretMoved(this, null);
+ } else if (pt.Y < ClientRectangle.Top) {
+ document.MoveCaret(CaretDirection.LineUp);
+ document.SetSelectionToCaret(false);
+
+ CaretMoved(this, null);
+ }
+ }
+
+ /// <summary>Ensure the caret is always visible</summary>
+ internal void CaretMoved (object sender, EventArgs e)
+ {
+ Point pos;
+ int height;
+
+ if (!IsHandleCreated || canvas_width < 1 || canvas_height < 1)
+ return;
+
+ document.MoveCaretToTextTag ();
+ pos = document.Caret;
+
+ //Console.WriteLine("Caret now at {0} (Thumb: {1}x{2}, Canvas: {3}x{4}, Document {5}x{6})", pos, hscroll.Value, vscroll.Value, canvas_width, canvas_height, document.Width, document.Height);
+
+
+ // Horizontal scrolling:
+ // If the caret moves to the left outside the visible area, we jump the document into view, not just one
+ // character, but 1/3 of the width of the document
+ // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
+
+ // Handle horizontal scrolling
+ if (document.CaretLine.alignment == HorizontalAlignment.Left) {
+ // Check if we moved out of view to the left
+ if (pos.X < (document.ViewPortX)) {
+ do {
+ if ((hscroll.Value - document.ViewPortWidth / 3) >= hscroll.Minimum) {
+ hscroll.SafeValueSet (hscroll.Value - document.ViewPortWidth / 3);
+ } else {
+ hscroll.Value = hscroll.Minimum;
+ }
+ } while (hscroll.Value > pos.X);
+ }
+
+ // Check if we moved out of view to the right
+ if ((pos.X >= (document.ViewPortWidth + document.ViewPortX)) && (hscroll.Value != hscroll.Maximum)) {
+ if ((pos.X - document.ViewPortWidth + 1) <= hscroll.Maximum) {
+ if (pos.X - document.ViewPortWidth >= 0) {
+ hscroll.SafeValueSet (pos.X - document.ViewPortWidth + 1);
+ } else {
+ hscroll.Value = 0;
+ }
+ } else {
+ hscroll.Value = hscroll.Maximum;
+ }
+ }
+ } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
+// hscroll.Value = pos.X;
+
+// if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
+// hscroll.Value = hscroll.Maximum;
+// }
+ } else {
+ // FIXME - implement center cursor alignment
+ }
+
+ if (Text.Length > 0)
+ RaiseSelectionChanged ();
+
+ if (!document.multiline)
+ return;
+
+ // Handle vertical scrolling
+ height = document.CaretLine.Height + 1;
+
+ if (pos.Y < document.ViewPortY)
+ vscroll.SafeValueSet (pos.Y);
+ if ((pos.Y + height) > (document.ViewPortY + canvas_height))
+ vscroll.Value = Math.Min (vscroll.Maximum, pos.Y - canvas_height + height);
+ }
+
+ internal bool Paste (IDataObject clip, DataFormats.Format format, bool obey_length)
+ {
+ string s;
+
+ if (clip == null)
+ return false;
+
+ if (format == null) {
+ if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
+ format = DataFormats.GetFormat(DataFormats.Rtf);
+ } else if ((this is RichTextBox) && clip.GetDataPresent (DataFormats.Bitmap)) {
+ format = DataFormats.GetFormat (DataFormats.Bitmap);
+ } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
+ format = DataFormats.GetFormat(DataFormats.UnicodeText);
+ } else if (clip.GetDataPresent(DataFormats.Text)) {
+ format = DataFormats.GetFormat(DataFormats.Text);
+ } else {
+ return false;
+ }
+ } else {
+ if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
+ return false;
+ }
+
+ if (!clip.GetDataPresent(format.Name)) {
+ return false;
+ }
+ }
+
+ if (format.Name == DataFormats.Rtf) {
+ document.undo.BeginUserAction (String.Format ("Paste"));
+ ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
+ document.undo.EndUserAction ();
+ Modified = true;
+ return true;
+ } else if (format.Name == DataFormats.Bitmap) {
+ document.undo.BeginUserAction (String.Format ("Paste"));
+ // document.InsertImage (document.caret.line, document.caret.pos, (Image) clip.GetData (DataFormats.Bitmap));
+ document.MoveCaret (CaretDirection.CharForward);
+ document.undo.EndUserAction ();
+ return true;
+ } else if (format.Name == DataFormats.UnicodeText) {
+ s = (string)clip.GetData(DataFormats.UnicodeText);
+ } else if (format.Name == DataFormats.Text) {
+ s = (string)clip.GetData(DataFormats.Text);
+ } else {
+ return false;
+ }
+
+ if (!obey_length) {
+ document.undo.BeginUserAction (String.Format ("Paste"));
+ this.SelectedText = s;
+ document.undo.EndUserAction ();
+ } else {
+ if ((s.Length + (document.Length - SelectedText.Length)) < max_length) {
+ document.undo.BeginUserAction (String.Format ("Paste"));
+ this.SelectedText = s;
+ document.undo.EndUserAction ();
+ } else if ((document.Length - SelectedText.Length) < max_length) {
+ document.undo.BeginUserAction (String.Format ("Paste"));
+ this.SelectedText = s.Substring (0, max_length - (document.Length - SelectedText.Length));
+ document.undo.EndUserAction ();
+ }
+ }
+
+ Modified = true;
+ return true;
+ }
+
+ internal virtual Color ChangeBackColor (Color backColor)
+ {
+ return backColor;
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+ #endregion // Private Methods
+
+ #region Private Classes
+ internal class LinkRectangle {
+ private Rectangle link_area_rectangle;
+ private LineTag link_tag;
+
+ public LinkRectangle (Rectangle rect)
+ {
+ link_tag = null;
+ link_area_rectangle = rect;
+ }
+
+ public Rectangle LinkAreaRectangle {
+ get { return link_area_rectangle; }
+ set { link_area_rectangle = value; }
+ }
+
+ public LineTag LinkTag {
+ get { return link_tag; }
+ set { link_tag = value; }
+ }
+
+ public void Scroll (int x_change, int y_change)
+ {
+ link_area_rectangle.X += x_change;
+ link_area_rectangle.Y += y_change;
+ }
+ }
+ #endregion
+
+ // This is called just before OnTextChanged is called.
+ internal virtual void OnTextUpdate ()
+ {
+ }
+
+ protected override void OnTextChanged (EventArgs e)
+ {
+ base.OnTextChanged (e);
+ }
+
+ public virtual int GetLineFromCharIndex (int index)
+ {
+ Line line_out;
+ LineTag tag_out;
+ int pos;
+
+ document.CharIndexToLineTag (index, out line_out, out tag_out, out pos);
+
+ return line_out.LineNo;
+ }
+
+ protected override void OnMouseUp (MouseEventArgs mevent)
+ {
+ base.OnMouseUp (mevent);
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/TrackBar.cs b/source/ShiftUI/Widgets/TrackBar.cs
new file mode 100644
index 0000000..a0952a8
--- /dev/null
+++ b/source/ShiftUI/Widgets/TrackBar.cs
@@ -0,0 +1,907 @@
+//
+// ShiftUI.TrackBar.cs
+//
+// 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-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Rolf Bjarne Kvinge, [email protected]
+//
+// TODO:
+// - The AutoSize functionality seems quite broken for vertical Widgets in .Net 1.1. Not
+// sure if we are implementing it the right way.
+//
+
+// NOT COMPLETE
+
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Drawing.Drawing2D;
+using System.Timers;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI
+{
+ [DefaultBindingProperty ("Value")]
+ [ComVisible (true)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ //[Designer("ShiftUI.Design.TrackBarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultEvent ("Scroll")]
+ [DefaultProperty("Value")]
+ [ToolboxWidget]
+ public class TrackBar : Widget, ISupportInitialize
+ {
+ private int minimum;
+ private int maximum;
+ internal int tickFrequency;
+ private bool autosize;
+ private int position;
+ private int smallChange;
+ private int largeChange;
+ private Orientation orientation;
+ private TickStyle tickStyle;
+ private Rectangle thumb_pos = new Rectangle (); /* Current position and size of the thumb */
+ private Rectangle thumb_area = new Rectangle (); /* Area where the thumb can scroll */
+ internal bool thumb_pressed = false;
+ private System.Timers.Timer holdclick_timer = new System.Timers.Timer ();
+ internal int thumb_mouseclick;
+ private bool mouse_clickmove;
+ private bool is_moving_right; // which way the thumb should move when mouse is down (right=up, left=down)
+ internal int mouse_down_x_offset; // how far from left side of thumb was the mouse clicked.
+ internal bool mouse_moved; // has the mouse moved since it was clicked?
+ private const int size_of_autosize = 45;
+ private bool right_to_left_layout;
+ bool thumb_entered;
+
+ #region events
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ [Browsable (true)]
+ public new event EventHandler AutoSizeChanged {
+ add {base.AutoSizeChanged += value;}
+ remove {base.AutoSizeChanged -= value;}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new event EventHandler BackgroundImageLayoutChanged
+ {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler DoubleClick {
+ add { base.DoubleClick += value; }
+ remove { base.DoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler FontChanged {
+ add { base.FontChanged += value; }
+ remove { base.FontChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ForeColorChanged {
+ add { base.ForeColorChanged += value; }
+ remove { base.ForeColorChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler ImeModeChanged {
+ add { base.ImeModeChanged += value; }
+ remove { base.ImeModeChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseClick {
+ add {base.MouseClick += value;}
+ remove {base.MouseClick -= value;}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event MouseEventHandler MouseDoubleClick
+ {
+ add { base.MouseDoubleClick += value; }
+ remove { base.MouseDoubleClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged
+ {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add {Events.AddHandler (RightToLeftLayoutChangedEvent, value);}
+ remove {Events.RemoveHandler (RightToLeftLayoutChangedEvent, value);}
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ static object RightToLeftLayoutChangedEvent = new object ();
+ static object ScrollEvent = new object ();
+ static object ValueChangedEvent = new object ();
+
+ public event EventHandler Scroll {
+ add { Events.AddHandler (ScrollEvent, value); }
+ remove { Events.RemoveHandler (ScrollEvent, value); }
+ }
+
+ public event EventHandler ValueChanged {
+ add { Events.AddHandler (ValueChangedEvent, value); }
+ remove { Events.RemoveHandler (ValueChangedEvent, value); }
+ }
+
+ #endregion // Events
+
+ #region UIA FrameWork Events
+ static object UIAValueParamChangedEvent = new object ();
+
+ internal event EventHandler UIAValueParamChanged {
+ add { Events.AddHandler (UIAValueParamChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAValueParamChangedEvent, value); }
+ }
+
+ internal void OnUIAValueParamChanged ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAValueParamChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+ #endregion
+
+ public TrackBar ()
+ {
+ orientation = Orientation.Horizontal;
+ minimum = 0;
+ maximum = 10;
+ tickFrequency = 1;
+ autosize = true;
+ position = 0;
+ tickStyle = TickStyle.BottomRight;
+ smallChange = 1;
+ largeChange = 5;
+ mouse_clickmove = false;
+ MouseDown += new MouseEventHandler (OnMouseDownTB);
+ MouseUp += new MouseEventHandler (OnMouseUpTB);
+ MouseMove += new MouseEventHandler (OnMouseMoveTB);
+ MouseLeave += new EventHandler (OnMouseLeave);
+ KeyDown += new KeyEventHandler (OnKeyDownTB);
+ LostFocus += new EventHandler (OnLostFocusTB);
+ GotFocus += new EventHandler (OnGotFocusTB);
+ holdclick_timer.Elapsed += new ElapsedEventHandler (OnFirstClickTimer);
+
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.Opaque | Widgetstyles.UseTextForAccessibility, false);
+ }
+
+ #region Private & Internal Properties
+ internal Rectangle ThumbPos {
+ get {
+ return thumb_pos;
+ }
+
+ set {
+ thumb_pos = value;
+ }
+ }
+
+ internal Rectangle ThumbArea {
+ get {
+ return thumb_area;
+ }
+
+ set {
+ thumb_area = value;
+ }
+ }
+
+ internal bool ThumbEntered {
+ get { return thumb_entered; }
+ set {
+ if (thumb_entered == value)
+ return;
+ thumb_entered = value;
+ if (ThemeEngine.Current.TrackBarHasHotThumbStyle)
+ Invalidate (GetRealThumbRectangle ());
+ }
+ }
+ #endregion // Private & Internal Properties
+
+ #region Public Properties
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ [DefaultValue (true)]
+ public override bool AutoSize {
+ get { return autosize; }
+ set { autosize = value;}
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override ImageLayout BackgroundImageLayout {
+ get {
+ return base.BackgroundImageLayout;
+ }
+ set {
+ base.BackgroundImageLayout = value;
+ }
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ return base.CreateParams;
+ }
+ }
+
+ protected override ImeMode DefaultImeMode {
+ get {return ImeMode.Disable; }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.TrackBarDefaultSize; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get {
+ return base.DoubleBuffered;
+ }
+ set {
+ base.DoubleBuffered = value;
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Font Font {
+ get { return base.Font; }
+ set { base.Font = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set { base.ForeColor = value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new ImeMode ImeMode {
+ get { return base.ImeMode; }
+ set { base.ImeMode = value; }
+ }
+
+ [DefaultValue (5)]
+ public int LargeChange
+ {
+ get { return largeChange; }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException (string.Format ("Value '{0}' must be greater than or equal to 0.", value));
+
+ largeChange = value;
+
+ OnUIAValueParamChanged ();
+ }
+ }
+
+ [DefaultValue (10)]
+ [RefreshProperties (RefreshProperties.All)]
+ public int Maximum {
+ get { return maximum; }
+ set {
+ if (maximum != value) {
+ maximum = value;
+
+ if (maximum < minimum)
+ minimum = maximum;
+
+ Refresh ();
+
+ OnUIAValueParamChanged ();
+ }
+ }
+ }
+
+ [DefaultValue (0)]
+ [RefreshProperties (RefreshProperties.All)]
+ public int Minimum {
+ get { return minimum; }
+ set {
+
+ if (Minimum != value) {
+ minimum = value;
+
+ if (minimum > maximum)
+ maximum = minimum;
+
+ Refresh ();
+
+ OnUIAValueParamChanged ();
+ }
+ }
+ }
+
+ [DefaultValue (Orientation.Horizontal)]
+ [Localizable (true)]
+ public Orientation Orientation {
+ get { return orientation; }
+ set {
+ if (!Enum.IsDefined (typeof (Orientation), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Orientation", value));
+
+ /* Orientation can be changed once the control has been created */
+ if (orientation != value) {
+ orientation = value;
+
+ if (this.IsHandleCreated) {
+ Size = new Size (Height, Width);
+ Refresh ();
+ }
+ }
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new Padding Padding {
+ get {
+ return base.Padding;
+ }
+ set {
+ base.Padding = value;
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public virtual bool RightToLeftLayout {
+ get {
+ return right_to_left_layout;
+ }
+ set {
+ if (value != right_to_left_layout) {
+ right_to_left_layout = value;
+ OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue (1)]
+ public int SmallChange {
+ get { return smallChange;}
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException (string.Format ("Value '{0}' must be greater than or equal to 0.", value));
+
+ if (smallChange != value) {
+ smallChange = value;
+
+ OnUIAValueParamChanged ();
+ }
+ }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Bindable (false)]
+ [Browsable (false)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ [DefaultValue (1)]
+ public int TickFrequency {
+ get { return tickFrequency; }
+ set {
+ if ( value > 0 ) {
+ tickFrequency = value;
+ Refresh ();
+ }
+ }
+ }
+
+ [DefaultValue (TickStyle.BottomRight)]
+ public TickStyle TickStyle {
+ get { return tickStyle; }
+ set {
+ if (!Enum.IsDefined (typeof (TickStyle), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for TickStyle", value));
+
+ if (tickStyle != value) {
+ tickStyle = value;
+ Refresh ();
+ }
+ }
+ }
+
+ [DefaultValue (0)]
+ [Bindable (true)]
+ public int Value {
+ get { return position; }
+ set {
+ SetValue (value, false);
+ }
+ }
+
+ void SetValue (int value, bool fire_onscroll)
+ {
+ if (value < Minimum || value > Maximum)
+ throw new ArgumentException(
+ String.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
+
+ if (position == value)
+ return;
+
+ position = value;
+
+ // OnScroll goes before OnValueChanged
+ if (fire_onscroll)
+ OnScroll (EventArgs.Empty);
+
+ // XXX any reason we don't call OnValueChanged here?
+ EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+
+ Invalidate (thumb_area);
+ }
+
+ #endregion //Public Properties
+
+ #region Public Methods
+
+ public void BeginInit ()
+ {
+
+ }
+
+ protected override void CreateHandle ()
+ {
+ base.CreateHandle ();
+ }
+
+ protected override void SetBoundsCore (int x, int y,int width, int height, BoundsSpecified specified)
+ {
+ if (AutoSize) {
+ if (orientation == Orientation.Vertical) {
+ width = size_of_autosize;
+ } else {
+ height = size_of_autosize;
+ }
+ }
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+
+ public void EndInit ()
+ {
+
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ if ((keyData & Keys.Alt) == 0) {
+ switch (keyData & Keys.KeyCode) {
+ case Keys.Down:
+ case Keys.Right:
+ case Keys.Up:
+ case Keys.Left:
+ case Keys.PageUp:
+ case Keys.PageDown:
+ case Keys.Home:
+ case Keys.End:
+ return true;
+ }
+ }
+ return base.IsInputKey (keyData);
+ }
+
+ protected override void OnBackColorChanged (EventArgs e)
+ {
+ base.OnBackColorChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ if (AutoSize)
+ if (Orientation == Orientation.Horizontal)
+ Size = new Size (Width, 40);
+ else
+ Size = new Size (50, Height);
+
+ UpdatePos (Value, true);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnMouseWheel (MouseEventArgs e)
+ {
+ base.OnMouseWheel (e);
+
+ if (!Enabled) return;
+
+ if (e.Delta > 0)
+ SmallDecrement ();
+ else
+ SmallIncrement ();
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)Events [RightToLeftLayoutChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnScroll (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ScrollEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnSystemColorsChanged (EventArgs e)
+ {
+ base.OnSystemColorsChanged (e);
+ Invalidate ();
+ }
+
+ protected virtual void OnValueChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ public void SetRange (int minValue, int maxValue)
+ {
+ Minimum = minValue;
+ Maximum = maxValue;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("ShiftUI.TrackBar, Minimum: {0}, Maximum: {1}, Value: {2}",
+ Minimum, Maximum, Value);
+ }
+
+
+ protected override void WndProc (ref Message m)
+ {
+ base.WndProc (ref m);
+
+ // Basically we want Widgetstyles.ResizeRedraw but
+ // tests say we can't set that flag
+ if ((Msg)m.Msg == Msg.WM_WINDOWPOSCHANGED && Visible)
+ Invalidate ();
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ private void UpdatePos (int newPos, bool update_trumbpos)
+ {
+ if (newPos < minimum){
+ SetValue (minimum, true);
+ }
+ else {
+ if (newPos > maximum) {
+ SetValue (maximum, true);
+ }
+ else {
+ SetValue (newPos, true);
+ }
+ }
+ }
+
+ // Used by UIA implementation, so making internal
+ internal void LargeIncrement ()
+ {
+ UpdatePos (position + LargeChange, true);
+ Invalidate (thumb_area);
+ }
+
+ // Used by UIA implementation, so making internal
+ internal void LargeDecrement ()
+ {
+ UpdatePos (position - LargeChange, true);
+ Invalidate (thumb_area);
+ }
+
+ private void SmallIncrement ()
+ {
+ UpdatePos (position + SmallChange, true);
+ Invalidate (thumb_area);
+ }
+
+ private void SmallDecrement ()
+ {
+ UpdatePos (position - SmallChange, true);
+ Invalidate (thumb_area);
+ }
+
+ private void OnMouseUpTB (object sender, MouseEventArgs e)
+ {
+ if (!Enabled) return;
+
+ if (thumb_pressed == true || mouse_clickmove == true) {
+ thumb_pressed = false;
+ holdclick_timer.Enabled = false;
+ this.Capture = false;
+ Invalidate (thumb_area);
+ }
+ }
+
+ private void OnMouseDownTB (object sender, MouseEventArgs e)
+ {
+ if (!Enabled) return;
+
+ mouse_moved = false;
+
+ bool fire_timer = false;
+
+ Point point = new Point (e.X, e.Y);
+
+ if (orientation == Orientation.Horizontal) {
+
+ if (thumb_pos.Contains (point)) {
+ this.Capture = true;
+ thumb_pressed = true;
+ thumb_mouseclick = e.X;
+ mouse_down_x_offset = e.X - thumb_pos.X;
+ Invalidate (thumb_area);
+ }
+ else {
+ if (thumb_area.Contains (point)) {
+ is_moving_right = e.X > thumb_pos.X + thumb_pos.Width;
+ if (is_moving_right)
+ LargeIncrement ();
+ else
+ LargeDecrement ();
+
+ Invalidate (thumb_area);
+ fire_timer = true;
+ mouse_clickmove = true;
+ }
+ }
+ }
+ else {
+ Rectangle vertical_thumb_pos = thumb_pos;
+ vertical_thumb_pos.Width = thumb_pos.Height;
+ vertical_thumb_pos.Height = thumb_pos.Width;
+ if (vertical_thumb_pos.Contains (point)) {
+ this.Capture = true;
+ thumb_pressed = true;
+ thumb_mouseclick = e.Y;
+ mouse_down_x_offset = e.Y - thumb_pos.Y;
+ Invalidate (thumb_area);
+ }
+ else {
+ if (thumb_area.Contains (point)) {
+ is_moving_right = e.Y > thumb_pos.Y + thumb_pos.Width;
+ if (is_moving_right)
+ LargeDecrement ();
+ else
+ LargeIncrement ();
+
+ Invalidate (thumb_area);
+ fire_timer = true;
+ mouse_clickmove = true;
+ }
+ }
+ }
+
+ if (fire_timer) {
+ holdclick_timer.Interval = 300;
+ holdclick_timer.Enabled = true;
+ }
+ }
+
+ private void OnMouseMoveTB (object sender, MouseEventArgs e)
+ {
+ if (!Enabled) return;
+
+ mouse_moved = true;
+
+ /* Moving the thumb */
+ if (thumb_pressed)
+ SetValue (ThemeEngine.Current.TrackBarValueFromMousePosition (e.X, e.Y, this), true);
+
+ ThumbEntered = GetRealThumbRectangle ().Contains (e.Location);
+ }
+
+ Rectangle GetRealThumbRectangle ()
+ {
+ Rectangle result = thumb_pos;
+ if (Orientation == Orientation.Vertical) {
+ result.Width = thumb_pos.Height;
+ result.Height = thumb_pos.Width;
+ }
+ return result;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ ThemeEngine.Current.DrawTrackBar (pevent.Graphics, pevent.ClipRectangle, this);
+ }
+
+ private void OnLostFocusTB (object sender, EventArgs e)
+ {
+ Invalidate();
+ }
+
+ private void OnGotFocusTB (object sender, EventArgs e)
+ {
+ Invalidate();
+ }
+ private void OnKeyDownTB (object sender, KeyEventArgs e)
+ {
+ bool horiz = Orientation == Orientation.Horizontal;
+ switch (e.KeyCode) {
+
+ case Keys.Down:
+ case Keys.Right:
+ if(horiz)
+ SmallIncrement();
+ else
+ SmallDecrement ();
+ break;
+
+ case Keys.Up:
+ case Keys.Left:
+ if (horiz)
+ SmallDecrement();
+ else
+ SmallIncrement();
+ break;
+
+ case Keys.PageUp:
+ if (horiz)
+ LargeDecrement();
+ else
+ LargeIncrement();
+ break;
+
+ case Keys.PageDown:
+ if (horiz)
+ LargeIncrement();
+ else
+ LargeDecrement();
+ break;
+
+ case Keys.Home:
+ if (horiz)
+ SetValue (Minimum, true);
+ else
+ SetValue (Maximum, true);
+ break;
+
+ case Keys.End:
+ if (horiz)
+ SetValue (Maximum, true);
+ else
+ SetValue (Minimum, true);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private void OnFirstClickTimer (Object source, ElapsedEventArgs e)
+ {
+ Point pnt;
+ pnt = PointToClient (MousePosition);
+ /*
+ On Win32 the thumb only moves in one direction after a click,
+ if the thumb passes the clicked point it will never go in the
+ other way unless the mouse is released and clicked again. This
+ is also true if the mouse moves while beeing hold down.
+ */
+
+ if (thumb_area.Contains (pnt)) {
+ bool invalidate = false;
+ if (orientation == Orientation.Horizontal) {
+ if (pnt.X > thumb_pos.X + thumb_pos.Width && is_moving_right) {
+ LargeIncrement ();
+ invalidate = true;
+ } else if (pnt.X < thumb_pos.X && !is_moving_right) {
+ LargeDecrement ();
+ invalidate = true;
+ }
+ } else {
+ if (pnt.Y > thumb_pos.Y + thumb_pos.Width && is_moving_right) {
+ LargeDecrement ();
+ invalidate = true;
+ } else if (pnt.Y < thumb_pos.Y && !is_moving_right) {
+ LargeIncrement ();
+ invalidate = true;
+ }
+ }
+ if (invalidate)
+ // A Refresh is necessary because the mouse is down and if we just invalidate
+ // we'll only get paint events once in a while.
+ Refresh();
+ }
+ }
+
+ void OnMouseLeave (object sender, EventArgs e)
+ {
+ ThumbEntered = false;
+ }
+ #endregion // Private Methods
+ }
+}
+
diff --git a/source/ShiftUI/Widgets/TreeNode.cs b/source/ShiftUI/Widgets/TreeNode.cs
new file mode 100644
index 0000000..0a4a13b
--- /dev/null
+++ b/source/ShiftUI/Widgets/TreeNode.cs
@@ -0,0 +1,1087 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+// Kazuki Oikawa ([email protected])
+
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace ShiftUI
+{
+ [DefaultProperty ("Text")]
+ [TypeConverter(typeof(TreeNodeConverter))]
+ [Serializable]
+ public class TreeNode : MarshalByRefObject, ICloneable, ISerializable
+ {
+ #region Fields
+ private TreeView tree_view;
+ internal TreeNode parent;
+
+ private string text;
+ private int image_index = -1;
+ private int selected_image_index = -1;
+ private ContextMenuStrip context_menu_strip;
+ private string image_key = String.Empty;
+ private string selected_image_key = String.Empty;
+ private int state_image_index = -1;
+ private string state_image_key = String.Empty;
+ private string tool_tip_text = String.Empty;
+ internal TreeNodeCollection nodes;
+ internal TreeViewAction check_reason = TreeViewAction.Unknown;
+
+ internal int visible_order = 0;
+ internal int width = -1;
+
+ internal bool is_expanded = false;
+ private bool check;
+ internal OwnerDrawPropertyBag prop_bag;
+
+ private object tag;
+
+ internal IntPtr handle;
+
+ private string name = string.Empty;
+ #endregion // Fields
+
+ #region Internal Constructors
+ internal TreeNode (TreeView tree_view) : this ()
+ {
+ this.tree_view = tree_view;
+ is_expanded = true;
+ }
+
+ protected TreeNode (SerializationInfo serializationInfo, StreamingContext context) : this ()
+ {
+ SerializationInfoEnumerator en;
+ SerializationEntry e;
+ int children;
+
+ en = serializationInfo.GetEnumerator();
+ children = 0;
+ while (en.MoveNext()) {
+ e = en.Current;
+ switch(e.Name) {
+ case "Text": Text = (string)e.Value; break;
+ case "PropBag": prop_bag = (OwnerDrawPropertyBag)e.Value; break;
+ case "ImageIndex": image_index = (int)e.Value; break;
+ case "SelectedImageIndex": selected_image_index = (int)e.Value; break;
+ case "Tag": tag = e.Value; break;
+ case "IsChecked": check = (bool)e.Value; break;
+ case "ChildCount": children = (int)e.Value; break;
+ }
+ }
+ if (children > 0) {
+ for (int i = 0; i < children; i++) {
+ TreeNode node = (TreeNode) serializationInfo.GetValue ("children" + i, typeof (TreeNode));
+ Nodes.Add (node);
+ }
+ }
+ }
+ #endregion // Internal Constructors
+
+ #region Public Constructors
+ public TreeNode ()
+ {
+ nodes = new TreeNodeCollection (this);
+ }
+
+ public TreeNode (string text) : this ()
+ {
+ Text = text;
+ }
+
+ public TreeNode (string text, TreeNode [] children) : this (text)
+ {
+ Nodes.AddRange (children);
+ }
+
+ public TreeNode (string text, int imageIndex, int selectedImageIndex) : this (text)
+ {
+ this.image_index = imageIndex;
+ this.selected_image_index = selectedImageIndex;
+ }
+
+ public TreeNode (string text, int imageIndex, int selectedImageIndex,
+ TreeNode[] children)
+ : this (text, imageIndex, selectedImageIndex)
+ {
+ Nodes.AddRange (children);
+ }
+
+ #endregion // Public Constructors
+
+ #region ICloneable Members
+ public virtual object Clone ()
+ {
+ TreeNode tn = (TreeNode)Activator.CreateInstance (GetType ());
+ tn.name = name;
+ tn.text = text;
+ tn.image_key = image_key;
+ tn.image_index = image_index;
+ tn.selected_image_index = selected_image_index;
+ tn.selected_image_key = selected_image_key;
+ tn.state_image_index = state_image_index;
+ tn.state_image_key = state_image_key;
+ tn.tag = tag;
+ tn.check = check;
+ tn.tool_tip_text = tool_tip_text;
+ tn.context_menu_strip = context_menu_strip;
+ if (nodes != null) {
+ foreach (TreeNode child in nodes)
+ tn.nodes.Add ((TreeNode)child.Clone ());
+ }
+ if (prop_bag != null)
+ tn.prop_bag = OwnerDrawPropertyBag.Copy (prop_bag);
+ return tn;
+ }
+
+ #endregion // ICloneable Members
+
+ #region ISerializable Members
+ void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
+ {
+ si.AddValue ("Text", Text);
+ si.AddValue ("prop_bag", prop_bag, typeof (OwnerDrawPropertyBag));
+ si.AddValue ("ImageIndex", ImageIndex);
+ si.AddValue ("SelectedImageIndex", SelectedImageIndex);
+ si.AddValue ("Tag", Tag);
+ si.AddValue ("Checked", Checked);
+
+ si.AddValue ("NumberOfChildren", Nodes.Count);
+ for (int i = 0; i < Nodes.Count; i++)
+ si.AddValue ("Child-" + i, Nodes [i], typeof (TreeNode));
+ }
+
+ protected virtual void Deserialize (SerializationInfo serializationInfo, StreamingContext context)
+ {
+ Text = serializationInfo.GetString ("Text");
+ prop_bag = (OwnerDrawPropertyBag)serializationInfo.GetValue ("prop_bag", typeof (OwnerDrawPropertyBag));
+ ImageIndex = serializationInfo.GetInt32 ("ImageIndex");
+ SelectedImageIndex = serializationInfo.GetInt32 ("SelectedImageIndex");
+ Tag = serializationInfo.GetValue ("Tag", typeof (Object));
+ Checked = serializationInfo.GetBoolean ("Checked");
+
+ int count = serializationInfo.GetInt32 ("NumberOfChildren");
+
+ for (int i = 0; i < count; i++)
+ Nodes.Add ((TreeNode)serializationInfo.GetValue ("Child-" + i, typeof (TreeNode)));
+ }
+
+ protected virtual void Serialize (SerializationInfo si, StreamingContext context)
+ {
+ si.AddValue ("Text", Text);
+ si.AddValue ("prop_bag", prop_bag, typeof (OwnerDrawPropertyBag));
+ si.AddValue ("ImageIndex", ImageIndex);
+ si.AddValue ("SelectedImageIndex", SelectedImageIndex);
+ si.AddValue ("Tag", Tag);
+ si.AddValue ("Checked", Checked);
+
+ si.AddValue ("NumberOfChildren", Nodes.Count);
+ for (int i = 0; i < Nodes.Count; i++)
+ si.AddValue ("Child-" + i, Nodes[i], typeof (TreeNode));
+ }
+ #endregion // ISerializable Members
+
+ #region Public Instance Properties
+ public Color BackColor {
+ get {
+ if (prop_bag != null)
+ return prop_bag.BackColor;
+ return Color.Empty;
+ }
+ set {
+ if (prop_bag == null)
+ prop_bag = new OwnerDrawPropertyBag ();
+ prop_bag.BackColor = value;
+
+ TreeView tree_view = TreeView;
+ if (tree_view != null)
+ tree_view.UpdateNode (this);
+ }
+ }
+
+ [Browsable (false)]
+ public Rectangle Bounds {
+ get {
+ if (TreeView == null)
+ return Rectangle.Empty;
+
+ int x = GetX ();
+ int y = GetY ();
+
+ if (width == -1)
+ width = TreeView.GetNodeWidth (this);
+
+ Rectangle res = new Rectangle (x, y, width, TreeView.ActualItemHeight);
+ return res;
+ }
+ }
+
+ internal int GetY ()
+ {
+ if (TreeView == null)
+ return 0;
+ return (visible_order - 1) * TreeView.ActualItemHeight - (TreeView.skipped_nodes * TreeView.ActualItemHeight);
+ }
+
+ internal int GetX ()
+ {
+ if (TreeView == null)
+ return 0;
+ int indent_level = IndentLevel;
+ int roots = (TreeView.ShowRootLines ? 1 : 0);
+ int cb = (TreeView.CheckBoxes ? 19 : 0);
+ if (!TreeView.CheckBoxes && StateImage != null)
+ cb = 19;
+ int imgs = (TreeView.ImageList != null ? TreeView.ImageList.ImageSize.Width + 3 : 0);
+ return ((indent_level + roots) * TreeView.Indent) + cb + imgs - TreeView.hbar_offset;
+ }
+
+ internal int GetLinesX ()
+ {
+ int roots = (TreeView.ShowRootLines ? 1 : 0);
+ return (IndentLevel + roots) * TreeView.Indent - TreeView.hbar_offset;
+ }
+
+ internal int GetImageX ()
+ {
+ return GetLinesX () + (TreeView.CheckBoxes || StateImage != null ? 19 : 0);
+ }
+
+ // In theory we should be able to track this instead of computing
+ // every single time we need it, however for now I am going to
+ // do it this way to reduce bugs in my new bounds computing code
+ internal int IndentLevel {
+ get {
+ TreeNode walk = this;
+ int res = 0;
+ while (walk.Parent != null) {
+ walk = walk.Parent;
+ res++;
+ }
+
+ return res;
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool Checked {
+ get { return check; }
+ set {
+ if (check == value)
+ return;
+ TreeViewCancelEventArgs args = new TreeViewCancelEventArgs (this, false, check_reason);
+ if (TreeView != null)
+ TreeView.OnBeforeCheck (args);
+ if (!args.Cancel) {
+ check = value;
+
+ // TreeView can become null after OnAfterCheck, this the double null check
+ if (TreeView != null)
+ TreeView.OnAfterCheck (new TreeViewEventArgs (this, check_reason));
+ if (TreeView != null)
+ TreeView.UpdateNode (this);
+ }
+ check_reason = TreeViewAction.Unknown;
+ }
+ }
+
+ [DefaultValue (null)]
+ public virtual ContextMenuStrip ContextMenuStrip {
+ get { return context_menu_strip; }
+ set { context_menu_strip = value; }
+ }
+
+ [Browsable (false)]
+ public TreeNode FirstNode {
+ get {
+ if (nodes.Count > 0)
+ return nodes [0];
+ return null;
+ }
+ }
+
+ public Color ForeColor {
+ get {
+ if (prop_bag != null)
+ return prop_bag.ForeColor;
+ if (TreeView != null)
+ return TreeView.ForeColor;
+ return Color.Empty;
+ }
+ set {
+ if (prop_bag == null)
+ prop_bag = new OwnerDrawPropertyBag ();
+ prop_bag.ForeColor = value;
+
+ TreeView tree_view = TreeView;
+ if (tree_view != null)
+ tree_view.UpdateNode (this);
+ }
+ }
+
+ [Browsable (false)]
+ public string FullPath {
+ get {
+ if (TreeView == null)
+ throw new InvalidOperationException ("No TreeView associated");
+
+ StringBuilder builder = new StringBuilder ();
+ BuildFullPath (builder);
+ return builder.ToString ();
+ }
+ }
+
+ [DefaultValue (-1)]
+ [RelatedImageList ("TreeView.ImageList")]
+ [TypeConverter (typeof (TreeViewImageIndexConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable(true)]
+ public int ImageIndex {
+ get { return image_index; }
+ set {
+ if (image_index == value)
+ return;
+ image_index = value;
+ image_key = string.Empty;
+ TreeView tree = TreeView;
+ if (tree != null)
+ tree.UpdateNode (this);
+ }
+ }
+
+ [Localizable(true)]
+ [DefaultValue ("")]
+ [RelatedImageList ("TreeView.ImageList")]
+ [TypeConverter (typeof (TreeViewImageKeyConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string ImageKey {
+ get { return image_key; }
+ set {
+ if (image_key == value)
+ return;
+ image_key = value;
+ image_index = -1;
+
+ TreeView tree = TreeView;
+ if (tree != null)
+ tree.UpdateNode(this);
+ }
+ }
+
+ [Browsable (false)]
+ public bool IsEditing {
+ get {
+ TreeView tv = TreeView;
+ if (tv == null)
+ return false;
+ return tv.edit_node == this;
+ }
+ }
+
+ [Browsable (false)]
+ public bool IsExpanded {
+ get {
+ TreeView tv = TreeView;
+
+ if (tv != null && tv.IsHandleCreated) {
+ // This is ridiculous
+ bool found = false;
+ foreach (TreeNode walk in TreeView.Nodes) {
+ if (walk.Nodes.Count > 0)
+ found = true;
+ }
+
+ if (!found)
+ return false;
+ }
+
+ return is_expanded;
+ }
+ }
+
+ [Browsable (false)]
+ public bool IsSelected {
+ get {
+ if (TreeView == null || !TreeView.IsHandleCreated)
+ return false;
+ return TreeView.SelectedNode == this;
+ }
+ }
+
+ [Browsable (false)]
+ public bool IsVisible {
+ get {
+ if (TreeView == null || !TreeView.IsHandleCreated || !TreeView.Visible)
+ return false;
+
+ if (visible_order <= TreeView.skipped_nodes || visible_order - TreeView.skipped_nodes > TreeView.VisibleCount)
+ return false;
+
+ return ArePreviousNodesExpanded;
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode LastNode {
+ get {
+ return (nodes == null || nodes.Count == 0) ? null : nodes [nodes.Count - 1];
+ }
+ }
+
+ [Browsable (false)]
+ public int Level {
+ get { return IndentLevel; }
+ }
+
+ public string Name
+ {
+ get { return this.name; }
+ set {
+ // Value should never be null as per spec
+ this.name = (value == null) ? string.Empty : value;
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode NextNode {
+ get {
+ if (parent == null)
+ return null;
+ int index = Index;
+ if (parent.Nodes.Count > index + 1)
+ return parent.Nodes [index + 1];
+ return null;
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode NextVisibleNode {
+ get {
+ OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
+ o.MoveNext (); // move to the node itself
+
+ if (!o.MoveNext ())
+ return null;
+ TreeNode c = o.CurrentNode;
+ if (!c.IsInClippingRect)
+ return null;
+ return c;
+ }
+ }
+
+ [DefaultValue (null)]
+ [Localizable (true)]
+ public Font NodeFont {
+ get {
+ if (prop_bag != null)
+ return prop_bag.Font;
+ if (TreeView != null)
+ return TreeView.Font;
+ return null;
+ }
+ set {
+ if (prop_bag == null)
+ prop_bag = new OwnerDrawPropertyBag ();
+ prop_bag.Font = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ [ListBindable (false)]
+ public TreeNodeCollection Nodes {
+ get {
+ if (nodes == null)
+ nodes = new TreeNodeCollection (this);
+ return nodes;
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode Parent {
+ get {
+ TreeView tree_view = TreeView;
+ if (tree_view != null && tree_view.root_node == parent)
+ return null;
+ return parent;
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode PrevNode {
+ get {
+ if (parent == null)
+ return null;
+ int index = Index;
+ if (index <= 0 || index > parent.Nodes.Count)
+ return null;
+ return parent.Nodes [index - 1];
+ }
+ }
+
+ [Browsable (false)]
+ public TreeNode PrevVisibleNode {
+ get {
+ OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
+ o.MovePrevious (); // move to the node itself
+
+ if (!o.MovePrevious ())
+ return null;
+ TreeNode c = o.CurrentNode;
+ if (!c.IsInClippingRect)
+ return null;
+ return c;
+ }
+ }
+
+ [DefaultValue (-1)]
+ [RelatedImageList ("TreeView.ImageList")]
+ [TypeConverter (typeof (TreeViewImageIndexConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [Localizable (true)]
+ public int SelectedImageIndex {
+ get { return selected_image_index; }
+ set { selected_image_index = value; }
+ }
+
+ [Localizable (true)]
+ [DefaultValue ("")]
+ [RelatedImageList ("TreeView.ImageList")]
+ [TypeConverter (typeof (TreeViewImageKeyConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string SelectedImageKey {
+ get { return selected_image_key; }
+ set { selected_image_key = value; }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (-1)]
+ [RelatedImageList ("TreeView.StateImageList")]
+ [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public int StateImageIndex {
+ get { return state_image_index; }
+ set {
+ if (state_image_index != value) {
+ state_image_index = value;
+ state_image_key = string.Empty;
+ Invalidate ();
+ }
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue ("")]
+ [RelatedImageList ("TreeView.StateImageList")]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string StateImageKey {
+ get { return state_image_key; }
+ set {
+ if (state_image_key != value) {
+ state_image_key = value;
+ state_image_index = -1;
+ Invalidate ();
+ }
+ }
+ }
+
+ [Bindable(true)]
+ [Localizable(false)]
+ [TypeConverter(typeof(System.ComponentModel.StringConverter))]
+ [DefaultValue(null)]
+ public object Tag {
+ get { return tag; }
+ set { tag = value; }
+ }
+
+ [Localizable(true)]
+ public string Text {
+ get {
+ if (text == null)
+ return String.Empty;
+ return text;
+ }
+ set {
+ if (text == value)
+ return;
+ text = value;
+ Invalidate ();
+ // UIA Framework Event: Text Changed
+ TreeView view = TreeView;
+ if (view != null)
+ view.OnUIANodeTextChanged (new TreeViewEventArgs (this));
+ }
+ }
+
+ [DefaultValue ("")]
+ [Localizable (false)]
+ public string ToolTipText {
+ get { return tool_tip_text; }
+ set { tool_tip_text = value; }
+ }
+
+ [Browsable (false)]
+ public TreeView TreeView {
+ get {
+ if (tree_view != null)
+ return tree_view;
+ TreeNode walk = parent;
+ while (walk != null) {
+ if (walk.TreeView != null)
+ break;
+ walk = walk.parent;
+ }
+ if (walk == null)
+ return null;
+ return walk.TreeView;
+ }
+ }
+
+ [Browsable (false)]
+ public IntPtr Handle {
+ get {
+ // MS throws a NullReferenceException if the TreeView isn't set...
+ if (handle == IntPtr.Zero && TreeView != null)
+ handle = TreeView.CreateNodeHandle ();
+ return handle;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+
+ public static TreeNode FromHandle (TreeView tree, IntPtr handle)
+ {
+ if (handle == IntPtr.Zero)
+ return null;
+ // No arg checking on MS it just throws a NullRef if treeview is null
+ return tree.NodeFromHandle (handle);
+ }
+
+ #region Public Instance Methods
+ public void BeginEdit ()
+ {
+ TreeView tv = TreeView;
+ if (tv != null)
+ tv.BeginEdit (this);
+ }
+
+ public void Collapse ()
+ {
+ CollapseInternal (false);
+ }
+
+ public void Collapse (bool ignoreChildren)
+ {
+ if (ignoreChildren)
+ Collapse ();
+ else
+ CollapseRecursive (this);
+ }
+
+ public void EndEdit (bool cancel)
+ {
+ TreeView tv = TreeView;
+ if (!cancel && tv != null)
+ tv.EndEdit (this);
+ else if (cancel && tv != null)
+ tv.CancelEdit (this);
+ }
+
+ public void Expand ()
+ {
+ Expand (false);
+ }
+
+ public void ExpandAll ()
+ {
+ ExpandRecursive (this);
+ if(TreeView != null)
+ TreeView.UpdateNode (TreeView.root_node);
+ }
+
+ public void EnsureVisible ()
+ {
+ if (TreeView == null)
+ return;
+
+ if (this.Parent != null)
+ ExpandParentRecursive (this.Parent);
+
+ Rectangle bounds = Bounds;
+ if (bounds.Y < 0) {
+ TreeView.SetTop (this);
+ } else if (bounds.Bottom > TreeView.ViewportRectangle.Bottom) {
+ TreeView.SetBottom (this);
+ }
+ }
+
+ public int GetNodeCount (bool includeSubTrees)
+ {
+ if (!includeSubTrees)
+ return Nodes.Count;
+
+ int count = 0;
+ GetNodeCountRecursive (this, ref count);
+
+ return count;
+ }
+
+ public void Remove ()
+ {
+ if (parent == null)
+ return;
+ int index = Index;
+ parent.Nodes.RemoveAt (index);
+ }
+
+ public void Toggle ()
+ {
+ if (is_expanded)
+ Collapse ();
+ else
+ Expand ();
+ }
+
+ public override String ToString ()
+ {
+ return String.Concat ("TreeNode: ", Text);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Internal & Private Methods and Properties
+
+ internal bool ArePreviousNodesExpanded {
+ get {
+ TreeNode parent = Parent;
+ while (parent != null) {
+ if (!parent.is_expanded)
+ return false;
+ parent = parent.Parent;
+ }
+
+ return true;
+ }
+ }
+
+ internal bool IsRoot {
+ get {
+ TreeView tree_view = TreeView;
+ if (tree_view == null)
+ return false;
+ if (tree_view.root_node == this)
+ return true;
+ return false;
+ }
+ }
+
+ bool BuildFullPath (StringBuilder path)
+ {
+ if (parent == null)
+ return false;
+
+ if (parent.BuildFullPath (path))
+ path.Append (TreeView.PathSeparator);
+
+ path.Append (text);
+ return true;
+ }
+
+ public int Index {
+ get {
+ if (parent == null)
+ return 0;
+ return parent.Nodes.IndexOf (this);
+ }
+ }
+
+ private void Expand (bool byInternal)
+ {
+ if (is_expanded || nodes.Count < 1) {
+ is_expanded = true;
+ return;
+ }
+
+ bool cancel = false;
+ TreeView tree_view = TreeView;
+ if (tree_view != null) {
+ TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Expand);
+ tree_view.OnBeforeExpand (e);
+ cancel = e.Cancel;
+ }
+
+ if (!cancel) {
+ is_expanded = true;
+ int count_to_next = CountToNext ();
+
+ if (tree_view != null) {
+ tree_view.OnAfterExpand (new TreeViewEventArgs (this));
+
+ tree_view.RecalculateVisibleOrder (this);
+ tree_view.UpdateScrollBars (false);
+
+ // ExpandBelow if we affect the visible area
+ if (visible_order < tree_view.skipped_nodes + tree_view.VisibleCount + 1 && ArePreviousNodesExpanded)
+ tree_view.ExpandBelow (this, count_to_next);
+ }
+ }
+ }
+
+ private void CollapseInternal (bool byInternal)
+ {
+ if (!is_expanded || nodes.Count < 1)
+ return;
+
+ if (IsRoot)
+ return;
+
+ bool cancel = false;
+ TreeView tree_view = TreeView;
+ if (tree_view != null) {
+ TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Collapse);
+ tree_view.OnBeforeCollapse (e);
+ cancel = e.Cancel;
+ }
+
+ if (!cancel) {
+ int count_to_next = CountToNext ();
+
+ is_expanded = false;
+
+ if (tree_view != null) {
+ tree_view.OnAfterCollapse (new TreeViewEventArgs (this));
+
+ bool hbar_visible = tree_view.hbar.Visible;
+ bool vbar_visible = tree_view.vbar.Visible;
+
+ tree_view.RecalculateVisibleOrder (this);
+ tree_view.UpdateScrollBars (false);
+
+ // CollapseBelow if we affect the visible area
+ if (visible_order < tree_view.skipped_nodes + tree_view.VisibleCount + 1 && ArePreviousNodesExpanded)
+ tree_view.CollapseBelow (this, count_to_next);
+ if(!byInternal && HasFocusInChildren ())
+ tree_view.SelectedNode = this;
+
+ // If one or both of our scrollbars disappeared,
+ // invalidate everything
+ if ((hbar_visible & !tree_view.hbar.Visible) || (vbar_visible & !tree_view.vbar.Visible))
+ tree_view.Invalidate ();
+ }
+ }
+ }
+
+ private int CountToNext ()
+ {
+ bool expanded = is_expanded;
+ is_expanded = false;
+ OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (this);
+
+ TreeNode next= null;
+ if (walk.MoveNext () && walk.MoveNext ())
+ next = walk.CurrentNode;
+
+ is_expanded = expanded;
+ walk.Reset ();
+ walk.MoveNext ();
+
+ int count = 0;
+ while (walk.MoveNext () && walk.CurrentNode != next)
+ count++;
+
+ return count;
+ }
+
+ private bool HasFocusInChildren()
+ {
+ if (TreeView == null)
+ return false;
+ foreach (TreeNode node in nodes) {
+ if(node == TreeView.SelectedNode)
+ return true;
+ if(node.HasFocusInChildren ())
+ return true;
+ }
+ return false;
+ }
+
+ private void ExpandRecursive (TreeNode node)
+ {
+ node.Expand (true);
+ foreach (TreeNode child in node.Nodes)
+ ExpandRecursive (child);
+ }
+
+ private void ExpandParentRecursive (TreeNode node)
+ {
+ node.Expand (true);
+ if (node.Parent != null)
+ ExpandParentRecursive (node.Parent);
+ }
+
+ internal void CollapseAll ()
+ {
+ CollapseRecursive (this);
+ }
+
+ internal void CollapseAllUncheck ()
+ {
+ CollapseUncheckRecursive (this);
+ }
+
+ private void CollapseRecursive (TreeNode node)
+ {
+ node.Collapse ();
+ foreach (TreeNode child in node.Nodes)
+ CollapseRecursive (child);
+ }
+
+ private void CollapseUncheckRecursive (TreeNode node)
+ {
+ node.Collapse ();
+ node.Checked = false;
+ foreach (TreeNode child in node.Nodes)
+ CollapseUncheckRecursive (child);
+ }
+
+ internal void SetNodes (TreeNodeCollection nodes)
+ {
+ this.nodes = nodes;
+ }
+
+ private void GetNodeCountRecursive (TreeNode node, ref int count)
+ {
+ count += node.Nodes.Count;
+ foreach (TreeNode child in node.Nodes)
+ GetNodeCountRecursive (child, ref count);
+ }
+
+ internal bool NeedsWidth {
+ get { return width == -1; }
+ }
+
+ internal void Invalidate ()
+ {
+ // invalidate width first so Bounds retrieves
+ // the updated value (we don't use it here however)
+ width = -1;
+
+ TreeView tv = TreeView;
+ if (tv == null)
+ return;
+
+ tv.UpdateNode (this);
+ }
+
+ internal void InvalidateWidth ()
+ {
+ // bounds.Width = 0;
+ width = -1;
+ }
+
+ internal void SetWidth (int width)
+ {
+ this.width = width;
+ }
+
+ internal void SetParent (TreeNode parent)
+ {
+ this.parent = parent;
+ }
+
+ private bool IsInClippingRect {
+ get {
+ if (TreeView == null)
+ return false;
+ Rectangle bounds = Bounds;
+ if (bounds.Y < 0 && bounds.Y > TreeView.ClientRectangle.Height)
+ return false;
+ return true;
+ }
+ }
+
+ internal Image StateImage {
+ get {
+ if (TreeView != null) {
+ if (TreeView.StateImageList == null)
+ return null;
+ if (state_image_index >= 0)
+ return TreeView.StateImageList.Images[state_image_index];
+ if (state_image_key != string.Empty)
+ return TreeView.StateImageList.Images[state_image_key];
+ }
+
+ return null;
+ }
+ }
+
+ // Order of operation:
+ // 1) Node.Image[Key|Index]
+ // 2) TreeView.Image[Key|Index]
+ // 3) First image in TreeView.ImageList
+ internal int Image {
+ get {
+ if (TreeView == null || TreeView.ImageList == null)
+ return -1;
+
+ if (IsSelected) {
+ if (selected_image_index >= 0)
+ return selected_image_index;
+ if (!string.IsNullOrEmpty (selected_image_key))
+ return TreeView.ImageList.Images.IndexOfKey (selected_image_key);
+ if (!string.IsNullOrEmpty (TreeView.SelectedImageKey))
+ return TreeView.ImageList.Images.IndexOfKey (TreeView.SelectedImageKey);
+ if (TreeView.SelectedImageIndex >= 0)
+ return TreeView.SelectedImageIndex;
+ } else {
+ if (image_index >= 0)
+ return image_index;
+ if (!string.IsNullOrEmpty (image_key))
+ return TreeView.ImageList.Images.IndexOfKey (image_key);
+ if (!string.IsNullOrEmpty (TreeView.ImageKey))
+ return TreeView.ImageList.Images.IndexOfKey (TreeView.ImageKey);
+ if (TreeView.ImageIndex >= 0)
+ return TreeView.ImageIndex;
+ }
+
+ if (TreeView.ImageList.Images.Count > 0)
+ return 0;
+
+ return -1;
+ }
+ }
+ #endregion // Internal & Private Methods and Properties
+ }
+}
diff --git a/source/ShiftUI/Widgets/TreeView.cs b/source/ShiftUI/Widgets/TreeView.cs
new file mode 100644
index 0000000..0e08ece
--- /dev/null
+++ b/source/ShiftUI/Widgets/TreeView.cs
@@ -0,0 +1,2438 @@
+// 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.
+//
+// Authors:
+// Jackson Harper ([email protected])
+// Kazuki Oikawa ([email protected])
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [ComVisible (true)]
+ [Docking (DockingBehavior.Ask)]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [DefaultProperty("Nodes")]
+ [DefaultEvent("AfterSelect")]
+ //[Designer("ShiftUI.Design.TreeViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [ToolboxWidget]
+ public class TreeView : Widget {
+ #region Fields
+ private string path_separator = "\\";
+ private int item_height = -1;
+ internal bool sorted;
+ internal TreeNode root_node;
+ internal bool nodes_added;
+ private TreeNodeCollection nodes;
+
+ private TreeViewAction selection_action;
+ internal TreeNode selected_node;
+ private TreeNode pre_selected_node;
+ private TreeNode focused_node;
+ internal TreeNode highlighted_node;
+ private Rectangle mouse_rect;
+ private bool select_mmove;
+
+ private ImageList image_list;
+ private int image_index = -1;
+ private int selected_image_index = -1;
+
+ private string image_key;
+ private bool is_hovering;
+ private TreeNode mouse_click_node;
+ private bool right_to_left_layout;
+ private string selected_image_key;
+ private bool show_node_tool_tips;
+ private ImageList state_image_list;
+ private TreeNode tooltip_currently_showing;
+ private ToolTip tooltip_window;
+ private bool full_row_select;
+ private bool hot_tracking;
+ private int indent = 19;
+
+ private NodeLabelEditEventArgs edit_args;
+ private LabelEditTextBox edit_text_box;
+ internal TreeNode edit_node;
+
+ private bool checkboxes;
+ private bool label_edit;
+ private bool scrollable = true;
+ private bool show_lines = true;
+ private bool show_root_lines = true;
+ private bool show_plus_minus = true;
+ private bool hide_selection = true;
+
+ private int max_visible_order = -1;
+ internal VScrollBar vbar;
+ internal HScrollBar hbar;
+ private bool vbar_bounds_set;
+ private bool hbar_bounds_set;
+ internal int skipped_nodes;
+ internal int hbar_offset;
+
+ private int update_stack;
+ private bool update_needed;
+
+ private Pen dash;
+ private Color line_color;
+ private StringFormat string_format;
+
+ private int drag_begin_x = -1;
+ private int drag_begin_y = -1;
+ private long handle_count = 1;
+
+ private TreeViewDrawMode draw_mode;
+
+ IComparer tree_view_node_sorter;
+ #endregion // Fields
+
+ #region Public Constructors
+ public TreeView ()
+ {
+ vbar = new ImplicitVScrollBar ();
+ hbar = new ImplicitHScrollBar ();
+
+ InternalBorderStyle = BorderStyle.Fixed3D;
+ base.background_color = ThemeEngine.Current.ColorWindow;
+ base.foreground_color = ThemeEngine.Current.ColorWindowText;
+ draw_mode = TreeViewDrawMode.Normal;
+
+ root_node = new TreeNode (this);
+ root_node.Text = "ROOT NODE";
+ nodes = new TreeNodeCollection (root_node);
+ root_node.SetNodes (nodes);
+
+ MouseDown += new MouseEventHandler (MouseDownHandler);
+ MouseUp += new MouseEventHandler(MouseUpHandler);
+ MouseMove += new MouseEventHandler(MouseMoveHandler);
+ SizeChanged += new EventHandler (SizeChangedHandler);
+ FontChanged += new EventHandler (FontChangedHandler);
+ LostFocus += new EventHandler (LostFocusHandler);
+ GotFocus += new EventHandler (GotFocusHandler);
+ MouseWheel += new MouseEventHandler(MouseWheelHandler);
+ VisibleChanged += new EventHandler (VisibleChangedHandler);
+
+ SetStyle (Widgetstyles.UserPaint | Widgetstyles.StandardClick | Widgetstyles.UseTextForAccessibility, false);
+
+ string_format = new StringFormat ();
+ string_format.LineAlignment = StringAlignment.Center;
+ string_format.Alignment = StringAlignment.Center;
+
+ vbar.Visible = false;
+ hbar.Visible = false;
+ vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
+ hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
+
+ SuspendLayout ();
+ Widgets.AddImplicit (vbar);
+ Widgets.AddImplicit (hbar);
+ ResumeLayout ();
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ public override Color BackColor {
+ get { return base.BackColor;}
+ set {
+ base.BackColor = value;
+
+ CreateDashPen ();
+ Invalidate ();
+ }
+ }
+
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set { base.BackgroundImage = value; }
+ }
+
+ [DefaultValue(BorderStyle.Fixed3D)]
+ [DispId(-504)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set { InternalBorderStyle = value; }
+ }
+
+ [DefaultValue(false)]
+ public bool CheckBoxes {
+ get { return checkboxes; }
+ set {
+ if (value == checkboxes)
+ return;
+ checkboxes = value;
+
+ // Match a "bug" in the MS implementation where disabling checkboxes
+ // collapses the entire tree, but enabling them does not affect the
+ // state of the tree.
+ if (!checkboxes)
+ root_node.CollapseAllUncheck ();
+
+ Invalidate ();
+
+ // UIA Framework Event: CheckBoxes Changed
+ OnUIACheckBoxesChanged (EventArgs.Empty);
+ }
+ }
+
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set { base.ForeColor = value; }
+ }
+ [DefaultValue(false)]
+ public bool FullRowSelect {
+ get { return full_row_select; }
+ set {
+ if (value == full_row_select)
+ return;
+ full_row_select = value;
+ Invalidate ();
+ }
+ }
+ [DefaultValue(true)]
+ public bool HideSelection {
+ get { return hide_selection; }
+ set {
+ if (hide_selection == value)
+ return;
+ hide_selection = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue(false)]
+ public bool HotTracking {
+ get { return hot_tracking; }
+ set { hot_tracking = value; }
+ }
+
+ [DefaultValue (-1)]
+ [RelatedImageList ("ImageList")]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
+ //[Editor("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Localizable(true)]
+ public int ImageIndex {
+ get { return image_index; }
+ set {
+ if (value < -1) {
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
+ "'value' must be greater than or equal to 0.");
+ }
+ if (image_index == value)
+ return;
+ image_index = value;
+ Invalidate ();
+ }
+ }
+
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [DefaultValue(null)]
+ public ImageList ImageList {
+ get { return image_list; }
+ set {
+ image_list = value;
+ Invalidate ();
+ }
+ }
+
+ [Localizable(true)]
+ public int Indent {
+ get { return indent; }
+ set {
+ if (indent == value)
+ return;
+ if (value > 32000) {
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
+ "'Indent' must be less than or equal to 32000");
+ }
+ if (value < 0) {
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
+ "'Indent' must be greater than or equal to 0.");
+ }
+ indent = value;
+ Invalidate ();
+ }
+ }
+
+ public int ItemHeight {
+ get {
+ if (item_height == -1)
+ return FontHeight + 3;
+ return item_height;
+ }
+ set {
+ if (value == item_height)
+ return;
+ item_height = value;
+ Invalidate ();
+ }
+ }
+
+ internal int ActualItemHeight {
+ get {
+ int res = ItemHeight;
+ if (ImageList != null && ImageList.ImageSize.Height > res)
+ res = ImageList.ImageSize.Height;
+ return res;
+ }
+ }
+
+ [DefaultValue(false)]
+ public bool LabelEdit {
+ get { return label_edit; }
+ set {
+ label_edit = value;
+
+ // UIA Framework Event: LabelEdit Changed
+ OnUIALabelEditChanged (EventArgs.Empty);
+ }
+ }
+
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+ [MergableProperty(false)]
+ [Localizable(true)]
+ public TreeNodeCollection Nodes {
+ get { return nodes; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return base.Padding; }
+ set { base.Padding = value; }
+ }
+
+ [DefaultValue("\\")]
+ public string PathSeparator {
+ get { return path_separator; }
+ set { path_separator = value; }
+ }
+
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public virtual bool RightToLeftLayout {
+ get { return right_to_left_layout; }
+ set {
+ if (right_to_left_layout != value) {
+ right_to_left_layout = value;
+ OnRightToLeftLayoutChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool Scrollable {
+ get { return scrollable; }
+ set {
+ if (scrollable == value)
+ return;
+ scrollable = value;
+ UpdateScrollBars (false);
+ }
+ }
+
+ [DefaultValue (-1)]
+ [RelatedImageList ("ImageList")]
+ [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
+ //[Editor("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Localizable(true)]
+ public int SelectedImageIndex {
+ get { return selected_image_index; }
+ set {
+ if (value < -1) {
+ throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
+ "'value' must be greater than or equal to 0.");
+ }
+ UpdateNode (SelectedNode);
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public TreeNode SelectedNode {
+ get {
+ if (!IsHandleCreated)
+ return pre_selected_node;
+ return selected_node;
+ }
+ set {
+ if (!IsHandleCreated) {
+ pre_selected_node = value;
+ return;
+ }
+
+ if (selected_node == value) {
+ selection_action = TreeViewAction.Unknown;
+ return;
+ }
+
+ if (value != null) {
+ TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (value, false, selection_action);
+ OnBeforeSelect (e);
+
+ if (e.Cancel)
+ return;
+ }
+
+ Rectangle invalid = Rectangle.Empty;
+
+ if (selected_node != null) {
+ invalid = Bloat (selected_node.Bounds);
+ }
+ if (focused_node != null) {
+ invalid = Rectangle.Union (invalid,
+ Bloat (focused_node.Bounds));
+ }
+
+ if (value != null)
+ invalid = Rectangle.Union (invalid, Bloat (value.Bounds));
+
+ highlighted_node = value;
+ selected_node = value;
+ focused_node = value;
+
+ if (full_row_select || draw_mode != TreeViewDrawMode.Normal) {
+ invalid.X = 0;
+ invalid.Width = ViewportRectangle.Width;
+ }
+
+ if (invalid != Rectangle.Empty)
+ Invalidate (invalid);
+
+ // We ensure its visible after we update because
+ // scrolling is used for insure visible
+ if (selected_node != null)
+ selected_node.EnsureVisible ();
+
+ if (value != null) {
+ OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction.Unknown));
+ }
+ selection_action = TreeViewAction.Unknown;
+ }
+ }
+
+ private Rectangle Bloat (Rectangle rect)
+ {
+ rect.Y--;
+ rect.X--;
+ rect.Height += 2;
+ rect.Width += 2;
+ return rect;
+ }
+
+ [DefaultValue(true)]
+ public bool ShowLines {
+ get { return show_lines; }
+ set {
+ if (show_lines == value)
+ return;
+ show_lines = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool ShowNodeToolTips {
+ get { return show_node_tool_tips; }
+ set { show_node_tool_tips = value; }
+ }
+
+ [DefaultValue(true)]
+ public bool ShowPlusMinus {
+ get { return show_plus_minus; }
+ set {
+ if (show_plus_minus == value)
+ return;
+ show_plus_minus = value;
+ Invalidate ();
+ }
+ }
+
+ [DefaultValue(true)]
+ public bool ShowRootLines {
+ get { return show_root_lines; }
+ set {
+ if (show_root_lines == value)
+ return;
+ show_root_lines = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [DefaultValue(false)]
+ public bool Sorted {
+ get { return sorted; }
+ set {
+ if (sorted == value)
+ return;
+ sorted = value;
+ //LAMESPEC: The documentation says that setting this to true should sort alphabetically if TreeViewNodeSorter is set.
+ // There seems to be a bug in the Microsoft implementation.
+ if (sorted && tree_view_node_sorter == null) {
+ Sort (null);
+ }
+ }
+ }
+
+ [DefaultValue (null)]
+ public ImageList StateImageList {
+ get { return state_image_list; }
+ set {
+ state_image_list = value;
+ Invalidate ();
+ }
+ }
+
+ [Browsable(false)]
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ [Bindable(false)]
+ public override string Text {
+ get { return base.Text; }
+ set { base.Text = value; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public TreeNode TopNode {
+ get {
+ if (root_node.FirstNode == null)
+ return null;
+ OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (root_node.FirstNode);
+ one.MoveNext ();
+ for (int i = 0; i < skipped_nodes; i++)
+ one.MoveNext ();
+ return one.CurrentNode;
+ }
+ set {
+ SetTop (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public IComparer TreeViewNodeSorter {
+ get {
+ return tree_view_node_sorter;
+ }
+ set {
+ tree_view_node_sorter = value;
+ if (tree_view_node_sorter != null) {
+ Sort();
+ //LAMESPEC: The documentation says that setting this should set Sorted to false.
+ // There seems to be a bug in the Microsoft implementation.
+ sorted = true;
+ }
+ }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int VisibleCount {
+ get {
+ return ViewportRectangle.Height / ActualItemHeight;
+ }
+ }
+
+ /// According to MSDN this property has no effect on the treeview
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ protected override bool DoubleBuffered {
+ get { return base.DoubleBuffered; }
+ set { base.DoubleBuffered = value; }
+ }
+
+ [DefaultValue ("Color [Black]")]
+ public Color LineColor {
+ get {
+ if (line_color == Color.Empty) {
+ Color res = WidgetPaint.Dark (BackColor);
+ if (res == BackColor)
+ res = WidgetPaint.Light (BackColor);
+ return res;
+ }
+ return line_color;
+ }
+ set {
+ line_color = value;
+ if (show_lines) {
+ CreateDashPen ();
+ Invalidate ();
+ }
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue ("")]
+ [RelatedImageList ("ImageList")]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string ImageKey {
+ get { return image_key; }
+ set {
+ if (image_key == value)
+ return;
+ image_index = -1;
+ image_key = value;
+ Invalidate ();
+ }
+ }
+
+ [Localizable (true)]
+ [DefaultValue ("")]
+ [RelatedImageList ("ImageList")]
+ [RefreshProperties (RefreshProperties.Repaint)]
+ [TypeConverter (typeof (ImageKeyConverter))]
+ //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ public string SelectedImageKey {
+ get { return selected_image_key; }
+ set {
+ if (selected_image_key == value)
+ return;
+ selected_image_index = -1;
+ selected_image_key = value;
+ UpdateNode (SelectedNode);
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue (TreeViewDrawMode.Normal)]
+ public TreeViewDrawMode DrawMode {
+ get { return draw_mode; }
+ set { draw_mode = value; }
+ }
+ #endregion // Public Instance Properties
+
+ #region UIA Framework Properties
+ internal ScrollBar UIAHScrollBar {
+ get { return hbar; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return vbar; }
+ }
+ #endregion // UIA Framework Properties
+
+ #region Protected Instance Properties
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+ return cp;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (121, 97); }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+ public void BeginUpdate ()
+ {
+ update_stack++;
+ }
+
+ public void EndUpdate ()
+ {
+ if (update_stack > 1) {
+ update_stack--;
+ } else {
+ update_stack = 0;
+ if (update_needed) {
+ RecalculateVisibleOrder (root_node);
+ UpdateScrollBars (false);
+ // if (SelectedNode != null)
+ // SelectedNode.EnsureVisible ();
+ Invalidate (ViewportRectangle);
+ update_needed = false;
+ }
+ }
+ }
+
+ public void Sort ()
+ {
+ Sort (tree_view_node_sorter);
+ }
+
+ void Sort (IComparer sorter)
+ {
+ sorted = true;
+ Nodes.Sort (sorter);
+ RecalculateVisibleOrder (root_node);
+ UpdateScrollBars (false);
+ Invalidate ();
+ }
+
+ void SetVScrollValue (int value)
+ {
+ if (value > vbar.Maximum)
+ value = vbar.Maximum;
+ else if (value < vbar.Minimum)
+ value = vbar.Minimum;
+
+ vbar.Value = value;
+ }
+
+ public void ExpandAll ()
+ {
+ BeginUpdate ();
+ root_node.ExpandAll ();
+
+ EndUpdate ();
+
+ ///
+ /// Everything below this is basically an emulation of a strange bug on MS
+ /// where they don't always scroll to the last node on ExpandAll
+ ///
+ if (!IsHandleCreated)
+ return;
+
+ bool found = false;
+ foreach (TreeNode child in Nodes) {
+ if (child.Nodes.Count > 0)
+ found = true;
+ }
+
+ if (!found)
+ return;
+
+ if (IsHandleCreated && vbar.VisibleInternal) {
+ SetVScrollValue (vbar.Maximum - VisibleCount + 1);
+ } else {
+ RecalculateVisibleOrder (root_node);
+ UpdateScrollBars (true);
+
+ // Only move the top node if we now have a scrollbar
+ if (vbar.VisibleInternal) {
+ SetTop (Nodes [Nodes.Count - 1]);
+ SelectedNode = Nodes [Nodes.Count - 1];
+ }
+ }
+ }
+
+ public void CollapseAll ()
+ {
+ BeginUpdate ();
+ root_node.CollapseAll ();
+ EndUpdate ();
+
+ if (vbar.VisibleInternal)
+ SetVScrollValue (vbar.Maximum - VisibleCount + 1);
+ }
+
+ public TreeNode GetNodeAt (Point pt) {
+ return GetNodeAt (pt.Y);
+ }
+
+ public TreeNode GetNodeAt (int x, int y)
+ {
+ return GetNodeAt (y);
+ }
+
+ private TreeNode GetNodeAtUseX (int x, int y) {
+ TreeNode node = GetNodeAt (y);
+ if (node == null || !(IsTextArea (node, x) || full_row_select))
+ return null;
+ return node;
+
+ }
+
+ public int GetNodeCount (bool includeSubTrees) {
+ return root_node.GetNodeCount (includeSubTrees);
+ }
+
+ public TreeViewHitTestInfo HitTest (Point pt)
+ {
+ return HitTest (pt.X, pt.Y);
+ }
+
+ public TreeViewHitTestInfo HitTest (int x, int y)
+ {
+ TreeNode n = GetNodeAt (y);
+
+ if (n == null)
+ return new TreeViewHitTestInfo (null, TreeViewHitTestLocations.None);
+
+ if (IsTextArea (n, x))
+ return new TreeViewHitTestInfo (n, TreeViewHitTestLocations.Label);
+ else if (IsPlusMinusArea (n, x))
+ return new TreeViewHitTestInfo (n, TreeViewHitTestLocations.PlusMinus);
+ else if ((checkboxes || n.StateImage != null) && IsCheckboxArea (n, x))
+ return new TreeViewHitTestInfo (n, TreeViewHitTestLocations.StateImage);
+ else if (x > n.Bounds.Right)
+ return new TreeViewHitTestInfo (n, TreeViewHitTestLocations.RightOfLabel);
+ else if (IsImage (n, x))
+ return new TreeViewHitTestInfo (n, TreeViewHitTestLocations.Image);
+ else
+ return new TreeViewHitTestInfo (null, TreeViewHitTestLocations.Indent);
+ }
+
+ public override string ToString () {
+ int count = Nodes.Count;
+ if (count <= 0)
+ return String.Concat (base.ToString (), ", Nodes.Count: 0");
+ return String.Concat (base.ToString (), ", Nodes.Count: ", count, ", Nodes[0]: ", Nodes [0]);
+
+ }
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+ protected override void CreateHandle () {
+ base.CreateHandle ();
+ RecalculateVisibleOrder (root_node);
+ UpdateScrollBars (false);
+
+ if (pre_selected_node != null)
+ SelectedNode = pre_selected_node;
+ }
+
+ protected override void Dispose (bool disposing) {
+ if (disposing)
+ image_list = null;
+
+ base.Dispose (disposing);
+ }
+
+ protected OwnerDrawPropertyBag GetItemRenderStyles (TreeNode node, int state) {
+ return node.prop_bag;
+ }
+
+ protected override bool IsInputKey (Keys keyData)
+ {
+ if (IsHandleCreated && (keyData & Keys.Alt) == 0) {
+ switch (keyData & Keys.KeyCode) {
+ case Keys.Left:
+ case Keys.Up:
+ case Keys.Right:
+ case Keys.Down:
+ return true;
+ case Keys.Enter:
+ case Keys.Escape:
+ case Keys.Prior:
+ case Keys.Next:
+ case Keys.End:
+ case Keys.Home:
+ if (edit_node != null)
+ return true;
+
+ break;
+ }
+ }
+ return base.IsInputKey (keyData);
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ OpenTreeNodeEnumerator ne;
+
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Add:
+ if (selected_node != null && selected_node.IsExpanded)
+ selected_node.Expand ();
+ break;
+ case Keys.Subtract:
+ if (selected_node != null && selected_node.IsExpanded)
+ selected_node.Collapse ();
+ break;
+ case Keys.Left:
+ if (selected_node != null) {
+ if (selected_node.IsExpanded && selected_node.Nodes.Count > 0)
+ selected_node.Collapse ();
+ else {
+ TreeNode parent = selected_node.Parent;
+ if (parent != null) {
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = parent;
+ }
+ }
+ }
+ break;
+ case Keys.Right:
+ if (selected_node != null) {
+ if (!selected_node.IsExpanded)
+ selected_node.Expand ();
+ else {
+ TreeNode child = selected_node.FirstNode;
+ if (child != null)
+ SelectedNode = child;
+ }
+ }
+ break;
+ case Keys.Up:
+ if (selected_node != null) {
+ ne = new OpenTreeNodeEnumerator (selected_node);
+ if (ne.MovePrevious () && ne.MovePrevious ()) {
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ }
+ break;
+ case Keys.Down:
+ if (selected_node != null) {
+ ne = new OpenTreeNodeEnumerator (selected_node);
+ if (ne.MoveNext () && ne.MoveNext ()) {
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ }
+ break;
+ case Keys.Home:
+ if (root_node.Nodes.Count > 0) {
+ ne = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
+ if (ne.MoveNext ()) {
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ }
+ break;
+ case Keys.End:
+ if (root_node.Nodes.Count > 0) {
+ ne = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
+ while (ne.MoveNext ())
+ { }
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ break;
+ case Keys.PageDown:
+ if (selected_node != null) {
+ ne = new OpenTreeNodeEnumerator (selected_node);
+ int move = VisibleCount;
+ for (int i = 0; i < move && ne.MoveNext (); i++) {
+
+ }
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ break;
+ case Keys.PageUp:
+ if (selected_node != null) {
+ ne = new OpenTreeNodeEnumerator (selected_node);
+ int move = VisibleCount;
+ for (int i = 0; i < move && ne.MovePrevious (); i++)
+ { }
+ selection_action = TreeViewAction.ByKeyboard;
+ SelectedNode = ne.CurrentNode;
+ }
+ break;
+ case Keys.Multiply:
+ if (selected_node != null)
+ selected_node.ExpandAll ();
+ break;
+ }
+ base.OnKeyDown (e);
+
+ if (!e.Handled && checkboxes &&
+ selected_node != null &&
+ (e.KeyData & Keys.KeyCode) == Keys.Space) {
+ selected_node.check_reason = TreeViewAction.ByKeyboard;
+ selected_node.Checked = !selected_node.Checked;
+ e.Handled = true;
+ }
+ }
+
+ protected override void OnKeyPress (KeyPressEventArgs e)
+ {
+ base.OnKeyPress (e);
+ if (e.KeyChar == ' ')
+ e.Handled = true;
+ }
+
+ protected override void OnKeyUp (KeyEventArgs e)
+ {
+ base.OnKeyUp (e);
+ if ((e.KeyData & Keys.KeyCode) == Keys.Space)
+ e.Handled = true;
+ }
+
+ protected override void OnMouseHover (EventArgs e)
+ {
+ base.OnMouseHover (e);
+
+ is_hovering = true;
+
+ TreeNode tn = GetNodeAt (PointToClient (MousePosition));
+
+ if (tn != null)
+ MouseEnteredItem (tn);
+ }
+
+ protected override void OnMouseLeave (EventArgs e)
+ {
+ base.OnMouseLeave (e);
+
+ is_hovering = false;
+
+ if (tooltip_currently_showing != null)
+ MouseLeftItem (tooltip_currently_showing);
+ }
+
+ protected virtual void OnNodeMouseClick (TreeNodeMouseClickEventArgs e)
+ {
+ TreeNodeMouseClickEventHandler eh = (TreeNodeMouseClickEventHandler)(Events[NodeMouseClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnNodeMouseDoubleClick (TreeNodeMouseClickEventArgs e)
+ {
+ TreeNodeMouseClickEventHandler eh = (TreeNodeMouseClickEventHandler)(Events[NodeMouseDoubleClickEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnNodeMouseHover (TreeNodeMouseHoverEventArgs e)
+ {
+ TreeNodeMouseHoverEventHandler eh = (TreeNodeMouseHoverEventHandler)(Events[NodeMouseHoverEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnItemDrag (ItemDragEventArgs e)
+ {
+ ItemDragEventHandler eh = (ItemDragEventHandler)(Events [ItemDragEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) {
+ DrawTreeNodeEventHandler eh = (DrawTreeNodeEventHandler)(Events[DrawNodeEvent]);
+ if (eh != null)
+ eh(this, e);
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected virtual void OnRightToLeftLayoutChanged (EventArgs e) {
+ EventHandler eh = (EventHandler)(Events[RightToLeftLayoutChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnAfterCheck (TreeViewEventArgs e) {
+ TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterCheckEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnAfterCollapse (TreeViewEventArgs e) {
+ TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterCollapseEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnAfterExpand (TreeViewEventArgs e) {
+ TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterExpandEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e) {
+ NodeLabelEditEventHandler eh = (NodeLabelEditEventHandler)(Events [AfterLabelEditEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnAfterSelect (TreeViewEventArgs e) {
+ TreeViewEventHandler eh = (TreeViewEventHandler)(Events [AfterSelectEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnBeforeCheck (TreeViewCancelEventArgs e) {
+ TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeCheckEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e) {
+ TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeCollapseEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e) {
+ TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeExpandEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e) {
+ NodeLabelEditEventHandler eh = (NodeLabelEditEventHandler)(Events [BeforeLabelEditEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e) {
+ TreeViewCancelEventHandler eh = (TreeViewCancelEventHandler)(Events [BeforeSelectEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated (e);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e) {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected override void WndProc(ref Message m) {
+ switch ((Msg) m.Msg) {
+
+ case Msg.WM_LBUTTONDBLCLK:
+ int val = m.LParam.ToInt32();
+ DoubleClickHandler (null, new
+ MouseEventArgs (MouseButtons.Left,
+ 2, val & 0xffff,
+ (val>>16) & 0xffff, 0));
+ break;
+ case Msg.WM_CONTEXTMENU:
+ if (WmContextMenu (ref m))
+ return;
+
+ break;
+ }
+ base.WndProc (ref m);
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region Internal & Private Methods and Properties
+ internal override bool ScaleChildrenInternal {
+ get { return false; }
+ }
+
+ internal IntPtr CreateNodeHandle ()
+ {
+ return (IntPtr) handle_count++;
+ }
+
+ // According to MSDN docs, for these to be raised,
+ // the click must occur over a TreeNode
+ internal override void HandleClick (int clicks, MouseEventArgs me)
+ {
+ if (GetNodeAt (me.Location) != null) {
+ if ((clicks > 1) && GetStyle (Widgetstyles.StandardDoubleClick)) {
+ OnDoubleClick (me);
+ OnMouseDoubleClick (me);
+ } else {
+ OnClick (me);
+ OnMouseClick (me);
+ }
+ }
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+
+ internal TreeNode NodeFromHandle (IntPtr handle)
+ {
+ // This method is called rarely, so instead of maintaining a table
+ // we just walk the tree nodes to find the matching handle
+ return NodeFromHandleRecursive (root_node, handle);
+ }
+
+ private TreeNode NodeFromHandleRecursive (TreeNode node, IntPtr handle)
+ {
+ if (node.handle == handle)
+ return node;
+ foreach (TreeNode child in node.Nodes) {
+ TreeNode match = NodeFromHandleRecursive (child, handle);
+ if (match != null)
+ return match;
+ }
+ return null;
+ }
+
+ internal Rectangle ViewportRectangle {
+ get {
+ Rectangle res = ClientRectangle;
+
+ if (vbar != null && vbar.Visible)
+ res.Width -= vbar.Width;
+ if (hbar != null && hbar.Visible)
+ res.Height -= hbar.Height;
+ return res;
+ }
+ }
+
+ private TreeNode GetNodeAt (int y)
+ {
+ if (nodes.Count <= 0)
+ return null;
+
+ OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (TopNode);
+ int move = y / ActualItemHeight;
+ for (int i = -1; i < move; i++) {
+ if (!o.MoveNext ())
+ return null;
+ }
+
+ return o.CurrentNode;
+ }
+
+ private bool IsTextArea (TreeNode node, int x)
+ {
+ return node != null && node.Bounds.Left <= x && node.Bounds.Right >= x;
+ }
+
+ private bool IsSelectableArea (TreeNode node, int x)
+ {
+ if (node == null)
+ return false;
+ int l = node.Bounds.Left;
+ if (ImageList != null)
+ l -= ImageList.ImageSize.Width;
+ return l <= x && node.Bounds.Right >= x;
+
+ }
+
+ private bool IsPlusMinusArea (TreeNode node, int x)
+ {
+ if (node.Nodes.Count == 0 || (node.parent == root_node && !show_root_lines))
+ return false;
+
+ int l = node.Bounds.Left + 5;
+
+ if (show_root_lines || node.Parent != null)
+ l -= indent;
+ if (ImageList != null)
+ l -= ImageList.ImageSize.Width + 3;
+ if (checkboxes)
+ l -= 19;
+ // StateImage is basically a custom checkbox
+ else if (node.StateImage != null)
+ l -= 19;
+ return (x > l && x < l + 8);
+ }
+
+ private bool IsCheckboxArea (TreeNode node, int x)
+ {
+ int l = CheckBoxLeft (node);
+ return (x > l && x < l + 10);
+ }
+
+ private bool IsImage (TreeNode node, int x)
+ {
+ if (ImageList == null)
+ return false;
+
+ int l = node.Bounds.Left;
+
+ l -= ImageList.ImageSize.Width + 5;
+
+ if (x >= l && x <= (l + ImageList.ImageSize.Width + 5))
+ return true;
+
+ return false;
+ }
+
+ private int CheckBoxLeft (TreeNode node)
+ {
+ int l = node.Bounds.Left + 5;
+
+ if (show_root_lines || node.Parent != null)
+ l -= indent;
+
+ if (!show_root_lines && node.Parent == null)
+ l -= indent;
+
+ if (ImageList != null)
+ l -= ImageList.ImageSize.Width + 3;
+
+ return l;
+ }
+
+ internal void RecalculateVisibleOrder (TreeNode start)
+ {
+ if (update_stack > 0)
+ return;
+
+ int order;
+ if (start == null) {
+ start = root_node;
+ order = 0;
+ } else
+ order = start.visible_order;
+
+
+
+ OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (start);
+ while (walk.MoveNext ()) {
+ walk.CurrentNode.visible_order = order;
+ order++;
+ }
+
+ max_visible_order = order;
+ }
+
+ internal void SetTop (TreeNode node)
+ {
+ int order = 0;
+ if (node != null)
+ order = Math.Max (0, node.visible_order - 1);
+
+ if (!vbar.is_visible) {
+ skipped_nodes = order;
+ return;
+ }
+
+ SetVScrollValue (Math.Min (order, vbar.Maximum - VisibleCount + 1));
+ }
+
+ internal void SetBottom (TreeNode node)
+ {
+ if (!vbar.is_visible)
+ return;
+
+ OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (node);
+
+ int bottom = ViewportRectangle.Bottom;
+ int offset = 0;
+ while (walk.MovePrevious ()) {
+ if (walk.CurrentNode.Bounds.Bottom <= bottom)
+ break;
+ offset++;
+ }
+
+ int nv = vbar.Value + offset;
+ if (vbar.Value + offset < vbar.Maximum) {
+ SetVScrollValue (nv);
+ } else {
+#if DEBUG
+ Console.Error.WriteLine ("setting bottom to value greater then maximum ({0}, {1})",
+ nv, vbar.Maximum);
+#endif
+ }
+
+ }
+
+ internal void UpdateBelow (TreeNode node)
+ {
+ if (update_stack > 0) {
+ update_needed = true;
+ return;
+ }
+
+ if (node == root_node) {
+ Invalidate (ViewportRectangle);
+ return;
+ }
+
+ // We need to update the current node so the plus/minus block gets update too
+ int top = Math.Max (node.Bounds.Top - 1, 0);
+ Rectangle invalid = new Rectangle (0, top,
+ Width, Height - top);
+ Invalidate (invalid);
+ }
+
+ internal void UpdateNode (TreeNode node)
+ {
+ if (node == null)
+ return;
+
+ if (update_stack > 0) {
+ update_needed = true;
+ return;
+ }
+
+ if (node == root_node) {
+ Invalidate ();
+ return;
+ }
+
+ Rectangle invalid = new Rectangle (0, node.Bounds.Top - 1, Width,
+ node.Bounds.Height + 1);
+ Invalidate (invalid);
+ }
+
+ internal void UpdateNodePlusMinus (TreeNode node)
+ {
+ if (update_stack > 0) {
+ update_needed = true;
+ return;
+ }
+
+ int l = node.Bounds.Left + 5;
+
+ if (show_root_lines || node.Parent != null)
+ l -= indent;
+ if (ImageList != null)
+ l -= ImageList.ImageSize.Width + 3;
+ if (checkboxes)
+ l -= 19;
+
+ Invalidate (new Rectangle (l, node.Bounds.Top, 8, node.Bounds.Height));
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pe)
+ {
+ Draw (pe.ClipRectangle, pe.Graphics);
+ }
+
+ internal void CreateDashPen ()
+ {
+ dash = new Pen (LineColor, 1);
+ dash.DashStyle = DashStyle.Dot;
+ }
+
+ private void Draw (Rectangle clip, Graphics dc)
+ {
+ dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
+
+ if (dash == null)
+ CreateDashPen ();
+
+ Rectangle viewport = ViewportRectangle;
+ Rectangle original_clip = clip;
+ if (clip.Bottom > viewport.Bottom)
+ clip.Height = viewport.Bottom - clip.Top;
+
+ OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (TopNode);
+ while (walk.MoveNext ()) {
+ TreeNode current = walk.CurrentNode;
+
+ // Haven't gotten to visible nodes yet
+ if (current.GetY () + ActualItemHeight < clip.Top)
+ continue;
+
+ // Past the visible nodes
+ if (current.GetY () > clip.Bottom)
+ break;
+
+ DrawTreeNode (current, dc, clip);
+ }
+
+ if (hbar.Visible && vbar.Visible) {
+ Rectangle corner = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
+ if (original_clip.IntersectsWith (corner))
+ dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl),
+ corner);
+ }
+ }
+
+ private void DrawNodeState (TreeNode node, Graphics dc, int x, int y)
+ {
+ if (node.Checked) {
+ if (StateImageList.Images[1] != null)
+ dc.DrawImage (StateImageList.Images[1], new Rectangle (x, y, 16, 16));
+ } else {
+ if (StateImageList.Images[0] != null)
+ dc.DrawImage (StateImageList.Images[0], new Rectangle (x, y, 16, 16));
+ }
+ }
+
+ private void DrawNodeCheckBox (TreeNode node, Graphics dc, int x, int middle)
+ {
+ Pen pen = ThemeEngine.Current.ResPool.GetSizedPen(Color.Black, 2);
+ dc.DrawRectangle (pen, x + 3, middle - 4, 11, 11);
+
+ if (node.Checked) {
+ Pen check_pen = ThemeEngine.Current.ResPool.GetPen(Color.Black);
+
+ int check_size = 5;
+ int lineWidth = 3;
+
+ Rectangle rect = new Rectangle (x + 4, middle - 3, check_size, check_size);
+
+ for (int i = 0; i < lineWidth; i++) {
+ dc.DrawLine (check_pen, rect.Left + 1, rect.Top + lineWidth + i, rect.Left + 3, rect.Top + 5 + i);
+ dc.DrawLine (check_pen, rect.Left + 3, rect.Top + 5 + i, rect.Left + 7, rect.Top + 1 + i);
+ }
+ }
+ }
+
+ private void DrawNodeLines (TreeNode node, Graphics dc, Rectangle clip, Pen dash, int x, int y, int middle)
+ {
+ int ladjust = 9;
+ int radjust = 0;
+
+ if (node.nodes.Count > 0 && show_plus_minus)
+ ladjust = 13;
+ if (checkboxes)
+ radjust = 3;
+
+ if (show_root_lines || node.Parent != null)
+ dc.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
+
+ if (node.PrevNode != null || node.Parent != null) {
+ ladjust = 9;
+ dc.DrawLine (dash, x - indent + ladjust, node.Bounds.Top,
+ x - indent + ladjust, middle - (show_plus_minus && node.Nodes.Count > 0 ? 4 : 0));
+ }
+
+ if (node.NextNode != null) {
+ ladjust = 9;
+ dc.DrawLine (dash, x - indent + ladjust, middle + (show_plus_minus && node.Nodes.Count > 0 ? 4 : 0),
+ x - indent + ladjust, node.Bounds.Bottom);
+
+ }
+
+ ladjust = 0;
+ if (show_plus_minus)
+ ladjust = 9;
+ TreeNode parent = node.Parent;
+ while (parent != null) {
+ if (parent.NextNode != null) {
+ int px = parent.GetLinesX () - indent + ladjust;
+ dc.DrawLine (dash, px, node.Bounds.Top, px, node.Bounds.Bottom);
+ }
+ parent = parent.Parent;
+ }
+ }
+
+ private void DrawNodeImage (TreeNode node, Graphics dc, Rectangle clip, int x, int y)
+ {
+ if (!RectsIntersect (clip, x, y, ImageList.ImageSize.Width, ImageList.ImageSize.Height))
+ return;
+
+ int use_index = node.Image;
+
+ if (use_index > -1 && use_index < ImageList.Images.Count)
+ ImageList.Draw (dc, x, y, ImageList.ImageSize.Width, ImageList.ImageSize.Height, use_index);
+ }
+
+ private void LabelEditFinished (object sender, EventArgs e)
+ {
+ EndEdit (edit_node);
+ }
+
+ internal void BeginEdit (TreeNode node)
+ {
+ if (edit_node != null)
+ EndEdit (edit_node);
+
+ if (edit_text_box == null) {
+ edit_text_box = new LabelEditTextBox ();
+ edit_text_box.BorderStyle = BorderStyle.FixedSingle;
+ edit_text_box.Visible = false;
+ edit_text_box.EditingCancelled += new EventHandler (LabelEditCancelled);
+ edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
+ edit_text_box.TextChanged += new EventHandler (LabelTextChanged);
+ Widgets.Add (edit_text_box);
+ }
+
+ node.EnsureVisible ();
+
+ edit_text_box.Bounds = node.Bounds;
+ edit_text_box.Text = node.Text;
+ edit_text_box.Visible = true;
+ edit_text_box.Focus ();
+ edit_text_box.SelectAll ();
+
+ edit_args = new NodeLabelEditEventArgs (node);
+ OnBeforeLabelEdit (edit_args);
+
+ edit_node = node;
+
+ if (edit_args.CancelEdit) {
+ edit_node = null;
+ EndEdit (node);
+ }
+ }
+
+ private void LabelEditCancelled (object sender, EventArgs e)
+ {
+ edit_args.SetLabel (null);
+ EndEdit (edit_node);
+ }
+
+ private void LabelTextChanged (object sender, EventArgs e)
+ {
+ int width = TextRenderer.MeasureTextInternal (edit_text_box.Text, edit_text_box.Font, false).Width + 4;
+ edit_text_box.Width = width;
+
+ if (edit_args != null)
+ edit_args.SetLabel (edit_text_box.Text);
+ }
+
+ internal void EndEdit (TreeNode node)
+ {
+ if (edit_text_box != null && edit_text_box.Visible) {
+ edit_text_box.Visible = false;
+ Focus ();
+ }
+
+ //
+ // If we get a call to BeginEdit from any AfterLabelEdit handler,
+ // the focus seems to always remain in the TreeView. This call seems
+ // to synchronize the focus events - I don't like it but it works
+ //
+ Application.DoEvents ();
+
+ if (edit_node != null && edit_node == node) {
+ edit_node = null;
+
+ NodeLabelEditEventArgs e = new NodeLabelEditEventArgs (edit_args.Node, edit_args.Label);
+
+ OnAfterLabelEdit (e);
+
+ if (e.CancelEdit)
+ return;
+
+ if (e.Label != null)
+ e.Node.Text = e.Label;
+ }
+
+ // EndEdit ends editing even if not called on the editing node
+ edit_node = null;
+ UpdateNode (node);
+ }
+
+ internal void CancelEdit (TreeNode node)
+ {
+ edit_args.SetLabel (null);
+
+ if (edit_text_box != null && edit_text_box.Visible) {
+ edit_text_box.Visible = false;
+ Focus ();
+ }
+
+ edit_node = null;
+ UpdateNode (node);
+ }
+
+ internal int GetNodeWidth (TreeNode node)
+ {
+ Font font = node.NodeFont;
+ if (node.NodeFont == null)
+ font = Font;
+ return (int)TextRenderer.MeasureString (node.Text, font, 0, string_format).Width + 3;
+ }
+
+ private void DrawSelectionAndFocus(TreeNode node, Graphics dc, Rectangle r)
+ {
+ if (Focused && focused_node == node && !full_row_select) {
+ WidgetPaint.DrawFocusRectangle (dc, r, ForeColor, BackColor);
+ }
+ if (draw_mode != TreeViewDrawMode.Normal)
+ return;
+
+ r.Inflate (-1, -1);
+
+ if (Focused && node == highlighted_node) {
+ // Use the node's BackColor if is not empty, and is not actually the selected one (yet)
+ Color back_color = node != selected_node && node.BackColor != Color.Empty ? node.BackColor :
+ ThemeEngine.Current.ColorHighlight;
+ dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (back_color), r);
+
+ } else if (!hide_selection && node == highlighted_node) {
+ dc.FillRectangle (SystemBrushes.Control, r);
+ } else {
+ // If selected_node is not the current highlighted one, use the color of the TreeView
+ Color back_color = node == selected_node ? BackColor : node.BackColor;
+ dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (back_color), r);
+ }
+ }
+
+ private void DrawStaticNode (TreeNode node, Graphics dc)
+ {
+ if (!full_row_select || show_lines)
+ DrawSelectionAndFocus(node, dc, node.Bounds);
+
+
+ Font font = node.NodeFont;
+ if (node.NodeFont == null)
+ font = Font;
+ Color text_color = (Focused && node == highlighted_node ?
+ ThemeEngine.Current.ColorHighlightText : node.ForeColor);
+ if (text_color.IsEmpty)
+ text_color = ForeColor;
+ dc.DrawString (node.Text, font,
+ ThemeEngine.Current.ResPool.GetSolidBrush (text_color),
+ node.Bounds, string_format);
+ }
+
+ private void DrawTreeNode (TreeNode node, Graphics dc, Rectangle clip)
+ {
+ int child_count = node.nodes.Count;
+ int y = node.GetY ();
+ int middle = y + (ActualItemHeight / 2);
+
+ if (full_row_select && !show_lines) {
+ Rectangle r = new Rectangle (1, y, ViewportRectangle.Width - 2, ActualItemHeight);
+ DrawSelectionAndFocus (node, dc, r);
+ }
+
+ if (draw_mode == TreeViewDrawMode.Normal || draw_mode == TreeViewDrawMode.OwnerDrawText) {
+ if ((show_root_lines || node.Parent != null) && show_plus_minus && child_count > 0)
+ ThemeEngine.Current.TreeViewDrawNodePlusMinus (this, node, dc, node.GetLinesX () - Indent + 5, middle);
+
+ if (checkboxes && state_image_list == null)
+ DrawNodeCheckBox (node, dc, CheckBoxLeft (node) - 3, middle);
+
+ if (checkboxes && state_image_list != null)
+ DrawNodeState (node, dc, CheckBoxLeft (node) - 3, y);
+
+ if (!checkboxes && node.StateImage != null)
+ dc.DrawImage (node.StateImage, new Rectangle (CheckBoxLeft (node) - 3, y, 16, 16));
+
+ if (show_lines)
+ DrawNodeLines (node, dc, clip, dash, node.GetLinesX (), y, middle);
+
+ if (ImageList != null)
+ DrawNodeImage (node, dc, clip, node.GetImageX (), y);
+ }
+
+ if (draw_mode != TreeViewDrawMode.Normal) {
+ dc.FillRectangle (Brushes.White, node.Bounds);
+ TreeNodeStates tree_node_state = TreeNodeStates.Default;;
+ if (node.IsSelected)
+ tree_node_state = TreeNodeStates.Selected;
+ if (node.Checked)
+ tree_node_state |= TreeNodeStates.Checked;
+ if (node == focused_node)
+ tree_node_state |= TreeNodeStates.Focused;
+ Rectangle node_bounds = node.Bounds;
+ if (draw_mode == TreeViewDrawMode.OwnerDrawText) {
+ node_bounds.X += 3;
+ node_bounds.Y += 1;
+ } else {
+ node_bounds.X = 0;
+ node_bounds.Width = Width;
+ }
+
+ DrawTreeNodeEventArgs e = new DrawTreeNodeEventArgs (dc, node, node_bounds, tree_node_state);
+
+ OnDrawNode (e);
+ if (!e.DrawDefault)
+ return;
+ }
+
+ if (!node.IsEditing)
+ DrawStaticNode (node, dc);
+ }
+
+ internal void UpdateScrollBars (bool force)
+ {
+ if (!force && (IsDisposed || update_stack > 0 || !IsHandleCreated || !Visible))
+ return;
+
+ bool vert = false;
+ bool horz = false;
+ int height = 0;
+ int width = -1;
+
+ int item_height = ActualItemHeight;
+ if (scrollable) {
+ OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (root_node);
+
+ while (walk.MoveNext ()) {
+ int r = walk.CurrentNode.Bounds.Right;
+ if (r > width)
+ width = r;
+
+ height += item_height;
+ }
+
+ height -= item_height; // root_node adjustment
+ width += hbar_offset;
+
+ if (height > ClientRectangle.Height) {
+ vert = true;
+
+ if (width > ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth)
+ horz = true;
+ } else if (width > ClientRectangle.Width) {
+ horz = true;
+ }
+
+ if (!vert && horz && height > ClientRectangle.Height - SystemInformation.HorizontalScrollBarHeight)
+ vert = true;
+ }
+
+ if (vert) {
+ int visible_height = horz ? ClientRectangle.Height - hbar.Height : ClientRectangle.Height;
+ vbar.SetValues (Math.Max (0, max_visible_order - 2), visible_height / ActualItemHeight);
+ /*
+ vbar.Maximum = max_visible_order;
+ vbar.LargeChange = ClientRectangle.Height / ItemHeight;
+ */
+
+ if (!vbar_bounds_set) {
+ vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width, 0, vbar.Width,
+ ClientRectangle.Height -
+ (horz ? SystemInformation.VerticalScrollBarWidth : 0));
+ vbar_bounds_set = true;
+
+ // We need to recalc the hbar if the vbar is now visible
+ hbar_bounds_set = false;
+ }
+
+
+ vbar.Visible = true;
+ if (skipped_nodes > 0) {
+ int skip = Math.Min (skipped_nodes, vbar.Maximum - VisibleCount + 1);
+ skipped_nodes = 0;
+ vbar.SafeValueSet (skip);
+ skipped_nodes = skip;
+ }
+ } else {
+ skipped_nodes = 0;
+ RecalculateVisibleOrder (root_node);
+ vbar.Visible = false;
+ SetVScrollValue (0);
+ vbar_bounds_set = false;
+ }
+
+ if (horz) {
+ hbar.SetValues (width + 1, ClientRectangle.Width - (vert ? SystemInformation.VerticalScrollBarWidth : 0));
+ /*
+ hbar.LargeChange = ClientRectangle.Width;
+ hbar.Maximum = width + 1;
+ */
+
+ if (!hbar_bounds_set) {
+ hbar.Bounds = new Rectangle (0, ClientRectangle.Height - hbar.Height,
+ ClientRectangle.Width - (vert ? SystemInformation.VerticalScrollBarWidth : 0),
+ hbar.Height);
+ hbar_bounds_set = true;
+ }
+ hbar.Visible = true;
+ } else {
+ hbar_offset = 0;
+ hbar.Visible = false;
+ hbar_bounds_set = false;
+ }
+ }
+
+ private void SizeChangedHandler (object sender, EventArgs e)
+ {
+ if (IsHandleCreated) {
+ if (max_visible_order == -1)
+ RecalculateVisibleOrder (root_node);
+ UpdateScrollBars (false);
+ }
+
+ if (vbar.Visible) {
+ vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width, 0, vbar.Width,
+ ClientRectangle.Height - (hbar.Visible ? SystemInformation.HorizontalScrollBarHeight : 0));
+ }
+
+ if (hbar.Visible) {
+ hbar.Bounds = new Rectangle (0, ClientRectangle.Height - hbar.Height,
+ ClientRectangle.Width - (vbar.Visible ? SystemInformation.VerticalScrollBarWidth : 0), hbar.Height);
+ }
+ }
+
+ private void VScrollBarValueChanged (object sender, EventArgs e)
+ {
+ if (edit_node != null)
+ EndEdit (edit_node);
+
+ SetVScrollPos (vbar.Value, null);
+ }
+
+ private void SetVScrollPos (int pos, TreeNode new_top)
+ {
+ if (!vbar.VisibleInternal)
+ return;
+
+ if (pos < 0)
+ pos = 0;
+
+ if (skipped_nodes == pos)
+ return;
+
+ int diff = skipped_nodes - pos;
+ skipped_nodes = pos;
+
+ if (!IsHandleCreated)
+ return;
+
+ int y_move = diff * ActualItemHeight;
+ XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, false);
+ }
+
+ /*private void SetVScrollTop (TreeNode new_top)
+ {
+ vbar.Value = new_top.visible_order - VisibleCount;
+ }*/
+
+ private void HScrollBarValueChanged(object sender, EventArgs e)
+ {
+ if (edit_node != null)
+ EndEdit (edit_node);
+
+ int old_offset = hbar_offset;
+ hbar_offset = hbar.Value;
+
+ if (hbar_offset < 0) {
+ hbar_offset = 0;
+ }
+
+ XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, false);
+ }
+
+ internal void ExpandBelow (TreeNode node, int count_to_next)
+ {
+ if (update_stack > 0) {
+ update_needed = true;
+ return;
+ }
+
+ // If node Bottom is less than 0, the node is above and not visible,
+ // and we need to scroll the entire viewport
+ int node_bottom = node.Bounds.Bottom >= 0 ? node.Bounds.Bottom : 0;
+ Rectangle below = new Rectangle (0, node_bottom, ViewportRectangle.Width,
+ ViewportRectangle.Height - node_bottom);
+
+ int amount = count_to_next * ActualItemHeight;
+
+ if (amount > 0)
+ XplatUI.ScrollWindow (Handle, below, 0, amount, false);
+
+ if (show_plus_minus) {
+ Invalidate (new Rectangle (0, node.GetY (), Width, ActualItemHeight));
+ }
+ }
+
+ internal void CollapseBelow (TreeNode node, int count_to_next)
+ {
+ if (update_stack > 0) {
+ update_needed = true;
+ return;
+ }
+
+ Rectangle below = new Rectangle (0, node.Bounds.Bottom, ViewportRectangle.Width,
+ ViewportRectangle.Height - node.Bounds.Bottom);
+
+ int amount = count_to_next * ActualItemHeight;
+
+ if (amount > 0)
+ XplatUI.ScrollWindow (Handle, below, 0, -amount, false);
+
+ if (show_plus_minus) {
+ Invalidate (new Rectangle (0, node.GetY (), Width, ActualItemHeight));
+ }
+ }
+
+ private void MouseWheelHandler(object sender, MouseEventArgs e) {
+
+ if (vbar == null || !vbar.is_visible) {
+ return;
+ }
+
+ if (e.Delta < 0) {
+ SetVScrollValue (Math.Min (vbar.Value + SystemInformation.MouseWheelScrollLines, vbar.Maximum - VisibleCount + 1));
+ } else {
+ SetVScrollValue (Math.Max (0, vbar.Value - SystemInformation.MouseWheelScrollLines));
+ }
+ }
+
+ private void VisibleChangedHandler (object sender, EventArgs e)
+ {
+ if (Visible) {
+ UpdateScrollBars (false);
+ }
+ }
+
+ private void FontChangedHandler (object sender, EventArgs e)
+ {
+ if (IsHandleCreated) {
+ TreeNode top = TopNode;
+ InvalidateNodeWidthRecursive (root_node);
+
+ SetTop (top);
+ }
+ }
+
+ private void InvalidateNodeWidthRecursive (TreeNode node)
+ {
+ node.InvalidateWidth ();
+ foreach (TreeNode child in node.Nodes) {
+ InvalidateNodeWidthRecursive (child);
+ }
+ }
+
+ private void GotFocusHandler (object sender, EventArgs e)
+ {
+ if (selected_node == null) {
+ if (pre_selected_node != null) {
+ SelectedNode = pre_selected_node;
+ return;
+ }
+
+ SelectedNode = TopNode;
+
+ } else if (selected_node != null)
+ UpdateNode (selected_node);
+ }
+
+ private void LostFocusHandler (object sender, EventArgs e)
+ {
+ UpdateNode (SelectedNode);
+ }
+
+ private void MouseDownHandler (object sender, MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Right)
+ Focus ();
+
+ TreeNode node = GetNodeAt (e.Y);
+ if (node == null)
+ return;
+
+ mouse_click_node = node;
+
+ if (show_plus_minus && IsPlusMinusArea (node, e.X) && e.Button == MouseButtons.Left) {
+ node.Toggle ();
+ return;
+ } else if (checkboxes && IsCheckboxArea (node, e.X) && e.Button == MouseButtons.Left) {
+ node.check_reason = TreeViewAction.ByMouse;
+ node.Checked = !node.Checked;
+ UpdateNode(node);
+ return;
+ } else if (IsSelectableArea (node, e.X) || full_row_select) {
+ TreeNode old_highlighted = highlighted_node;
+ highlighted_node = node;
+ if (label_edit && e.Clicks == 1 && highlighted_node == old_highlighted && e.Button == MouseButtons.Left) {
+ BeginEdit (node);
+ } else if (highlighted_node != focused_node) {
+ Size ds = SystemInformation.DragSize;
+ mouse_rect.X = e.X - ds.Width;
+ mouse_rect.Y = e.Y - ds.Height;
+ mouse_rect.Width = ds.Width * 2;
+ mouse_rect.Height = ds.Height * 2;
+
+ select_mmove = true;
+ }
+
+ Invalidate (highlighted_node.Bounds);
+ if (old_highlighted != null)
+ Invalidate (Bloat (old_highlighted.Bounds));
+
+ drag_begin_x = e.X;
+ drag_begin_y = e.Y;
+ }
+ }
+
+ private void MouseUpHandler (object sender, MouseEventArgs e) {
+ TreeNode node = GetNodeAt (e.Y);
+
+ if (node != null && node == mouse_click_node) {
+ if (e.Clicks == 2)
+ OnNodeMouseDoubleClick (new TreeNodeMouseClickEventArgs (node, e.Button, e.Clicks, e.X, e.Y));
+ else
+ OnNodeMouseClick (new TreeNodeMouseClickEventArgs (node, e.Button, e.Clicks, e.X, e.Y));
+ }
+
+ mouse_click_node = null;
+
+ drag_begin_x = -1;
+ drag_begin_y = -1;
+
+ if (!select_mmove)
+ return;
+
+ select_mmove = false;
+
+ if (e.Button == MouseButtons.Right && selected_node != null) {
+ Invalidate (highlighted_node.Bounds);
+ highlighted_node = selected_node;
+ Invalidate (selected_node.Bounds);
+ return;
+ }
+
+ TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (highlighted_node, false, TreeViewAction.ByMouse);
+ OnBeforeSelect (ce);
+
+ Rectangle invalid;
+ if (!ce.Cancel) {
+ TreeNode prev_focused_node = focused_node;
+ TreeNode prev_highlighted_node = highlighted_node;
+
+ selected_node = highlighted_node;
+ focused_node = highlighted_node;
+ OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
+
+ if (prev_highlighted_node != null) {
+ if (prev_focused_node != null) {
+ invalid = Rectangle.Union (Bloat (prev_focused_node.Bounds),
+ Bloat (prev_highlighted_node.Bounds));
+ } else {
+ invalid = Bloat (prev_highlighted_node.Bounds);
+ }
+
+ invalid.X = 0;
+ invalid.Width = ViewportRectangle.Width;
+
+ Invalidate (invalid);
+ }
+
+ } else {
+ if (highlighted_node != null)
+ Invalidate (highlighted_node.Bounds);
+
+ highlighted_node = focused_node;
+ selected_node = focused_node;
+ if (selected_node != null)
+ Invalidate (selected_node.Bounds);
+ }
+ }
+
+ private void MouseMoveHandler (object sender, MouseEventArgs e) {
+ // XXX - This should use HitTest and only fire when we are over
+ // the important parts of a node, not things like gridlines or
+ // whitespace
+ TreeNode tn = GetNodeAt (e.Location);
+
+ if (tn != tooltip_currently_showing)
+ MouseLeftItem (tooltip_currently_showing);
+
+ if (tn != null && tn != tooltip_currently_showing)
+ MouseEnteredItem (tn);
+
+ if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right) {
+ if (drag_begin_x != -1 && drag_begin_y != -1) {
+ double rise = Math.Pow (drag_begin_x - e.X, 2);
+ double run = Math.Pow (drag_begin_y - e.Y, 2);
+ double move = Math.Sqrt (rise + run);
+ if (move > 3) {
+ TreeNode drag = GetNodeAtUseX (e.X, e.Y);
+
+ if (drag != null) {
+ OnItemDrag (new ItemDragEventArgs (e.Button, drag));
+ }
+ drag_begin_x = -1;
+ drag_begin_y = -1;
+ }
+ }
+
+ }
+
+ // If there is enough movement before the mouse comes up,
+ // selection is reverted back to the originally selected node
+ if (!select_mmove || mouse_rect.Contains (e.X, e.Y))
+ return;
+
+ Invalidate (highlighted_node.Bounds);
+ if (selected_node != null)
+ Invalidate (selected_node.Bounds);
+ if (focused_node != null)
+ Invalidate (focused_node.Bounds);
+
+ highlighted_node = selected_node;
+ focused_node = selected_node;
+
+ select_mmove = false;
+ }
+
+ private void DoubleClickHandler (object sender, MouseEventArgs e) {
+ TreeNode node = GetNodeAtUseX (e.X,e.Y);
+ if(node != null && node.Nodes.Count > 0) {
+ node.Toggle();
+ }
+ }
+
+
+ private bool RectsIntersect (Rectangle r, int left, int top, int width, int height)
+ {
+ return !((r.Left > left + width) || (r.Right < left) ||
+ (r.Top > top + height) || (r.Bottom < top));
+ }
+
+ // Return true if message was handled, false to send it to base
+ private bool WmContextMenu (ref Message m)
+ {
+ Point pt;
+ TreeNode tn;
+
+ pt = new Point (LowOrder ((int)m.LParam.ToInt32 ()), HighOrder ((int)m.LParam.ToInt32 ()));
+
+ // This means it's a keyboard menu request
+ if (pt.X == -1 || pt.Y == -1) {
+ tn = SelectedNode;
+
+ if (tn == null)
+ return false;
+
+ pt = new Point (tn.Bounds.Left, tn.Bounds.Top + (tn.Bounds.Height / 2));
+ } else {
+ pt = PointToClient (pt);
+
+ tn = GetNodeAt (pt);
+
+ if (tn == null)
+ return false;
+ }
+
+ if (tn.ContextMenuStrip != null) {
+ tn.ContextMenuStrip.Show (this, pt);
+ return true;
+ }
+
+ // The node we found did not have its own menu, let the parent try to display its menu
+ return false;
+ }
+
+ #region Stuff for ToolTips
+ private void MouseEnteredItem (TreeNode item)
+ {
+ tooltip_currently_showing = item;
+
+ if (!is_hovering)
+ return;
+
+ if (ShowNodeToolTips && !string.IsNullOrEmpty (tooltip_currently_showing.ToolTipText))
+ ToolTipWindow.Present (this, tooltip_currently_showing.ToolTipText);
+
+ OnNodeMouseHover (new TreeNodeMouseHoverEventArgs (tooltip_currently_showing));
+ }
+
+ private void MouseLeftItem (TreeNode item)
+ {
+ ToolTipWindow.Hide (this);
+ tooltip_currently_showing = null;
+ }
+
+ private ToolTip ToolTipWindow {
+ get {
+ if (tooltip_window == null)
+ tooltip_window = new ToolTip ();
+
+ return tooltip_window;
+ }
+ }
+ #endregion
+
+ #endregion // Internal & Private Methods and Properties
+
+ #region Events
+ static object ItemDragEvent = new object ();
+ static object AfterCheckEvent = new object ();
+ static object AfterCollapseEvent = new object ();
+ static object AfterExpandEvent = new object ();
+ static object AfterLabelEditEvent = new object ();
+ static object AfterSelectEvent = new object ();
+ static object BeforeCheckEvent = new object ();
+ static object BeforeCollapseEvent = new object ();
+ static object BeforeExpandEvent = new object ();
+ static object BeforeLabelEditEvent = new object ();
+ static object BeforeSelectEvent = new object ();
+ static object DrawNodeEvent = new object ();
+ static object NodeMouseClickEvent = new object ();
+ static object NodeMouseDoubleClickEvent = new object();
+ static object NodeMouseHoverEvent = new object ();
+ static object RightToLeftLayoutChangedEvent = new object ();
+
+ public event ItemDragEventHandler ItemDrag {
+ add { Events.AddHandler (ItemDragEvent, value); }
+ remove { Events.RemoveHandler (ItemDragEvent, value); }
+ }
+
+ public event TreeViewEventHandler AfterCheck {
+ add { Events.AddHandler (AfterCheckEvent, value); }
+ remove { Events.RemoveHandler (AfterCheckEvent, value); }
+ }
+
+ public event TreeViewEventHandler AfterCollapse {
+ add { Events.AddHandler (AfterCollapseEvent, value); }
+ remove { Events.RemoveHandler (AfterCollapseEvent, value); }
+ }
+
+ public event TreeViewEventHandler AfterExpand {
+ add { Events.AddHandler (AfterExpandEvent, value); }
+ remove { Events.RemoveHandler (AfterExpandEvent, value); }
+ }
+
+ public event NodeLabelEditEventHandler AfterLabelEdit {
+ add { Events.AddHandler (AfterLabelEditEvent, value); }
+ remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
+ }
+
+ public event TreeViewEventHandler AfterSelect {
+ add { Events.AddHandler (AfterSelectEvent, value); }
+ remove { Events.RemoveHandler (AfterSelectEvent, value); }
+ }
+
+ public event TreeViewCancelEventHandler BeforeCheck {
+ add { Events.AddHandler (BeforeCheckEvent, value); }
+ remove { Events.RemoveHandler (BeforeCheckEvent, value); }
+ }
+
+ public event TreeViewCancelEventHandler BeforeCollapse {
+ add { Events.AddHandler (BeforeCollapseEvent, value); }
+ remove { Events.RemoveHandler (BeforeCollapseEvent, value); }
+ }
+
+ public event TreeViewCancelEventHandler BeforeExpand {
+ add { Events.AddHandler (BeforeExpandEvent, value); }
+ remove { Events.RemoveHandler (BeforeExpandEvent, value); }
+ }
+
+ public event NodeLabelEditEventHandler BeforeLabelEdit {
+ add { Events.AddHandler (BeforeLabelEditEvent, value); }
+ remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
+ }
+
+ public event TreeViewCancelEventHandler BeforeSelect {
+ add { Events.AddHandler (BeforeSelectEvent, value); }
+ remove { Events.RemoveHandler (BeforeSelectEvent, value); }
+ }
+
+ public event DrawTreeNodeEventHandler DrawNode {
+ add { Events.AddHandler (DrawNodeEvent, value); }
+ remove { Events.RemoveHandler (DrawNodeEvent, value); }
+ }
+
+ public event TreeNodeMouseClickEventHandler NodeMouseClick {
+ add { Events.AddHandler (NodeMouseClickEvent, value); }
+ remove { Events.RemoveHandler (NodeMouseClickEvent, value); }
+ }
+
+
+ public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick {
+ add { Events.AddHandler (NodeMouseDoubleClickEvent, value); }
+ remove { Events.RemoveHandler (NodeMouseDoubleClickEvent, value); }
+ }
+
+ public event TreeNodeMouseHoverEventHandler NodeMouseHover {
+ add { Events.AddHandler (NodeMouseHoverEvent, value); }
+ remove { Events.RemoveHandler (NodeMouseHoverEvent, value); }
+ }
+
+ public event EventHandler RightToLeftLayoutChanged {
+ add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
+ remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+
+ #region UIA Framework Events
+ static object UIACheckBoxesChangedEvent = new object ();
+
+ internal event EventHandler UIACheckBoxesChanged {
+ add { Events.AddHandler (UIACheckBoxesChangedEvent, value); }
+ remove { Events.RemoveHandler (UIACheckBoxesChangedEvent, value); }
+ }
+
+ internal void OnUIACheckBoxesChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [UIACheckBoxesChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ static object UIALabelEditChangedEvent = new object ();
+
+ internal event EventHandler UIALabelEditChanged {
+ add { Events.AddHandler (UIALabelEditChangedEvent, value); }
+ remove { Events.RemoveHandler (UIALabelEditChangedEvent, value); }
+ }
+
+ internal void OnUIALabelEditChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) Events [UIALabelEditChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ static object UIANodeTextChangedEvent = new object ();
+
+ internal event TreeViewEventHandler UIANodeTextChanged {
+ add { Events.AddHandler (UIANodeTextChangedEvent, value); }
+ remove { Events.RemoveHandler (UIANodeTextChangedEvent, value); }
+ }
+
+ internal void OnUIANodeTextChanged (TreeViewEventArgs e)
+ {
+ TreeViewEventHandler eh =
+ (TreeViewEventHandler) Events [UIANodeTextChangedEvent];
+ if (eh != null)
+ eh (this, e);
+ }
+
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChanged (object sender, CollectionChangeEventArgs e)
+ {
+ CollectionChangeEventHandler eh =
+ (CollectionChangeEventHandler) Events [UIACollectionChangedEvent];
+ if (eh != null) {
+ if (sender == root_node)
+ sender = this;
+ eh (sender, e);
+ }
+ }
+ #endregion // UIA Framework Events
+ #endregion // Events
+ }
+}
+
+
diff --git a/source/ShiftUI/Widgets/UserWidget.cs b/source/ShiftUI/Widgets/UserWidget.cs
new file mode 100644
index 0000000..e0bb759
--- /dev/null
+++ b/source/ShiftUI/Widgets/UserWidget.cs
@@ -0,0 +1,236 @@
+// 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.
+//
+// Authors:
+// Peter Bartok [email protected]
+//
+
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+ [DefaultEvent("Load")]
+ [DesignerCategory("UserWidget")]
+ [Designer("ShiftUI.Design.WidgetDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [Designer("ShiftUI.Design.UserWidgetDocumentDesigner, " + Consts.AssemblySystem_Design, typeof(IRootDesigner))]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ public class UserWidget : ContainerWidget {
+ #region Public Constructors
+ public UserWidget() {
+ SetStyle (Widgetstyles.SupportsTransparentBackColor, true);
+ }
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
+ public override bool AutoSize {
+ get { return base.AutoSize; }
+ set { base.AutoSize = value; }
+ }
+
+ [Browsable (true)]
+ [LocalizableAttribute(true)]
+ [DefaultValue (AutoSizeMode.GrowOnly)]
+ public AutoSizeMode AutoSizeMode {
+ get { return base.GetAutoSizeMode (); }
+ set {
+ if (base.GetAutoSizeMode () != value) {
+ base.SetAutoSizeMode (value);
+ }
+ }
+ }
+
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public override AutoValidate AutoValidate {
+ get { return base.AutoValidate; }
+ set { base.AutoValidate = value; }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ return new Size(150, 150);
+ }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+
+ set {
+ base.Text = value;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Public Instance Methods
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public override bool ValidateChildren ()
+ {
+ return base.ValidateChildren ();
+ }
+
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public override bool ValidateChildren (ValidationConstraints validationConstraints)
+ {
+ return base.ValidateChildren (validationConstraints);
+ }
+ #endregion
+
+ #region Protected Instance Methods
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected override void OnCreateWidget() {
+ base.OnCreateWidget();
+
+ // The OnCreateWidget isn't neccessarily raised *before* it
+ // becomes first visible, but that's the best we've got
+ OnLoad(EventArgs.Empty);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected virtual void OnLoad(EventArgs e) {
+ EventHandler eh = (EventHandler)(Events [LoadEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected override void OnMouseDown(MouseEventArgs e) {
+ base.OnMouseDown(e);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ protected override void WndProc(ref Message m) {
+ switch ((Msg) m.Msg) {
+ case Msg.WM_SETFOCUS:
+ if (ActiveWidget == null)
+ SelectNextWidget (null, true, true, true, false);
+ base.WndProc (ref m);
+ break;
+ default:
+ base.WndProc (ref m);
+ break;
+ }
+ }
+ #endregion // Protected Instance Methods
+
+ #region Protected Properties
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+ cp.Style |= (int)WindowStyles.WS_TABSTOP;
+ cp.ExStyle |= (int)WindowExStyles.WS_EX_WidgetPARENT;
+ return cp;
+ }
+ }
+ #endregion
+
+ #region Events
+ static object LoadEvent = new object ();
+
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler AutoSizeChanged {
+ add { base.AutoSizeChanged += value; }
+ remove { base.AutoSizeChanged -= value; }
+ }
+
+ [Browsable (true)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler AutoValidateChanged {
+ add { base.AutoValidateChanged += value; }
+ remove { base.AutoValidateChanged -= value; }
+ }
+
+ public event EventHandler Load {
+ add { Events.AddHandler (LoadEvent, value); }
+ remove { Events.RemoveHandler (LoadEvent, value); }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion // Events
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+ }
+
+ [Browsable (true)]
+ [DefaultValue (BorderStyle.None)]
+ [EditorBrowsable (EditorBrowsableState.Always)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set { InternalBorderStyle = value; }
+ }
+
+ internal override Size GetPreferredSizeCore (Size proposedSize)
+ {
+ Size retsize = Size.Empty;
+
+ // Add up the requested sizes for Docked controls
+ foreach (Widget child in Widgets) {
+ if (!child.is_visible)
+ continue;
+
+ if (child.Dock == DockStyle.Left || child.Dock == DockStyle.Right)
+ retsize.Width += child.PreferredSize.Width;
+ else if (child.Dock == DockStyle.Top || child.Dock == DockStyle.Bottom)
+ retsize.Height += child.PreferredSize.Height;
+ }
+
+ // See if any non-Docked control is positioned lower or more right than our size
+ foreach (Widget child in Widgets) {
+ if (!child.is_visible)
+ continue;
+
+ if (child.Dock != DockStyle.None)
+ continue;
+
+ // If its anchored to the bottom or right, that doesn't really count
+ if ((child.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom || (child.Anchor & AnchorStyles.Right) == AnchorStyles.Right)
+ continue;
+
+ retsize.Width = Math.Max (retsize.Width, child.Bounds.Right + child.Margin.Right);
+ retsize.Height = Math.Max (retsize.Height, child.Bounds.Bottom + child.Margin.Bottom);
+ }
+
+ return retsize;
+ }
+ }
+}
diff --git a/source/ShiftUI/Widgets/VScrollBar.cs b/source/ShiftUI/Widgets/VScrollBar.cs
new file mode 100644
index 0000000..f458f66
--- /dev/null
+++ b/source/ShiftUI/Widgets/VScrollBar.cs
@@ -0,0 +1,79 @@
+//
+// ShiftUI.HScrollBar.cs
+//
+// 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.
+//
+// Authors:
+// Jordi Mas i Hernandez [email protected]
+//
+
+
+using System.Drawing;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System;
+
+namespace ShiftUI
+{
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ public class VScrollBar : ScrollBar
+ {
+ #region events
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public new event EventHandler RightToLeftChanged {
+ add { base.RightToLeftChanged += value; }
+ remove { base.RightToLeftChanged -= value; }
+ }
+
+ #endregion Events
+
+ public VScrollBar()
+ {
+ vert = true;
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ [Browsable (false)]
+ public override RightToLeft RightToLeft {
+ get { return base.RightToLeft; }
+ set {
+ if (RightToLeft == value)
+ return;
+
+ base.RightToLeft = value;
+
+ OnRightToLeftChanged (EventArgs.Empty);
+ }
+ }
+
+ protected override Size DefaultSize {
+ get { return ThemeEngine.Current.VScrollBarDefaultSize; }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams; }
+ }
+ }
+}