Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Core.Views/3.3/Clipboard.cs @ 5819

Last change on this file since 5819 was 5744, checked in by swagner, 14 years ago

Implemented dragging of multiple items in all collection views and refactored drag & drop code (#1112)

File size: 16.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections;
24using System.Collections.Generic;
25using System.IO;
26using System.Linq;
27using System.Threading;
28using System.Windows.Forms;
29using HeuristicLab.MainForm;
30using HeuristicLab.Persistence.Default.Xml;
31using HeuristicLab.PluginInfrastructure;
32
33namespace HeuristicLab.Core.Views {
34  [View("Clipboard")]
35  public sealed partial class Clipboard<T> : HeuristicLab.MainForm.WindowsForms.Sidebar where T : class, IItem {
36    private TypeSelectorDialog typeSelectorDialog;
37    private Dictionary<T, ListViewItem> itemListViewItemMapping;
38    private bool validDragOperation;
39    private bool draggedItemsAlreadyContained;
40
41    private string itemsPath;
42    public string ItemsPath {
43      get { return itemsPath; }
44      private set {
45        if (string.IsNullOrEmpty(value)) throw new ArgumentException(string.Format("Invalid items path \"{0}\".", value));
46        itemsPath = value;
47        try {
48          if (!Directory.Exists(itemsPath)) {
49            Directory.CreateDirectory(itemsPath);
50            // directory creation might take some time -> wait until it is definitively created
51            while (!Directory.Exists(itemsPath)) {
52              Thread.Sleep(100);
53              Directory.CreateDirectory(itemsPath);
54            }
55          }
56        }
57        catch (Exception ex) {
58          throw new ArgumentException(string.Format("Invalid items path \"{0}\".", itemsPath), ex);
59        }
60      }
61    }
62
63    public Clipboard() {
64      InitializeComponent();
65      ItemsPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
66                  Path.DirectorySeparatorChar + "HeuristicLab" + Path.DirectorySeparatorChar + "Clipboard";
67      itemListViewItemMapping = new Dictionary<T, ListViewItem>();
68    }
69    public Clipboard(string itemsPath) {
70      InitializeComponent();
71      ItemsPath = itemsPath;
72      itemListViewItemMapping = new Dictionary<T, ListViewItem>();
73    }
74
75    protected override void Dispose(bool disposing) {
76      if (disposing) {
77        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
78        foreach (T item in itemListViewItemMapping.Keys) {
79          item.ItemImageChanged -= new EventHandler(Item_ItemImageChanged);
80          item.ToStringChanged -= new EventHandler(Item_ToStringChanged);
81        }
82        if (components != null) components.Dispose();
83      }
84      base.Dispose(disposing);
85    }
86
87    protected override void OnInitialized(EventArgs e) {
88      base.OnInitialized(e);
89      SetEnabledStateOfControls();
90      Enabled = false;
91      infoLabel.Text = "Loading ...";
92      progressBar.Value = 0;
93      infoPanel.Visible = true;
94      ThreadPool.QueueUserWorkItem(new WaitCallback(LoadItems));
95    }
96
97    protected override void SetEnabledStateOfControls() {
98      base.SetEnabledStateOfControls();
99      addButton.Enabled = !ReadOnly;
100      removeButton.Enabled = !ReadOnly && listView.SelectedItems.Count > 0;
101      saveButton.Enabled = !ReadOnly;
102    }
103
104    public void AddItem(T item) {
105      if (InvokeRequired)
106        Invoke(new Action<T>(AddItem), item);
107      else {
108        if (item == null) throw new ArgumentNullException("item", "Cannot add null item to clipboard.");
109        if (!itemListViewItemMapping.ContainsKey(item)) {
110          ListViewItem listViewItem = new ListViewItem(item.ToString());
111          listViewItem.ToolTipText = item.ItemName + ": " + item.ItemDescription;
112          listView.SmallImageList.Images.Add(item.ItemImage);
113          listViewItem.ImageIndex = listView.SmallImageList.Images.Count - 1;
114          listViewItem.Tag = item;
115          listView.Items.Add(listViewItem);
116          itemListViewItemMapping.Add(item, listViewItem);
117          item.ItemImageChanged += new EventHandler(Item_ItemImageChanged);
118          item.ToStringChanged += new EventHandler(Item_ToStringChanged);
119          sortAscendingButton.Enabled = sortDescendingButton.Enabled = listView.Items.Count > 1;
120          AdjustListViewColumnSizes();
121        }
122      }
123    }
124
125    private void RemoveItem(T item) {
126      if (InvokeRequired)
127        Invoke(new Action<T>(RemoveItem), item);
128      else {
129        if (itemListViewItemMapping.ContainsKey(item)) {
130          item.ItemImageChanged -= new EventHandler(Item_ItemImageChanged);
131          item.ToStringChanged -= new EventHandler(Item_ToStringChanged);
132          ListViewItem listViewItem = itemListViewItemMapping[item];
133          listViewItem.Remove();
134          itemListViewItemMapping.Remove(item);
135          sortAscendingButton.Enabled = sortDescendingButton.Enabled = listView.Items.Count > 1;
136        }
137      }
138    }
139    private void Save() {
140      if (InvokeRequired)
141        Invoke(new Action(Save));
142      else {
143        Enabled = false;
144        infoLabel.Text = "Saving ...";
145        progressBar.Value = 0;
146        infoPanel.Visible = true;
147        ThreadPool.QueueUserWorkItem(new WaitCallback(SaveItems));
148      }
149    }
150
151    #region Loading/Saving Items
152    private void LoadItems(object state) {
153      string[] items = Directory.GetFiles(ItemsPath);
154      foreach (string filename in items) {
155        try {
156          T item = XmlParser.Deserialize<T>(filename);
157          OnItemLoaded(item, progressBar.Maximum / items.Length);
158        }
159        catch (Exception) { }
160      }
161      OnAllItemsLoaded();
162    }
163    private void OnItemLoaded(T item, int progress) {
164      if (InvokeRequired)
165        Invoke(new Action<T, int>(OnItemLoaded), item, progress);
166      else {
167        AddItem(item);
168        progressBar.Value += progress;
169      }
170    }
171    private void OnAllItemsLoaded() {
172      if (InvokeRequired)
173        Invoke(new Action(OnAllItemsLoaded));
174      else {
175        Enabled = true;
176        if (listView.Items.Count > 0) {
177          for (int i = 0; i < listView.Columns.Count; i++)
178            listView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
179        }
180        infoPanel.Visible = false;
181      }
182    }
183    private void SaveItems(object param) {
184      Directory.Delete(ItemsPath, true);
185      Directory.CreateDirectory(ItemsPath);
186      // directory creation might take some time -> wait until it is definitively created
187      while (!Directory.Exists(ItemsPath)) {
188        Thread.Sleep(100);
189        Directory.CreateDirectory(ItemsPath);
190      }
191
192      int i = 0;
193      T[] items = GetStorableItems(itemListViewItemMapping.Keys);
194
195      foreach (T item in items) {
196        try {
197          i++;
198          SetEnabledStateOfContentViews(item, false);
199          XmlGenerator.Serialize(item, ItemsPath + Path.DirectorySeparatorChar + i.ToString("00000000") + ".hl", 9);
200          OnItemSaved(item, progressBar.Maximum / listView.Items.Count);
201        }
202        catch (Exception) { }
203        finally {
204          SetEnabledStateOfContentViews(item, true);
205        }
206      }
207      OnAllItemsSaved();
208    }
209
210    private void OnItemSaved(T item, int progress) {
211      if (item != null) {
212        if (InvokeRequired)
213          Invoke(new Action<T, int>(OnItemSaved), item, progress);
214        else {
215          progressBar.Value += progress;
216        }
217      }
218    }
219    private void OnAllItemsSaved() {
220      if (InvokeRequired)
221        Invoke(new Action(OnAllItemsLoaded));
222      else {
223        Enabled = true;
224        infoPanel.Visible = false;
225      }
226    }
227
228    private void SetEnabledStateOfContentViews(IItem item, bool enabled) {
229      if (InvokeRequired)
230        Invoke((Action<IItem, bool>)SetEnabledStateOfContentViews, item, enabled);
231      else {
232        var views = MainFormManager.MainForm.Views.OfType<IContentView>().Where(v => v.Content == item).ToList();
233        views.ForEach(v => v.Enabled = enabled);
234      }
235    }
236
237    private static T[] GetStorableItems(IEnumerable<T> items) {
238      var query = from item in items
239                  let executeable = item as IExecutable
240                  let views = MainFormManager.MainForm.Views.OfType<IContentView>().Where(v => v.Content == item)
241                  where executeable == null || executeable.ExecutionState != ExecutionState.Started
242                  where !views.Any(v => v.Locked)
243                  select item;
244      T[] itemArray = query.ToArray();
245      return itemArray;
246    }
247    #endregion
248
249    #region ListView Events
250    private void listView_SelectedIndexChanged(object sender, EventArgs e) {
251      removeButton.Enabled = !ReadOnly && listView.SelectedItems.Count > 0;
252    }
253    private void listView_KeyDown(object sender, KeyEventArgs e) {
254      if (e.KeyCode == Keys.Delete) {
255        if (!ReadOnly && (listView.SelectedItems.Count > 0)) {
256          foreach (ListViewItem item in listView.SelectedItems)
257            RemoveItem((T)item.Tag);
258          RebuildImageList();
259        }
260      }
261    }
262    private void listView_DoubleClick(object sender, EventArgs e) {
263      if (listView.SelectedItems.Count == 1) {
264        T item = (T)listView.SelectedItems[0].Tag;
265        IContentView view = MainFormManager.MainForm.ShowContent(item, true);
266      }
267    }
268    private void listView_ItemDrag(object sender, ItemDragEventArgs e) {
269      List<T> items = new List<T>();
270      foreach (ListViewItem listViewItem in listView.SelectedItems) {
271        T item = listViewItem.Tag as T;
272        if (item != null) items.Add(item);
273      }
274
275      if (items.Count > 0) {
276        DataObject data = new DataObject();
277        if (items.Count == 1) data.SetData("HeuristicLab", items[0]);
278        else data.SetData("HeuristicLab", items);
279        if (ReadOnly) {
280          DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
281        } else {
282          DragDropEffects result = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
283          if ((result & DragDropEffects.Move) == DragDropEffects.Move) {
284            foreach (T item in items) RemoveItem(item);
285            RebuildImageList();
286          }
287        }
288      }
289    }
290    private void listView_DragEnter(object sender, DragEventArgs e) {
291      validDragOperation = false;
292      draggedItemsAlreadyContained = false;
293      if (e.Data.GetData("HeuristicLab") is T) {
294        validDragOperation = true;
295        draggedItemsAlreadyContained = itemListViewItemMapping.ContainsKey((T)e.Data.GetData("HeuristicLab"));
296      } else if (e.Data.GetData("HeuristicLab") is IEnumerable) {
297        validDragOperation = true;
298        IEnumerable items = (IEnumerable)e.Data.GetData("HeuristicLab");
299        foreach (object item in items) {
300          validDragOperation = validDragOperation && (item is T);
301          draggedItemsAlreadyContained = draggedItemsAlreadyContained || itemListViewItemMapping.ContainsKey((T)item);
302        }
303      }
304      validDragOperation = validDragOperation && !ReadOnly;
305    }
306    private void listView_DragOver(object sender, DragEventArgs e) {
307      e.Effect = DragDropEffects.None;
308      if (validDragOperation) {
309        if (((e.KeyState & 32) == 32) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Link;  // ALT key
310        else if (((e.KeyState & 4) == 4) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Move;  // SHIFT key
311        else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
312        else if (e.AllowedEffect.HasFlag(DragDropEffects.Move) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Move;
313        else if (e.AllowedEffect.HasFlag(DragDropEffects.Link) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Link;
314      }
315    }
316    private void listView_DragDrop(object sender, DragEventArgs e) {
317      if (e.Effect != DragDropEffects.None) {
318        try {
319          if (e.Data.GetData("HeuristicLab") is T) {
320            T item = (T)e.Data.GetData("HeuristicLab");
321            AddItem(e.Effect.HasFlag(DragDropEffects.Copy) ? (T)item.Clone() : item);
322          } else if (e.Data.GetData("HeuristicLab") is IEnumerable) {
323            IEnumerable<T> items = ((IEnumerable)e.Data.GetData("HeuristicLab")).Cast<T>();
324            foreach (T item in items)
325              AddItem(e.Effect.HasFlag(DragDropEffects.Copy) ? (T)item.Clone() : item);
326          }
327        }
328        catch (Exception ex) {
329          ErrorHandling.ShowErrorDialog(this, ex);
330        }
331      }
332    }
333    #endregion
334
335    #region Button Events
336    private void addButton_Click(object sender, EventArgs e) {
337      if (typeSelectorDialog == null) {
338        typeSelectorDialog = new TypeSelectorDialog();
339        typeSelectorDialog.Caption = "Select Item";
340        typeSelectorDialog.TypeSelector.Caption = "Available Items";
341        typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
342      }
343
344      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
345        try {
346          AddItem((T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType());
347        }
348        catch (Exception ex) {
349          ErrorHandling.ShowErrorDialog(this, ex);
350        }
351      }
352    }
353    private void sortAscendingButton_Click(object sender, EventArgs e) {
354      listView.Sorting = SortOrder.None;
355      listView.Sorting = SortOrder.Ascending;
356    }
357    private void sortDescendingButton_Click(object sender, EventArgs e) {
358      listView.Sorting = SortOrder.None;
359      listView.Sorting = SortOrder.Descending;
360    }
361    private void removeButton_Click(object sender, EventArgs e) {
362      if (listView.SelectedItems.Count > 0) {
363        foreach (ListViewItem item in listView.SelectedItems)
364          RemoveItem((T)item.Tag);
365        RebuildImageList();
366      }
367    }
368    private void saveButton_Click(object sender, EventArgs e) {
369      IEnumerable<T> items = itemListViewItemMapping.Keys.Except(GetStorableItems(itemListViewItemMapping.Keys));
370      if (items.Any()) {
371        string itemNames = string.Join(Environment.NewLine, items.Select(item => item.ToString()).ToArray());
372        MessageBox.Show("The following items are not saved, because they are locked (e.g. used in a running algorithm):" + Environment.NewLine + Environment.NewLine +
373          itemNames + Environment.NewLine + Environment.NewLine + "All other items will be saved.", "Cannot save all items", MessageBoxButtons.OK, MessageBoxIcon.Warning);
374      }
375      Save();
376    }
377    #endregion
378
379    #region Item Events
380    private void Item_ItemImageChanged(object sender, EventArgs e) {
381      if (InvokeRequired)
382        Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
383      else {
384        T item = (T)sender;
385        ListViewItem listViewItem = itemListViewItemMapping[item];
386        int i = listViewItem.ImageIndex;
387        listViewItem.ImageList.Images[i] = item.ItemImage;
388        listViewItem.ImageIndex = -1;
389        listViewItem.ImageIndex = i;
390      }
391    }
392    private void Item_ToStringChanged(object sender, EventArgs e) {
393      if (InvokeRequired)
394        Invoke(new EventHandler(Item_ToStringChanged), sender, e);
395      else {
396        T item = (T)sender;
397        itemListViewItemMapping[item].Text = item.ToString();
398        listView.Sort();
399        AdjustListViewColumnSizes();
400      }
401    }
402    #endregion
403
404    #region Helpers
405    private void AdjustListViewColumnSizes() {
406      if (listView.Items.Count > 0) {
407        for (int i = 0; i < listView.Columns.Count; i++)
408          listView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
409      }
410    }
411    private void RebuildImageList() {
412      listView.SmallImageList.Images.Clear();
413      foreach (ListViewItem item in listView.Items) {
414        listView.SmallImageList.Images.Add(((T)item.Tag).ItemImage);
415        item.ImageIndex = listView.SmallImageList.Images.Count - 1;
416      }
417    }
418    #endregion
419  }
420}
Note: See TracBrowser for help on using the repository browser.