Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2839 improved project- & resource selection handling

File size: 21.7 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("Resources View")]
38  [Content(typeof(IItemList<Resource>), false)]
39  public partial class ResourcesView : ItemView, IDisposable {
40    private const int slaveImageIndex = 0;
41    private const int slaveGroupImageIndex = 1;
42    public const string ungroupedGroupName = "UNGROUPED";
43    public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group.";
44
45    private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
46    private readonly Color selectedColor = Color.FromArgb(255, 240, 194, 59); // #f0c23b
47
48    private Resource selectedResource = null;
49    public Resource SelectedResource {
50      get { return selectedResource; }
51      set { if (selectedResource != value) ChangeSelectedResource(value); }
52    }
53
54    private readonly object locker = new object();
55
56    public new IItemList<Resource> Content {
57      get { return (IItemList<Resource>)base.Content; }
58      set { base.Content = value; }
59    }
60
61    public ResourcesView() {
62      InitializeComponent();
63
64      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
65      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
66
67      HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
68      HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed;
69      AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing;
70      AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed;
71    }
72
73    #region Overrides
74    protected override void OnClosing(FormClosingEventArgs e) {
75      AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed;
76      AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing;
77      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
78      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
79      base.OnClosing(e);
80    }
81
82    protected override void RegisterContentEvents() {
83      base.RegisterContentEvents();
84      Content.ItemsAdded += Content_ItemsAdded;
85      Content.ItemsRemoved += Content_ItemsRemoved;
86    }
87
88    protected override void DeregisterContentEvents() {
89      Content.ItemsRemoved -= Content_ItemsRemoved;
90      Content.ItemsAdded -= Content_ItemsAdded;
91      base.DeregisterContentEvents();
92    }
93
94    protected override void OnContentChanged() {
95      base.OnContentChanged();
96      if (Content == null) {
97        treeView.Nodes.Clear();
98        viewHost.Content = null;
99        scheduleView.Content = null;
100      } else {
101        BuildResourceTree(Content);
102      }
103    }
104
105    protected override void SetEnabledStateOfControls() {
106      base.SetEnabledStateOfControls();
107      bool enabled = Content != null && !Locked && !ReadOnly && IsAdmin();
108      btnAddGroup.Enabled = enabled;
109      btnRemoveGroup.Enabled = enabled;
110      btnSave.Enabled = enabled;
111      scheduleView.SetEnabledStateOfSchedule(enabled && IsAdmin());
112    }
113    #endregion
114
115    #region Event Handlers
116    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
117      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsAdded, sender, e);
118      else {
119        OnContentChanged();
120      }
121    }
122
123    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
124      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsRemoved, sender, e);
125      else {
126        OnContentChanged();
127      }
128    }
129
130    private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
131      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)SlaveViewContent_PropertyChanged, sender, e);
132      else {
133        OnContentChanged();
134        if (e.PropertyName == "HbInterval") {
135          UpdateChildHbIntervall((Resource)viewHost.Content);
136        }
137      }
138    }
139
140    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
141      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
142      else {
143        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
144        mainForm.AddOperationProgressToView(this, "Refreshing ...");
145        SetEnabledStateOfControls();
146      }
147    }
148
149    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
150      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
151      else {
152        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
153        mainForm.RemoveOperationProgressFromView(this);
154        SetEnabledStateOfControls();
155      }
156    }
157
158    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
159      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
160      else {
161        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
162        mainForm.AddOperationProgressToView(this, "Refreshing ...");
163        SetEnabledStateOfControls();
164      }
165    }
166
167    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
168      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
169      else {
170        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
171        mainForm.RemoveOperationProgressFromView(this);
172        SetEnabledStateOfControls();
173      }
174    }
175
176    private async void ResourcesView_Load(object sender, EventArgs e) {
177      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
178        action: () => UpdateResources());
179    }
180
181    private async void btnRefresh_Click(object sender, EventArgs e) {
182      lock (locker) {
183        if (!btnRefresh.Enabled) return;
184        btnRefresh.Enabled = false;
185      }
186
187      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
188        action: () => UpdateResources(),
189        finallyCallback: () => btnRefresh.Enabled = true);
190    }
191
192    private void btnAddGroup_Click(object sender, EventArgs e) {
193      Guid? parentResourceId = null;
194      if(!IsAdmin()) {
195        MessageBox.Show(
196          "You have no permission to add a resource group.",
197          "HeuristicLab Hive Administrator",
198          MessageBoxButtons.OK,
199          MessageBoxIcon.Information);
200        return;
201      } else if(selectedResource != null && selectedResource.Id == Guid.Empty) {
202        MessageBox.Show(
203          "You cannot add a resource group to a not yet stored group.",
204          "HeuristicLab Hive Administrator",
205          MessageBoxButtons.OK,
206          MessageBoxIcon.Information);
207        return;
208      }
209     
210      if (selectedResource != null && selectedResource is SlaveGroup) parentResourceId = selectedResource.Id;
211      var group = new SlaveGroup {
212        Name = "New Group",
213        OwnerUserId = UserInformation.Instance.User.Id,
214        ParentResourceId = parentResourceId
215      };
216
217      SelectedResource = group;
218      Content.Add(group);
219    }
220
221    private async void btnRemoveGroup_Click(object sender, EventArgs e) {
222      if (selectedResource == null) return;
223
224      lock (locker) {
225        if (!btnRemoveGroup.Enabled) return;
226        btnRemoveGroup.Enabled = false;
227      }
228
229      if (!IsAdmin()) {
230        MessageBox.Show(
231          "You have no permission to delete resources.",
232          "HeuristicLab Hive Administrator",
233          MessageBoxButtons.OK,
234          MessageBoxIcon.Information);
235        return;
236      }
237
238      if (Content.Any(x => x.ParentResourceId == selectedResource.Id)) {
239        MessageBox.Show(
240          "Only empty resources can be deleted.",
241          "HeuristicLab Hive Administrator",
242          MessageBoxButtons.OK,
243          MessageBoxIcon.Error);
244        return;
245      }
246
247      var result = MessageBox.Show(
248        "Do you really want to delete " + selectedResource.Name + "?",
249        "HeuristicLab Hive Administrator",
250        MessageBoxButtons.YesNo,
251        MessageBoxIcon.Question);
252      if (result == DialogResult.Yes) {
253        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
254          action: () => RemoveResource(selectedResource),
255          finallyCallback: () => {
256            btnRemoveGroup.Enabled = true;
257          });
258      }
259    }
260
261    private async void btnSave_Click(object sender, EventArgs e) {
262      lock (locker) {
263        if (!btnSave.Enabled) return;
264        btnSave.Enabled = false;
265      }
266
267      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
268        action: () => {
269          var resourcesToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
270          foreach (var resource in resourcesToSave)
271            resource.Store();
272          UpdateResources();
273        },
274        finallyCallback: () => btnSave.Enabled = true);
275
276      OnContentChanged();
277    }
278
279    private void treeSlaveGroup_MouseDown(object sender, MouseEventArgs e) {
280      var node = treeView.GetNodeAt(e.Location);
281      if(node != null && node.Name != ungroupedGroupName) ChangeSelectedResourceNode(node);
282    }
283
284    private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
285      e.Cancel = true;
286    }
287
288    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
289      if (e.Effect == DragDropEffects.None) return;
290
291      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
292      if (sourceNode == null) return;
293      var sourceResource = ((Resource)sourceNode.Tag);
294      if (sourceResource == null) return;
295
296      var treeView = (TreeView)sender;
297      if (sourceNode.TreeView != treeView) return;
298
299      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
300      var targetNode = treeView.GetNodeAt(targetPoint);
301
302      var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
303
304      if(!HiveAdminClient.Instance.CheckParentChange(sourceResource, targetResource)) {
305        MessageBox.Show(
306          "You cannot drag resources to this group.",
307          "HeuristicLab Hive Administrator",
308          MessageBoxButtons.OK,
309          MessageBoxIcon.Information);
310        return;
311      }
312
313      if (sourceNode.Parent == null)
314        treeView.Nodes.Remove(sourceNode);
315      else {
316        sourceNode.Parent.Nodes.Remove(sourceNode);
317        sourceResource.ParentResourceId = null;
318      }
319
320      if (targetNode == null) {
321        treeView.Nodes.Add(sourceNode);
322      } else if(targetResource.Id != Guid.Empty) {
323        targetNode.Nodes.Add(sourceNode);
324        sourceResource.ParentResourceId = targetResource.Id;
325      }
326
327      SelectedResource = sourceResource;
328      OnContentChanged();
329    }
330
331    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
332      TreeNode sourceNode = (TreeNode)e.Item;
333      if (IsAdmin())
334        DoDragDrop(sourceNode, DragDropEffects.All);
335    }
336
337    private void treeSlaveGroup_DragEnterOver(object sender, DragEventArgs e) {
338      e.Effect = DragDropEffects.Move;
339
340      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
341      var sourceResource = ((Resource)sourceNode.Tag);
342
343      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
344      var targetNode = treeView.GetNodeAt(targetPoint);
345      var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
346
347      if(!IsAdmin()
348        || sourceNode == null
349        || sourceResource == null
350        || sourceNode == targetNode
351        || !HiveAdminClient.Instance.CheckParentChange(sourceResource, targetResource)
352        || (targetNode != null && (targetNode.Text == ungroupedGroupName || targetNode.Parent != null && targetNode.Parent.Text == ungroupedGroupName))) {
353        e.Effect = DragDropEffects.None;
354      }
355    }
356
357    private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
358      e.Action = DragAction.Continue;
359    }
360    #endregion
361
362    #region Helpers
363    private void ResetTreeNodes(TreeNodeCollection nodes, Color c1, Color c2, bool resetText) {
364      foreach (TreeNode n in nodes) {
365        var res = ((Resource)n.Tag);
366        if (n.BackColor.Equals(c1)) {
367          n.BackColor = c2;
368        }
369        if (resetText) {
370          n.Text = res.Name;
371
372          if (res.Id == Guid.Empty && res.Name != ungroupedGroupName) {
373            n.Text += " [not stored]";
374          } else if (res.Modified && res.Name != ungroupedGroupName) {
375            n.Text += " [changes not stored]";
376          }
377        }
378        if (n.Nodes.Count > 0) {
379          ResetTreeNodes(n.Nodes, c1, c2, resetText);
380        }
381      }
382    }
383
384    private void BuildResourceTree(IEnumerable<Resource> resources) {
385      treeView.Nodes.Clear();
386      if (!resources.Any()) return;
387
388      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
389        .Where(x => x.ParentResourceId == null));
390      var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
391        .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
392      mainResources.UnionWith(parentedMainResources);
393      var subResources = new HashSet<Resource>(resources.Except(mainResources).OrderByDescending(x => x.Name));
394
395      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
396      if (selectedResource != null) SelectedResource = resources.Where(x => x.Id == selectedResource.Id).FirstOrDefault();
397      bool nodeSelected = false;
398
399      TreeNode currentNode = null;
400      Resource currentResource = null;
401
402      while(stack.Any()) {
403        var newResource = stack.Pop();
404        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
405
406        if(newResource.Id == Guid.Empty) {
407          newNode.Text += " [not stored]";
408        } else if(newResource.Modified) {
409          newNode.Text += " [changes not stored]";
410        }
411        if (selectedResource == null) {
412          SelectedResource = newResource;
413        }
414        if (newResource.Id == selectedResource.Id && !nodeSelected) {
415          newNode.BackColor = selectedColor;
416          newNode.Text += " [selected]";
417          nodeSelected = true;
418        }
419
420        // search for parent node of newNode and save in currentNode
421        // necessary since newNodes (stack top items) might be siblings
422        // or grand..grandparents of previous node (currentNode)
423        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
424          currentNode = currentNode.Parent;
425          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
426        }
427
428        if(currentNode == null) {
429          treeView.Nodes.Add(newNode);
430        } else {
431          currentNode.Nodes.Add(newNode);
432        }
433
434        if(newResource is Slave) {
435          newNode.ImageIndex = slaveImageIndex;
436        } else {
437          newNode.ImageIndex = slaveGroupImageIndex;
438
439          var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id);
440          if(childResources.Any()) {
441            foreach(var resource in childResources.OrderByDescending(x => x.Name)) {
442              subResources.Remove(resource);
443              stack.Push(resource);
444            }
445            currentNode = newNode;
446            currentResource = newResource;
447          }
448        }
449        newNode.SelectedImageIndex = newNode.ImageIndex;
450        //if (newResource.OwnerUserId == UserInformation.Instance.User.Id)
451        //  newNode.BackColor = ownedResourceColor;
452      }
453
454      var ungroupedNode = new TreeNode(ungroupedGroupName) {
455        ForeColor = SystemColors.GrayText,
456        Tag = new SlaveGroup() {
457          Name = ungroupedGroupName,
458          Description = ungroupedGroupDescription
459        }
460      };
461
462      foreach (var slave in subResources.OfType<Slave>().OrderBy(x => x.Name)) {
463        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
464        ungroupedNode.Nodes.Add(slaveNode);
465        if(selectedResource == null) {
466          SelectedResource = slave;
467        }
468        if (slave.Id == Guid.Empty) {
469          slaveNode.Text += " [not stored]";
470        } else if (slave.Modified) {
471          slaveNode.Text += " [changes not stored]";
472        }
473        if (slave.Id == selectedResource.Id && !nodeSelected) {
474          slaveNode.BackColor = selectedColor;
475          slaveNode.Text += " [selected]";
476          nodeSelected = true;
477        }
478      }
479
480      treeView.Nodes.Add(ungroupedNode);
481      treeView.ExpandAll();
482    }
483
484    private void UpdateChildHbIntervall(Resource resource) {
485      foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) {
486        r.HbInterval = resource.HbInterval;
487        if (r is SlaveGroup) {
488          UpdateChildHbIntervall(r);
489        }
490      }
491    }
492
493    private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) {
494      TreeNode tmp = dest;
495
496      while (tmp != null) {
497        if (tmp == movedNode) {
498          return true;
499        }
500        tmp = tmp.Parent;
501      }
502      return false;
503    }
504
505    private void ResetView() {
506      if (InvokeRequired) Invoke((Action)ResetView);
507      else {
508        treeView.Nodes.Clear();
509
510        if (viewHost.Content != null && viewHost.Content is SlaveGroup) {
511          ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
512        }
513
514        viewHost.Content = null;
515        if (scheduleView.Content != null) {
516          scheduleView.Content.Clear();
517        }
518
519        HiveAdminClient.Instance.ResetDowntime();
520      }
521    }
522
523    private void UpdateResources() {
524      try {
525        HiveAdminClient.Instance.Refresh();
526        Content = HiveAdminClient.Instance.Resources;
527      } catch(AnonymousUserException) {
528        ShowHiveInformationDialog();
529      }
530    }
531
532    private void RemoveResource(Resource resource) {
533      if (resource == null) return;
534
535      try {
536        if(resource.Id != Guid.Empty) HiveAdminClient.Delete(resource);
537        Content.Remove(selectedResource);
538      } catch(AnonymousUserException) {
539        ShowHiveInformationDialog();
540      }
541    }
542
543    private void UpdateSchedule() {
544      try {
545        HiveAdminClient.Instance.RefreshCalendar();
546      } catch (AnonymousUserException) {
547        ShowHiveInformationDialog();
548      }
549    }
550
551    private bool IsAuthorized(Resource resource) {
552      return resource != null
553          && resource.Name != ungroupedGroupName
554          //&& resource.Id != Guid.Empty
555          && UserInformation.Instance.UserExists
556          && (HiveRoles.CheckAdminUserPermissions()
557            || HiveAdminClient.Instance.CheckOwnershipOfResource(resource, UserInformation.Instance.User.Id));
558    }
559
560    private bool IsAdmin() {
561      return HiveRoles.CheckAdminUserPermissions();
562    }
563
564    private async void ChangeSelectedResource(Resource resource) {
565      selectedResource = resource;
566      viewHost.Content = selectedResource;
567      HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty;
568      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
569        action: () => UpdateSchedule(),
570        finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
571
572      bool locked = !IsAdmin() || resource == null;
573      bool addLocked = locked || (resource != null && resource.Id == Guid.Empty);
574
575      btnAddGroup.Enabled = !addLocked;
576      btnRemoveGroup.Enabled = !locked;
577      btnSave.Enabled = !locked;
578      viewHost.Locked = locked;
579      scheduleView.Locked = locked;
580      scheduleView.SetEnabledStateOfSchedule(!addLocked);
581    }
582
583    private void ChangeSelectedResourceNode(TreeNode resourceNode) {
584      if (resourceNode == null) return;
585      SelectedResource = (Resource)resourceNode.Tag;
586      ResetTreeNodes(treeView.Nodes, selectedColor, Color.Transparent, true);
587      resourceNode.BackColor = selectedColor;
588      resourceNode.Text += " [selected]";
589    }
590
591    private void ShowHiveInformationDialog() {
592      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
593      else {
594        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
595          dialog.ShowDialog(this);
596        }
597      }
598    }
599    #endregion
600  }
601}
Note: See TracBrowser for help on using the repository browser.