Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs @ 15777

Last change on this file since 15777 was 15777, checked in by jzenisek, 7 years ago

#2839 improved project- & resource selection handling

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