using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.ComponentModel; using System.Data; using System.Data.Common; using System.ComponentModel.Design; using System.Drawing.Design; using System.Windows.Forms; namespace SQLite.Designer.Design { internal class IndexEditor : CollectionEditor { Table _table; bool _cancel = false; object[] _items; internal IndexEditor(Table parent) : base(typeof(List)) { _table = parent; } protected override object CreateInstance(Type itemType) { if (itemType == typeof(Index)) { return new Index(null, _table, null); } throw new NotSupportedException(); } protected override void CancelChanges() { _cancel = true; } protected override bool CanRemoveInstance(object value) { return !(value is PrimaryKey); } protected override object[] GetItems(object editValue) { if (_items == null) { List value = editValue as List; int extra = (_table.PrimaryKey.Columns.Count > 0) ? 1 : 0; _items = new object[value.Count + extra]; for (int n = extra; n < _items.Length; n++) _items[n] = ((ICloneable)value[n - extra]).Clone(); if (extra > 0) _items[0] = _table.PrimaryKey; } return _items; } protected override object SetItems(object editValue, object[] value) { if (editValue != null) { int length = this.GetItems(editValue).Length; int num2 = value.Length; if (!(editValue is IList)) { return editValue; } IList list = (IList)editValue; list.Clear(); for (int i = 0; i < value.Length; i++) { Index idx = value[i] as Index; if (idx is PrimaryKey) { _table.PrimaryKey = (PrimaryKey)idx; } else { if (idx != null && idx.Columns.Count > 0) list.Add(value[i]); } } } if (_cancel == false) _table._owner.MakeDirty(); return editValue; } } internal class IndexColumnEditor : CollectionEditor { Index _index; bool _cancel = false; object[] _items; public IndexColumnEditor() : base(typeof(List)) { } public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { _index = context.Instance as Index; _items = null; _cancel = false; return base.EditValue(context, provider, value); } protected override object CreateInstance(Type itemType) { if (itemType == typeof(IndexColumn)) { return new IndexColumn(_index, null); } throw new NotSupportedException(); } protected override void CancelChanges() { _cancel = true; } protected override object[] GetItems(object editValue) { if (_items == null) { List value = editValue as List; _items = new object[value.Count]; for (int n = 0; n < _items.Length; n++) _items[n] = value[n].Clone(); } return _items; } protected override object SetItems(object editValue, object[] value) { if (editValue != null) { int length = this.GetItems(editValue).Length; int num2 = value.Length; if (!(editValue is IList)) { return editValue; } IList list = (IList)editValue; list.Clear(); for (int i = 0; i < value.Length; i++) { IndexColumn idx = value[i] as IndexColumn; if (idx != null && String.IsNullOrEmpty(idx.Column) == false) list.Add(value[i]); } } if (_cancel == false) { if (_index.Columns.Count > 0 && String.IsNullOrEmpty(_index._name) == true) _index.Name = _index.Name; _index.Table._owner.MakeDirty(); } return editValue; } } internal class IndexTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) return true; return base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { StringBuilder builder = new StringBuilder(); string separator = ""; foreach (IndexColumn c in (List)value) { builder.AppendFormat("{0}[{1}]", separator, c.Column); if (c.SortMode != ColumnSortMode.Ascending) builder.Append(" DESC"); if (c.Collate != "BINARY") builder.AppendFormat(" COLLATE {0}", c.Collate); separator = ", "; } return builder.ToString(); } else return base.ConvertTo(context, culture, value, destinationType); } } internal enum ColumnSortMode { Ascending = 0, Descending = 1 } [DefaultProperty("Column")] internal class IndexColumn : IHaveConnectionScope, ICloneable { private Index _parent; private string _column; private ColumnSortMode _mode = ColumnSortMode.Ascending; private string _collate = "BINARY"; [Editor(typeof(ColumnsTypeEditor), typeof(UITypeEditor))] [DisplayName("Base Column")] public string Column { get { return _column; } set { _column = value; } } [DefaultValue(ColumnSortMode.Ascending)] public ColumnSortMode SortMode { get { return _mode; } set { _mode = value; } } [DefaultValue("BINARY")] public string Collate { get { return _collate; } set { if (String.IsNullOrEmpty(value)) _collate = "BINARY"; else _collate = value; } } public override string ToString() { if (String.IsNullOrEmpty(_column) == true) return "(none)"; return _column; } private IndexColumn(IndexColumn source) { _parent = source._parent; _column = source._column; _mode = source._mode; _collate = source._collate; } internal IndexColumn(Index parent, DataRow row) { _parent = parent; if (row != null) { _column = row["COLUMN_NAME"].ToString(); //.Trim().TrimStart('[').TrimEnd(']') if (row.IsNull("SORT_MODE") == false && (string)row["SORT_MODE"] != "ASC") _mode = ColumnSortMode.Descending; if (row.IsNull("COLLATION_NAME") == false && (string)row["COLLATION_NAME"] != "BINARY") _collate = row["COLLATION_NAME"].ToString(); } } public object Clone() { return new IndexColumn(this); } #region IHaveConnectionScope Members [Browsable(false)] public string CatalogScope { get { return _parent.Table.Catalog; } } [Browsable(false)] public string TableScope { get { return _parent.Table.Name; } } #endregion #region IHaveConnection Members public DbConnection GetConnection() { return ((IHaveConnection)_parent).GetConnection(); } #endregion } public enum IndexTypeEnum { Index = 0, PrimaryKey = 1, } [DefaultProperty("Columns")] internal class Index : IHaveConnection, ICloneable { private Table _table; internal string _name = null; private bool _unique; private List _columns = new List(); private string _definition; private bool _calcname; internal ConflictEnum _conflict = ConflictEnum.Abort; protected Index(Index source) { _table = source._table; _name = source._name; _unique = source._unique; _columns = source._columns; _definition = source._definition; _conflict = source._conflict; } internal Index(DbConnection cnn, Table table, DataRow index) { _table = table; if (index != null) { _name = index["INDEX_NAME"].ToString(); _unique = (bool)index["UNIQUE"]; _definition = index["INDEX_DEFINITION"].ToString(); using (DataTable tbl = cnn.GetSchema("IndexColumns", new string[] { table.Catalog, null, table.Name, Name })) { foreach (DataRow row in tbl.Rows) { _columns.Add(new IndexColumn(this, row)); //builder.AppendFormat("{0}[{1}]", separator, row["COLUMN_NAME"]); //if (row.IsNull("SORT_MODE") == false && (string)row["SORT_MODE"] != "ASC") // builder.AppendFormat(" {0}", row["SORT_MODE"]); //if (row.IsNull("COLLATION_NAME") == false && (string)row["COLLATION_NAME"] != "BINARY") // builder.AppendFormat(" COLLATE {0}", row["COLLATION_NAME"]); //separator = ", "; } } } } [DisplayName("Index Type")] public virtual IndexTypeEnum IndexType { get { return IndexTypeEnum.Index; } } #region IHaveConnection Members public DbConnection GetConnection() { return ((IHaveConnection)_table).GetConnection(); } #endregion internal virtual void WriteSql(StringBuilder builder) { string separator = ""; builder.AppendFormat("CREATE {0}INDEX [{1}].[{2}] ON [{3}] (", (_unique == true) ? "UNIQUE " : "", _table.Catalog, Name, _table.Name); foreach (IndexColumn c in Columns) { builder.AppendFormat("{0}[{1}]", separator, c.Column); if (c.SortMode != ColumnSortMode.Ascending) builder.Append(" DESC"); if (c.Collate != "BINARY") builder.AppendFormat(" COLLATE {0}", c.Collate); separator = ", "; } builder.AppendFormat(");"); } [Browsable(false)] internal Table Table { get { return _table; } } [Browsable(false)] public string OriginalSql { get { return _definition; } } [DefaultValue(false)] public virtual bool Unique { get { return _unique; } set { _unique = value; } } [Browsable(false)] protected virtual string NamePrefix { get { return "IX"; } } [Browsable(false)] protected virtual string NewName { get { return "NewIndex"; } } [ParenthesizePropertyName(true)] [RefreshProperties(RefreshProperties.All)] public virtual string Name { get { if (String.IsNullOrEmpty(_name)) { if (_calcname == true) return GetHashCode().ToString(); string name = String.Format("{0}_{1}", NamePrefix, NewName); if (Columns.Count > 0 && NewName != Table.Name) { name = String.Format("{0}_", NamePrefix); for (int n = 0; n < Columns.Count; n++) { if (n > 0) name += "_"; name += Columns[n].Column; } } int count = 0; string proposed = name; _calcname = true; for (int n = 0; n < _table.Indexes.Count; n++) { Index idx = _table.Indexes[n]; proposed = string.Format("{0}{1}", name, (count > 0) ? count.ToString() : ""); if (idx.Name == proposed) { count++; n = -1; } } _calcname = false; return proposed; } return _name; } set { _name = value; } } [TypeConverter(typeof(IndexTypeConverter))] [Editor(typeof(IndexColumnEditor), typeof(UITypeEditor))] [RefreshProperties(RefreshProperties.All)] public List Columns { get { return _columns; } } #region ICloneable Members object ICloneable.Clone() { return new Index(this); } #endregion } public class ColumnsMultiSelectEditor : UITypeEditor { private System.Windows.Forms.Design.IWindowsFormsEditorService _edSvc; private CheckedListBox _list; private bool _cancel; public ColumnsMultiSelectEditor() { // build selector list _list = new CheckedListBox(); _list.BorderStyle = BorderStyle.FixedSingle; _list.CheckOnClick = true; _list.ThreeDCheckBoxes = false; _list.KeyPress += new KeyPressEventHandler(_list_KeyPress); } override public UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext ctx) { return UITypeEditorEditStyle.DropDown; } override public object EditValue(ITypeDescriptorContext ctx, IServiceProvider provider, object value) { Index idx = ctx.Instance as Index; Table parent = idx.Table; // initialize editor service _edSvc = (System.Windows.Forms.Design.IWindowsFormsEditorService)provider.GetService(typeof(System.Windows.Forms.Design.IWindowsFormsEditorService)); if (_edSvc == null) return value; if (value == null) value = String.Empty; if (String.IsNullOrEmpty(value.ToString()) == true) value = String.Empty; string[] values = value.ToString().Split(','); // populate the list _list.Items.Clear(); using (DataTable tbl = parent._connection.GetSchema("Columns", new string[] { parent.Catalog, null, parent.Name })) { foreach (DataRow item in tbl.Rows) { // add this item with the proper check state CheckState check = CheckState.Unchecked; for (int n = 0; n < values.Length; n++) { if (values[n].Trim() == String.Format("[{0}]", item["COLUMN_NAME"].ToString())) { check = CheckState.Checked; break; } } _list.Items.Add(item["COLUMN_NAME"].ToString(), check); } } _list.Height = Math.Min(300, (_list.Items.Count + 1) * _list.Font.Height); // show the list _cancel = false; _edSvc.DropDownControl(_list); // build return value from checked items on the list if (!_cancel) { // build a comma-delimited string with the checked items StringBuilder sb = new StringBuilder(); foreach (object item in _list.CheckedItems) { if (sb.Length > 0) sb.Append(", "); sb.AppendFormat("[{0}]", item.ToString()); } return sb.ToString(); } // done return value; } // ** event handlers // close editor if the user presses enter or escape private void _list_KeyPress(object sender, KeyPressEventArgs e) { switch (e.KeyChar) { case (char)27: _cancel = true; _edSvc.CloseDropDown(); break; case (char)13: _edSvc.CloseDropDown(); break; } } } internal class ColumnsTypeEditor : ObjectSelectorEditor { public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } protected override void FillTreeWithData(Selector selector, ITypeDescriptorContext context, IServiceProvider provider) { base.FillTreeWithData(selector, context, provider); IHaveConnectionScope source = context.Instance as IHaveConnectionScope; if (source == null) return; using (DataTable table = source.GetConnection().GetSchema("Columns", new string[] { source.CatalogScope, null, source.TableScope })) { foreach (DataRow row in table.Rows) { selector.AddNode(row[3].ToString(), row[3], null); } } } } internal class TablesTypeEditor : ObjectSelectorEditor { public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } protected override void FillTreeWithData(Selector selector, ITypeDescriptorContext context, IServiceProvider provider) { base.FillTreeWithData(selector, context, provider); IHaveConnectionScope source = context.Instance as IHaveConnectionScope; if (source == null) return; using (DataTable table = source.GetConnection().GetSchema("Tables", new string[] { source.CatalogScope })) { foreach (DataRow row in table.Rows) { selector.AddNode(row[2].ToString(), row[2], null); } } } } }