a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
612 lines
15 KiB
C#
612 lines
15 KiB
C#
// 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 Gilbert <logic@deltaq.org>
|
|
//
|
|
// Integration into MWF:
|
|
// Peter Bartok <pbartok@novell.com>
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms;
|
|
|
|
namespace System.Windows.Forms
|
|
{
|
|
[DefaultProperty("Items")]
|
|
[DefaultEvent("SelectedItemChanged")]
|
|
[DefaultBindingProperty ("SelectedItem")]
|
|
[ClassInterface (ClassInterfaceType.AutoDispatch)]
|
|
[ComVisible (true)]
|
|
public class DomainUpDown : UpDownBase {
|
|
#region Local Variables
|
|
private DomainUpDownItemCollection items;
|
|
private int selected_index = -1;
|
|
private bool sorted;
|
|
private bool wrap;
|
|
private int typed_to_index = -1;
|
|
#endregion // Local Variables
|
|
|
|
#region DomainUpDownAccessibleObject sub-class
|
|
[ComVisible(true)]
|
|
public class DomainItemAccessibleObject : AccessibleObject {
|
|
#region DomainItemAccessibleObject Local Variables
|
|
private AccessibleObject parent;
|
|
#endregion // DomainItemAccessibleObject Local Variables
|
|
|
|
#region DomainItemAccessibleObject Constructors
|
|
public DomainItemAccessibleObject(string name, AccessibleObject parent) {
|
|
this.name = name;
|
|
this.parent = parent;
|
|
}
|
|
#endregion // DomainItemAccessibleObject Constructors
|
|
|
|
#region DomainItemAccessibleObject Properties
|
|
public override string Name {
|
|
get {
|
|
return base.Name;
|
|
}
|
|
|
|
set {
|
|
base.Name = value;
|
|
}
|
|
}
|
|
|
|
public override AccessibleObject Parent {
|
|
get {
|
|
return parent;
|
|
}
|
|
}
|
|
|
|
public override AccessibleRole Role {
|
|
get {
|
|
return base.Role;
|
|
}
|
|
}
|
|
|
|
public override AccessibleStates State {
|
|
get {
|
|
return base.State;
|
|
}
|
|
}
|
|
|
|
public override string Value {
|
|
get {
|
|
return base.Value;
|
|
}
|
|
}
|
|
#endregion // DomainItemAccessibleObject Properties
|
|
}
|
|
#endregion // DomainItemAccessibleObject sub-class
|
|
|
|
#region DomainUpDownAccessibleObject sub-class
|
|
[ComVisible(true)]
|
|
public class DomainUpDownAccessibleObject : ControlAccessibleObject {
|
|
#region DomainUpDownAccessibleObject Local Variables
|
|
//private Control owner;
|
|
#endregion // DomainUpDownAccessibleObject Local Variables
|
|
|
|
#region DomainUpDownAccessibleObject Constructors
|
|
public DomainUpDownAccessibleObject(Control owner) : base(owner)
|
|
{
|
|
//this.owner = owner;
|
|
}
|
|
#endregion // DomainUpDownAccessibleObject Constructors
|
|
|
|
#region DomainUpDownAccessibleObject Properties
|
|
public override AccessibleRole Role {
|
|
get {
|
|
return base.Role;
|
|
}
|
|
}
|
|
#endregion // DomainUpDownAccessibleObject Properties
|
|
|
|
#region DomainUpDownAccessibleObject Methods
|
|
public override AccessibleObject GetChild(int index) {
|
|
return base.GetChild (index);
|
|
}
|
|
|
|
public override int GetChildCount() {
|
|
return base.GetChildCount ();
|
|
}
|
|
#endregion // DomainUpDownAccessibleObject Methods
|
|
}
|
|
#endregion // DomainUpDownAccessibleObject sub-class
|
|
|
|
#region DomainUpDownItemCollection sub-class
|
|
public class DomainUpDownItemCollection : ArrayList {
|
|
#region Local Variables
|
|
#endregion // Local Variables
|
|
|
|
#region Constructors
|
|
internal DomainUpDownItemCollection() {}
|
|
#endregion // Constructors
|
|
|
|
#region Public Instance Properties
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public override object this[int index] {
|
|
get {
|
|
return base[index];
|
|
}
|
|
|
|
set {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
|
|
}
|
|
|
|
base[index] = value;
|
|
OnCollectionChanged(index, 0);
|
|
}
|
|
}
|
|
#endregion // Public Instance Properties
|
|
|
|
#region Public Instance Methods
|
|
public override int Add(object item) {
|
|
if (item == null)
|
|
throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
|
|
|
|
int ret = base.Add(item);
|
|
OnCollectionChanged(Count - 1, +1);
|
|
return ret;
|
|
}
|
|
|
|
public override void Insert(int index, object item) {
|
|
if (item == null)
|
|
throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
|
|
|
|
base.Insert(index, item);
|
|
OnCollectionChanged(index, +1);
|
|
}
|
|
|
|
public override void Remove(object item) {
|
|
int index = IndexOf(item);
|
|
|
|
if (index >= 0)
|
|
RemoveAt(index);
|
|
}
|
|
|
|
public override void RemoveAt(int item) {
|
|
base.RemoveAt(item);
|
|
OnCollectionChanged(item, -1);
|
|
}
|
|
#endregion // Public Instance Methods
|
|
|
|
#region Internal Methods and Events
|
|
internal void OnCollectionChanged(int index, int size_delta) {
|
|
CollectionChangedEventHandler handler = CollectionChanged;
|
|
|
|
if (handler != null) {
|
|
handler(index, size_delta);
|
|
}
|
|
}
|
|
|
|
internal void PrivSort()
|
|
{
|
|
base.Sort (new ToStringSorter ());
|
|
}
|
|
|
|
private class ToStringSorter : IComparer
|
|
{
|
|
public int Compare (object x, object y)
|
|
{
|
|
return string.Compare (x.ToString (), y.ToString ());
|
|
}
|
|
}
|
|
|
|
internal event CollectionChangedEventHandler CollectionChanged;
|
|
#endregion // Internal Methods and Events
|
|
}
|
|
#endregion // DomainUpDownItemCollection sub-class
|
|
|
|
#region Private Methods
|
|
// normally I'd use an EventArgs class, but I don't want to create spurious objects here
|
|
internal delegate void CollectionChangedEventHandler(int index, int size_delta);
|
|
|
|
internal void items_CollectionChanged(int index, int size_delta) {
|
|
bool new_item = false;
|
|
|
|
if ((index == selected_index) && (size_delta <= 0))
|
|
new_item = true;
|
|
else if (index <= selected_index)
|
|
selected_index += size_delta;
|
|
|
|
if (sorted && (index >= 0)) // index < 0 means it is already sorting
|
|
items.PrivSort();
|
|
|
|
// XXX this might be wrong - it might be an explict 'Text = ...' assignment.
|
|
UpdateEditText();
|
|
|
|
if (new_item) {
|
|
OnSelectedItemChanged(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
void go_to_user_input() {
|
|
UserEdit = false;
|
|
|
|
if (typed_to_index >= 0) {
|
|
selected_index = typed_to_index;
|
|
OnSelectedItemChanged(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
private void TextBoxLostFocus(object source, EventArgs e) {
|
|
Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
|
|
}
|
|
|
|
int SearchTextWithPrefix (char key_char)
|
|
{
|
|
string prefix = key_char.ToString ();
|
|
int start_index, i;
|
|
|
|
start_index = selected_index == -1 ? 0 : selected_index;
|
|
i = selected_index == -1 || selected_index + 1 >= items.Count ? 0 : start_index + 1;
|
|
|
|
while (true) {
|
|
string item_text = items [i].ToString ();
|
|
if (String.Compare (prefix, 0, item_text, 0, 1, true) == 0)
|
|
return i;
|
|
|
|
if (i + 1 >= items.Count)
|
|
i = 0;
|
|
else
|
|
i++;
|
|
|
|
if (i == start_index)
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool IsValidInput (char key_char)
|
|
{
|
|
return Char.IsLetterOrDigit (key_char)
|
|
|| Char.IsNumber (key_char)
|
|
|| Char.IsPunctuation (key_char)
|
|
|| Char.IsSymbol (key_char)
|
|
|| Char.IsWhiteSpace (key_char);
|
|
}
|
|
|
|
private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
|
|
if (ReadOnly) {
|
|
char key_char = e.KeyChar;
|
|
if (IsValidInput (key_char) && items.Count > 0) {
|
|
int idx = SearchTextWithPrefix (key_char);
|
|
if (idx > -1) {
|
|
SelectedIndex = idx;
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!UserEdit) {
|
|
base.txtView.SelectionLength = 0;
|
|
typed_to_index = -1;
|
|
}
|
|
|
|
if (base.txtView.SelectionLength == 0) {
|
|
base.txtView.SelectionStart = 0;
|
|
}
|
|
|
|
if (base.txtView.SelectionStart != 0) {
|
|
return;
|
|
}
|
|
|
|
if (e.KeyChar == '\b') { // backspace
|
|
if (base.txtView.SelectionLength > 0) {
|
|
string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
|
|
|
|
bool found = false;
|
|
|
|
if (typed_to_index < 0) {
|
|
typed_to_index = 0;
|
|
}
|
|
|
|
if (sorted) {
|
|
for (int i=typed_to_index; i >= 0; i--) {
|
|
int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
|
|
|
|
if (difference == 0) {
|
|
found = true;
|
|
typed_to_index = i;
|
|
}
|
|
|
|
if (difference > 0) { // since it is sorted, no strings after this point will match
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i=0; i < items.Count; i++) {
|
|
if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
|
|
found = true;
|
|
typed_to_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ChangingText = true;
|
|
|
|
if (found)
|
|
Text = items[typed_to_index].ToString();
|
|
else
|
|
Text = prefix;
|
|
|
|
Select(0, prefix.Length);
|
|
|
|
UserEdit = true;
|
|
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
else {
|
|
char key_char = e.KeyChar;
|
|
|
|
if (IsValidInput (key_char)) {
|
|
string prefix = base.txtView.SelectedText + key_char;
|
|
|
|
bool found = false;
|
|
|
|
if (typed_to_index < 0) {
|
|
typed_to_index = 0;
|
|
}
|
|
|
|
if (sorted) {
|
|
for (int i=typed_to_index; i < items.Count; i++) {
|
|
int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
|
|
|
|
if (difference == 0) {
|
|
found = true;
|
|
typed_to_index = i;
|
|
}
|
|
|
|
if (difference <= 0) { // since it is sorted, no strings after this point will match
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i=0; i < items.Count; i++) {
|
|
if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
|
|
found = true;
|
|
typed_to_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ChangingText = true;
|
|
|
|
if (found) {
|
|
Text = items[typed_to_index].ToString();
|
|
} else {
|
|
Text = prefix;
|
|
}
|
|
|
|
Select(0, prefix.Length);
|
|
|
|
UserEdit = true;
|
|
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
}
|
|
#endregion // Private Methods
|
|
|
|
#region Public Constructors
|
|
public DomainUpDown() {
|
|
selected_index = -1;
|
|
sorted = false;
|
|
wrap = false;
|
|
typed_to_index = -1;
|
|
|
|
items = new DomainUpDownItemCollection();
|
|
items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
|
|
|
|
this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
|
|
this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
|
|
|
|
UpdateEditText ();
|
|
}
|
|
#endregion // Public Constructors
|
|
|
|
#region Public Instance Properties
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
[Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
|
|
[Localizable(true)]
|
|
public DomainUpDownItemCollection Items {
|
|
get {
|
|
return items;
|
|
}
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
public new Padding Padding {
|
|
get { return Padding.Empty; }
|
|
set { }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[DefaultValue(-1)]
|
|
public int SelectedIndex {
|
|
get {
|
|
return selected_index;
|
|
}
|
|
set {
|
|
object before = (selected_index >= 0) ? items[selected_index] : null;
|
|
|
|
selected_index = value;
|
|
UpdateEditText();
|
|
|
|
object after = (selected_index >= 0) ? items[selected_index] : null;
|
|
|
|
if (!ReferenceEquals(before, after)) {
|
|
OnSelectedItemChanged(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object SelectedItem {
|
|
get {
|
|
if (selected_index >= 0) {
|
|
return items[selected_index];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
set {
|
|
SelectedIndex = items.IndexOf(value);
|
|
}
|
|
}
|
|
|
|
[DefaultValue(false)]
|
|
public bool Sorted {
|
|
get {
|
|
return sorted;
|
|
}
|
|
set {
|
|
sorted = value;
|
|
|
|
if (sorted)
|
|
items.PrivSort();
|
|
}
|
|
}
|
|
|
|
[DefaultValue(false)]
|
|
[Localizable(true)]
|
|
public bool Wrap {
|
|
get {
|
|
return wrap;
|
|
}
|
|
set {
|
|
wrap = value;
|
|
}
|
|
}
|
|
#endregion // Public Instance Properties
|
|
|
|
#region Public Instance Methods
|
|
public override void DownButton() {
|
|
if (UserEdit)
|
|
go_to_user_input();
|
|
|
|
int new_index = selected_index + 1;
|
|
|
|
if (new_index >= items.Count) {
|
|
if (!wrap)
|
|
return;
|
|
|
|
new_index = 0;
|
|
}
|
|
|
|
SelectedIndex = new_index;
|
|
|
|
// UIA Framework Event: DownButton Click
|
|
OnUIADownButtonClick (EventArgs.Empty);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
|
|
}
|
|
|
|
public override void UpButton() {
|
|
if (UserEdit)
|
|
go_to_user_input();
|
|
|
|
int new_index = selected_index - 1;
|
|
|
|
if (new_index < 0) {
|
|
if (!wrap) {
|
|
return;
|
|
}
|
|
|
|
new_index = items.Count - 1;
|
|
}
|
|
|
|
SelectedIndex = new_index;
|
|
|
|
// UIA Framework Event: UpButton Click
|
|
OnUIAUpButtonClick (EventArgs.Empty);
|
|
}
|
|
#endregion // Public Instance Methods
|
|
|
|
#region Protected Instance Methods
|
|
protected override AccessibleObject CreateAccessibilityInstance() {
|
|
AccessibleObject acc;
|
|
|
|
acc = new AccessibleObject(this);
|
|
acc.role = AccessibleRole.SpinButton;
|
|
|
|
return acc;
|
|
}
|
|
|
|
protected override void OnChanged(object source, EventArgs e) {
|
|
base.OnChanged (source, e);
|
|
}
|
|
|
|
protected void OnSelectedItemChanged(object source, EventArgs e) {
|
|
EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
|
|
protected override void UpdateEditText() {
|
|
if ((selected_index >= 0) && (selected_index < items.Count)) {
|
|
ChangingText = true;
|
|
Text = items[selected_index].ToString();
|
|
}
|
|
}
|
|
|
|
protected override void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
|
|
{
|
|
base.OnTextBoxKeyPress (source, e);
|
|
}
|
|
#endregion // Protected Instance Methods
|
|
|
|
#region Events
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event EventHandler PaddingChanged {
|
|
add { base.PaddingChanged += value; }
|
|
remove { base.PaddingChanged -= value; }
|
|
}
|
|
|
|
static object SelectedItemChangedEvent = new object ();
|
|
public event EventHandler SelectedItemChanged {
|
|
add { Events.AddHandler (SelectedItemChangedEvent, value); }
|
|
remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }
|
|
}
|
|
#endregion // Events
|
|
}
|
|
}
|