// // SimpleEffectDialog.cs // // Author: // Jonathan Pobst // // Copyright (c) 2010 Jonathan Pobst // // 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. // // Inspiration and reflection code is from Miguel de Icaza's MIT Licensed MonoTouch.Dialog: // http://github.com/migueldeicaza/MonoTouch.Dialog using System; using System.Reflection; using System.Text; using System.Collections.Generic; using System.ComponentModel; using System.Collections; using Mono.Unix; namespace Pinta.Gui.Widgets { public class SimpleEffectDialog : Gtk.Dialog { [ThreadStatic] Random random = new Random (); const uint event_delay_millis = 100; uint event_delay_timeout_id; public SimpleEffectDialog (string title, Gdk.Pixbuf icon, object effectData) : base (title, Pinta.Core.PintaCore.Chrome.MainWindow, Gtk.DialogFlags.Modal, Gtk.Stock.Cancel, Gtk.ResponseType.Cancel, Gtk.Stock.Ok, Gtk.ResponseType.Ok) { Icon = icon; EffectData = effectData; BorderWidth = 6; VBox.Spacing = 12; WidthRequest = 400; DefaultResponse = Gtk.ResponseType.Ok; AlternativeButtonOrder = new int[] { (int) Gtk.ResponseType.Ok, (int) Gtk.ResponseType.Cancel }; BuildDialog (); } public object EffectData { get; private set; } public event PropertyChangedEventHandler EffectDataChanged; #region EffectData Parser private void BuildDialog () { var members = EffectData.GetType ().GetMembers (); foreach (var mi in members) { Type mType = GetTypeForMember (mi); if (mType == null) continue; string caption = null; string hint = null; bool skip = false; bool combo = false; object[] attrs = mi.GetCustomAttributes (false); foreach (var attr in attrs) { if (attr is SkipAttribute) skip = true; else if (attr is CaptionAttribute) caption = ((CaptionAttribute)attr).Caption; else if (attr is HintAttribute) hint = ((HintAttribute)attr).Hint; else if (attr is StaticListAttribute) combo = true; } if (skip || string.Compare (mi.Name, "IsDefault", true) == 0) continue; if (caption == null) caption = MakeCaption (mi.Name); if (mType == typeof (int) && (caption == "Seed")) AddWidget (CreateSeed (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof (int)) AddWidget (CreateSlider (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof (double) && (caption == "Angle" || caption == "Rotation")) AddWidget (CreateAnglePicker (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof(double)) AddWidget (CreateDoubleSlider (Catalog.GetString (caption), EffectData, mi, attrs)); else if (combo && mType == typeof (string)) AddWidget (CreateComboBox (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof (bool)) AddWidget (CreateCheckBox (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof (Gdk.Point)) AddWidget (CreatePointPicker (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType == typeof (Cairo.PointD)) AddWidget (CreateOffsetPicker (Catalog.GetString (caption), EffectData, mi, attrs)); else if (mType.IsEnum) AddWidget (CreateEnumComboBox (Catalog.GetString (caption), EffectData, mi, attrs)); if (hint != null) AddWidget (CreateHintLabel (Catalog.GetString (hint))); } } private void AddWidget (Gtk.Widget widget) { widget.Show (); this.VBox.Add (widget); } #endregion #region Control Builders private ComboBoxWidget CreateEnumComboBox (string caption, object o, System.Reflection.MemberInfo member, System.Object[] attributes) { Type myType = GetTypeForMember (member); string[] entries = Enum.GetNames(myType); ComboBoxWidget widget = new ComboBoxWidget (entries); widget.Label = caption; widget.AddEvents ((int)Gdk.EventMask.ButtonPressMask); widget.Active = ((IList)entries).IndexOf (GetValue (member, o).ToString()); widget.Changed += delegate (object sender, EventArgs e) { SetValue (member, o, Enum.Parse(myType, widget.ActiveText)); }; return widget; } private ComboBoxWidget CreateComboBox (string caption, object o, System.Reflection.MemberInfo member, System.Object[] attributes) { Dictionary dict = null;; string dictName; foreach (var attr in attributes) { if (attr is StaticListAttribute) dict = (Dictionary)GetValue (((StaticListAttribute)attr).dictionaryName, o); } List entries = new List (); foreach (string str in dict.Keys) entries.Add (str); ComboBoxWidget widget = new ComboBoxWidget (entries.ToArray ()); widget.Label = caption; widget.AddEvents ((int)Gdk.EventMask.ButtonPressMask); widget.Active = entries.IndexOf( (string)GetValue (member, o)); widget.Changed += delegate (object sender, EventArgs e) { SetValue (member, o, widget.ActiveText); }; return widget; } private HScaleSpinButtonWidget CreateDoubleSlider (string caption, object o, MemberInfo member, object[] attributes) { HScaleSpinButtonWidget widget = new HScaleSpinButtonWidget (); int min_value = -100; int max_value = 100; double inc_value = 0.01; int digits_value = 2; foreach (var attr in attributes) { if (attr is MinimumValueAttribute) min_value = ((MinimumValueAttribute)attr).Value; else if (attr is MaximumValueAttribute) max_value = ((MaximumValueAttribute)attr).Value; else if (attr is IncrementValueAttribute) inc_value = ((IncrementValueAttribute)attr).Value; else if (attr is DigitsValueAttribute) digits_value = ((DigitsValueAttribute)attr).Value; } widget.Label = caption; widget.MinimumValue = min_value; widget.MaximumValue = max_value; widget.IncrementValue = inc_value; widget.DigitsValue = digits_value; widget.DefaultValue = (double)GetValue (member, o); widget.ValueChanged += delegate (object sender, EventArgs e) { if (event_delay_timeout_id != 0) GLib.Source.Remove (event_delay_timeout_id); event_delay_timeout_id = GLib.Timeout.Add (event_delay_millis, () => { event_delay_timeout_id = 0; SetValue (member, o, widget.Value); return false; }); }; return widget; } private HScaleSpinButtonWidget CreateSlider (string caption, object o, MemberInfo member, object[] attributes) { HScaleSpinButtonWidget widget = new HScaleSpinButtonWidget (); int min_value = -100; int max_value = 100; double inc_value = 1.0; int digits_value = 0; foreach (var attr in attributes) { if (attr is MinimumValueAttribute) min_value = ((MinimumValueAttribute)attr).Value; else if (attr is MaximumValueAttribute) max_value = ((MaximumValueAttribute)attr).Value; else if (attr is IncrementValueAttribute) inc_value = ((IncrementValueAttribute)attr).Value; else if (attr is DigitsValueAttribute) digits_value = ((DigitsValueAttribute)attr).Value; } widget.Label = caption; widget.MinimumValue = min_value; widget.MaximumValue = max_value; widget.IncrementValue = inc_value; widget.DigitsValue = digits_value; widget.DefaultValue = (int)GetValue (member, o); widget.ValueChanged += delegate (object sender, EventArgs e) { if (event_delay_timeout_id != 0) GLib.Source.Remove (event_delay_timeout_id); event_delay_timeout_id = GLib.Timeout.Add (event_delay_millis, () => { event_delay_timeout_id = 0; SetValue (member, o, widget.ValueAsInt); return false; }); }; return widget; } private Gtk.CheckButton CreateCheckBox (string caption, object o, MemberInfo member, object[] attributes) { Gtk.CheckButton widget = new Gtk.CheckButton (); widget.Label = caption; widget.Active = (bool)GetValue (member, o); widget.Toggled += delegate (object sender, EventArgs e) { SetValue (member, o, widget.Active); }; return widget; } private PointPickerWidget CreateOffsetPicker (string caption, object o, MemberInfo member, object[] attributes) { PointPickerWidget widget = new PointPickerWidget (); widget.Label = caption; widget.DefaultOffset = (Cairo.PointD)GetValue (member, o); widget.PointPicked += delegate (object sender, EventArgs e) { SetValue (member, o, widget.Offset); }; return widget; } private PointPickerWidget CreatePointPicker (string caption, object o, MemberInfo member, object[] attributes) { PointPickerWidget widget = new PointPickerWidget (); widget.Label = caption; widget.DefaultPoint = (Gdk.Point)GetValue (member, o); widget.PointPicked += delegate (object sender, EventArgs e) { SetValue (member, o, widget.Point); }; return widget; } private AnglePickerWidget CreateAnglePicker (string caption, object o, MemberInfo member, object[] attributes) { AnglePickerWidget widget = new AnglePickerWidget (); widget.Label = caption; widget.DefaultValue = (double)GetValue (member, o); widget.ValueChanged += delegate (object sender, EventArgs e) { if (event_delay_timeout_id != 0) GLib.Source.Remove (event_delay_timeout_id); event_delay_timeout_id = GLib.Timeout.Add (event_delay_millis, () => { event_delay_timeout_id = 0; SetValue (member, o, widget.Value); return false; }); }; return widget; } private Gtk.Label CreateHintLabel (string hint) { Gtk.Label label = new Gtk.Label (hint); label.LineWrap = true; return label; } private ReseedButtonWidget CreateSeed (string caption, object o, MemberInfo member, object[] attributes) { ReseedButtonWidget widget = new ReseedButtonWidget (); widget.Clicked += delegate (object sender, EventArgs e) { SetValue (member, o, random.Next ()); }; return widget; } #endregion #region Static Reflection Methods private static object GetValue (MemberInfo mi, object o) { var fi = mi as FieldInfo; if (fi != null) return fi.GetValue (o); var pi = mi as PropertyInfo; var getMethod = pi.GetGetMethod (); return getMethod.Invoke (o, new object[0]); } private void SetValue (MemberInfo mi, object o, object val) { var fi = mi as FieldInfo; var pi = mi as PropertyInfo; string fieldName = null; if (fi != null) { fi.SetValue (o, val); fieldName = fi.Name; } else if (pi != null) { var setMethod = pi.GetSetMethod (); setMethod.Invoke (o, new object[] { val }); fieldName = pi.Name; } if (EffectDataChanged != null) EffectDataChanged (this, new PropertyChangedEventArgs(fieldName)); } // Returns the type for fields and properties and null for everything else private static Type GetTypeForMember (MemberInfo mi) { if (mi is FieldInfo) return ((FieldInfo)mi).FieldType; else if (mi is PropertyInfo) return ((PropertyInfo)mi).PropertyType; return null; } private static string MakeCaption (string name) { var sb = new StringBuilder (name.Length); bool nextUp = true; foreach (char c in name) { if (nextUp) { sb.Append (Char.ToUpper (c)); nextUp = false; } else { if (c == '_') { sb.Append (' '); nextUp = true; continue; } if (Char.IsUpper (c)) sb.Append (' '); sb.Append (c); } } return sb.ToString (); } private object GetValue(string name, object o) { var fi = o.GetType ().GetField (name); if (fi != null) return fi.GetValue (o); var pi = o.GetType ().GetProperty (name); if (pi == null) return null; var getMethod = pi.GetGetMethod (); return getMethod.Invoke (o, new object[0]); } #endregion } }