diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs')
-rw-r--r-- | main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs new file mode 100644 index 0000000000..841494977e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs @@ -0,0 +1,304 @@ +// +// TextEditor.cs +// +// Author: +// Lluis Sanchez Gual +// Michael Hutchinson +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; + +using Gtk; +using Gdk; + +namespace MonoDevelop.Components.PropertyGrid.PropertyEditors +{ + [PropertyEditorType (typeof (string))] + public class PropertyTextEditor: Gtk.HBox, IPropertyEditor + { + EditSession session; + bool disposed; + string initialText; + object currentValue; + Entry entry; + ComboBox combo; + ListStore store; + bool changed; + + public void Initialize (EditSession session) + { + this.session = session; + + //if standard values are supported by the converter, then + //we list them in a combo + if (session.Property.Converter.GetStandardValuesSupported (session)) + { + store = new ListStore (typeof(string), typeof(object)); + + //if converter doesn't allow nonstandard values, or can't convert from strings, don't have an entry + if (session.Property.Converter.GetStandardValuesExclusive (session) || !session.Property.Converter.CanConvertFrom (session, typeof(string))) { + combo = new ComboBox (store); + var crt = new CellRendererText (); + combo.PackStart (crt, true); + combo.AddAttribute (crt, "text", 0); + } else { + combo = new ComboBoxEntry (store, 0); + entry = ((ComboBoxEntry)combo).Entry; + entry.HeightRequest = combo.SizeRequest ().Height; + } + + PackStart (combo, true, true, 0); + combo.Changed += TextChanged; + + //fill the list + foreach (object stdValue in session.Property.Converter.GetStandardValues (session)) { + store.AppendValues (session.Property.Converter.ConvertToString (session, stdValue), ObjectBox.Box (stdValue)); + } + + //a value of "--" gets rendered as a --, if typeconverter marked with UsesDashesForSeparator + object[] atts = session.Property.Converter.GetType () + .GetCustomAttributes (typeof (StandardValuesSeparatorAttribute), true); + if (atts.Length > 0) { + string separator = ((StandardValuesSeparatorAttribute)atts[0]).Separator; + combo.RowSeparatorFunc = (model, iter) => separator == ((string)model.GetValue (iter, 0)); + } + } + // no standard values, so just use an entry + else { + entry = new Entry (); + PackStart (entry, true, true, 0); + } + + //if we have an entry, fix it up a little + if (entry != null) { + entry.HasFrame = false; + entry.Changed += TextChanged; + entry.FocusOutEvent += FirePendingChangeEvent; + } + + if (entry != null && ShouldShowDialogButton ()) { + var button = new Button ("..."); + PackStart (button, false, false, 0); + button.Clicked += ButtonClicked; + } + + Spacing = 3; + ShowAll (); + } + + protected virtual bool ShouldShowDialogButton () + { + //if the object's Localizable, show a dialog, since the text's likely to be more substantial + var at = (LocalizableAttribute) session.Property.Attributes [typeof(LocalizableAttribute)]; + return (at != null && at.IsLocalizable); + } + + void ButtonClicked (object s, EventArgs a) + { + using (var dlg = new TextEditorDialog ()) { + dlg.TransientFor = Toplevel as Gtk.Window; + dlg.Text = entry.Text; + if (dlg.Run () == (int) ResponseType.Ok) { + entry.Text = dlg.Text; + TextChanged (null, null); + } + } + } + + bool GetValue (out object value, out bool isStandard) + { + isStandard = false; + + //combo box, just find the active value + if (store != null && entry == null) { + TreeIter it; + if (combo.GetActiveIter (out it)) { + value = ObjectBox.Unbox (store.GetValue (it, 1)); + isStandard = true; + return true; + } + value = null; + return false; + } + + var text = entry.Text; + + // combo plus entry, try to find matching value + if (store != null) { + TreeIter it; + if (store.GetIterFirst (out it)) { + do { + if ((string)store.GetValue (it, 0) == text) { + value = ObjectBox.Unbox (store.GetValue (it, 1)); + isStandard = true; + return true; + } + } while (store.IterNext (ref it)); + } + } + + //finally, convert the value + try { + value = session.Property.Converter.ConvertFromString (session, entry.Text); + return true; + } catch { + // Invalid format + value = null; + return false; + } + } + + void TextChanged (object s, EventArgs a) + { + //ignore if nothing changed + if (entry != null) { + if (initialText == entry.Text) { + return; + } + initialText = entry.Text; + } + + object val; + bool isStandard; + if (GetValue (out val, out isStandard)) { + currentValue = val; + if (entry != null) + entry.ModifyFg (StateType.Normal); + } else { + entry.ModifyFg (StateType.Normal, new Color (255, 0, 0)); + } + + //if it's a standard value, fire the event immediately + //else defer till the entry loses focus + changed = true; + if (isStandard) { + FirePendingChangeEvent (null, null); + } + } + + void FirePendingChangeEvent (object s, EventArgs a) + { + if (changed) { + if (ValueChanged != null) { + ValueChanged (this, EventArgs.Empty); + } + changed = false; + } + } + + // Gets/Sets the value of the editor. If the editor supports + // several value types, it is the responsibility of the editor + // to return values with the expected type. + public object Value { + get { return currentValue; } + set { + currentValue = value; + if (combo != null) { + int index; + if (FindComboValue (value, out index)) { + combo.Active = index; + initialText = combo.ActiveText; + return; + } + } + if (entry != null) { + string val = session.Property.Converter.ConvertToString (session, value); + entry.Text = val ?? string.Empty; + initialText = entry.Text; + } + } + } + + bool FindComboValue (object val, out int index) + { + index = 0; + TreeIter it; + if (!store.GetIterFirst (out it)) { + return false; + } + do { + if (object.Equals (ObjectBox.Unbox (store.GetValue (it, 1)), val)) { + return true; + } + index++; + } while (store.IterNext (ref it)); + return false; + } + + protected override void OnDestroyed () + { + base.OnDestroyed (); + ((IDisposable)this).Dispose (); + } + + void IDisposable.Dispose () + { + if (!disposed) { + disposed = true; + return; + } + if (entry != null) { + TextChanged (null, null); + FirePendingChangeEvent (null, null); + } + disposed = true; + } + + // To be fired when the edited value changes. + public event EventHandler ValueChanged; + + //GTK# doesn't like it when you put a string in a column of type Object + class ObjectBox + { + public object Value; + public static object Box (object o) + { + if (o is string) + return new ObjectBox { Value = o }; + return o; + } + public static object Unbox (object o) + { + var b = o as ObjectBox; + if (b == null) + return o; + return b.Value; + } + } + + } + + public class StandardValuesSeparatorAttribute : Attribute + { + readonly string separator; + + public string Separator { get { return separator; } } + + public StandardValuesSeparatorAttribute (string separator) + { + this.separator = separator; + } + } +} |