source: branches/2965_CancelablePersistence/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs @ 16433

Last change on this file since 16433 was 16433, checked in by pfleck, 12 months ago

#2965 Merged recent trunk changes.
Enabled the prepared hooks that allows to cancel the save file using the recently introduced cancelable progressbars (in FileManager).

File size: 21.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 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.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Clients.Access;
29using HeuristicLab.Clients.Hive.Views;
30using HeuristicLab.Collections;
31using HeuristicLab.Common.Resources;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.MainForm;
35
36namespace HeuristicLab.Clients.Hive.Administrator.Views {
37  [View("Projects View")]
38  [Content(typeof(IItemList<Project>), false)]
39  public partial class ProjectsView : ItemView, IDisposable {
40    private const int greenFlagImageIndex = 0;
41    private const int redFlagImageIndex = 1;
42    private const string SELECTED_TAG = ""; // " [selected]";
43    private const string NOT_STORED_TAG = "*"; // " [not stored]";
44    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
45    private const string INACTIVE_TAG = " [inactive]";
46
47    private readonly Color selectedBackColor = Color.DodgerBlue;
48    private readonly Color selectedForeColor = Color.White;
49    private readonly Color grayTextColor = SystemColors.GrayText;
50
51    private Project selectedProject = null;
52    public Project SelectedProject {
53      get { return selectedProject; }
54      set { if (selectedProject != value) ChangeSelectedProject(value); }
55    }
56
57    private readonly object locker = new object();
58
59    public new IItemList<Project> Content {
60      get { return (IItemList<Project>)base.Content; }
61      set { base.Content = value; }
62    }
63
64    public ProjectsView() {
65      InitializeComponent();
66
67      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen);
68      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed);
69
70      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
71      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
72      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
73      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
74    }
75
76    #region Overrides
77    protected override void OnClosing(FormClosingEventArgs e) {
78      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
79      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
80      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
81      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
82      base.OnClosing(e);
83    }
84
85    protected override void RegisterContentEvents() {
86      base.RegisterContentEvents();
87      Content.ItemsAdded += Content_ItemsAdded;
88      Content.ItemsRemoved += Content_ItemsRemoved;
89    }
90
91    protected override void DeregisterContentEvents() {
92      Content.ItemsRemoved -= Content_ItemsRemoved;
93      Content.ItemsAdded -= Content_ItemsAdded;
94      base.DeregisterContentEvents();
95    }
96
97    protected override void OnContentChanged() {
98      base.OnContentChanged();
99      if (Content == null) {
100        projectsTreeView.Nodes.Clear();
101        projectView.Content = null;
102        projectPermissionsView.Content = null;
103        projectResourcesView.Content = null;
104        projectJobsView.Content = null;
105        selectedProject = null;
106      } else {
107        BuildProjectTree(Content);
108      }
109      SetEnabledStateOfControls();
110    }
111
112    protected override void SetEnabledStateOfControls() {
113      base.SetEnabledStateOfControls();
114
115      bool locked = Content == null || Locked || ReadOnly;
116      bool parentOwner = selectedProject != null && HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id);
117      bool selectedProjectDisabled = selectedProject == null
118                                     || selectedProject != null && selectedProject.Id == Guid.Empty;
119
120      bool selectedProjectHasJobs =
121        !selectedProjectDisabled && HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
122                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
123                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any();
124
125      bool addLocked = locked
126                       || (selectedProject == null && !IsAdmin())
127                       || (selectedProject != null && selectedProject.Id == Guid.Empty)
128                       || (selectedProject != null && !IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id)
129                       || (selectedProject != null && (DateTime.Now < selectedProject.StartDate || DateTime.Now > selectedProject.EndDate));
130
131      bool deleteLocked = locked
132                          || !Content.Any()
133                          || selectedProject == null
134                          || Content.Any(x => x.ParentProjectId == selectedProject.Id)
135                          || selectedProjectHasJobs
136                          || (!IsAdmin() && !parentOwner);
137
138      bool saveLocked = locked
139                        || !Content.Any()
140                        || selectedProject == null
141                        || (!IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id);
142
143
144      addButton.Enabled = !addLocked;
145      removeButton.Enabled = !deleteLocked;
146      saveProjectButton.Enabled = !saveLocked;
147
148      projectView.Enabled = !locked;
149      projectPermissionsView.Enabled = !locked && !selectedProjectDisabled;
150      projectResourcesView.Enabled = !locked && !selectedProjectDisabled;
151      projectJobsView.Enabled = !locked && !selectedProjectDisabled;
152
153      projectView.Locked = locked;
154      projectPermissionsView.Locked = locked || selectedProjectDisabled;
155      projectResourcesView.Locked = locked || selectedProjectDisabled;
156      projectJobsView.Locked = locked || selectedProjectDisabled;
157    }
158    #endregion
159
160    #region Event Handlers
161    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
162      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsAdded, sender, e);
163      else {
164        OnContentChanged();
165      }
166    }
167
168    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
169      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsRemoved, sender, e);
170      else {
171        OnContentChanged();
172      }
173    }
174
175    private void ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
176      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)ProjectViewContent_PropertyChanged, sender, e);
177      else {
178        OnContentChanged();
179      }
180    }
181
182    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
183      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
184      else {
185        Progress.Show(this, "Refreshing ...", ProgressMode.Indeterminate);
186        SetEnabledStateOfControls();
187      }
188    }
189
190    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
191      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
192      else {
193        Progress.Hide(this);
194        SetEnabledStateOfControls();
195      }
196    }
197
198    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
199      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
200      else {
201        Progress.Show(this, "Refreshing ...", ProgressMode.Indeterminate);
202        SetEnabledStateOfControls();
203      }
204    }
205
206    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
207      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
208      else {
209        Progress.Hide(this);
210        SetEnabledStateOfControls();
211      }
212    }
213
214    private async void ProjectsView_Load(object sender, EventArgs e) {
215      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
216        action: () => UpdateProjects());
217    }
218
219    private async void refreshButton_Click(object sender, EventArgs e) {
220      lock (locker) {
221        if (!refreshButton.Enabled) return;
222        refreshButton.Enabled = false;
223      }
224
225      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
226        action: () => UpdateProjects(),
227        finallyCallback: () => {
228          refreshButton.Enabled = true;
229        });
230    }
231
232    private void addButton_Click(object sender, EventArgs e) {
233
234      if (selectedProject == null && !IsAdmin()) {
235        MessageBox.Show(
236          "You are not allowed to add a root project - please select a parent project.",
237          "HeuristicLab Hive Administrator",
238          MessageBoxButtons.OK,
239          MessageBoxIcon.Information);
240        return;
241      }
242
243      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
244        MessageBox.Show(
245          "You cannot add a project to a not yet stored project.",
246          "HeuristicLab Hive Administrator",
247          MessageBoxButtons.OK,
248          MessageBoxIcon.Information);
249        return;
250      }
251
252      var project = new Project {
253        Name = "New Project",
254        OwnerUserId = UserInformation.Instance.User.Id,
255      };
256      if (selectedProject != null) {
257        project.ParentProjectId = selectedProject.Id;
258        project.EndDate = selectedProject.EndDate;
259      }
260
261      SelectedProject = project;
262      Content.Add(project);
263    }
264
265    private async void removeButton_Click(object sender, EventArgs e) {
266      if (selectedProject == null) return;
267
268      lock (locker) {
269        // for details go to ChangeSelectedProject(..)
270        if (!removeButton.Enabled) return;
271        removeButton.Enabled = false;
272      }
273
274      var result = MessageBox.Show(
275        "Do you really want to delete " + selectedProject.Name + "?",
276        "HeuristicLab Hive Administrator",
277        MessageBoxButtons.YesNo,
278        MessageBoxIcon.Question);
279
280      if (result == DialogResult.Yes) {
281        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
282          action: () => {
283            RemoveProject(selectedProject);
284          });
285      }
286      SetEnabledStateOfControls();
287    }
288
289    private async void saveProjectButton_Click(object sender, EventArgs e) {
290      lock (locker) {
291        if (!saveProjectButton.Enabled) return;
292        saveProjectButton.Enabled = false;
293      }
294
295      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
296        action: () => {
297          //if (selectedProject != null && selectedProject.Id == Guid.Empty)
298          //  SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(selectedProject.Id).LastOrDefault();
299          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
300          foreach (var project in projectsToSave)
301            project.Store();
302
303          UpdateProjects();
304        },
305        finallyCallback: () => saveProjectButton.Enabled = true);
306
307      OnContentChanged();
308    }
309
310    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
311      var node = projectsTreeView.GetNodeAt(e.Location);
312      if (node == null) return;
313      var p = (Project)node.Tag;
314      if (!HiveAdminClient.Instance.DisabledParentProjects.Contains(p)) ChangeSelectedProjectNode(node);
315    }
316
317    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
318      e.Cancel = true;
319    }
320
321    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
322      if (e.Effect == DragDropEffects.None) return;
323
324      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
325      if (sourceNode == null) return;
326      var sourceProject = ((Project)sourceNode.Tag);
327      if (sourceProject == null) return;
328
329      var treeView = (TreeView)sender;
330      if (sourceNode.TreeView != treeView) return;
331
332      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
333      var targetNode = treeView.GetNodeAt(targetPoint);
334
335      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
336
337      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
338        MessageBox.Show(
339          "You cannot drag projects to this project.",
340          "HeuristicLab Hive Administrator",
341          MessageBoxButtons.OK,
342          MessageBoxIcon.Information);
343        return;
344      }
345
346      if (sourceNode.Parent == null) {
347        treeView.Nodes.Remove(sourceNode);
348      } else {
349        sourceNode.Parent.Nodes.Remove(sourceNode);
350        sourceProject.ParentProjectId = null;
351      }
352
353      if (targetNode == null) {
354        treeView.Nodes.Add(sourceNode);
355      } else if (targetProject.Id != Guid.Empty) {
356        targetNode.Nodes.Add(sourceNode);
357        sourceProject.ParentProjectId = targetProject.Id;
358      }
359
360      SelectedProject = sourceProject;
361      OnContentChanged();
362    }
363
364    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
365      var sourceNode = (TreeNode)e.Item;
366      if (IsAuthorized((Project)sourceNode.Tag))
367        DoDragDrop(sourceNode, DragDropEffects.All);
368    }
369
370    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
371      e.Effect = DragDropEffects.Move;
372
373      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
374      var sourceProject = ((Project)sourceNode.Tag);
375
376      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
377      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
378      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
379
380      if ((!IsAdmin() && (targetNode == null || targetProject == null))
381      || sourceNode == null
382      || sourceProject == null
383      || sourceNode == targetNode
384      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
385        e.Effect = DragDropEffects.None;
386      }
387    }
388
389    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
390      e.Action = DragAction.Continue;
391    }
392    #endregion
393
394    #region Helpers
395    private void BuildProjectTree(IEnumerable<Project> projects) {
396      projectsTreeView.Nodes.Clear();
397      if (!projects.Any()) return;
398
399      var disabledParentProjects = HiveAdminClient.Instance.DisabledParentProjects;
400      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
401      //var parentedMainProjects = new HashSet<Project>(projects
402      //  .Where(x => x.ParentProjectId.HasValue
403      //  && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
404      //mainProjects.UnionWith(parentedMainProjects);
405      var mainDisabledParentProjects = new HashSet<Project>(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty));
406      mainProjects.UnionWith(mainDisabledParentProjects);
407      var subProbjects = new HashSet<Project>(projects.Union(disabledParentProjects).Except(mainProjects));
408
409      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
410      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
411      bool nodeSelected = false;
412
413      TreeNode currentNode = null;
414      Project currentProject = null;
415
416      while (stack.Any()) {
417        var newProject = stack.Pop();
418        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
419        StyleTreeNode(newNode, newProject);
420
421        if (selectedProject == null && !disabledParentProjects.Contains(newProject)) {
422          SelectedProject = newProject;
423        }
424        if (!nodeSelected && selectedProject != null && selectedProject.Id == newProject.Id) {
425          newNode.BackColor = selectedBackColor;
426          newNode.ForeColor = selectedForeColor;
427          newNode.Text += SELECTED_TAG;
428          nodeSelected = true;
429        }
430
431        // search for parent node of newNode and save in currentNode
432        // necessary since newNodes (stack top items) might be siblings
433        // or grand..grandparents of previous node (currentNode)
434        while (currentNode != null && newProject.ParentProjectId != currentProject.Id) {
435          currentNode = currentNode.Parent;
436          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
437        }
438
439        if (currentNode == null) {
440          projectsTreeView.Nodes.Add(newNode);
441          newNode.ImageIndex = greenFlagImageIndex;
442        } else {
443          currentNode.Nodes.Add(newNode);
444          newNode.ImageIndex = redFlagImageIndex;
445        }
446
447        newNode.SelectedImageIndex = newNode.ImageIndex;
448
449        if (disabledParentProjects.Contains(newProject)) {
450          newNode.Checked = false;
451          newNode.ForeColor = grayTextColor;
452        }
453
454        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
455        if (childProjects.Any()) {
456          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
457            subProbjects.Remove(project);
458            stack.Push(project);
459          }
460          currentNode = newNode;
461          currentProject = newProject;
462        }
463      }
464
465      projectsTreeView.ExpandAll();
466    }
467
468    private void StyleTreeNode(TreeNode n, Project p) {
469      n.Text = p.Name;
470      n.BackColor = Color.Transparent;
471      n.ForeColor = Color.Black;
472
473      if (HiveAdminClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) {
474        n.ForeColor = grayTextColor;
475      } else {
476        if (p.Id == Guid.Empty) {
477          n.Text += NOT_STORED_TAG;
478        } else if (p.Modified) {
479          n.Text += CHANGES_NOT_STORED_TAG;
480        }
481
482        if (!IsActive(p)) {
483          n.ForeColor = grayTextColor;
484          n.Text += INACTIVE_TAG;
485        }
486      }
487    }
488
489    private void ResetTreeNodes(TreeNodeCollection nodes) {
490      foreach (TreeNode n in nodes) {
491        StyleTreeNode(n, (Project)n.Tag);
492        if (n.Nodes.Count > 0) {
493          ResetTreeNodes(n.Nodes);
494        }
495      }
496    }
497
498    private void ChangeSelectedProject(Project project) {
499      projectView.Content = project;
500      projectPermissionsView.Content = project;
501      projectResourcesView.Content = project;
502      projectJobsView.Content = project;
503      selectedProject = project;
504      SetEnabledStateOfControls();
505    }
506
507    private void ChangeSelectedProjectNode(TreeNode projectNode) {
508      if (projectNode == null) return;
509      SelectedProject = (Project)projectNode.Tag;
510      ResetTreeNodes(projectsTreeView.Nodes);
511      projectNode.BackColor = selectedBackColor;
512      projectNode.ForeColor = selectedForeColor;
513      projectNode.Text += SELECTED_TAG;
514    }
515
516    private void UpdateProjects() {
517      try {
518        HiveAdminClient.Instance.Refresh();
519        Content = HiveAdminClient.Instance.Projects;
520      } catch (AnonymousUserException) {
521        ShowHiveInformationDialog();
522      }
523    }
524
525    private void RemoveProject(Project project) {
526      if (project == null) return;
527
528      try {
529        if (project.Id != Guid.Empty) {
530          SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(project.Id).LastOrDefault();
531          HiveAdminClient.Delete(project);
532          UpdateProjects();
533        } else {
534          SelectedProject = Content.FirstOrDefault(x => x.Id == project.ParentProjectId);
535          Content.Remove(project);
536        }
537      } catch (AnonymousUserException) {
538        ShowHiveInformationDialog();
539      }
540    }
541
542    private bool IsActive(Project project) {
543      return DateTime.Now >= project.StartDate
544             && (project.EndDate == null
545                 || DateTime.Now < project.EndDate.Value);
546    }
547
548    private bool IsAuthorized(Project project) {
549      return project != null && UserInformation.Instance.UserExists;
550    }
551
552    private bool IsAdmin() {
553      return HiveRoles.CheckAdminUserPermissions();
554    }
555
556    private void ShowHiveInformationDialog() {
557      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
558      else {
559        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
560          dialog.ShowDialog(this);
561        }
562      }
563    }
564    #endregion
565  }
566}
Note: See TracBrowser for help on using the repository browser.