#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading; using System.Windows.Forms; using HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.MainForm; using HeuristicLab.Persistence.Default.Xml; using HeuristicLab.PluginInfrastructure; namespace HeuristicLab.Core.Views { [View("Clipboard")] public sealed partial class Clipboard : HeuristicLab.MainForm.WindowsForms.Sidebar where T : class, IItem { private TypeSelectorDialog typeSelectorDialog; private Dictionary itemListViewItemMapping; private bool validDragOperation; private bool draggedItemsAlreadyContained; private string itemsPath; public string ItemsPath { get { return itemsPath; } private set { if (string.IsNullOrEmpty(value)) throw new ArgumentException(string.Format("Invalid items path \"{0}\".", value)); itemsPath = value; try { if (!Directory.Exists(itemsPath)) { Directory.CreateDirectory(itemsPath); // directory creation might take some time -> wait until it is definitively created while (!Directory.Exists(itemsPath)) { Thread.Sleep(100); Directory.CreateDirectory(itemsPath); } } } catch (Exception ex) { throw new ArgumentException(string.Format("Invalid items path \"{0}\".", itemsPath), ex); } } } public Clipboard() { InitializeComponent(); ItemsPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + Path.DirectorySeparatorChar + "HeuristicLab" + Path.DirectorySeparatorChar + "Clipboard"; itemListViewItemMapping = new Dictionary(); } public Clipboard(string itemsPath) { InitializeComponent(); ItemsPath = itemsPath; itemListViewItemMapping = new Dictionary(); } protected override void Dispose(bool disposing) { if (disposing) { if (typeSelectorDialog != null) typeSelectorDialog.Dispose(); foreach (T item in itemListViewItemMapping.Keys) { item.ItemImageChanged -= new EventHandler(Item_ItemImageChanged); item.ToStringChanged -= new EventHandler(Item_ToStringChanged); } if (components != null) components.Dispose(); } base.Dispose(disposing); } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); SetEnabledStateOfControls(); Enabled = false; infoLabel.Text = "Loading ..."; progressBar.Value = 0; infoPanel.Visible = true; ThreadPool.QueueUserWorkItem(new WaitCallback(LoadItems)); } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); addButton.Enabled = !ReadOnly; removeButton.Enabled = !ReadOnly && listView.SelectedItems.Count > 0; saveButton.Enabled = !ReadOnly; } public void AddItem(T item) { if (InvokeRequired) Invoke(new Action(AddItem), item); else { if (item == null) throw new ArgumentNullException("item", "Cannot add null item to clipboard."); if (!itemListViewItemMapping.ContainsKey(item)) { ListViewItem listViewItem = new ListViewItem(item.ToString()); listViewItem.ToolTipText = item.ItemName + ": " + item.ItemDescription; listView.SmallImageList.Images.Add(item.ItemImage); listViewItem.ImageIndex = listView.SmallImageList.Images.Count - 1; listViewItem.Tag = item; listView.Items.Add(listViewItem); itemListViewItemMapping.Add(item, listViewItem); item.ItemImageChanged += new EventHandler(Item_ItemImageChanged); item.ToStringChanged += new EventHandler(Item_ToStringChanged); sortAscendingButton.Enabled = sortDescendingButton.Enabled = listView.Items.Count > 1; AdjustListViewColumnSizes(); } } } private void RemoveItem(T item) { if (InvokeRequired) Invoke(new Action(RemoveItem), item); else { if (itemListViewItemMapping.ContainsKey(item)) { item.ItemImageChanged -= new EventHandler(Item_ItemImageChanged); item.ToStringChanged -= new EventHandler(Item_ToStringChanged); ListViewItem listViewItem = itemListViewItemMapping[item]; listViewItem.Remove(); itemListViewItemMapping.Remove(item); sortAscendingButton.Enabled = sortDescendingButton.Enabled = listView.Items.Count > 1; } } } private void Save() { if (InvokeRequired) Invoke(new Action(Save)); else { Enabled = false; infoLabel.Text = "Saving ..."; progressBar.Value = 0; infoPanel.Visible = true; ThreadPool.QueueUserWorkItem(new WaitCallback(SaveItems)); } } #region Loading/Saving Items private void LoadItems(object state) { string[] items = Directory.GetFiles(ItemsPath); foreach (string filename in items) { try { var ser = new ProtoBufSerializer(); T item = (T)ser.Deserialize(filename); OnItemLoaded(item, progressBar.Maximum / items.Length); } catch (Exception) { try { // try old format if protobuf deserialization fails T item = XmlParser.Deserialize(filename); OnItemLoaded(item, progressBar.Maximum / items.Length); } catch (Exception) { } } } OnAllItemsLoaded(); } private void OnItemLoaded(T item, int progress) { if (InvokeRequired) Invoke(new Action(OnItemLoaded), item, progress); else { AddItem(item); progressBar.Value += progress; } } private void OnAllItemsLoaded() { if (InvokeRequired) Invoke(new Action(OnAllItemsLoaded)); else { Enabled = true; if (listView.Items.Count > 0) { for (int i = 0; i < listView.Columns.Count; i++) listView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent); } infoPanel.Visible = false; } } private void SaveItems(object param) { Directory.Delete(ItemsPath, true); Directory.CreateDirectory(ItemsPath); // directory creation might take some time -> wait until it is definitively created while (!Directory.Exists(ItemsPath)) { Thread.Sleep(100); Directory.CreateDirectory(ItemsPath); } int i = 0; T[] items = GetStorableItems(itemListViewItemMapping.Keys); foreach (T item in items) { try { i++; SetEnabledStateOfContentViews(item, false); var ser = new ProtoBufSerializer(); ser.Serialize(item, ItemsPath + Path.DirectorySeparatorChar + i.ToString("00000000") + ".hl"); OnItemSaved(item, progressBar.Maximum / listView.Items.Count); } catch (Exception) { } finally { SetEnabledStateOfContentViews(item, true); } } OnAllItemsSaved(); } private void OnItemSaved(T item, int progress) { if (item != null) { if (InvokeRequired) Invoke(new Action(OnItemSaved), item, progress); else { progressBar.Value += progress; } } } private void OnAllItemsSaved() { if (InvokeRequired) Invoke(new Action(OnAllItemsLoaded)); else { Enabled = true; infoPanel.Visible = false; } } private void SetEnabledStateOfContentViews(IItem item, bool enabled) { if (InvokeRequired) Invoke((Action)SetEnabledStateOfContentViews, item, enabled); else { var views = MainFormManager.MainForm.Views.OfType().Where(v => v.Content == item).ToList(); views.ForEach(v => v.Enabled = enabled); } } private static T[] GetStorableItems(IEnumerable items) { var query = from item in items let executeable = item as IExecutable let views = MainFormManager.MainForm.Views.OfType().Where(v => v.Content == item) where executeable == null || executeable.ExecutionState != ExecutionState.Started where !views.Any(v => v.Locked) select item; T[] itemArray = query.ToArray(); return itemArray; } #endregion #region ListView Events private void listView_SelectedIndexChanged(object sender, EventArgs e) { removeButton.Enabled = !ReadOnly && listView.SelectedItems.Count > 0; } private void listView_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete) { if (!ReadOnly && (listView.SelectedItems.Count > 0)) { foreach (ListViewItem item in listView.SelectedItems) RemoveItem((T)item.Tag); RebuildImageList(); } } } private void listView_DoubleClick(object sender, EventArgs e) { if (listView.SelectedItems.Count == 1) { T item = (T)listView.SelectedItems[0].Tag; IContentView view = MainFormManager.MainForm.ShowContent(item, true); } } private void listView_ItemDrag(object sender, ItemDragEventArgs e) { List items = new List(); foreach (ListViewItem listViewItem in listView.SelectedItems) { T item = listViewItem.Tag as T; if (item != null) items.Add(item); } if (items.Count > 0) { DataObject data = new DataObject(); if (items.Count == 1) data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items[0]); else data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, items); if (ReadOnly) { DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link); } else { DragDropEffects result = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move); if ((result & DragDropEffects.Move) == DragDropEffects.Move) { foreach (T item in items) RemoveItem(item); RebuildImageList(); } } } } private void listView_DragEnter(object sender, DragEventArgs e) { validDragOperation = false; draggedItemsAlreadyContained = false; if (!ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T)) { validDragOperation = true; draggedItemsAlreadyContained = itemListViewItemMapping.ContainsKey((T)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat)); } else if (!ReadOnly && (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable)) { validDragOperation = true; IEnumerable items = (IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat); foreach (object item in items) { validDragOperation = validDragOperation && (item is T); draggedItemsAlreadyContained = draggedItemsAlreadyContained || itemListViewItemMapping.ContainsKey((T)item); } } } private void listView_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.None; if (validDragOperation) { if (((e.KeyState & 32) == 32) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Link; // ALT key else if (((e.KeyState & 4) == 4) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Move; // SHIFT key else if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy; else if (e.AllowedEffect.HasFlag(DragDropEffects.Move) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Move; else if (e.AllowedEffect.HasFlag(DragDropEffects.Link) && !draggedItemsAlreadyContained) e.Effect = DragDropEffects.Link; } } private void listView_DragDrop(object sender, DragEventArgs e) { if (e.Effect != DragDropEffects.None) { try { if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is T) { T item = (T)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat); AddItem(e.Effect.HasFlag(DragDropEffects.Copy) ? (T)item.Clone() : item); } else if (e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) is IEnumerable) { IEnumerable items = ((IEnumerable)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat)).Cast(); if (e.Effect.HasFlag(DragDropEffects.Copy)) { Cloner cloner = new Cloner(); items = items.Select(x => cloner.Clone(x)); } foreach (T item in items) AddItem(item); } } catch (Exception ex) { ErrorHandling.ShowErrorDialog(this, ex); } } } #endregion #region Button Events private void addButton_Click(object sender, EventArgs e) { if (typeSelectorDialog == null) { typeSelectorDialog = new TypeSelectorDialog(); typeSelectorDialog.Caption = "Select Item"; typeSelectorDialog.TypeSelector.Caption = "Available Items"; typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true); } if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) { try { AddItem((T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType()); } catch (Exception ex) { ErrorHandling.ShowErrorDialog(this, ex); } } } private void sortAscendingButton_Click(object sender, EventArgs e) { listView.Sorting = SortOrder.None; listView.Sorting = SortOrder.Ascending; } private void sortDescendingButton_Click(object sender, EventArgs e) { listView.Sorting = SortOrder.None; listView.Sorting = SortOrder.Descending; } private void removeButton_Click(object sender, EventArgs e) { if (listView.SelectedItems.Count > 0) { foreach (ListViewItem item in listView.SelectedItems) RemoveItem((T)item.Tag); RebuildImageList(); } } private void saveButton_Click(object sender, EventArgs e) { IEnumerable items = itemListViewItemMapping.Keys.Except(GetStorableItems(itemListViewItemMapping.Keys)); if (items.Any()) { string itemNames = string.Join(Environment.NewLine, items.Select(item => item.ToString()).ToArray()); MessageBox.Show("The following items are not saved, because they are locked (e.g. used in a running algorithm):" + Environment.NewLine + Environment.NewLine + itemNames + Environment.NewLine + Environment.NewLine + "All other items will be saved.", "Cannot save all items", MessageBoxButtons.OK, MessageBoxIcon.Warning); } Save(); } #endregion #region Item Events private void Item_ItemImageChanged(object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(Item_ItemImageChanged), sender, e); else { T item = (T)sender; ListViewItem listViewItem = itemListViewItemMapping[item]; int i = listViewItem.ImageIndex; listView.SmallImageList.Images[i] = item.ItemImage; listViewItem.ImageIndex = -1; listViewItem.ImageIndex = i; } } private void Item_ToStringChanged(object sender, EventArgs e) { if (InvokeRequired) Invoke(new EventHandler(Item_ToStringChanged), sender, e); else { T item = (T)sender; itemListViewItemMapping[item].Text = item.ToString(); listView.Sort(); AdjustListViewColumnSizes(); } } #endregion #region Helpers private void AdjustListViewColumnSizes() { if (listView.Items.Count > 0) { for (int i = 0; i < listView.Columns.Count; i++) listView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent); } } private void RebuildImageList() { listView.SmallImageList.Images.Clear(); foreach (ListViewItem item in listView.Items) { listView.SmallImageList.Images.Add(((T)item.Tag).ItemImage); item.ImageIndex = listView.SmallImageList.Images.Count - 1; } } #endregion } }