#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 changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
private readonly Color selectedColor = Color.FromArgb(255, 240, 194, 59); // #f0c23b
private Resource selectedResource = null;
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);
SetEnabledStateOfControlsForSelectedResource();
viewHost.Content = top;
bool locked = !IsAdmin();
viewHost.Locked = locked;
scheduleView.Locked = locked;
if (top != null && top.Id == Guid.Empty) {
scheduleView.SetEnabledStateOfSchedule(false);
}
}
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
bool enabled = Content != null && !Locked && IsAdmin();
btnAddGroup.Enabled = enabled;
btnRemoveGroup.Enabled = enabled;
btnSave.Enabled = enabled;
scheduleView.SetEnabledStateOfSchedule(enabled && IsAdmin());
}
#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) {
Guid? parentResourceId = null;
if(!IsAdmin()) {
MessageBox.Show(
"You have no permission to add a resource group.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
} else if(selectedResource != null && selectedResource.Id == Guid.Empty) {
MessageBox.Show(
"You cannot add a resource group to a not yet stored group.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
if (selectedResource != null && selectedResource is SlaveGroup) parentResourceId = selectedResource.Id;
var group = new SlaveGroup {
Name = "New Group",
OwnerUserId = UserInformation.Instance.User.Id,
ParentResourceId = parentResourceId
};
selectedResource = group;
Content.Add(group);
}
private async void btnRemoveGroup_Click(object sender, EventArgs e) {
if (selectedResource == null) return;
lock (locker) {
if (!btnRemoveGroup.Enabled) return;
btnRemoveGroup.Enabled = false;
}
if (!IsAdmin()) {
MessageBox.Show(
"You have no permission to delete resources.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
if (Content.Any(x => x.ParentResourceId == selectedResource.Id)) {
MessageBox.Show(
"Only empty resources can be deleted.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
var result = MessageBox.Show(
"Do you really want to delete " + selectedResource.Name + "?",
"HeuristicLab Hive Administrator",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes) {
await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
action: () => RemoveResource(selectedResource),
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();
UpdateResources();
},
finallyCallback: () => btnSave.Enabled = true);
OnContentChanged();
}
private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
if (((Resource)e.Node.Tag).Name == ungroupedGroupName) e.Cancel = true;
}
private async void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
ChangeSelectedResourceNode(e.Node);
SetEnabledStateOfControlsForSelectedResource();
//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;
bool locked = !IsAdmin();
viewHost.Locked = locked;
scheduleView.Locked = locked;
}
private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (sourceNode == null) return;
var sourceResource = ((Resource)sourceNode.Tag);
if (sourceResource == 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;
var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
if (!IsAdmin()) {
MessageBox.Show(
"You cannot drag resources. This is solely granted for HeuristicLab Hive Administrators.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
} else if (targetResource != null && targetResource is Slave) {
MessageBox.Show(
"You cannot drag resources on to a slave.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
} else if(targetResource != null && targetResource.Id == Guid.Empty) {
MessageBox.Show(
"You cannot drag resources to a not yet stored resource group.",
"HeuristicLab Hive Administrator",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
} else if (targetNode != null && (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);
sourceResource.ParentResourceId = null;
}
if (targetNode == null) {
treeView.Nodes.Add(sourceNode);
} else if(targetResource.Id != Guid.Empty) {
targetNode.Nodes.Add(sourceNode);
sourceResource.ParentResourceId = targetResource.Id;
}
selectedResource = sourceResource;
OnContentChanged();
}
private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
TreeNode sourceNode = (TreeNode)e.Item;
if (IsAdmin())
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 void ResetTreeNodes(TreeNodeCollection nodes, Color c1, Color c2, bool resetText) {
foreach (TreeNode n in nodes) {
var res = ((Resource)n.Tag);
if (n.BackColor.Equals(c1)) {
n.BackColor = c2;
}
if (resetText) {
n.Text = res.Name;
if (res.Id == Guid.Empty && res.Name != ungroupedGroupName) {
n.Text += " [not stored]";
} else if (res.Modified && res.Name != ungroupedGroupName) {
n.Text += " [changes not stored]";
}
}
if (n.Nodes.Count > 0) {
ResetTreeNodes(n.Nodes, c1, c2, resetText);
}
}
}
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 parentedMainResources = new HashSet(resources.OfType()
.Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
mainResources.UnionWith(parentedMainResources);
var subResources = new HashSet(resources.Except(mainResources).OrderByDescending(x => x.Name));
var stack = new Stack(mainResources.OrderByDescending(x => x.Name));
if (selectedResource != null) selectedResource = resources.Where(x => x.Id == selectedResource.Id).FirstOrDefault();
bool nodeSelected = false;
TreeNode currentNode = null;
Resource currentResource = null;
while(stack.Any()) {
var newResource = stack.Pop();
var newNode = new TreeNode(newResource.Name) { Tag = newResource };
if(newResource.Id == Guid.Empty) {
newNode.Text += " [not stored]";
} else if(newResource.Modified) {
newNode.Text += " [changes not stored]";
}
if (selectedResource == null) {
selectedResource = newResource;
}
if (newResource.Id == selectedResource.Id && !nodeSelected) {
newNode.BackColor = selectedColor;
newNode.Text += " [selected]";
nodeSelected = true;
}
// 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);
if(selectedResource == null) {
selectedResource = slave;
}
if (slave.Id == Guid.Empty) {
slaveNode.Text += " [not stored]";
} else if (slave.Modified) {
slaveNode.Text += " [changes not stored]";
}
if (slave.Id == selectedResource.Id && !nodeSelected) {
slaveNode.BackColor = selectedColor;
slaveNode.Text += " [selected]";
nodeSelected = true;
}
}
treeView.Nodes.Add(ungroupedNode);
treeView.ExpandAll();
return selectedResource;
}
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() {
try {
HiveAdminClient.Instance.Refresh();
Content = HiveAdminClient.Instance.Resources;
} catch(AnonymousUserException) {
ShowHiveInformationDialog();
}
}
private void RemoveResource(Resource resource) {
if (resource == null || resource.Id == Guid.Empty) return;
try {
HiveAdminClient.Delete(resource);
Content.Remove(selectedResource);
} catch(AnonymousUserException) {
ShowHiveInformationDialog();
}
}
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
&& (HiveRoles.CheckAdminUserPermissions()
|| HiveAdminClient.Instance.CheckOwnershipOfResource(resource, UserInformation.Instance.User.Id));
}
private bool IsAdmin() {
return HiveRoles.CheckAdminUserPermissions();
}
private void ChangeSelectedResourceNode(TreeNode resourceNode) {
selectedResource = (Resource)resourceNode.Tag;
ResetTreeNodes(treeView.Nodes, selectedColor, Color.Transparent, true);
resourceNode.BackColor = selectedColor;
resourceNode.Text += " [selected]";
}
private void SetEnabledStateOfControlsForSelectedResource() {
bool enabled = (IsAdmin()) ? true : false;
btnAddGroup.Enabled = enabled;
btnRemoveGroup.Enabled = enabled;
btnSave.Enabled = enabled;
}
private void ShowHiveInformationDialog() {
if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
else {
using (HiveInformationDialog dialog = new HiveInformationDialog()) {
dialog.ShowDialog(this);
}
}
}
#endregion
}
}