moel@345: using System;
moel@345: using System.Collections.Generic;
moel@345: using System.Text;
moel@345: using System.Drawing;
moel@345: using System.Windows.Forms;
moel@345: using System.Reflection;
moel@345: using System.ComponentModel;
moel@345: using System.Drawing.Design;
moel@345: 
moel@345: namespace Aga.Controls.Tree.NodeControls
moel@345: {
moel@345: 	public class NodeComboBox : BaseTextControl
moel@345: 	{
moel@345: 		#region Properties
moel@345: 
moel@345: 		private int _editorWidth = 100;
moel@345: 		[DefaultValue(100)]
moel@345: 		public int EditorWidth
moel@345: 		{
moel@345: 			get { return _editorWidth; }
moel@345: 			set { _editorWidth = value; }
moel@345: 		}
moel@345: 
moel@345: 		private int _editorHeight = 100;
moel@345: 		[DefaultValue(100)]
moel@345: 		public int EditorHeight
moel@345: 		{
moel@345: 			get { return _editorHeight; }
moel@345: 			set { _editorHeight = value; }
moel@345: 		}
moel@345: 
moel@345: 		private List<object> _dropDownItems;
moel@345: 		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
moel@345: 		[Editor(typeof(StringCollectionEditor), typeof(UITypeEditor)), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
moel@345: 		public List<object> DropDownItems
moel@345: 		{
moel@345: 			get { return _dropDownItems; }
moel@345: 		}
moel@345: 
moel@345: 		#endregion
moel@345: 
moel@345: 		public event EventHandler<EditEventArgs> CreatingEditor;
moel@345: 
moel@345: 		public NodeComboBox()
moel@345: 		{
moel@345: 			_dropDownItems = new List<object>();
moel@345: 		}
moel@345: 
moel@345: 		protected override Size CalculateEditorSize(EditorContext context)
moel@345: 		{
moel@345: 			if (Parent.UseColumns)
moel@345: 			{
moel@345: 				if (context.Editor is CheckedListBox)
moel@345: 					return new Size(context.Bounds.Size.Width, EditorHeight);
moel@345: 				else
moel@345: 					return context.Bounds.Size;
moel@345: 			}
moel@345: 			else
moel@345: 			{
moel@345: 				if (context.Editor is CheckedListBox)
moel@345: 					return new Size(EditorWidth, EditorHeight);
moel@345: 				else
moel@345: 					return new Size(EditorWidth, context.Bounds.Height);
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		protected override Control CreateEditor(TreeNodeAdv node)
moel@345: 		{
moel@345: 			Control c;
moel@345: 			object value = GetValue(node);
moel@345: 			if (IsCheckedListBoxRequired(node))
moel@345: 				c = CreateCheckedListBox(node);
moel@345: 			else
moel@345: 				c = CreateCombo(node);
moel@345: 			OnCreatingEditor(new EditEventArgs(node, c));
moel@345: 			return c;
moel@345: 		}
moel@345: 
moel@345: 		protected override void DisposeEditor(Control editor)
moel@345: 		{
moel@345: 		}
moel@345: 
moel@345: 		protected virtual void OnCreatingEditor(EditEventArgs args)
moel@345: 		{
moel@345: 			if (CreatingEditor != null)
moel@345: 				CreatingEditor(this, args);
moel@345: 		}
moel@345: 
moel@345: 		protected virtual bool IsCheckedListBoxRequired(TreeNodeAdv node)
moel@345: 		{
moel@345: 			object value = GetValue(node);
moel@345: 			if (value != null)
moel@345: 			{
moel@345: 				Type t = value.GetType();
moel@345: 				object[] arr = t.GetCustomAttributes(typeof(FlagsAttribute), false);
moel@345: 				return (t.IsEnum && arr.Length == 1);
moel@345: 			}
moel@345: 			return false;
moel@345: 		}
moel@345: 
moel@345: 		private Control CreateCombo(TreeNodeAdv node)
moel@345: 		{
moel@345: 			ComboBox comboBox = new ComboBox();
moel@345: 			if (DropDownItems != null)
moel@345: 				comboBox.Items.AddRange(DropDownItems.ToArray());
moel@345: 			comboBox.SelectedItem = GetValue(node);
moel@345: 			comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
moel@345: 			comboBox.DropDownClosed += new EventHandler(EditorDropDownClosed);
moel@345: 			SetEditControlProperties(comboBox, node);
moel@345: 			return comboBox;
moel@345: 		}
moel@345: 
moel@345: 		private Control CreateCheckedListBox(TreeNodeAdv node)
moel@345: 		{
moel@345: 			CheckedListBox listBox = new CheckedListBox();
moel@345: 			listBox.CheckOnClick = true;
moel@345: 
moel@345: 			object value = GetValue(node);
moel@345: 			Type enumType = GetEnumType(node);
moel@345: 			foreach (object obj in Enum.GetValues(enumType))
moel@345: 			{
moel@345: 				object[] attributes = enumType.GetField(obj.ToString()).GetCustomAttributes(typeof(BrowsableAttribute), false);
moel@345: 				if (attributes.Length == 0 || ((BrowsableAttribute)attributes[0]).Browsable)
moel@345: 					listBox.Items.Add(obj, IsContain(value, obj));
moel@345: 			}
moel@345: 
moel@345: 			SetEditControlProperties(listBox, node);
moel@345: 			if (CreatingEditor != null)
moel@345: 				CreatingEditor(this, new EditEventArgs(node, listBox));
moel@345: 			return listBox;
moel@345: 		}
moel@345: 
moel@345: 		protected virtual Type GetEnumType(TreeNodeAdv node)
moel@345: 		{
moel@345: 			object value = GetValue(node);
moel@345: 			return value.GetType();
moel@345: 		}
moel@345: 
moel@345: 		private bool IsContain(object value, object enumElement)
moel@345: 		{
moel@345: 			if (value == null || enumElement == null)
moel@345: 				return false;
moel@345: 			if (value.GetType().IsEnum)
moel@345: 			{
moel@345: 				int i1 = (int)value;
moel@345: 				int i2 = (int)enumElement;
moel@345: 				return (i1 & i2) == i2;
moel@345: 			}
moel@345: 			else
moel@345: 			{
moel@345: 				var arr = value as object[];
moel@345: 				foreach (object obj in arr)
moel@345: 					if ((int)obj == (int)enumElement)
moel@345: 						return true;
moel@345: 				return false;
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		protected override string FormatLabel(object obj)
moel@345: 		{
moel@345: 			var arr = obj as object[];
moel@345: 			if (arr != null)
moel@345: 			{
moel@345: 				StringBuilder sb = new StringBuilder();
moel@345: 				foreach (object t in arr)
moel@345: 				{
moel@345: 					if (sb.Length > 0)
moel@345: 						sb.Append(", ");
moel@345: 					sb.Append(t);
moel@345: 				}
moel@345: 				return sb.ToString();
moel@345: 			}
moel@345: 			else
moel@345: 				return base.FormatLabel(obj);
moel@345: 		}
moel@345: 
moel@345: 		void EditorDropDownClosed(object sender, EventArgs e)
moel@345: 		{
moel@345: 			EndEdit(true);
moel@345: 		}
moel@345: 
moel@345: 		public override void UpdateEditor(Control control)
moel@345: 		{
moel@345: 			if (control is ComboBox)
moel@345: 				(control as ComboBox).DroppedDown = true;
moel@345: 		}
moel@345: 
moel@345: 		protected override void DoApplyChanges(TreeNodeAdv node, Control editor)
moel@345: 		{
moel@345: 			var combo = editor as ComboBox;
moel@345: 			if (combo != null)
moel@345: 			{
moel@345: 				if (combo.DropDownStyle == ComboBoxStyle.DropDown)
moel@345: 					SetValue(node, combo.Text);
moel@345: 				else
moel@345: 					SetValue(node, combo.SelectedItem);
moel@345: 			}
moel@345: 			else
moel@345: 			{
moel@345: 				var listBox = editor as CheckedListBox;
moel@345: 				Type type = GetEnumType(node);
moel@345: 				if (IsFlags(type))
moel@345: 				{
moel@345: 					int res = 0;
moel@345: 					foreach (object obj in listBox.CheckedItems)
moel@345: 						res |= (int)obj;
moel@345: 					object val = Enum.ToObject(type, res);
moel@345: 					SetValue(node, val);
moel@345: 				}
moel@345: 				else
moel@345: 				{
moel@345: 					List<object> list = new List<object>();
moel@345: 					foreach (object obj in listBox.CheckedItems)
moel@345: 						list.Add(obj);
moel@345: 					SetValue(node, list.ToArray());
moel@345: 				}
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		private bool IsFlags(Type type)
moel@345: 		{
moel@345: 			object[] atr = type.GetCustomAttributes(typeof(FlagsAttribute), false);
moel@345: 			return atr.Length == 1;
moel@345: 		}
moel@345: 
moel@345: 		public override void MouseUp(TreeNodeAdvMouseEventArgs args)
moel@345: 		{
moel@345: 			if (args.Node != null && args.Node.IsSelected) //Workaround of specific ComboBox control behavior
moel@345: 				base.MouseUp(args);
moel@345: 		}
moel@345: 	}
moel@345: }