source: trunk/sources/HeuristicLab.MainForm.WindowsForms/3.3/MainForms/MainForm.cs @ 11467

Last change on this file since 11467 was 11467, checked in by bburlacu, 8 years ago

#2265: Added drag&drop file loading support. To avoid confusion between files and all the other data types that can be dragged and dropped in HeuristicLab, the drop area for loading files is restricted to the toolbar and menubar of the HL window.

File size: 22.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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.Generic;
24using System.Linq;
25using System.Windows.Forms;
26using HeuristicLab.Common;
27using HeuristicLab.PluginInfrastructure;
28using WeifenLuo.WinFormsUI.Docking;
29
30namespace HeuristicLab.MainForm.WindowsForms {
31  public partial class MainForm : Form, IMainForm {
32    private bool initialized;
33    private int appStartingCursors;
34    private int waitingCursors;
35
36    protected MainForm()
37      : base() {
38      InitializeComponent();
39      this.views = new Dictionary<IView, Form>();
40      this.userInterfaceItems = new List<IUserInterfaceItem>();
41      this.initialized = false;
42      this.showContentInViewHost = false;
43      appStartingCursors = 0;
44      waitingCursors = 0;
45    }
46
47    protected MainForm(Type userInterfaceItemType)
48      : this() {
49      this.userInterfaceItemType = userInterfaceItemType;
50    }
51
52    #region properties
53    private bool showContentInViewHost;
54    public bool ShowContentInViewHost {
55      get { return this.showContentInViewHost; }
56      set { this.showContentInViewHost = value; }
57    }
58
59    public string Title {
60      get { return this.Text; }
61      set {
62        if (InvokeRequired) {
63          Action<string> action = delegate(string s) { this.Title = s; };
64          Invoke(action, value);
65        } else
66          this.Text = value;
67      }
68    }
69
70    public override Cursor Cursor {
71      get { return base.Cursor; }
72      set {
73        if (InvokeRequired) {
74          Action<Cursor> action = delegate(Cursor c) { this.Cursor = c; };
75          Invoke(action, value);
76        } else
77          base.Cursor = value;
78      }
79    }
80
81    private Type userInterfaceItemType;
82    public Type UserInterfaceItemType {
83      get { return this.userInterfaceItemType; }
84    }
85
86    private Dictionary<IView, Form> views;
87    public IEnumerable<IView> Views {
88      get { return views.Keys; }
89    }
90
91    private IView activeView;
92    public IView ActiveView {
93      get { return this.activeView; }
94      protected set {
95        if (this.activeView != value) {
96          if (InvokeRequired) {
97            Action<IView> action = delegate(IView activeView) { this.ActiveView = activeView; };
98            Invoke(action, value);
99          } else {
100            this.activeView = value;
101            OnActiveViewChanged();
102          }
103        }
104      }
105    }
106
107    private List<IUserInterfaceItem> userInterfaceItems;
108    protected IEnumerable<IUserInterfaceItem> UserInterfaceItems {
109      get { return this.userInterfaceItems; }
110    }
111    #endregion
112
113    #region events
114    public event EventHandler ActiveViewChanged;
115    protected virtual void OnActiveViewChanged() {
116      if (InvokeRequired)
117        Invoke((MethodInvoker)OnActiveViewChanged);
118      else {
119        EventHandler handler = ActiveViewChanged;
120        if (handler != null)
121          handler(this, EventArgs.Empty);
122      }
123    }
124
125    public event EventHandler<ViewEventArgs> ViewClosed;
126    protected virtual void OnViewClosed(IView view) {
127      if (InvokeRequired) Invoke((Action<IView>)OnViewClosed, view);
128      else {
129        EventHandler<ViewEventArgs> handler = ViewClosed;
130        if (handler != null)
131          handler(this, new ViewEventArgs(view));
132      }
133    }
134
135    public event EventHandler<ViewShownEventArgs> ViewShown;
136    protected virtual void OnViewShown(IView view, bool firstTimeShown) {
137      if (InvokeRequired) Invoke((Action<IView, bool>)OnViewShown, view, firstTimeShown);
138      else {
139        EventHandler<ViewShownEventArgs> handler = ViewShown;
140        if (handler != null)
141          handler(this, new ViewShownEventArgs(view, firstTimeShown));
142      }
143    }
144
145    public event EventHandler<ViewEventArgs> ViewHidden;
146    protected virtual void OnViewHidden(IView view) {
147      if (InvokeRequired) Invoke((Action<IView>)OnViewHidden, view);
148      else {
149        EventHandler<ViewEventArgs> handler = ViewHidden;
150        if (handler != null)
151          handler(this, new ViewEventArgs(view));
152      }
153    }
154
155    public event EventHandler Changed;
156    protected void OnChanged() {
157      if (InvokeRequired)
158        Invoke((MethodInvoker)OnChanged);
159      else {
160        EventHandler handler = Changed;
161        if (handler != null)
162          Changed(this, EventArgs.Empty);
163      }
164    }
165
166    private void MainFormBase_Load(object sender, EventArgs e) {
167      if (!DesignMode) {
168        MainFormManager.RegisterMainForm(this);
169        this.CreateGUI();
170        if (!this.initialized) {
171          this.initialized = true;
172          this.OnInitialized(EventArgs.Empty);
173        }
174      }
175    }
176
177    protected virtual void OnInitialized(EventArgs e) { }
178
179    public virtual void UpdateTitle() { }
180
181    private void FormActivated(object sender, EventArgs e) {
182      this.ActiveView = GetView((Form)sender);
183    }
184
185    protected override void OnFormClosing(FormClosingEventArgs e) {
186      foreach (KeyValuePair<IView, Form> pair in this.views) {
187        DockForm dockForm = pair.Value as DockForm;
188        View view = pair.Key as View;
189        if (view != null && dockForm != null && dockForm.DockState != DockState.Document) {
190          view.CloseReason = CloseReason.ApplicationExitCall;
191          view.OnClosingHelper(dockForm, e);
192        }
193      }
194      base.OnFormClosing(e);
195    }
196
197    protected override void OnFormClosed(FormClosedEventArgs e) {
198      foreach (KeyValuePair<IView, Form> pair in this.views.ToList()) {
199        DockForm dockForm = pair.Value as DockForm;
200        View view = pair.Key as View;
201        if (view != null && dockForm != null && dockForm.DockState != DockState.Document) {
202          view.CloseReason = CloseReason.ApplicationExitCall;
203          view.OnClosedHelper(dockForm, e);
204          dockForm.Close();
205        }
206      }
207      base.OnFormClosed(e);
208    }
209    #endregion
210
211    #region create, get, show, hide, close views
212    protected virtual Form CreateForm(IView view) {
213      throw new NotImplementedException("CreateForm must be implemented in subclasses of MainForm.");
214    }
215
216    internal Form GetForm(IView view) {
217      if (views.ContainsKey(view))
218        return views[view];
219      return null;
220    }
221    protected IView GetView(Form form) {
222      return views.Where(x => x.Value == form).Single().Key;
223    }
224
225    public IContentView ShowContent(IContent content) {
226      if (content == null) throw new ArgumentNullException("Content cannot be null.");
227      Type viewType = MainFormManager.GetDefaultViewType(content.GetType());
228      if (viewType != null) return ShowContent(content, viewType);
229      return null;
230    }
231
232    public IContentView ShowContent<T>(T content, bool reuseExistingView, IEqualityComparer<T> comparer = null) where T : class,IContent {
233      if (content == null) throw new ArgumentNullException("Content cannot be null.");
234      if (!reuseExistingView) return ShowContent(content);
235
236      IContentView view = null;
237      if (comparer == null) view = Views.OfType<IContentView>().Where(v => (v.Content as T) == content).FirstOrDefault();
238      else view = Views.OfType<IContentView>().Where(v => comparer.Equals((v.Content as T), content)).FirstOrDefault();
239
240      if (view == null) view = ShowContent(content);
241      else view.Show();
242
243      return view;
244    }
245
246    public IContentView ShowContent(IContent content, Type viewType) {
247      if (InvokeRequired) return (IContentView)Invoke((Func<IContent, Type, IContentView>)ShowContent, content, viewType);
248      else {
249        if (content == null) throw new ArgumentNullException("Content cannot be null.");
250        if (viewType == null) throw new ArgumentNullException("ViewType cannot be null.");
251
252        IContentView view = null;
253        if (ShowContentInViewHost) {
254          ViewHost viewHost = new ViewHost();
255          viewHost.ViewType = viewType;
256          view = viewHost;
257
258        } else view = MainFormManager.CreateView(viewType);
259
260        view.Content = content;
261        view.Show();
262        return view;
263      }
264    }
265
266    internal void ShowView(IView view) {
267      if (InvokeRequired) Invoke((Action<IView>)ShowView, view);
268      else {
269        Form form = GetForm(view);
270        bool firstTimeShown = form == null;
271        if (firstTimeShown) {
272          form = CreateForm(view);
273          form.Activated += new EventHandler(FormActivated);
274          form.FormClosed += new FormClosedEventHandler(ChildFormClosed);
275          view.Changed += new EventHandler(View_Changed);
276          views[view] = form;
277        }
278        this.ShowView(view, firstTimeShown);
279        this.OnViewShown(view, firstTimeShown);
280      }
281    }
282
283    private void View_Changed(object sender, EventArgs e) {
284      IView view = (IView)sender;
285      if (view == this.ActiveView)
286        this.OnActiveViewChanged();
287      this.OnChanged();
288    }
289
290    protected virtual void ShowView(IView view, bool firstTimeShown) {
291    }
292
293    internal void HideView(IView view) {
294      if (InvokeRequired) Invoke((Action<IView>)HideView, view);
295      else {
296        this.Hide(view);
297        if (this.activeView == view)
298          this.ActiveView = null;
299        this.OnViewHidden(view);
300      }
301    }
302
303    protected virtual void Hide(IView view) {
304    }
305
306    private void ChildFormClosed(object sender, FormClosedEventArgs e) {
307      Form form = (Form)sender;
308      IView view = GetView(form);
309
310      view.Changed -= new EventHandler(View_Changed);
311      form.Activated -= new EventHandler(FormActivated);
312      form.FormClosed -= new FormClosedEventHandler(ChildFormClosed);
313
314      views.Remove(view);
315      this.OnViewClosed(view);
316      if (ActiveView == view)
317        ActiveView = null;
318    }
319
320    internal void CloseView(IView view) {
321      if (InvokeRequired) Invoke((Action<IView>)CloseView, view);
322      else if (views.ContainsKey(view)) {
323        this.views[view].Close();
324        this.OnViewClosed(view);
325      }
326    }
327
328    internal void CloseView(IView view, CloseReason closeReason) {
329      if (InvokeRequired) Invoke((Action<IView>)CloseView, view);
330      else if (views.ContainsKey(view)) {
331        ((View)view).CloseReason = closeReason;
332        this.CloseView(view);
333      }
334    }
335
336    public void CloseAllViews() {
337      foreach (IView view in views.Keys.ToArray())
338        CloseView(view);
339    }
340
341    public void CloseAllViews(CloseReason closeReason) {
342      foreach (IView view in views.Keys.ToArray())
343        CloseView(view, closeReason);
344    }
345    #endregion
346
347    #region progress views
348    private readonly Dictionary<IContent, IProgress> contentProgressLookup = new Dictionary<IContent, IProgress>();
349    private readonly Dictionary<Control, IProgress> viewProgressLookup = new Dictionary<Control, IProgress>();
350    private readonly List<ProgressView> progressViews = new List<ProgressView>();
351
352    /// <summary>
353    /// Adds a <see cref="ProgressView"/> to the <see cref="ContentView"/>s showing the specified content.
354    /// </summary>
355    public IProgress AddOperationProgressToContent(IContent content, string progressMessage, bool addToObjectGraphObjects = true) {
356      if (InvokeRequired) {
357        IProgress result = (IProgress)Invoke((Func<IContent, string, bool, IProgress>)AddOperationProgressToContent, content, progressMessage, addToObjectGraphObjects);
358        return result;
359      }
360      if (contentProgressLookup.ContainsKey(content))
361        throw new ArgumentException("A progress is already registered for the specified content.", "content");
362
363      var contentViews = views.Keys.OfType<ContentView>();
364      if (!contentViews.Any(v => v.Content == content))
365        throw new ArgumentException("The content is not displayed in a top-level view", "content");
366
367      if (addToObjectGraphObjects) {
368        var containedObjects = content.GetObjectGraphObjects();
369        contentViews = contentViews.Where(v => containedObjects.Contains(v.Content));
370      } else
371        contentViews = contentViews.Where(v => v.Content == content);
372
373      var progress = new Progress(progressMessage, ProgressState.Started);
374      foreach (var contentView in contentViews) {
375        progressViews.Add(new ProgressView(contentView, progress));
376      }
377
378      contentProgressLookup[content] = progress;
379      return progress;
380    }
381
382    /// <summary>
383    /// Adds a <see cref="ProgressView"/> to the specified view.
384    /// </summary>
385    public IProgress AddOperationProgressToView(Control control, string progressMessage) {
386      var progress = new Progress(progressMessage, ProgressState.Started);
387      AddOperationProgressToView(control, progress);
388      return progress;
389    }
390
391    public void AddOperationProgressToView(Control control, IProgress progress) {
392      if (InvokeRequired) {
393        Invoke((Action<Control, IProgress>)AddOperationProgressToView, control, progress);
394        return;
395      }
396      if (control == null) throw new ArgumentNullException("control", "The view must not be null.");
397      if (progress == null) throw new ArgumentNullException("progress", "The progress must not be null.");
398
399      IProgress oldProgress;
400      if (viewProgressLookup.TryGetValue(control, out oldProgress)) {
401        foreach (var progressView in progressViews.Where(v => v.Content == oldProgress).ToList()) {
402          progressView.Dispose();
403          progressViews.Remove(progressView);
404        }
405        viewProgressLookup.Remove(control);
406      }
407
408      progressViews.Add(new ProgressView(control, progress));
409      viewProgressLookup[control] = progress;
410    }
411
412    /// <summary>
413    /// Removes an existing <see cref="ProgressView"/> from the <see cref="ContentView"/>s showing the specified content.
414    /// </summary>
415    public void RemoveOperationProgressFromContent(IContent content, bool finishProgress = true) {
416      if (InvokeRequired) {
417        Invoke((Action<IContent, bool>)RemoveOperationProgressFromContent, content, finishProgress);
418        return;
419      }
420
421      IProgress progress;
422      if (!contentProgressLookup.TryGetValue(content, out progress))
423        throw new ArgumentException("No progress is registered for the specified content.", "content");
424
425      if (finishProgress) progress.Finish();
426      foreach (var progressView in progressViews.Where(v => v.Content == progress).ToList()) {
427        progressView.Dispose();
428        progressViews.Remove(progressView);
429      }
430      contentProgressLookup.Remove(content);
431
432    }
433
434    /// <summary>
435    /// Removes an existing <see cref="ProgressView"/> from the specified view.
436    /// </summary>
437    public void RemoveOperationProgressFromView(Control control, bool finishProgress = true) {
438      if (InvokeRequired) {
439        Invoke((Action<Control, bool>)RemoveOperationProgressFromView, control, finishProgress);
440        return;
441      }
442
443      IProgress progress;
444      if (!viewProgressLookup.TryGetValue(control, out progress))
445        throw new ArgumentException("No progress is registered for the specified control.", "control");
446
447      if (finishProgress) progress.Finish();
448      foreach (var progressView in progressViews.Where(v => v.Content == progress).ToList()) {
449        progressView.Dispose();
450        progressViews.Remove(progressView);
451      }
452      viewProgressLookup.Remove(control);
453    }
454    #endregion
455
456    #region create menu and toolbar
457    private void CreateGUI() {
458      IEnumerable<object> allUserInterfaceItems = ApplicationManager.Manager.GetInstances(userInterfaceItemType);
459
460      IEnumerable<IPositionableUserInterfaceItem> toolStripMenuItems =
461        from mi in allUserInterfaceItems
462        where (mi is IPositionableUserInterfaceItem) &&
463              (mi is IMenuItem || mi is IMenuSeparatorItem)
464        orderby ((IPositionableUserInterfaceItem)mi).Position
465        select (IPositionableUserInterfaceItem)mi;
466
467      foreach (IPositionableUserInterfaceItem menuItem in toolStripMenuItems) {
468        if (menuItem is IMenuItem)
469          AddToolStripMenuItem((IMenuItem)menuItem);
470        else if (menuItem is IMenuSeparatorItem)
471          AddToolStripMenuItem((IMenuSeparatorItem)menuItem);
472      }
473
474      IEnumerable<IPositionableUserInterfaceItem> toolStripButtonItems =
475        from bi in allUserInterfaceItems
476        where (bi is IPositionableUserInterfaceItem) &&
477              (bi is IToolBarItem || bi is IToolBarSeparatorItem)
478        orderby ((IPositionableUserInterfaceItem)bi).Position
479        select (IPositionableUserInterfaceItem)bi;
480
481      foreach (IPositionableUserInterfaceItem toolStripButtonItem in toolStripButtonItems) {
482        if (toolStripButtonItem is IToolBarItem)
483          AddToolStripButtonItem((IToolBarItem)toolStripButtonItem);
484        else if (toolStripButtonItem is IToolBarSeparatorItem)
485          AddToolStripButtonItem((IToolBarSeparatorItem)toolStripButtonItem);
486      }
487
488      this.AdditionalCreationOfGuiElements();
489    }
490
491    protected virtual void AdditionalCreationOfGuiElements() {
492    }
493
494    private void AddToolStripMenuItem(IMenuItem menuItem) {
495      ToolStripMenuItem item = new ToolStripMenuItem();
496      this.SetToolStripItemProperties(item, menuItem);
497      this.InsertItem(menuItem.Structure, typeof(ToolStripMenuItem), item, menuStrip.Items);
498      if (menuItem is MenuItem) {
499        MenuItem menuItemBase = (MenuItem)menuItem;
500        menuItemBase.ToolStripItem = item;
501        item.ShortcutKeys = menuItemBase.ShortCutKeys;
502        item.DisplayStyle = menuItemBase.ToolStripItemDisplayStyle;
503      }
504    }
505
506    private void AddToolStripMenuItem(IMenuSeparatorItem menuItem) {
507      this.InsertItem(menuItem.Structure, typeof(ToolStripMenuItem), new ToolStripSeparator(), menuStrip.Items);
508    }
509
510    private void AddToolStripButtonItem(IToolBarItem buttonItem) {
511      ToolStripItem item = new ToolStripButton();
512      if (buttonItem is ToolBarItem) {
513        ToolBarItem buttonItemBase = (ToolBarItem)buttonItem;
514        if (buttonItemBase.IsDropDownButton)
515          item = new ToolStripDropDownButton();
516
517        item.DisplayStyle = buttonItemBase.ToolStripItemDisplayStyle;
518        buttonItemBase.ToolStripItem = item;
519      }
520
521      this.SetToolStripItemProperties(item, buttonItem);
522      this.InsertItem(buttonItem.Structure, typeof(ToolStripDropDownButton), item, toolStrip.Items);
523    }
524
525    private void AddToolStripButtonItem(IToolBarSeparatorItem buttonItem) {
526      this.InsertItem(buttonItem.Structure, typeof(ToolStripDropDownButton), new ToolStripSeparator(), toolStrip.Items);
527    }
528
529    private void InsertItem(IEnumerable<string> structure, Type t, ToolStripItem item, ToolStripItemCollection parentItems) {
530      ToolStripDropDownItem parent = null;
531      foreach (string s in structure) {
532        if (parentItems.ContainsKey(s))
533          parent = (ToolStripDropDownItem)parentItems[s];
534        else {
535          parent = (ToolStripDropDownItem)Activator.CreateInstance(t, s, null, null, s); ;
536          parentItems.Add(parent);
537        }
538        parentItems = parent.DropDownItems;
539      }
540      parentItems.Add(item);
541    }
542
543    private void SetToolStripItemProperties(ToolStripItem toolStripItem, IActionUserInterfaceItem userInterfaceItem) {
544      toolStripItem.Name = userInterfaceItem.Name;
545      toolStripItem.Text = userInterfaceItem.Name;
546      toolStripItem.ToolTipText = userInterfaceItem.ToolTipText;
547      toolStripItem.Tag = userInterfaceItem;
548      toolStripItem.Image = userInterfaceItem.Image;
549      toolStripItem.Click += new EventHandler(ToolStripItemClicked);
550      this.userInterfaceItems.Add(userInterfaceItem);
551    }
552
553    private void ToolStripItemClicked(object sender, EventArgs e) {
554      System.Windows.Forms.ToolStripItem item = (System.Windows.Forms.ToolStripItem)sender;
555      try {
556        ((IActionUserInterfaceItem)item.Tag).Execute();
557      }
558      catch (Exception ex) {
559        ErrorHandling.ShowErrorDialog((Control)MainFormManager.MainForm, ex);
560      }
561    }
562    #endregion
563
564    #region Cursor Handling
565    public void SetAppStartingCursor() {
566      if (InvokeRequired)
567        Invoke(new Action(SetAppStartingCursor));
568      else {
569        appStartingCursors++;
570        SetCursor();
571      }
572    }
573    public void ResetAppStartingCursor() {
574      if (InvokeRequired)
575        Invoke(new Action(ResetAppStartingCursor));
576      else {
577        appStartingCursors--;
578        SetCursor();
579      }
580    }
581    public void SetWaitCursor() {
582      if (InvokeRequired)
583        Invoke(new Action(SetWaitCursor));
584      else {
585        waitingCursors++;
586        SetCursor();
587      }
588    }
589    public void ResetWaitCursor() {
590      if (InvokeRequired)
591        Invoke(new Action(ResetWaitCursor));
592      else {
593        waitingCursors--;
594        SetCursor();
595      }
596    }
597    private void SetCursor() {
598      if (waitingCursors > 0) Cursor = Cursors.WaitCursor;
599      else if (appStartingCursors > 0) Cursor = Cursors.AppStarting;
600      else Cursor = Cursors.Default;
601    }
602    #endregion
603
604    private void MainFormBase_DragEnter(object sender, DragEventArgs e) {
605      // perform type checking to ensure that the data being dragged is of an acceptable type
606      e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
607    }
608
609    private void MainFormBase_DragDrop(object sender, DragEventArgs e) {
610      if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
611        string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
612        foreach (var path in files) {
613          SetAppStartingCursor();
614          ContentManager.LoadAsync(path, LoadingCompleted);
615        }
616      }
617    }
618
619    private static void LoadingCompleted(IStorableContent content, Exception error) {
620      try {
621        if (error != null) throw error;
622        IView view = MainFormManager.MainForm.ShowContent(content);
623        if (view == null)
624          ErrorHandling.ShowErrorDialog("There is no view for the loaded item. It cannot be displayed.", new InvalidOperationException("No View Available"));
625      }
626      catch (Exception ex) {
627        ErrorHandling.ShowErrorDialog((Control)MainFormManager.MainForm, "Cannot open file.", ex);
628      }
629      finally {
630        ((MainForm)MainFormManager.MainForm).ResetAppStartingCursor();
631      }
632    }
633  }
634}
Note: See TracBrowser for help on using the repository browser.