#region License Information /* HeuristicLab * Copyright (C) 2002-2017 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.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; using HeuristicLab.Clients.Access; using HeuristicLab.Clients.Hive.Views; using HeuristicLab.Collections; using HeuristicLab.Common.Resources; using HeuristicLab.Core; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; namespace HeuristicLab.Clients.Hive.Administrator.Views { [View("Resources View")] [Content(typeof(IItemList), false)] public partial class ResourcesView : ItemView, IDisposable { private const int slaveImageIndex = 0; private const int slaveGroupImageIndex = 1; public const string ungroupedGroupName = "UNGROUPED"; public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group."; private readonly Color ownedResourceColor = Color.LightGreen; private readonly object locker = new object(); public new IItemList Content { get { return (IItemList)base.Content; } set { base.Content = value; } } public ResourcesView() { InitializeComponent(); treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge); treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge); HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing; HiveAdminClient.Instance.Refreshed += HiveAdminClient_Instance_Refreshed; AccessClient.Instance.Refreshing += AccessClient_Instance_Refreshing; AccessClient.Instance.Refreshed += AccessClient_Instance_Refreshed; } #region Overrides protected override void OnClosing(FormClosingEventArgs e) { AccessClient.Instance.Refreshed -= AccessClient_Instance_Refreshed; AccessClient.Instance.Refreshing -= AccessClient_Instance_Refreshing; HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed; HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing; base.OnClosing(e); } protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ItemsAdded += Content_ItemsAdded; Content.ItemsRemoved += Content_ItemsRemoved; } protected override void DeregisterContentEvents() { Content.ItemsRemoved -= Content_ItemsRemoved; Content.ItemsAdded -= Content_ItemsAdded; base.DeregisterContentEvents(); } protected override void OnContentChanged() { base.OnContentChanged(); if (Content == null) { treeView.Nodes.Clear(); viewHost.Content = null; scheduleView.Content = null; } else { var top = BuildResourceTree(Content); viewHost.Content = top; } } protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); bool enabled = Content != null; btnAddGroup.Enabled = enabled; btnRemoveGroup.Enabled = enabled; btnSave.Enabled = enabled; scheduleView.SetEnabledStateOfSchedule(enabled && IsAuthorized((Resource)viewHost.Content)); } #endregion #region Event Handlers private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs> e) { if (InvokeRequired) Invoke((Action>>)Content_ItemsAdded, sender, e); else { OnContentChanged(); } } private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs> e) { if (InvokeRequired) Invoke((Action>>)Content_ItemsRemoved, sender, e); else { OnContentChanged(); } } private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (InvokeRequired) Invoke((Action)SlaveViewContent_PropertyChanged, sender, e); else { OnContentChanged(); if (e.PropertyName == "HbInterval") { UpdateChildHbIntervall((Resource)viewHost.Content); } } } private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshing, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.AddOperationProgressToView(this, "Refreshing ..."); SetEnabledStateOfControls(); } } private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)HiveAdminClient_Instance_Refreshed, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.RemoveOperationProgressFromView(this); SetEnabledStateOfControls(); } } private void AccessClient_Instance_Refreshing(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)AccessClient_Instance_Refreshing, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.AddOperationProgressToView(this, "Refreshing ..."); SetEnabledStateOfControls(); } } private void AccessClient_Instance_Refreshed(object sender, EventArgs e) { if (InvokeRequired) Invoke((Action)AccessClient_Instance_Refreshed, sender, e); else { var mainForm = MainFormManager.GetMainForm(); mainForm.RemoveOperationProgressFromView(this); SetEnabledStateOfControls(); } } private async void ResourcesView_Load(object sender, EventArgs e) { await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateResources()); } private async void btnRefresh_Click(object sender, EventArgs e) { lock (locker) { if (!btnRefresh.Enabled) return; btnRefresh.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateResources(), finallyCallback: () => btnRefresh.Enabled = true); } private void btnAddGroup_Click(object sender, EventArgs e) { var group = new SlaveGroup { Name = "New Group", OwnerUserId = UserInformation.Instance.User.Id }; Content.Add(group); } private async void btnRemoveGroup_Click(object sender, EventArgs e) { lock (locker) { if (!btnRemoveGroup.Enabled) return; btnRemoveGroup.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => RemoveResource(), finallyCallback: () => btnRemoveGroup.Enabled = true); } private async void btnSave_Click(object sender, EventArgs e) { lock (locker) { if (!btnSave.Enabled) return; btnSave.Enabled = false; } await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => { var resourcesToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified); foreach (var resource in resourcesToSave) resource.Store(); }, finallyCallback: () => btnSave.Enabled = true); } private async void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) { var selectedResource = (Resource)e.Node.Tag; if (viewHost.Content != null && viewHost.Content is SlaveGroup) ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged; viewHost.Content = selectedResource; HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty; await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions( action: () => UpdateSchedule(), finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes); if (selectedResource != null && selectedResource is SlaveGroup) selectedResource.PropertyChanged += SlaveViewContent_PropertyChanged; if (IsAuthorized(selectedResource)) { if (!tabSlaveGroup.TabPages.Contains(tabSchedule)) tabSlaveGroup.TabPages.Add(tabSchedule); } else { if (tabSlaveGroup.TabPages.Contains(tabSchedule)) tabSlaveGroup.TabPages.Remove(tabSchedule); } } private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) { var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode)); if (sourceNode == null) return; var treeView = (TreeView)sender; if (sourceNode.TreeView != treeView) return; var targetPoint = treeView.PointToClient(new Point(e.X, e.Y)); var targetNode = treeView.GetNodeAt(targetPoint); if (sourceNode == targetNode) return; if (targetNode.Text == ungroupedGroupName || targetNode.Parent != null && targetNode.Parent.Text == ungroupedGroupName) { MessageBox.Show( string.Format(@"You cannot drag resources to group ""{0}"". This group only contains slaves which have not been assigned to a real group yet.", ungroupedGroupName), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (sourceNode.Parent == null) treeView.Nodes.Remove(sourceNode); else { sourceNode.Parent.Nodes.Remove(sourceNode); ((Resource)sourceNode.Tag).ParentResourceId = null; } if (targetNode == null) { treeView.Nodes.Add(sourceNode); } else { targetNode.Nodes.Add(sourceNode); ((Resource)sourceNode.Tag).ParentResourceId = ((Project)targetNode.Tag).Id; } } private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) { TreeNode sourceNode = (TreeNode)e.Item; if (IsAuthorized((Resource)sourceNode.Tag)) DoDragDrop(sourceNode, DragDropEffects.All); } private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) { e.Action = DragAction.Continue; } #endregion #region Helpers private Resource BuildResourceTree(IEnumerable resources) { treeView.Nodes.Clear(); if (!resources.Any()) return null; var mainResources = new HashSet(resources.OfType().Where(x => x.ParentResourceId == null)); var subResources = new HashSet(resources.Except(mainResources)); var stack = new Stack(mainResources.OrderByDescending(x => x.Name)); var top = stack.Peek(); TreeNode currentNode = null; Resource currentResource = null; while(stack.Any()) { var newResource = stack.Pop(); var newNode = new TreeNode(newResource.Name) { Tag = newResource }; // search for parent node of newNode and save in currentNode // necessary since newNodes (stack top items) might be siblings // or grand..grandparents of previous node (currentNode) while(currentNode != null && newResource.ParentResourceId != currentResource.Id) { currentNode = currentNode.Parent; currentResource = currentNode == null ? null : (Resource)currentNode.Tag; } if(currentNode == null) { treeView.Nodes.Add(newNode); } else { currentNode.Nodes.Add(newNode); } if(newResource is Slave) { newNode.ImageIndex = slaveImageIndex; } else { newNode.ImageIndex = slaveGroupImageIndex; var childResources = subResources.Where(x => x.ParentResourceId == newResource.Id); if(childResources.Any()) { foreach(var resource in childResources.OrderByDescending(x => x.Name)) { subResources.Remove(resource); stack.Push(resource); } currentNode = newNode; currentResource = newResource; } } newNode.SelectedImageIndex = newNode.ImageIndex; if (newResource.OwnerUserId == UserInformation.Instance.User.Id) newNode.BackColor = ownedResourceColor; } var ungroupedNode = new TreeNode(ungroupedGroupName) { ForeColor = SystemColors.GrayText, Tag = new SlaveGroup() { Name = ungroupedGroupName, Description = ungroupedGroupDescription } }; foreach (var slave in subResources.OfType().OrderBy(x => x.Name)) { var slaveNode = new TreeNode(slave.Name) { Tag = slave }; ungroupedNode.Nodes.Add(slaveNode); } treeView.Nodes.Add(ungroupedNode); treeView.ExpandAll(); return top; } private void UpdateChildHbIntervall(Resource resource) { foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) { r.HbInterval = resource.HbInterval; if (r is SlaveGroup) { UpdateChildHbIntervall(r); } } } private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) { TreeNode tmp = dest; while (tmp != null) { if (tmp == movedNode) { return true; } tmp = tmp.Parent; } return false; } private void ResetView() { if (InvokeRequired) Invoke((Action)ResetView); else { treeView.Nodes.Clear(); if (viewHost.Content != null && viewHost.Content is SlaveGroup) { ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged; } viewHost.Content = null; if (scheduleView.Content != null) { scheduleView.Content.Clear(); } HiveAdminClient.Instance.ResetDowntime(); } } private void UpdateResources() { HiveAdminClient.Instance.Refresh(); Content = HiveAdminClient.Instance.Resources; } private void RemoveResource() { var selectedNode = treeView.SelectedNode; if (selectedNode == null || selectedNode.Tag == null) return; var resource = (Resource)selectedNode.Tag; var result = MessageBox.Show( "Do you really want to delete " + resource.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { if (resource is Slave) { Content.Remove(resource); HiveAdminClient.Delete(resource); } else if (resource is SlaveGroup) { if (Content.Any(x => x.ParentResourceId == resource.Id)) { MessageBox.Show( "Only empty resources can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Content.Remove(resource); HiveAdminClient.Delete(resource); } } } } private void UpdateSchedule() { try { HiveAdminClient.Instance.RefreshCalendar(); } catch (AnonymousUserException) { ShowHiveInformationDialog(); } } private bool IsAuthorized(Resource resource) { return resource != null && resource.Name != ungroupedGroupName && resource.Id != Guid.Empty && UserInformation.Instance.UserExists && (resource.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions()); } private void ShowHiveInformationDialog() { if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog); else { using (HiveInformationDialog dialog = new HiveInformationDialog()) { dialog.ShowDialog(this); } } } #endregion } }