Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 17059)
@@ -110,5 +110,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="HiveRoles.cs" />
     <Compile Include="Views\DowntimeTypeDialog.cs">
       <SubType>Form</SubType>
@@ -133,4 +132,34 @@
     </Compile>
     <Compile Include="MenuItems\AdministratorMenuItem.cs" />
+    <Compile Include="Views\ProjectJobsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectJobsView.Designer.cs">
+      <DependentUpon>ProjectJobsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectPermissionsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectPermissionsView.Designer.cs">
+      <DependentUpon>ProjectPermissionsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.Designer.cs">
+      <DependentUpon>ProjectResourcesView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectsView.Designer.cs">
+      <DependentUpon>ProjectsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectView.Designer.cs">
+      <DependentUpon>ProjectView.cs</DependentUpon>
+    </Compile>
     <Compile Include="Views\Recurrence.cs">
       <SubType>Form</SubType>
@@ -151,4 +180,10 @@
     <Compile Include="Views\ScheduleView.Designer.cs">
       <DependentUpon>ScheduleView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ResourceView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ResourceView.Designer.cs">
+      <DependentUpon>ResourceView.cs</DependentUpon>
     </Compile>
     <Compile Include="Views\SlaveView.cs">
@@ -167,4 +202,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Administration\3.3\HeuristicLab.Clients.Access.Administration-3.3.csproj">
+      <Project>{C9CF6AE8-4637-4A75-9141-042F479B5D50}</Project>
+      <Name>HeuristicLab.Clients.Access.Administration-3.3</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Views\3.3\HeuristicLab.Clients.Access.Views-3.3.csproj">
       <Project>{FE856595-64CD-46DA-9CD2-FFF3E6B0D4F2}</Project>
@@ -214,4 +253,8 @@
       <Name>HeuristicLab.Core-3.3</Name>
       <Private>False</Private>
+    </ProjectReference>
+    <ProjectReference Include="..\..\HeuristicLab.Data.Views\3.3\HeuristicLab.Data.Views-3.3.csproj">
+      <Project>{72104A0B-90E7-42F3-9ABE-9BBBADD4B943}</Project>
+      <Name>HeuristicLab.Data.Views-3.3</Name>
     </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Data\3.3\HeuristicLab.Data-3.3.csproj">
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/HiveRoles.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/HiveRoles.cs	(revision 15587)
+++ 	(revision )
@@ -1,35 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2018 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 <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System.Linq;
-using HeuristicLab.Clients.Access;
-
-namespace HeuristicLab.Clients.Hive.Administrator {
-  public static class HiveRoles {
-    public const string Administrator = "Hive Administrator";
-    public const string User = "Hive User";
-    public const string Slave = "Hive Slave";
-
-    public static bool CheckAdminUserPermissions() {
-      return UserInformation.Instance.UserExists && UserInformation.Instance.User.Roles.Any(x => x.Name == HiveRoles.Administrator);
-    }
-  }
-}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/MenuItems/AdministratorMenuItem.cs	(revision 17059)
@@ -20,21 +20,44 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
-using HeuristicLab.Clients.Hive;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
 using HeuristicLab.MainForm;
+using HeuristicLab.Optimizer;
 
-namespace HeuristicLab.Optimizer.MenuItems {
-  public class AdministratorMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
+namespace HeuristicLab.Clients.Hive.Administrator {
+  public class AdministratorMenuItem : MainForm.WindowsForms.MenuItem, IOptimizerUserInterfaceItemProvider {
     public override string Name {
       get { return "&Administrator"; }
     }
+
     public override IEnumerable<string> Structure {
       get { return new string[] { "&Services", "&Hive" }; }
     }
+
     public override void Execute() {
-      MainFormManager.MainForm.ShowContent(HiveAdminClient.Instance);
+
+      if (HiveRoles.CheckAdminUserPermissions() || HiveAdminClient.Instance.CheckAccessToAdminAreaGranted()) {
+        MainFormManager.MainForm.ShowContent(HiveAdminClient.Instance);
+      } else if (!UserInformation.Instance.UserExists) {
+        MessageBox.Show(
+          "Couldn't fetch user information from the server." + Environment.NewLine +
+          "Please verify that you have an existing user and that your user name and password is correct.",
+          "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      } else {
+        MessageBox.Show(
+          "You do not seem to have the permissions to use the Hive Administrator." + Environment.NewLine +
+          "If that's not the case or you have any questions please write an email to support@heuristiclab.com",
+          "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      }
     }
+
     public override int Position {
-      get { return 10000; }
+      get { return 8000; }
+    }
+
+    public override Keys ShortCutKeys {
+      get { return Keys.Control | Keys.Shift | Keys.H; }
     }
   }
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Plugin.cs.frame	(revision 17059)
@@ -32,6 +32,9 @@
   [PluginDependency("HeuristicLab.Core", "3.3")]
   [PluginDependency("HeuristicLab.Core.Views", "3.3")]  
+  [PluginDependency("HeuristicLab.Clients.Common", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive", "3.3")]
   [PluginDependency("HeuristicLab.Clients.Hive.Views", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Data.Views", "3.3")]
   [PluginDependency("HeuristicLab.DayView", "1.0.0.0")]
   [PluginDependency("HeuristicLab.Optimizer", "3.3")]
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/HiveAdministratorView.Designer.cs	(revision 17059)
@@ -29,17 +29,21 @@
       this.components = new System.ComponentModel.Container();
       this.tabAdmin = new System.Windows.Forms.TabControl();
-      this.tabSlaves = new System.Windows.Forms.TabPage();
+      this.tabResources = new System.Windows.Forms.TabPage();
+      this.resourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ResourcesView();
+      this.tabProjects = new System.Windows.Forms.TabPage();
       this.imageListUsers = new System.Windows.Forms.ImageList(this.components);
-      this.resourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ResourcesView();
+      this.projectsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectsView();
       this.tabAdmin.SuspendLayout();
-      this.tabSlaves.SuspendLayout();
+      this.tabResources.SuspendLayout();
+      this.tabProjects.SuspendLayout();
       this.SuspendLayout();
       // 
       // tabAdmin
       // 
-      this.tabAdmin.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
-      this.tabAdmin.Controls.Add(this.tabSlaves);
+      this.tabAdmin.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.tabAdmin.Controls.Add(this.tabResources);
+      this.tabAdmin.Controls.Add(this.tabProjects);
       this.tabAdmin.Location = new System.Drawing.Point(3, 0);
       this.tabAdmin.Name = "tabAdmin";
@@ -48,26 +52,20 @@
       this.tabAdmin.TabIndex = 0;
       // 
-      // tabSlaves
+      // tabResources
       // 
-      this.tabSlaves.Controls.Add(this.resourcesView);
-      this.tabSlaves.Location = new System.Drawing.Point(4, 22);
-      this.tabSlaves.Name = "tabSlaves";
-      this.tabSlaves.Padding = new System.Windows.Forms.Padding(3);
-      this.tabSlaves.Size = new System.Drawing.Size(734, 520);
-      this.tabSlaves.TabIndex = 3;
-      this.tabSlaves.Text = "Slaves";
-      this.tabSlaves.UseVisualStyleBackColor = true;
-      // 
-      // imageListUsers
-      // 
-      this.imageListUsers.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
-      this.imageListUsers.ImageSize = new System.Drawing.Size(16, 16);
-      this.imageListUsers.TransparentColor = System.Drawing.Color.Transparent;
+      this.tabResources.Controls.Add(this.resourcesView);
+      this.tabResources.Location = new System.Drawing.Point(4, 22);
+      this.tabResources.Name = "tabResources";
+      this.tabResources.Padding = new System.Windows.Forms.Padding(3);
+      this.tabResources.Size = new System.Drawing.Size(734, 520);
+      this.tabResources.TabIndex = 3;
+      this.tabResources.Text = "Resources";
+      this.tabResources.UseVisualStyleBackColor = true;
       // 
       // resourcesView
       // 
-      this.resourcesView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                  | System.Windows.Forms.AnchorStyles.Left)
-                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.resourcesView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
       this.resourcesView.Caption = "ResourcesView";
       this.resourcesView.Content = null;
@@ -78,13 +76,42 @@
       this.resourcesView.TabIndex = 0;
       // 
-      // HiveAdministrationView
+      // tabProjects
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.tabProjects.Controls.Add(this.projectsView);
+      this.tabProjects.Location = new System.Drawing.Point(4, 22);
+      this.tabProjects.Name = "tabProjects";
+      this.tabProjects.Size = new System.Drawing.Size(734, 520);
+      this.tabProjects.TabIndex = 4;
+      this.tabProjects.Text = "Projects";
+      this.tabProjects.UseVisualStyleBackColor = true;
+      // 
+      // imageListUsers
+      // 
+      this.imageListUsers.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
+      this.imageListUsers.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageListUsers.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // projectsView
+      // 
+      this.projectsView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectsView.Caption = "Resources View";
+      this.projectsView.Content = null;
+      this.projectsView.Location = new System.Drawing.Point(0, 0);
+      this.projectsView.Name = "projectsView";
+      this.projectsView.ReadOnly = false;
+      this.projectsView.Size = new System.Drawing.Size(734, 520);
+      this.projectsView.TabIndex = 0;
+      // 
+      // HiveAdministratorView
+      // 
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.Controls.Add(this.tabAdmin);
-      this.Name = "HiveAdministrationView";
+      this.Name = "HiveAdministratorView";
       this.Size = new System.Drawing.Size(745, 546);
       this.tabAdmin.ResumeLayout(false);
-      this.tabSlaves.ResumeLayout(false);
+      this.tabResources.ResumeLayout(false);
+      this.tabProjects.ResumeLayout(false);
       this.ResumeLayout(false);
 
@@ -95,6 +122,7 @@
     private System.ComponentModel.IContainer components;
     private System.Windows.Forms.ImageList imageListUsers;
-    private System.Windows.Forms.TabPage tabSlaves;
-
+    private System.Windows.Forms.TabPage tabResources;
+    private System.Windows.Forms.TabPage tabProjects;
+    private ProjectsView projectsView;
   }
 }
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.Designer.cs	(revision 17059)
@@ -0,0 +1,161 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Windows.Forms;
+using HeuristicLab.Data.Views;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectJobsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.removeButton = new System.Windows.Forms.Button();
+      this.startButton = new System.Windows.Forms.Button();
+      this.stopButton = new System.Windows.Forms.Button();
+      this.pauseButton = new System.Windows.Forms.Button();
+      this.matrixView = new HeuristicLab.Data.Views.StringConvertibleMatrixView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // removeButton
+      // 
+      this.removeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Remove;
+      this.removeButton.Location = new System.Drawing.Point(33, 3);
+      this.removeButton.Name = "removeButton";
+      this.removeButton.Size = new System.Drawing.Size(24, 24);
+      this.removeButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.removeButton, "Remove job(s)");
+      this.removeButton.UseVisualStyleBackColor = true;
+      this.removeButton.Click += new System.EventHandler(this.removeButton_Click);
+      // 
+      // startButton
+      // 
+      this.startButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.startButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Play;
+      this.startButton.Location = new System.Drawing.Point(63, 3);
+      this.startButton.Name = "startButton";
+      this.startButton.Size = new System.Drawing.Size(24, 24);
+      this.startButton.TabIndex = 4;
+      this.toolTip.SetToolTip(this.startButton, "Resume job(s)");
+      this.startButton.UseVisualStyleBackColor = true;
+      this.startButton.Click += new System.EventHandler(this.startButton_Click);
+      // 
+      // pauseButton
+      // 
+      this.pauseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.pauseButton.Enabled = false;
+      this.pauseButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Pause;
+      this.pauseButton.Location = new System.Drawing.Point(93, 3);
+      this.pauseButton.Name = "pauseButton";
+      this.pauseButton.Size = new System.Drawing.Size(24, 24);
+      this.pauseButton.TabIndex = 5;
+      this.toolTip.SetToolTip(this.pauseButton, "Pause job(s)");
+      this.pauseButton.UseVisualStyleBackColor = true;
+      this.pauseButton.Click += new System.EventHandler(this.pauseButton_Click);
+      // 
+      // stopButton
+      // 
+      this.stopButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
+      this.stopButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Stop;
+      this.stopButton.Location = new System.Drawing.Point(123, 3);
+      this.stopButton.Name = "stopButton";
+      this.stopButton.Size = new System.Drawing.Size(24, 24);
+      this.stopButton.TabIndex = 6;
+      this.toolTip.SetToolTip(this.stopButton, "Stop job(s)");
+      this.stopButton.UseVisualStyleBackColor = true;
+      this.stopButton.Click += new System.EventHandler(this.stopButton_Click);
+      // 
+      // matrixView
+      // 
+      this.matrixView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+            | System.Windows.Forms.AnchorStyles.Left)
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.matrixView.Caption = "StringConvertibleMatrix View";
+      this.matrixView.Content = null;
+      this.matrixView.Location = new System.Drawing.Point(3, 33);
+      this.matrixView.Name = "matrixView";
+      this.matrixView.ReadOnly = false;
+      this.matrixView.ShowRowsAndColumnsTextBox = false;
+      this.matrixView.ShowStatisticalInformation = false;
+      this.matrixView.Size = new System.Drawing.Size(504, 349);
+      this.matrixView.TabIndex = 3;
+      // 
+      // ProjectJobsView
+      // 
+      this.AllowDrop = true;
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.matrixView);
+      this.Controls.Add(this.refreshButton);
+      this.Controls.Add(this.removeButton);
+      this.Controls.Add(this.startButton);
+      this.Controls.Add(this.pauseButton);
+      this.Controls.Add(this.stopButton);
+      this.Name = "ProjectJobsView";
+      this.Size = new System.Drawing.Size(510, 385);
+      this.Load += new System.EventHandler(this.ProjectJobsView_Load);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button removeButton;
+    private System.Windows.Forms.Button startButton;
+    private System.Windows.Forms.Button stopButton;
+    private System.Windows.Forms.Button pauseButton;
+    private StringConvertibleMatrixView matrixView;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 17059)
@@ -0,0 +1,410 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.Core.Views;
+using HeuristicLab.Data;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Core;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectJobsView : ItemView {
+    private const string JOB_ID = "Id";
+    private const string JOB_NAME = "Name";
+    private const string JOB_OWNER = "Owner";
+    private const string JOB_OWNERID = "Owner Id";
+    private const string JOB_DATECREATED = "Date Created";
+    private const string JOB_STATE = "State";
+    private const string JOB_EXECUTIONSTATE = "Execution State";
+    private const string JOB_EXECUTIONTIME = "Execution Time";
+    private const string JOB_DESCRIPTION = "Description";
+    private const string JOB_TASKCOUNT = "Tasks";
+    private const string JOB_WAITINGTASKCOUNT = "Waiting";
+    private const string JOB_CALCULATINGTASKCOUNT = "Calculating";
+    private const string JOB_FINISHEDTASKCOUNT = "Finished";
+
+    private readonly Color onlineStatusColor = Color.FromArgb(255, 189, 249, 143); // #bdf98f
+    private readonly Color onlineStatusColor2 = Color.FromArgb(255, 157, 249, 143); // #9df98f
+    private readonly Color statisticsPendingStatusColor = Color.FromArgb(255, 249, 210, 145); // #f9d291
+    private readonly Color deletionPendingStatusColor = Color.FromArgb(255, 249, 172, 144); // #f9ac90
+    private readonly Color deletionPendingStatusColor2 = Color.FromArgb(255, 249, 149, 143); // #f9958f
+
+    private IProgress progress;
+    public IProgress Progress {
+      get { return progress; }
+      set {
+        this.progress = value;
+        OnIsProgressingChanged();
+      }
+    }
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectJobsView() {
+      InitializeComponent();
+      progress = new Progress();
+
+      removeButton.Enabled = false;      
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      matrixView.DataGridView.SelectionChanged += DataGridView_SelectionChanged;
+      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, progress);
+    }
+
+    protected override void DeregisterContentEvents() {
+      matrixView.DataGridView.SelectionChanged -= DataGridView_SelectionChanged;
+      MainFormManager.GetMainForm<HeuristicLab.MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this, false);
+      base.DeregisterContentEvents();
+    }
+
+    private void DataGridView_SelectionChanged(object sender, EventArgs e) {
+      SetEnabledStateOfControls();
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      removeButton.Enabled = false;
+      UpdateJobs();
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      matrixView.Enabled = enabled;
+
+      // Buttons (start/resume, pause, stop, remove)
+      refreshButton.Enabled = startButton.Enabled = pauseButton.Enabled = stopButton.Enabled = removeButton.Enabled = false;
+
+      if (enabled && progress.ProgressState != ProgressState.Started) {
+        var jobs = GetSelectedJobs().ToList();
+        if (jobs.Any()) {
+                    
+          startButton.Enabled = jobs.All(x =>
+            !x.IsProgressing && HiveAdminClient.Instance.Tasks.ContainsKey(x.Id) && HiveAdminClient.Instance.Tasks[x.Id].Count > 0
+            && x.Job.ProjectId != Guid.Empty //&& x.Job.ResourceIds != null && x.Job.ResourceIds.Any()
+            && (x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused));
+          pauseButton.Enabled = jobs.All(x => !x.IsProgressing && x.ExecutionState == ExecutionState.Started);
+          stopButton.Enabled = jobs.All(x => !x.IsProgressing && (x.ExecutionState == ExecutionState.Started || x.ExecutionState == ExecutionState.Paused));
+          removeButton.Enabled = jobs.All(x => !x.IsProgressing && x.Job.State == JobState.Online);
+        }
+      }
+
+      // refresh Button
+      if (Content != null && !Locked && progress.ProgressState != ProgressState.Started) {
+        refreshButton.Enabled = true;
+      }
+    }
+
+    #endregion Overrides
+
+    #region Event Handlers
+    private void ProjectJobsView_Load(object sender, EventArgs e) {
+      
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      progress.Start("Refreshing jobs...");
+      SetEnabledStateOfControls();
+      var task = System.Threading.Tasks.Task.Factory.StartNew(RefreshJobsAsync);
+
+      task.ContinueWith((t) => {
+        progress.Finish();        
+        SetEnabledStateOfControls();
+      });
+    }
+
+    private void removeButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to remove following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Removing job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(RemoveJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured removing the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void startButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to resume following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Resuming job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(ResumeJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();            
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured resuming the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void pauseButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to pause following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Pausing job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(PauseJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();            
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured pausing the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    private void stopButton_Click(object sender, EventArgs e) {
+      var jobs = GetSelectedJobs();
+
+      if (jobs.Any()) {
+        var result = MessageBox.Show("Do you really want to stop following job(s):\n\n"
+                                     + String.Join("\n", jobs.Select(x => x.Job.Name)),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          progress.Start("Stopping job(s)...");
+          SetEnabledStateOfControls();
+          var task = System.Threading.Tasks.Task.Factory.StartNew(StopJobsAsync, jobs);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+          }, TaskContinuationOptions.NotOnFaulted);
+
+          task.ContinueWith((t) => {
+            RefreshJobs();
+            progress.Finish();
+            SetEnabledStateOfControls();
+            MessageBox.Show("An error occured stopping the job(s).", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);            
+          }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+      }
+    }
+
+    public event EventHandler IsProgressingChanged;
+    private void OnIsProgressingChanged() {
+      var handler = IsProgressingChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+    #endregion Event Handlers
+
+    #region Helpers
+
+    private IEnumerable<RefreshableJob> GetSelectedJobs() {
+      if (Content == null || matrixView.DataGridView.SelectedRows == null || matrixView.DataGridView.SelectedRows.Count == 0)
+        return Enumerable.Empty<RefreshableJob>() ;
+
+      var jobs = new List<RefreshableJob>();
+      foreach (DataGridViewRow r in matrixView.DataGridView.SelectedRows) {
+        if (((string)r.Cells[0].Value) == JobState.Online.ToString()) {
+          jobs.Add(HiveAdminClient.Instance.Jobs[Content.Id].FirstOrDefault(x => x.Id == Guid.Parse((string) r.Cells[11].Value)));
+        }
+      }
+
+      return jobs;
+    }
+
+    private void RefreshJobs() {
+      HiveAdminClient.Instance.RefreshJobs();
+      UpdateJobs();
+      SetEnabledStateOfControls();
+    }
+
+    private StringMatrix CreateValueMatrix() {
+      if (Content == null || Content.Id == Guid.Empty)
+        return new StringMatrix();
+
+      var jobs = HiveAdminClient.Instance.Jobs[Content.Id];
+      var resources = HiveAdminClient.Instance.Resources;
+      string[,] values = new string[jobs.Count, 13];
+
+      for(int i = 0; i < jobs.Count; i++) {
+        var job = jobs.ElementAt(i);        
+        values[i, 0] = job.Job.State.ToString();
+        values[i, 1] = job.ExecutionState.ToString();
+        values[i, 2] = job.ExecutionTime.ToString();
+        values[i, 3] = job.Job.DateCreated.ToString();
+        values[i, 4] = job.Job.OwnerUsername;
+        values[i, 5] = job.Job.Name;
+        values[i, 6] = job.Job.JobCount.ToString();
+        values[i, 7] = (job.Job.JobCount - job.Job.CalculatingCount - job.Job.FinishedCount).ToString();
+        values[i, 8] = job.Job.CalculatingCount.ToString();
+        values[i, 9] = job.Job.FinishedCount.ToString();
+        values[i, 10] = job.Job.Description;        
+        values[i, 11] = job.Job.Id.ToString();
+        values[i, 12] = job.Job.OwnerUserId.ToString();
+      }
+      
+      var matrix = new StringMatrix(values);
+      matrix.ColumnNames = new string[] { JOB_STATE, JOB_EXECUTIONSTATE, JOB_EXECUTIONTIME, JOB_DATECREATED, JOB_OWNER, JOB_NAME, JOB_TASKCOUNT, JOB_WAITINGTASKCOUNT, JOB_CALCULATINGTASKCOUNT, JOB_FINISHEDTASKCOUNT, JOB_DESCRIPTION, JOB_ID, JOB_OWNERID };
+      matrix.SortableView = true;
+      return matrix;
+    }
+    
+    private void UpdateJobs() {
+      if (InvokeRequired) Invoke((Action)UpdateJobs);
+      else {
+        if(Content != null && Content.Id != null && Content.Id != Guid.Empty) {
+          var matrix = CreateValueMatrix();
+          matrixView.Content = matrix;
+          if(matrix != null) {
+            foreach (DataGridViewRow row in matrixView.DataGridView.Rows) {
+              string val = ((string)row.Cells[0].Value);
+              if (val == JobState.Online.ToString()) {
+                row.DefaultCellStyle.BackColor = onlineStatusColor;
+              } else if (val == JobState.StatisticsPending.ToString()) {
+                row.DefaultCellStyle.BackColor = statisticsPendingStatusColor;
+              } else if (val == JobState.DeletionPending.ToString()) {
+                row.DefaultCellStyle.BackColor = deletionPendingStatusColor;
+              }
+            }
+
+            matrixView.DataGridView.AutoResizeColumns();
+            matrixView.DataGridView.Columns[0].MinimumWidth = 90;
+            matrixView.DataGridView.Columns[1].MinimumWidth = 108;
+          }
+        } 
+      }
+    }
+
+    private void RefreshJobsAsync() {
+      HiveAdminClient.Instance.RefreshJobs();
+      UpdateJobs();
+    }
+
+    private void ResumeJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      foreach (var job in jobList) {
+        progress.Status = "Resuming job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.ResumeJob(job);
+      }
+    }
+
+    private void PauseJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      foreach (var job in jobList) {
+        progress.Status = "Pausing job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.PauseJob(job);
+      }
+    }
+
+    private void StopJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>) jobs;
+      foreach (var job in jobList) {        
+        progress.Status = "Stopping job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.StopJob(job);
+      }
+    }
+
+    private void RemoveJobsAsync(object jobs) {
+      var jobList = (IEnumerable<RefreshableJob>)jobs;
+      progress.Start();
+      foreach (var job in jobList) {
+        progress.Status = "Removing job \"" + job.Job.Name + "\"...";
+        HiveAdminClient.RemoveJob(job);
+      }
+      progress.Finish();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion Helpers
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 17059)
@@ -0,0 +1,195 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectPermissionsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.permissionsGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.permissionsGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.inheritButton, "Save and hand down permission settings to all descendant projects");
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.saveButton, "Save permission settings");
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.permissionsGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 6;
+      // 
+      // permissionsGroupBox
+      // 
+      this.permissionsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.permissionsGroupBox.Controls.Add(this.treeView);
+      this.permissionsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.permissionsGroupBox.Name = "permissionsGroupBox";
+      this.permissionsGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.permissionsGroupBox.TabIndex = 4;
+      this.permissionsGroupBox.TabStop = false;
+      this.permissionsGroupBox.Text = "Permissions (Assigned + Included)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 5;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 6;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 7;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectPermissionsView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectPermissionsView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectPermissionsView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.permissionsGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox permissionsGroupBox;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 17059)
@@ -0,0 +1,338 @@
+﻿#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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectPermissionsView : ItemView {
+    private const int userImageIndex = 0;
+    private const int userGroupImageIndex = 1;
+
+    private readonly HashSet<UserGroupBase> assignedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> inheritedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> newAssignedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> newInheritedPermissions = new HashSet<UserGroupBase>();
+    private readonly Dictionary<Guid, HashSet<UserGroupBase>> userGroupAncestors = new Dictionary<Guid, HashSet<UserGroupBase>>();
+    private readonly Dictionary<Guid, HashSet<UserGroupBase>> userGroupDescendants = new Dictionary<Guid, HashSet<UserGroupBase>>();
+
+    private IEnumerable<UserGroupBase> addedPermissions;
+    private IEnumerable<UserGroupBase> removedPermissions;
+    private IEnumerable<UserGroupBase> addedIncludes;
+    private IEnumerable<UserGroupBase> removedIncludes;
+
+    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
+    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
+    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
+    private readonly Color projectOwnerColor = Color.DarkRed;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectPermissionsView() {
+      InitializeComponent();
+
+      treeView.ImageList.Images.Add(VSImageLibrary.User);
+      treeView.ImageList.Images.Add(VSImageLibrary.UserAccounts);
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedPermissions.Clear();
+        inheritedPermissions.Clear();
+        newAssignedPermissions.Clear();
+        newInheritedPermissions.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdatePermissionList();        
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      inheritButton.Enabled = enabled;
+      saveButton.Enabled = enabled;
+      treeView.Enabled = enabled;
+
+      if (detailsViewHost != null) {
+        detailsViewHost.Locked = true;
+      }
+
+    }
+    #endregion
+
+    #region Event Handlers
+    private void ProjectPermissionsView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      UpdatePermissionList();
+    }
+
+    private async void inheritButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, true, false));
+      UpdatePermissionList();
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetGrantedProjectPermissions(Content.Id, newAssignedPermissions.Select(x => x.Id), false, false, false));
+      UpdatePermissionList();
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedPermission = (UserGroupBase)e.Node.Tag;
+      detailsViewHost.Content = selectedPermission;
+      if (selectedPermission is LightweightUser)
+        detailsViewHost.ViewType = typeof(Access.Views.RefreshableLightweightUserInformationView);
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Parent == null 
+        || newInheritedPermissions.Contains(checkedPermission) 
+        || checkedPermission.Id == Guid.Empty
+        || Content.OwnerUserId == checkedPermission.Id)
+          e.Cancel = true;
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Checked)
+        newAssignedPermissions.Add(checkedPermission);
+      else
+        newAssignedPermissions.Remove(checkedPermission);
+
+      UpdateNewPermissionList();
+    }
+    #endregion
+
+    #region Helpers
+    private void UpdatePermissionList() {
+      AccessClient.Instance.Refresh();
+
+      UpdateUserGroupGenealogy();
+      UpdateAssignedPermissions();
+      UpdateInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateNewPermissionList() {
+      UpdateNewAssignedPermissions();
+      UpdateNewInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateAssignedPermissions() {
+      assignedPermissions.Clear();
+      newAssignedPermissions.Clear();
+      var grantedPermissions = GetGrantedPermissionsForProject(Content.Id);
+      foreach (var r in grantedPermissions) {
+        assignedPermissions.Add(r);
+        newAssignedPermissions.Add(r);
+      }
+    }
+
+    private void UpdateNewAssignedPermissions() {
+      for(int i = newAssignedPermissions.Count-1; i >= 0; i--) {
+        if(newAssignedPermissions.Intersect(userGroupAncestors[newAssignedPermissions.ElementAt(i).Id]).Any() 
+          && newAssignedPermissions.ElementAt(i).Id != Content.OwnerUserId) {
+          newAssignedPermissions.Remove(newAssignedPermissions.ElementAt(i));
+        }
+      }
+    }
+
+    private void UpdateInheritedPermissions() {
+      inheritedPermissions.Clear();
+      newInheritedPermissions.Clear();
+      foreach(var item in assignedPermissions) {
+        if(userGroupDescendants.ContainsKey(item.Id)) {
+          foreach(var descendant in userGroupDescendants[item.Id]) {
+            if(!assignedPermissions.Contains(descendant)) {
+              inheritedPermissions.Add(descendant);
+              newInheritedPermissions.Add(descendant);
+            }
+          }
+        }
+      }
+    }
+
+    private void UpdateNewInheritedPermissions() {
+      newInheritedPermissions.Clear();
+      foreach(var item in newAssignedPermissions) {
+        if(userGroupDescendants.ContainsKey(item.Id)) {
+          foreach(var descendant in userGroupDescendants[item.Id]) {
+            if(!newAssignedPermissions.Contains(descendant))
+              newInheritedPermissions.Add(descendant);
+          }
+        }
+      }
+    }
+
+    private void UpdateUserGroupGenealogy() {
+      userGroupAncestors.Clear();
+      userGroupDescendants.Clear();
+
+      var usersAndGroups = AccessClient.Instance.UsersAndGroups;
+      foreach (var ug in usersAndGroups) {
+        userGroupAncestors.Add(ug.Id, new HashSet<UserGroupBase>());
+        userGroupDescendants.Add(ug.Id, new HashSet<UserGroupBase>());
+      }
+
+      var userGroupTree = HiveServiceLocator.Instance.CallHiveService(s => s.GetUserGroupTree());
+      foreach(var branch in userGroupTree) {
+        var parent = usersAndGroups.Where(x => x.Id == branch.Key).SingleOrDefault();
+        if(parent != null) {
+          var userGroupsToAdd = usersAndGroups.Where(x => userGroupTree[parent.Id].Contains(x.Id));
+          foreach (var node in userGroupsToAdd) {
+            userGroupDescendants[parent.Id].Add(node);
+            userGroupAncestors[node.Id].Add(parent);
+          }
+        }
+      }
+    }
+
+    private static IEnumerable<UserGroupBase> GetGrantedPermissionsForProject(Guid projectId) {
+      if (projectId == Guid.Empty) return Enumerable.Empty<UserGroupBase>();
+      var projectPermissions = HiveServiceLocator.Instance.CallHiveService(s => s.GetProjectPermissions(projectId));
+      var userIds = new HashSet<Guid>(projectPermissions.Select(x => x.GrantedUserId));
+      return AccessClient.Instance.UsersAndGroups.Where(x => userIds.Contains(x.Id));
+    }
+
+    private static void SetGrantedProjectPermissions(Guid projectId, IEnumerable<Guid> userIds, bool reassign, bool cascading, bool reassignCascading) {
+      if (projectId == null || userIds == null) return;
+      HiveServiceLocator.Instance.CallHiveService(s => {
+        s.SaveProjectPermissions(projectId, userIds.ToList(), reassign, cascading, reassignCascading);
+      });
+    }
+
+    private UserGroupBase BuildPermissionsList(IEnumerable<UserGroupBase> usersAndGroups) {
+      addedPermissions = newAssignedPermissions.Except(assignedPermissions);
+      removedPermissions = assignedPermissions.Except(newAssignedPermissions);
+      addedIncludes = newInheritedPermissions.Except(inheritedPermissions);
+      removedIncludes = inheritedPermissions.Except(newInheritedPermissions);
+
+      treeView.Nodes.Clear();
+      if (!usersAndGroups.Any()) return null;
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var userGroups = new HashSet<UserGroup>(usersAndGroups.OfType<UserGroup>());
+      var users = new HashSet<LightweightUser>(usersAndGroups.OfType<LightweightUser>());
+      UserGroupBase first = null;
+
+      var groupsNode = new TreeNode("Groups") { ForeColor = SystemColors.GrayText };
+      groupsNode.ImageIndex = groupsNode.SelectedImageIndex = userGroupImageIndex;
+
+      foreach (var group in userGroups.OrderBy(x => x.Name)) {
+        var node = new TreeNode(group.Name) { Tag = group };
+        node.ImageIndex = userGroupImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+        BuildNode(group, node);
+        groupsNode.Nodes.Add(node);
+        if (first == null) first = group;
+      }
+
+      var usersNode = new TreeNode("Users") { ForeColor = SystemColors.GrayText };
+      usersNode.ImageIndex = usersNode.SelectedImageIndex = userImageIndex;
+
+      foreach (var user in users.OrderBy(x => x.ToString())) {
+        var node = new TreeNode(user.ToString()) { Tag = user };
+        node.ImageIndex = userImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+        BuildNode(user, node);
+        usersNode.Nodes.Add(node);
+        if (first == null) first = user;
+      }
+
+      treeView.Nodes.Add(groupsNode);
+      treeView.Nodes.Add(usersNode);
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;
+      treeView.ExpandAll();
+
+      return first;
+    }
+
+    private void BuildNode(UserGroupBase ug, TreeNode node) {
+
+      if (newAssignedPermissions.Contains(ug)) {
+        node.Checked = true;
+      } else if (newInheritedPermissions.Contains(ug)) {
+        node.Checked = true;
+        node.ForeColor = SystemColors.GrayText;
+      }
+
+      if (inheritedPermissions.Contains(ug) && newInheritedPermissions.Contains(ug)) {
+        node.Text += " [included]";
+      } else if (addedIncludes.Contains(ug)) {
+        node.BackColor = addedIncludeColor;
+        node.ForeColor = SystemColors.GrayText;
+        node.Text += " [added include]";
+      } else if (removedIncludes.Contains(ug)) {
+        node.BackColor = removedIncludeColor;
+        node.ForeColor = SystemColors.GrayText;
+        node.Text += " [removed include]";
+      }
+
+      if (addedPermissions.Contains(ug)) {
+        node.BackColor = addedAssignmentColor;
+        node.ForeColor = SystemColors.ControlText;
+        node.Text += " [added assignment]";
+      } else if (removedPermissions.Contains(ug)) {
+        node.BackColor = removedAssignmentColor;
+        node.ForeColor = SystemColors.ControlText;
+        node.Text += " [removed assignment]";
+      }
+
+      if(Content != null && ug != null && ug.Id != Guid.Empty 
+        && Content.OwnerUserId == ug.Id) {
+        node.ForeColor = projectOwnerColor;
+      }
+
+    }
+
+    #endregion
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 17059)
@@ -0,0 +1,197 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectResourcesView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.resourcesGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.resourcesGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 1;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 2;
+      this.toolTip.SetToolTip(this.inheritButton, "Save and hand down resource assignments to all descendant projects");
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 3;
+      this.toolTip.SetToolTip(this.saveButton, "Save resource assignments");
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.resourcesGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 5;
+      // 
+      // resourcesGroupBox
+      // 
+      this.resourcesGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.resourcesGroupBox.Controls.Add(this.treeView);
+      this.resourcesGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.resourcesGroupBox.Name = "resourcesGroupBox";
+      this.resourcesGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.resourcesGroupBox.TabIndex = 0;
+      this.resourcesGroupBox.TabStop = false;
+      this.resourcesGroupBox.Text = "Resources (Assigned + Included)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 0;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeSelect);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 0;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 0;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectResourcesView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectResourcesView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectResourcesView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.resourcesGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox resourcesGroupBox;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
+    private System.Windows.Forms.ToolTip toolTip;
+    private System.Windows.Forms.ImageList imageList;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 17059)
@@ -0,0 +1,522 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+using System.Collections;
+using HeuristicLab.Common;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectResourcesView : ItemView {
+    private const int slaveImageIndex = 0;
+    private const int slaveGroupImageIndex = 1;
+    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
+    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
+    public const string IMMUTABLE_TAG = " [assigned, immutable]";
+    public const string INCLUDED_TAG = " [included]";
+    public const string ADDED_ASSIGNMENT_TAG = " [added assignment]";
+    public const string REMOVED_ASSIGNMENT_TAG = " [removed assignment]";
+    public const string ADDED_INCLUDE_TAG = " [added include]";
+    public const string REMOVED_INCLUDE_TAG = " [removed include]";
+
+    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newAssignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> includedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> newIncludedResources = new HashSet<Resource>();
+
+    private readonly Color addedAssignmentColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color removedAssignmentColor = Color.FromArgb(255, 236, 159, 72); // #ec9f48
+    private readonly Color addedIncludeColor = Color.FromArgb(25, 169, 221, 221); // #a9dddd
+    private readonly Color removedIncludeColor = Color.FromArgb(25, 249, 210, 145); // #f9d291
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+    private HashSet<Resource> projectExclusiveResources = new HashSet<Resource>();
+    private TreeNode ungroupedGroupNode;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectResourcesView() {
+      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;
+    }
+
+    #region Overrides
+    protected override void OnClosing(FormClosingEventArgs e) {
+      HiveAdminClient.Instance.Refreshed -= HiveAdminClient_Instance_Refreshed;
+      HiveAdminClient.Instance.Refreshing -= HiveAdminClient_Instance_Refreshing;
+      base.OnClosing(e);
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedResources.Clear();
+        newAssignedResources.Clear();
+        includedResources.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdateAssignedResources();
+        UpdateIncludedResources();
+        detailsViewHost.Content = BuildResourceTree();
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+
+      inheritButton.Enabled = enabled;
+      saveButton.Enabled = enabled;
+      treeView.Enabled = enabled;
+
+      if (detailsViewHost != null) {
+        detailsViewHost.Locked = true;
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void ProjectResourcesView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      HiveAdminClient.Instance.Refresh();
+      UpdateAssignedResources();
+      UpdateIncludedResources();
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private async void inheritButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, true, false);
+        });
+      HiveAdminClient.Instance.Refresh();
+      UpdateResourceTree();
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          SetAssignedProjectResources(Content.Id, newAssignedResources.Select(x => x.Id), false, false, false);
+        });
+      HiveAdminClient.Instance.Refresh();
+      UpdateResourceTree();
+    }
+
+    private void treeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      if (e.Node == null || e.Node == ungroupedGroupNode)
+        e.Cancel = true;
+
+      var selectedResource = (Resource)e.Node.Tag;
+      if(HiveAdminClient.Instance.DisabledParentResources.Contains(selectedResource))
+        e.Cancel = true;
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedResource = (Resource)e.Node.Tag;
+      detailsViewHost.Content = selectedResource;
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      if (e.Node == null || e.Node == ungroupedGroupNode) {
+        e.Cancel = true;
+        return;
+      }
+        
+      var checkedResource = (Resource)e.Node.Tag;
+      if (checkedResource == null
+        || checkedResource.Id == Guid.Empty
+        || HiveAdminClient.Instance.DisabledParentResources.Contains(checkedResource) 
+        || newIncludedResources.Contains(checkedResource)) {
+        e.Cancel = true;
+      } else if (!IsAdmin()) {
+          if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id) 
+            || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any() 
+            || projectExclusiveResources.Contains(checkedResource)) {
+            e.Cancel = true;
+          }
+      }
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedResource = (Resource)e.Node.Tag;
+      if (e.Node.Checked) {
+        newAssignedResources.Add(checkedResource);
+      } else {
+        newAssignedResources.Remove(checkedResource);
+      }
+
+      UpdateNewResourceTree();
+    }
+    #endregion
+
+    #region Helpers
+
+    private void UpdateResourceTree() {
+      UpdateAssignedResources();
+      UpdateIncludedResources();
+
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private void UpdateNewResourceTree() {
+      UpdateNewAssignedResources();
+      UpdateNewIncludedResources();
+      var top = BuildResourceTree();
+      detailsViewHost.Content = top;
+    }
+
+    private IEnumerable<Resource> GetAvailableResourcesForProjectAdministration(Guid projectId) {
+      projectExclusiveResources.Clear();
+      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
+      var resources = HiveAdminClient.Instance.Resources.Where(x => x.Id != Guid.Empty);
+      if (!resources.Any()) return resources;
+      if (IsAdmin()) return resources;
+
+      // get project specific assigned resources
+      var projectResources = resources.Where(x => 
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => a.ProjectId == projectId)
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+
+      // look up for assignments of ancestor projects
+      var projectIds = new HashSet<Guid>();
+      HiveAdminClient.Instance.GetAvailableProjectAncestors(projectId).ToList().ForEach(x => projectIds.Add(x.Id));
+
+      var ancestorProjectResources = resources.Where(x => 
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => projectIds.Contains(a.ProjectId))
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+
+      // look down for included resources of ancestor projects
+      HashSet<Resource> availableResources = new HashSet<Resource>(ancestorProjectResources);
+      foreach (var r in ancestorProjectResources) {
+        foreach(var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
+          availableResources.Add(d);
+        }
+      }
+
+      // determine resources, which are exclusively assigned to the currently selected project
+      projectResources
+        .Except(availableResources)
+        .ToList()
+        .ForEach(x => projectExclusiveResources.Add(x));
+
+      // look down for included resources of currently selected project
+      if (projectExclusiveResources.Any()) {
+        foreach (var r in projectExclusiveResources.ToArray()) {
+          foreach (var d in HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id)) {
+            projectExclusiveResources.Add(d);
+          }  
+        }
+      }
+
+      return availableResources.Union(projectExclusiveResources);
+    }
+
+    private IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
+      if (projectId == Guid.Empty) return Enumerable.Empty<Resource>();
+      return HiveAdminClient.Instance.Resources.Where(x =>
+        HiveAdminClient.Instance.ProjectResourceAssignments
+          .Where(a => a.ProjectId == projectId)
+          .Select(a => a.ResourceId)
+        .Contains(x.Id));
+    }
+
+    private void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds, bool reassign, bool cascading, bool reassignCascading) {
+      if (projectId == null || resourceIds == null) return;
+      HiveServiceLocator.Instance.CallHiveService(s => {
+       s.SaveProjectResourceAssignments(projectId, resourceIds.ToList(), reassign, cascading, reassignCascading);
+      });
+    }
+
+    private void UpdateNewAssignedResources() {
+      for(int i = newAssignedResources.Count -1; i >= 0; i--) {
+        if(newAssignedResources.Intersect(HiveAdminClient.Instance.GetAvailableResourceAncestors(newAssignedResources.ElementAt(i).Id)).Any()) {
+          newAssignedResources.Remove(newAssignedResources.ElementAt(i));
+        }
+      }
+    }
+
+    private void UpdateAssignedResources() {
+      assignedResources.Clear();
+      newAssignedResources.Clear();
+      foreach (var r in GetAssignedResourcesForProject(Content.Id)) {
+        assignedResources.Add(r);
+        newAssignedResources.Add(r);
+      }
+    }
+
+    private void UpdateIncludedResources() {
+      includedResources.Clear();
+      newIncludedResources.Clear();
+      foreach (var a in assignedResources) {
+        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
+          includedResources.Add(r);
+          newIncludedResources.Add(r);
+        }
+      }
+    }
+
+    private void UpdateNewIncludedResources() {
+      newIncludedResources.Clear();
+      foreach (var a in newAssignedResources) {
+        foreach (var r in HiveAdminClient.Instance.GetAvailableResourceDescendants(a.Id)) {
+          newIncludedResources.Add(r);
+        }
+      }
+    }
+
+    private Resource BuildResourceTree() {
+      treeView.Nodes.Clear();
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var resources = GetAvailableResourcesForProjectAdministration(Content.Id);
+
+      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
+      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>().Where(x => x.ParentResourceId == null));
+      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
+      //mainResources.UnionWith(parentedMainResources);
+      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
+      mainResources.UnionWith(mainDisabledParentResources);
+      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
+
+      var stack = new Stack<Resource>(mainResources.OrderByDescending(x => x.Name));
+      
+      Resource top = null;
+      //bool nodeSelected = false;
+      if (detailsViewHost != null && detailsViewHost.Content != null && detailsViewHost.Content is Resource) {
+        var resourceId = ((Resource)detailsViewHost.Content).Id;
+        top = resources.Where(x => x.Id == resourceId).FirstOrDefault();
+      }
+      
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      var addedAssignments = newAssignedResources.Except(assignedResources);
+      var removedAssignments = assignedResources.Except(newAssignedResources);
+      var addedIncludes = newIncludedResources.Except(includedResources);
+      var removedIncludes = includedResources.Except(newIncludedResources);
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+
+        if(top == null && !disabledParentResources.Contains(newResource)) {
+          top = newResource;
+        }
+
+        //if(!nodeSelected && top != null && newResource.Id == top.Id) {
+        //  newNode.BackColor = selectedBackColor;
+        //  newNode.ForeColor = selectedForeColor;
+        //  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 (disabledParentResources.Contains(newResource)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        } else if (newAssignedResources.Contains(newResource)) {
+          newNode.Checked = true;
+          if(!HiveRoles.CheckAdminUserPermissions()) {
+            if (!HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id) 
+              || !HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).Any() 
+              || projectExclusiveResources.Contains(newResource)) {
+              newNode.ForeColor = SystemColors.GrayText;
+              newNode.Text += IMMUTABLE_TAG;
+            }
+          }
+
+        } else if (newIncludedResources.Contains(newResource)) {
+          newNode.Checked = true;
+          newNode.ForeColor = SystemColors.GrayText;
+        }
+
+        if (includedResources.Contains(newResource) && newIncludedResources.Contains(newResource)) {
+          newNode.Text += INCLUDED_TAG;
+        } else if (addedIncludes.Contains(newResource)) {
+          newNode.BackColor = addedIncludeColor;
+          newNode.ForeColor = SystemColors.GrayText;
+          newNode.Text += ADDED_INCLUDE_TAG;
+        } else if (removedIncludes.Contains(newResource)) {
+          newNode.BackColor = removedIncludeColor;
+          newNode.Text += REMOVED_INCLUDE_TAG;
+        }
+
+        if (addedAssignments.Contains(newResource)) {
+          newNode.BackColor = addedAssignmentColor;
+          newNode.ForeColor = SystemColors.ControlText;
+          newNode.Text += ADDED_ASSIGNMENT_TAG;
+        } else if (removedAssignments.Contains(newResource)) {
+          newNode.BackColor = removedAssignmentColor;
+          newNode.ForeColor = SystemColors.ControlText;
+          newNode.Text += REMOVED_ASSIGNMENT_TAG;
+        }
+
+        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;
+      }
+
+      ExpandResourceNodesOfInterest(treeView.Nodes);
+
+      bool expandUngroupedGroupNode = false;
+      var ungroupedSlaves = subResources.OfType<Slave>().OrderBy(x => x.Name);
+      if(ungroupedSlaves.Any()) {
+        ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
+          ForeColor = SystemColors.GrayText,
+          Tag = new SlaveGroup() {
+            Name = UNGROUPED_GROUP_NAME,
+            Description = UNGROUPED_GROUP_DESCRIPTION
+          }
+        };
+
+        foreach (var slave in ungroupedSlaves) {
+          var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+          if (newAssignedResources.Contains(slave)) {
+            slaveNode.Checked = true;
+            expandUngroupedGroupNode = true;
+          }
+
+          if (!HiveRoles.CheckAdminUserPermissions()) {
+            slaveNode.ForeColor = SystemColors.GrayText;
+            slaveNode.Text += IMMUTABLE_TAG;
+          } else {
+            if (addedAssignments.Contains(slave)) {
+              slaveNode.BackColor = addedAssignmentColor;
+              slaveNode.ForeColor = SystemColors.ControlText;
+              slaveNode.Text += ADDED_ASSIGNMENT_TAG;
+            } else if (removedAssignments.Contains(slave)) {
+              slaveNode.BackColor = removedAssignmentColor;
+              slaveNode.ForeColor = SystemColors.ControlText;
+              slaveNode.Text += REMOVED_ASSIGNMENT_TAG;
+              expandUngroupedGroupNode = true;
+            }
+          }
+          ungroupedGroupNode.Nodes.Add(slaveNode);
+        }
+
+        if(expandUngroupedGroupNode) ungroupedGroupNode.Expand();
+        treeView.Nodes.Add(ungroupedGroupNode);
+      } else if (ungroupedGroupNode != null) {
+        ungroupedGroupNode.Nodes.Clear();
+      }
+
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;          
+
+      return top;
+    }
+
+    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        Resource r = (Resource)n.Tag;
+        if (n.Nodes.Count > 0) {
+          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
+            n.Expand();
+            ExpandResourceNodesOfInterest(n.Nodes);
+          } else {
+            n.Collapse();
+          }
+        } else {
+          n.Collapse();
+        }
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    #endregion
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 17059)
@@ -0,0 +1,253 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.idLabel = new System.Windows.Forms.Label();
+      this.idTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
+      this.ownerComboBox = new System.Windows.Forms.ComboBox();
+      this.ownerLabel = new System.Windows.Forms.Label();
+      this.createdLabel = new System.Windows.Forms.Label();
+      this.startDateTimePicker = new System.Windows.Forms.DateTimePicker();
+      this.startLabel = new System.Windows.Forms.Label();
+      this.endDateTimePicker = new System.Windows.Forms.DateTimePicker();
+      this.endLabel = new System.Windows.Forms.Label();
+      this.indefiniteCheckBox = new System.Windows.Forms.CheckBox();
+      this.createdTextBox = new System.Windows.Forms.TextBox();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.SuspendLayout();
+      // 
+      // idLabel
+      // 
+      this.idLabel.AutoSize = true;
+      this.idLabel.Location = new System.Drawing.Point(3, 11);
+      this.idLabel.Name = "idLabel";
+      this.idLabel.Size = new System.Drawing.Size(19, 13);
+      this.idLabel.TabIndex = 0;
+      this.idLabel.Text = "Id:";
+      // 
+      // idTextBox
+      // 
+      this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.idTextBox.Location = new System.Drawing.Point(72, 8);
+      this.idTextBox.Name = "idTextBox";
+      this.idTextBox.ReadOnly = true;
+      this.idTextBox.Size = new System.Drawing.Size(464, 20);
+      this.idTextBox.TabIndex = 1;
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 37);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 0;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(72, 34);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(464, 20);
+      this.nameTextBox.TabIndex = 2;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      this.nameTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.nameTextBox_Validating);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 63);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 0;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(72, 60);
+      this.descriptionTextBox.Multiline = true;
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(464, 98);
+      this.descriptionTextBox.TabIndex = 3;
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
+      // 
+      // ownerLabel
+      // 
+      this.ownerLabel.AutoSize = true;
+      this.ownerLabel.Location = new System.Drawing.Point(3, 167);
+      this.ownerLabel.Name = "ownerLabel";
+      this.ownerLabel.Size = new System.Drawing.Size(41, 13);
+      this.ownerLabel.TabIndex = 0;
+      this.ownerLabel.Text = "Owner:";
+      // 
+      // ownerComboBox
+      // 
+      //this.ownerComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+      //      | System.Windows.Forms.AnchorStyles.Right)));
+      this.ownerComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+      this.ownerComboBox.FormattingEnabled = true;
+      this.ownerComboBox.Location = new System.Drawing.Point(72, 164);
+      this.ownerComboBox.Name = "ownerComboBox";
+      this.ownerComboBox.Size = new System.Drawing.Size(200, 21);
+      this.ownerComboBox.TabIndex = 4;
+      this.ownerComboBox.SelectedIndexChanged += new System.EventHandler(this.ownerComboBox_SelectedIndexChanged);
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(278, 162);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 5;
+      this.toolTip.SetToolTip(this.refreshButton, "Refresh data");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // createdLabel
+      // 
+      this.createdLabel.AutoSize = true;
+      this.createdLabel.Location = new System.Drawing.Point(3, 197);
+      this.createdLabel.Name = "createdLabel";
+      this.createdLabel.Size = new System.Drawing.Size(47, 13);
+      this.createdLabel.TabIndex = 8;
+      this.createdLabel.Text = "Created:";
+      // 
+      // startDateTimePicker
+      // 
+      this.startDateTimePicker.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
+      this.startDateTimePicker.Enabled = false;
+      this.startDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.startDateTimePicker.Location = new System.Drawing.Point(72, 216);
+      this.startDateTimePicker.Name = "startDateTimePicker";
+      this.startDateTimePicker.Size = new System.Drawing.Size(200, 20);
+      this.startDateTimePicker.TabIndex = 9;
+      this.startDateTimePicker.ValueChanged += new System.EventHandler(this.startDateTimePicker_ValueChanged);      
+      // 
+      // startLabel
+      // 
+      this.startLabel.AutoSize = true;
+      this.startLabel.Location = new System.Drawing.Point(3, 223);
+      this.startLabel.Name = "startLabel";
+      this.startLabel.Size = new System.Drawing.Size(32, 13);
+      this.startLabel.TabIndex = 10;
+      this.startLabel.Text = "Start:";
+      // 
+      // endDateTimePicker
+      // 
+      this.endDateTimePicker.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
+      this.endDateTimePicker.Enabled = false;
+      this.endDateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.endDateTimePicker.Location = new System.Drawing.Point(72, 243);
+      this.endDateTimePicker.Name = "endDateTimePicker";
+      this.endDateTimePicker.Size = new System.Drawing.Size(200, 20);
+      this.endDateTimePicker.TabIndex = 11;
+      this.endDateTimePicker.ValueChanged += new System.EventHandler(this.endDateTimePicker_ValueChanged);
+      // 
+      // endLabel
+      // 
+      this.endLabel.AutoSize = true;
+      this.endLabel.Location = new System.Drawing.Point(3, 249);
+      this.endLabel.Name = "endLabel";
+      this.endLabel.Size = new System.Drawing.Size(29, 13);
+      this.endLabel.TabIndex = 10;
+      this.endLabel.Text = "End:";
+      // 
+      // indefiniteCheckBox
+      // 
+      this.indefiniteCheckBox.AutoSize = true;
+      this.indefiniteCheckBox.Location = new System.Drawing.Point(278, 245);
+      this.indefiniteCheckBox.Name = "indefiniteCheckBox";
+      this.indefiniteCheckBox.Size = new System.Drawing.Size(69, 17);
+      this.indefiniteCheckBox.TabIndex = 13;
+      this.indefiniteCheckBox.Text = "Indefinite";
+      this.indefiniteCheckBox.UseVisualStyleBackColor = true;
+      this.indefiniteCheckBox.CheckedChanged += new System.EventHandler(this.indefiniteCheckBox_CheckedChanged);
+      // 
+      // createdTextBox
+      // 
+      this.createdTextBox.Location = new System.Drawing.Point(72, 190);
+      this.createdTextBox.Name = "createdTextBox";
+      this.createdTextBox.ReadOnly = true;
+      this.createdTextBox.Size = new System.Drawing.Size(200, 20);
+      this.createdTextBox.TabIndex = 14;
+      // 
+      // ProjectView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.refreshButton);
+      this.Controls.Add(this.createdTextBox);
+      this.Controls.Add(this.indefiniteCheckBox);
+      this.Controls.Add(this.endLabel);
+      this.Controls.Add(this.endDateTimePicker);
+      this.Controls.Add(this.startLabel);
+      this.Controls.Add(this.startDateTimePicker);
+      this.Controls.Add(this.createdLabel);
+      this.Controls.Add(this.ownerLabel);
+      this.Controls.Add(this.ownerComboBox);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.idTextBox);
+      this.Controls.Add(this.idLabel);
+      this.Name = "ProjectView";
+      this.Size = new System.Drawing.Size(539, 271);
+      this.Load += new System.EventHandler(this.ProjectView_Load);
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label idLabel;
+    protected System.Windows.Forms.TextBox idTextBox;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.TextBox nameTextBox;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.TextBox descriptionTextBox;
+    private System.Windows.Forms.ComboBox ownerComboBox;
+    private System.Windows.Forms.Label ownerLabel;
+    private System.Windows.Forms.Label createdLabel;
+    private System.Windows.Forms.DateTimePicker startDateTimePicker;
+    private System.Windows.Forms.Label startLabel;
+    private System.Windows.Forms.DateTimePicker endDateTimePicker;
+    private System.Windows.Forms.Label endLabel;
+    private System.Windows.Forms.CheckBox indefiniteCheckBox;
+    private System.Windows.Forms.TextBox createdTextBox;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.ToolTip toolTip;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 17059)
@@ -0,0 +1,333 @@
+﻿#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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Clients.Hive.Views;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = true)]
+  public partial class ProjectView : ItemView {
+    private readonly object locker = new object();
+
+    private Guid persistedOwnerUserId;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; persistedOwnerUserId = Content != null ? Content.OwnerUserId : Guid.Empty; }
+    }
+
+    public ProjectView() {
+      InitializeComponent();
+
+      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;
+      base.OnClosing(e);
+    }
+
+    protected override void RegisterContentEvents() {
+      base.RegisterContentEvents();
+      Content.PropertyChanged += Content_PropertyChanged;
+    }
+
+    protected override void DeregisterContentEvents() {
+      Content.PropertyChanged -= Content_PropertyChanged;
+      base.DeregisterContentEvents();
+    }
+
+    protected void RegisterControlEvents() {
+      nameTextBox.TextChanged += nameTextBox_TextChanged;
+      nameTextBox.Validating += nameTextBox_Validating;
+      descriptionTextBox.TextChanged += descriptionTextBox_TextChanged;
+      ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+      startDateTimePicker.ValueChanged += startDateTimePicker_ValueChanged;
+      endDateTimePicker.ValueChanged += endDateTimePicker_ValueChanged;
+      indefiniteCheckBox.CheckedChanged += indefiniteCheckBox_CheckedChanged;
+    }
+
+    protected void DeregisterControlEvents() {
+      nameTextBox.TextChanged -= nameTextBox_TextChanged;
+      nameTextBox.Validating -= nameTextBox_Validating;
+      descriptionTextBox.TextChanged -= descriptionTextBox_TextChanged;
+      ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+      startDateTimePicker.ValueChanged -= startDateTimePicker_ValueChanged;
+      endDateTimePicker.ValueChanged -= endDateTimePicker_ValueChanged;
+      indefiniteCheckBox.CheckedChanged -= indefiniteCheckBox_CheckedChanged;
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      DeregisterControlEvents();
+      if (Content == null) {
+        idTextBox.Clear();
+        nameTextBox.Clear();
+        descriptionTextBox.Clear();
+        ownerComboBox.SelectedItem = null;
+        createdTextBox.Clear();
+        startDateTimePicker.Value = DateTime.Now;
+        endDateTimePicker.Value = startDateTimePicker.Value;
+        indefiniteCheckBox.Checked = false;
+      } else {
+        idTextBox.Text = Content.Id.ToString();
+        nameTextBox.Text = Content.Name;
+        descriptionTextBox.Text = Content.Description;
+
+        ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+        var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+        if (!Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+        ownerComboBox.DataSource = users.ToList();
+        ownerComboBox.SelectedItem = users.FirstOrDefault(x => x.Id == Content.OwnerUserId);
+        ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+
+        createdTextBox.Text = Content.DateCreated.ToString("ddd, dd.MM.yyyy, HH:mm:ss");
+        startDateTimePicker.Value = Content.StartDate;
+
+        indefiniteCheckBox.Checked = !Content.EndDate.HasValue;
+        if (!indefiniteCheckBox.Checked) endDateTimePicker.Value = Content.EndDate.Value;
+        else endDateTimePicker.Value = Content.StartDate;
+        endDateTimePicker.Enabled = !indefiniteCheckBox.Checked;
+      }
+      SetEnabledStateOfControls();
+      RegisterControlEvents();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked && !ReadOnly;
+      nameTextBox.Enabled = enabled;
+      descriptionTextBox.Enabled = enabled;
+      refreshButton.Enabled = enabled;
+      ownerComboBox.Enabled = enabled;
+      createdTextBox.Enabled = enabled;
+      startDateTimePicker.Enabled = enabled;
+      endDateTimePicker.Enabled = enabled && Content.EndDate.HasValue;
+      indefiniteCheckBox.Enabled = enabled;
+
+      if (Content != null) {
+        //var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        var parentProject = HiveAdminClient.Instance.Projects.FirstOrDefault(x => x.Id == Content.ParentProjectId);
+        if ((!IsAdmin() && (parentProject == null || parentProject.EndDate.HasValue))
+           || (IsAdmin() && parentProject != null && parentProject.EndDate.HasValue)) {
+          indefiniteCheckBox.Enabled = false;
+        }
+
+        if (Content.Id != Guid.Empty && !IsAdmin() && !HiveAdminClient.Instance.CheckOwnershipOfParentProject(Content, UserInformation.Instance.User.Id)) {
+          ownerComboBox.Enabled = false;
+          startDateTimePicker.Enabled = false;
+          endDateTimePicker.Enabled = false;
+          indefiniteCheckBox.Enabled = false;
+        }
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)Content_PropertyChanged, sender, e);
+      else OnContentChanged();
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private async void ProjectView_Load(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateUsers(),
+        finallyCallback: () => {
+          ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+          var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+          if (Content != null && !Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+          ownerComboBox.DataSource = users.ToList();
+          ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+        });
+    }
+
+    private async void refreshButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateUsers(),
+        finallyCallback: () => {
+          ownerComboBox.SelectedIndexChanged -= ownerComboBox_SelectedIndexChanged;
+          var users = AccessClient.Instance.UsersAndGroups.OfType<LightweightUser>();
+          if (Content != null && !Content.ParentProjectId.HasValue) users = users.Where(x => x.Roles.Select(y => y.Name).Contains(HiveRoles.Administrator));
+          ownerComboBox.DataSource = users.ToList();
+          ownerComboBox.SelectedItem = users.FirstOrDefault(x => x.Id == persistedOwnerUserId);
+          ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
+          refreshButton.Enabled = true;
+        });
+    }
+
+    private void nameTextBox_Validating(object sender, CancelEventArgs e) {
+      if (string.IsNullOrEmpty(nameTextBox.Text)) {
+        MessageBox.Show(
+          "Project must have a name.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Error);
+        e.Cancel = true;
+      }
+    }
+
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text) {
+        DeregisterContentEvents();
+        Content.Name = nameTextBox.Text;
+        RegisterContentEvents();
+      }
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text) {
+        DeregisterContentEvents();
+        Content.Description = descriptionTextBox.Text;
+        RegisterContentEvents();
+      }
+    }
+
+    private void ownerComboBox_SelectedIndexChanged(object sender, EventArgs e) {
+      var selectedItem = (LightweightUser)ownerComboBox.SelectedItem;
+      var selectedOwnerUserId = selectedItem != null ? selectedItem.Id : Guid.Empty;
+      if (Content != null && Content.OwnerUserId != selectedOwnerUserId) {
+        DeregisterContentEvents();
+        Content.OwnerUserId = selectedOwnerUserId;
+        RegisterContentEvents();
+      }
+    }
+
+    private void startDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+      startDateTimePicker.ValueChanged -= startDateTimePicker_ValueChanged;
+
+      if (!IsAdmin()) {
+        var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        if (parentProject != null) {
+          if (startDateTimePicker.Value < parentProject.StartDate)
+            startDateTimePicker.Value = parentProject.StartDate;
+        } else {
+          startDateTimePicker.Value = Content.StartDate;
+        }
+      }
+
+      if (!Content.EndDate.HasValue || startDateTimePicker.Value > Content.EndDate)
+        endDateTimePicker.Value = startDateTimePicker.Value;
+      if (Content.StartDate != startDateTimePicker.Value) {
+        DeregisterContentEvents();
+        Content.StartDate = startDateTimePicker.Value;
+        RegisterContentEvents();
+      }
+
+      startDateTimePicker.ValueChanged += startDateTimePicker_ValueChanged;
+    }
+
+    private void endDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+      endDateTimePicker.ValueChanged -= endDateTimePicker_ValueChanged;
+
+      if (!IsAdmin()) {
+        var parentProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(Content.Id).LastOrDefault();
+        if (parentProject != null) {
+          if (parentProject.EndDate.HasValue && endDateTimePicker.Value > parentProject.EndDate.Value) {
+            endDateTimePicker.Value = parentProject.EndDate.Value;
+          }
+        } else if (Content.EndDate.HasValue) {
+          endDateTimePicker.Value = Content.EndDate.Value;
+        }
+      }
+
+      if (endDateTimePicker.Value < startDateTimePicker.Value)
+        endDateTimePicker.Value = startDateTimePicker.Value;
+      if (Content.EndDate != endDateTimePicker.Value) {
+        DeregisterContentEvents();
+        Content.EndDate = endDateTimePicker.Value;
+        RegisterContentEvents();
+      }
+
+      endDateTimePicker.ValueChanged += endDateTimePicker_ValueChanged;
+    }
+
+    private void indefiniteCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (Content == null) return;
+
+      var newEndDate = indefiniteCheckBox.Checked ? (DateTime?)null : endDateTimePicker.Value;
+      endDateTimePicker.Enabled = !indefiniteCheckBox.Checked;
+      if (Content.EndDate != newEndDate) {
+        DeregisterContentEvents();
+        Content.EndDate = newEndDate;
+        RegisterContentEvents();
+      }
+    }
+    #endregion
+
+    #region Helpers
+    private void UpdateUsers() {
+      try {
+        AccessClient.Instance.Refresh();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 17059)
@@ -0,0 +1,295 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.saveProjectButton = new System.Windows.Forms.Button();
+      this.removeButton = new System.Windows.Forms.Button();
+      this.addButton = new System.Windows.Forms.Button();
+      this.projectsTreeView = new System.Windows.Forms.TreeView();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.tabControl = new System.Windows.Forms.TabControl();
+      this.detailsTabPage = new System.Windows.Forms.TabPage();
+      this.projectView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectView();
+      this.permissionsTabPage = new System.Windows.Forms.TabPage();
+      this.projectPermissionsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectPermissionsView();
+      this.resourcesTabPage = new System.Windows.Forms.TabPage();
+      this.projectResourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectResourcesView();
+      this.jobsTabPage = new System.Windows.Forms.TabPage();
+      this.projectJobsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectJobsView();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.tabControl.SuspendLayout();
+      this.detailsTabPage.SuspendLayout();
+      this.permissionsTabPage.SuspendLayout();
+      this.resourcesTabPage.SuspendLayout();
+      this.jobsTabPage.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 3);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.refreshButton);
+      this.splitContainer.Panel1.Controls.Add(this.saveProjectButton);
+      this.splitContainer.Panel1.Controls.Add(this.removeButton);
+      this.splitContainer.Panel1.Controls.Add(this.addButton);
+      this.splitContainer.Panel1.Controls.Add(this.projectsTreeView);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.tabControl);
+      this.splitContainer.Size = new System.Drawing.Size(847, 547);
+      this.splitContainer.SplitterDistance = 249;
+      this.splitContainer.TabIndex = 0;
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 14;
+      this.toolTip.SetToolTip(this.refreshButton, "Fetch list from server");
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // saveProjectButton
+      // 
+      this.saveProjectButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveProjectButton.Location = new System.Drawing.Point(93, 3);
+      this.saveProjectButton.Name = "saveProjectButton";
+      this.saveProjectButton.Size = new System.Drawing.Size(24, 24);
+      this.saveProjectButton.TabIndex = 12;
+      this.toolTip.SetToolTip(this.saveProjectButton, "Store project on the server");
+      this.saveProjectButton.UseVisualStyleBackColor = true;
+      this.saveProjectButton.Click += new System.EventHandler(this.saveProjectButton_Click);
+      // 
+      // removeButton
+      // 
+      this.removeButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Remove;
+      this.removeButton.Location = new System.Drawing.Point(63, 3);
+      this.removeButton.Name = "removeButton";
+      this.removeButton.Size = new System.Drawing.Size(24, 24);
+      this.removeButton.TabIndex = 11;
+      this.toolTip.SetToolTip(this.removeButton, "Remove a project");
+      this.removeButton.UseVisualStyleBackColor = true;
+      this.removeButton.Click += new System.EventHandler(this.removeButton_Click);
+      // 
+      // addButton
+      // 
+      this.addButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Add;
+      this.addButton.Location = new System.Drawing.Point(33, 3);
+      this.addButton.Name = "addButton";
+      this.addButton.Size = new System.Drawing.Size(24, 24);
+      this.addButton.TabIndex = 10;
+      this.toolTip.SetToolTip(this.addButton, "Add a new project");
+      this.addButton.UseVisualStyleBackColor = true;
+      this.addButton.Click += new System.EventHandler(this.addButton_Click);
+      // 
+      // projectsTreeView
+      // 
+      this.projectsTreeView.AllowDrop = true;
+      this.projectsTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectsTreeView.ImageIndex = 0;
+      this.projectsTreeView.ImageList = this.imageList;
+      this.projectsTreeView.Location = new System.Drawing.Point(3, 33);
+      this.projectsTreeView.Name = "projectsTreeView";
+      this.projectsTreeView.SelectedImageIndex = 0;
+      this.projectsTreeView.Size = new System.Drawing.Size(243, 511);
+      this.projectsTreeView.TabIndex = 9;
+      this.projectsTreeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.projectsTreeView_ItemDrag);
+      this.projectsTreeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.projectsTreeView_BeforeSelect);
+      this.projectsTreeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.projectsTreeView_MouseDown);
+      this.projectsTreeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragDrop);
+      this.projectsTreeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragEnterOver);
+      this.projectsTreeView.DragOver += new System.Windows.Forms.DragEventHandler(this.projectsTreeView_DragEnterOver);
+      this.projectsTreeView.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.projectsTreeView_QueryContinueDrag);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // tabControl
+      // 
+      this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.tabControl.Controls.Add(this.detailsTabPage);
+      this.tabControl.Controls.Add(this.permissionsTabPage);
+      this.tabControl.Controls.Add(this.resourcesTabPage);
+      this.tabControl.Controls.Add(this.jobsTabPage);
+      this.tabControl.Location = new System.Drawing.Point(3, 3);
+      this.tabControl.Name = "tabControl";
+      this.tabControl.SelectedIndex = 0;
+      this.tabControl.Size = new System.Drawing.Size(585, 541);
+      this.tabControl.TabIndex = 0;
+      // 
+      // detailsTabPage
+      // 
+      this.detailsTabPage.Controls.Add(this.projectView);
+      this.detailsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.detailsTabPage.Name = "detailsTabPage";
+      this.detailsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.detailsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.detailsTabPage.TabIndex = 0;
+      this.detailsTabPage.Text = "Details";
+      this.detailsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectView
+      // 
+      this.projectView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.projectView.Caption = "ProjectView";
+      this.projectView.Content = null;
+      this.projectView.Location = new System.Drawing.Point(6, 6);
+      this.projectView.Name = "projectView";
+      this.projectView.ReadOnly = false;
+      this.projectView.Size = new System.Drawing.Size(565, 503);
+      this.projectView.TabIndex = 0;
+      // 
+      // permissionsTabPage
+      // 
+      this.permissionsTabPage.Controls.Add(this.projectPermissionsView);
+      this.permissionsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.permissionsTabPage.Name = "permissionsTabPage";
+      this.permissionsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.permissionsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.permissionsTabPage.TabIndex = 1;
+      this.permissionsTabPage.Text = "Permissions";
+      this.permissionsTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectPermissionsView
+      // 
+      this.projectPermissionsView.Caption = "ProjectView";
+      this.projectPermissionsView.Content = null;
+      this.projectPermissionsView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectPermissionsView.Location = new System.Drawing.Point(3, 3);
+      this.projectPermissionsView.Name = "projectPermissionsView";
+      this.projectPermissionsView.ReadOnly = false;
+      this.projectPermissionsView.Size = new System.Drawing.Size(571, 509);
+      this.projectPermissionsView.TabIndex = 0;
+      // 
+      // resourcesTabPage
+      // 
+      this.resourcesTabPage.Controls.Add(this.projectResourcesView);
+      this.resourcesTabPage.Location = new System.Drawing.Point(4, 22);
+      this.resourcesTabPage.Name = "resourcesTabPage";
+      this.resourcesTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.resourcesTabPage.Size = new System.Drawing.Size(577, 515);
+      this.resourcesTabPage.TabIndex = 2;
+      this.resourcesTabPage.Text = "Resources";
+      this.resourcesTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectResourcesView
+      // 
+      this.projectResourcesView.Caption = "ProjectView";
+      this.projectResourcesView.Content = null;
+      this.projectResourcesView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectResourcesView.Location = new System.Drawing.Point(3, 3);
+      this.projectResourcesView.Name = "projectResourcesView";
+      this.projectResourcesView.ReadOnly = false;
+      this.projectResourcesView.Size = new System.Drawing.Size(571, 509);
+      this.projectResourcesView.TabIndex = 0;
+      //
+      // jobsTabPage
+      //
+      this.jobsTabPage.Controls.Add(this.projectJobsView);
+      this.jobsTabPage.Location = new System.Drawing.Point(4, 22);
+      this.jobsTabPage.Name = "jobsTabPage";
+      this.jobsTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.jobsTabPage.Size = new System.Drawing.Size(577, 515);
+      this.jobsTabPage.TabIndex = 3;
+      this.jobsTabPage.Text = "Jobs";
+      this.jobsTabPage.UseVisualStyleBackColor = true;
+      //
+      // projectJobsView
+      //
+      this.projectJobsView.Caption = "ProjectView";
+      this.projectJobsView.Content = null;
+      this.projectJobsView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectJobsView.Location = new System.Drawing.Point(3, 3);
+      this.projectJobsView.Name = "projectJobsView";
+      this.projectJobsView.ReadOnly = false;
+      this.projectJobsView.Size = new System.Drawing.Size(571, 509);
+      this.projectJobsView.TabIndex = 0;
+      // 
+      // ProjectsView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Name = "ProjectsView";
+      this.Size = new System.Drawing.Size(853, 553);
+      this.Load += new System.EventHandler(this.ProjectsView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.tabControl.ResumeLayout(false);
+      this.detailsTabPage.ResumeLayout(false);
+      this.permissionsTabPage.ResumeLayout(false);
+      this.resourcesTabPage.ResumeLayout(false);
+      this.jobsTabPage.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button saveProjectButton;
+    private System.Windows.Forms.Button removeButton;
+    private System.Windows.Forms.Button addButton;
+    private System.Windows.Forms.TreeView projectsTreeView;
+    private System.Windows.Forms.TabControl tabControl;
+    private System.Windows.Forms.TabPage detailsTabPage;
+    private System.Windows.Forms.TabPage permissionsTabPage;
+    private ProjectView projectView;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.TabPage resourcesTabPage;
+    private System.Windows.Forms.TabPage jobsTabPage;
+    private ProjectResourcesView projectResourcesView;
+    private ProjectPermissionsView projectPermissionsView;
+    private ProjectJobsView projectJobsView;
+    private System.Windows.Forms.ToolTip toolTip;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 17059)
@@ -0,0 +1,570 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 <http://www.gnu.org/licenses/>.
+ */
+#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("Projects View")]
+  [Content(typeof(IItemList<Project>), false)]
+  public partial class ProjectsView : ItemView, IDisposable {
+    private const int greenFlagImageIndex = 0;
+    private const int redFlagImageIndex = 1;
+    private const string SELECTED_TAG = ""; // " [selected]";
+    private const string NOT_STORED_TAG = "*"; // " [not stored]";
+    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
+    private const string INACTIVE_TAG = " [inactive]"; 
+
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+    private Project selectedProject = null;
+    public Project SelectedProject {
+      get { return selectedProject; }
+      set { if (selectedProject != value) ChangeSelectedProject(value); }
+    }
+
+    private readonly object locker = new object();
+
+    public new IItemList<Project> Content {
+      get { return (IItemList<Project>)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectsView() {
+      InitializeComponent();
+
+      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagGreen);
+      projectsTreeView.ImageList.Images.Add(VSImageLibrary.FlagRed);
+
+      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) {
+        projectsTreeView.Nodes.Clear();
+        projectView.Content = null;
+        projectPermissionsView.Content = null;
+        projectResourcesView.Content = null;
+        projectJobsView.Content = null;
+        selectedProject = null;
+      } else {
+        BuildProjectTree(Content);
+      }
+      SetEnabledStateOfControls();
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();      
+
+      bool locked = Content == null || Locked || ReadOnly;      
+      bool parentOwner = selectedProject != null && HiveAdminClient.Instance.CheckOwnershipOfParentProject(selectedProject, UserInformation.Instance.User.Id);
+      bool selectedProjectDisabled = selectedProject == null 
+                                     || selectedProject != null && selectedProject.Id == Guid.Empty;
+
+      bool selectedProjectHasJobs =
+        !selectedProjectDisabled && HiveAdminClient.Instance.Jobs.ContainsKey(selectedProject.Id)
+                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id] != null
+                                 && HiveAdminClient.Instance.Jobs[selectedProject.Id].Any();
+
+      bool addLocked = locked
+                       || (selectedProject == null && !IsAdmin())
+                       || (selectedProject != null && selectedProject.Id == Guid.Empty)
+                       || (selectedProject != null && !IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id)
+                       || (selectedProject != null && (DateTime.Now < selectedProject.StartDate || DateTime.Now > selectedProject.EndDate));
+
+      bool deleteLocked = locked
+                          || !Content.Any()
+                          || selectedProject == null
+                          || Content.Any(x => x.ParentProjectId == selectedProject.Id)
+                          || selectedProjectHasJobs
+                          || (!IsAdmin() && !parentOwner);
+
+      bool saveLocked = locked
+                        || !Content.Any()
+                        || selectedProject == null
+                        || (!IsAdmin() && !parentOwner && selectedProject.OwnerUserId != UserInformation.Instance.User.Id);
+
+
+      addButton.Enabled = !addLocked;
+      removeButton.Enabled = !deleteLocked;
+      saveProjectButton.Enabled = !saveLocked;
+
+      projectView.Enabled = !locked;
+      projectPermissionsView.Enabled = !locked && !selectedProjectDisabled;
+      projectResourcesView.Enabled = !locked && !selectedProjectDisabled;
+      projectJobsView.Enabled = !locked && !selectedProjectDisabled;
+
+      projectView.Locked = locked;
+      projectPermissionsView.Locked = locked || selectedProjectDisabled;
+      projectResourcesView.Locked = locked || selectedProjectDisabled;
+      projectJobsView.Locked = locked || selectedProjectDisabled;
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsAdded, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Project>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Project>>>)Content_ItemsRemoved, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void ProjectViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)ProjectViewContent_PropertyChanged, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private async void ProjectsView_Load(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects());
+    }
+
+    private async void refreshButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateProjects(),
+        finallyCallback: () => {
+          refreshButton.Enabled = true;
+        });
+    }
+
+    private void addButton_Click(object sender, EventArgs e) {      
+
+      if (selectedProject == null && !IsAdmin()) {
+        MessageBox.Show(
+          "You are not allowed to add a root project - please select a parent project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+      
+      if (selectedProject != null && selectedProject.Id == Guid.Empty) {
+        MessageBox.Show(
+          "You cannot add a project to a not yet stored project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+
+      var project = new Project {
+        Name = "New Project",
+        OwnerUserId = UserInformation.Instance.User.Id,        
+      };
+      if(selectedProject != null) {
+        project.ParentProjectId = selectedProject.Id;
+        project.EndDate = selectedProject.EndDate;
+      }
+
+      SelectedProject = project;
+      Content.Add(project);
+    }
+
+    private async void removeButton_Click(object sender, EventArgs e) {
+      if (selectedProject == null) return;
+
+      lock (locker) {
+        // for details go to ChangeSelectedProject(..)
+        if (!removeButton.Enabled) return;
+        removeButton.Enabled = false;
+      }
+
+      var result = MessageBox.Show(
+        "Do you really want to delete " + selectedProject.Name + "?",
+        "HeuristicLab Hive Administrator",
+        MessageBoxButtons.YesNo,
+        MessageBoxIcon.Question);
+
+      if (result == DialogResult.Yes) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => {
+            RemoveProject(selectedProject);  
+          });
+      }
+      SetEnabledStateOfControls();
+    }
+
+    private async void saveProjectButton_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!saveProjectButton.Enabled) return;
+        saveProjectButton.Enabled = false;
+      }
+
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => {
+          //if (selectedProject != null && selectedProject.Id == Guid.Empty)
+          //  SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(selectedProject.Id).LastOrDefault();
+          var projectsToSave = Content.Where(x => x.Id == Guid.Empty || x.Modified);
+          foreach (var project in projectsToSave)
+            project.Store();
+          
+          UpdateProjects();
+        },
+        finallyCallback: () => saveProjectButton.Enabled = true);
+
+      OnContentChanged();
+    }
+
+    private void projectsTreeView_MouseDown(object sender, MouseEventArgs e) {
+      var node = projectsTreeView.GetNodeAt(e.Location);
+      if (node == null) return;
+      var p = (Project)node.Tag;
+      if(!HiveAdminClient.Instance.DisabledParentProjects.Contains(p)) ChangeSelectedProjectNode(node);
+    }
+
+    private void projectsTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      e.Cancel = true;
+    }
+
+    private void projectsTreeView_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect == DragDropEffects.None) return;
+
+      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
+      if (sourceNode == null) return;
+      var sourceProject = ((Project)sourceNode.Tag);
+      if (sourceProject == 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);
+
+      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
+
+      if (!HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
+        MessageBox.Show(
+          "You cannot drag projects to this project.",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+
+      if (sourceNode.Parent == null) {
+        treeView.Nodes.Remove(sourceNode);
+      } else {
+        sourceNode.Parent.Nodes.Remove(sourceNode);
+        sourceProject.ParentProjectId = null;
+      }
+
+      if (targetNode == null) {
+        treeView.Nodes.Add(sourceNode);
+      } else if(targetProject.Id != Guid.Empty) {
+        targetNode.Nodes.Add(sourceNode);
+        sourceProject.ParentProjectId = targetProject.Id;
+      }
+
+      SelectedProject = sourceProject;
+      OnContentChanged();
+    }
+
+    private void projectsTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
+      var sourceNode = (TreeNode)e.Item;
+      if (IsAuthorized((Project)sourceNode.Tag))
+        DoDragDrop(sourceNode, DragDropEffects.All);
+    }
+
+    private void projectsTreeView_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.Move;
+
+      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
+      var sourceProject = ((Project)sourceNode.Tag);
+
+      var targetPoint = projectsTreeView.PointToClient(new Point(e.X, e.Y));
+      var targetNode = projectsTreeView.GetNodeAt(targetPoint);
+      var targetProject = (targetNode != null) ? (Project)targetNode.Tag : null;
+
+      if ((!IsAdmin() && (targetNode == null || targetProject == null))
+      || sourceNode == null
+      || sourceProject == null
+      || sourceNode == targetNode
+      || !HiveAdminClient.Instance.CheckParentChange(sourceProject, targetProject)) {
+        e.Effect = DragDropEffects.None;
+      }
+    }
+
+    private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
+      e.Action = DragAction.Continue;
+    }
+    #endregion
+
+    #region Helpers
+    private void BuildProjectTree(IEnumerable<Project> projects) {
+      projectsTreeView.Nodes.Clear();
+      if (!projects.Any()) return;
+
+      var disabledParentProjects = HiveAdminClient.Instance.DisabledParentProjects;
+      var mainProjects = new HashSet<Project>(projects.Where(x => x.ParentProjectId == null));
+      //var parentedMainProjects = new HashSet<Project>(projects
+      //  .Where(x => x.ParentProjectId.HasValue
+      //  && !projects.Select(y => y.Id).Contains(x.ParentProjectId.Value)));
+      //mainProjects.UnionWith(parentedMainProjects);
+      var mainDisabledParentProjects = new HashSet<Project>(disabledParentProjects.Where(x => x.ParentProjectId == null || x.ParentProjectId == Guid.Empty));
+      mainProjects.UnionWith(mainDisabledParentProjects);
+      var subProbjects = new HashSet<Project>(projects.Union(disabledParentProjects).Except(mainProjects));
+
+      var stack = new Stack<Project>(mainProjects.OrderByDescending(x => x.Name));
+      if (selectedProject != null) SelectedProject = projects.Where(x => x.Id == selectedProject.Id).FirstOrDefault();
+      bool nodeSelected = false;
+
+      TreeNode currentNode = null;
+      Project currentProject = null;
+
+      while (stack.Any()) {
+        var newProject = stack.Pop();
+        var newNode = new TreeNode(newProject.Name) { Tag = newProject };
+        StyleTreeNode(newNode, newProject);
+      
+        if (selectedProject == null && !disabledParentProjects.Contains(newProject)) {
+          SelectedProject = newProject;
+        }
+        if (!nodeSelected && selectedProject != null && selectedProject.Id == newProject.Id) {
+          newNode.BackColor = selectedBackColor;
+          newNode.ForeColor = selectedForeColor;
+          newNode.Text += SELECTED_TAG;
+          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 && newProject.ParentProjectId != currentProject.Id) {
+          currentNode = currentNode.Parent;
+          currentProject = currentNode == null ? null : (Project)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          projectsTreeView.Nodes.Add(newNode);
+          newNode.ImageIndex = greenFlagImageIndex;
+        } else {
+          currentNode.Nodes.Add(newNode);
+          newNode.ImageIndex = redFlagImageIndex;
+        }
+
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+
+        if (disabledParentProjects.Contains(newProject)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        }
+
+        var childProjects = subProbjects.Where(x => x.ParentProjectId == newProject.Id);
+        if (childProjects.Any()) {
+          foreach (var project in childProjects.OrderByDescending(x => x.Name)) {
+            subProbjects.Remove(project);
+            stack.Push(project);
+          }
+          currentNode = newNode;
+          currentProject = newProject;
+        }
+      }
+
+      projectsTreeView.ExpandAll();
+    }
+
+    private void StyleTreeNode(TreeNode n, Project p) {
+      n.Text = p.Name;
+      n.BackColor = Color.Transparent;
+      n.ForeColor = Color.Black;
+
+      if (HiveAdminClient.Instance.DisabledParentProjects.Select(x => x.Id).Contains(p.Id)) {
+        n.ForeColor = grayTextColor;
+      } else {
+        if (p.Id == Guid.Empty) {
+          n.Text += NOT_STORED_TAG;
+        } else if (p.Modified) {
+          n.Text += CHANGES_NOT_STORED_TAG;
+        }
+
+        if (!IsActive(p)) {
+          n.ForeColor = grayTextColor;
+          n.Text += INACTIVE_TAG;
+        }
+      }
+    }
+
+    private void ResetTreeNodes(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        StyleTreeNode(n, (Project)n.Tag);
+        if (n.Nodes.Count > 0) {
+          ResetTreeNodes(n.Nodes);
+        }
+      }
+    }
+
+    private void ChangeSelectedProject(Project project) {
+      projectView.Content = project;
+      projectPermissionsView.Content = project;
+      projectResourcesView.Content = project;
+      projectJobsView.Content = project;
+      selectedProject = project;
+      SetEnabledStateOfControls();
+    }
+
+    private void ChangeSelectedProjectNode(TreeNode projectNode) {
+      if (projectNode == null) return;
+      SelectedProject = (Project)projectNode.Tag;
+      ResetTreeNodes(projectsTreeView.Nodes);
+      projectNode.BackColor = selectedBackColor;
+      projectNode.ForeColor = selectedForeColor;
+      projectNode.Text += SELECTED_TAG;
+    }
+
+    private void UpdateProjects() {
+      try {
+        HiveAdminClient.Instance.Refresh();
+        Content = HiveAdminClient.Instance.Projects;
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveProject(Project project) {
+      if (project == null) return;
+
+      try {
+        if (project.Id != Guid.Empty) {
+          SelectedProject = HiveAdminClient.Instance.GetAvailableProjectAncestors(project.Id).LastOrDefault();
+          HiveAdminClient.Delete(project);          
+          UpdateProjects();
+        } else {          
+          SelectedProject = Content.FirstOrDefault(x => x.Id == project.ParentProjectId);
+          Content.Remove(project);
+        }
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsActive(Project project) {
+      return DateTime.Now >= project.StartDate
+             && (project.EndDate == null
+                 || DateTime.Now < project.EndDate.Value);
+    }
+
+    private bool IsAuthorized(Project project) {
+      return project != null && UserInformation.Instance.UserExists;
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    #endregion
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.Designer.cs	(revision 17059)
@@ -48,8 +48,6 @@
       this.chbade = new System.Windows.Forms.CheckBox();
       this.dtpEnd = new System.Windows.Forms.DateTimePicker();
-      this.dtpToTime = new System.Windows.Forms.DateTimePicker();
       this.label5 = new System.Windows.Forms.Label();
       this.dtpStart = new System.Windows.Forms.DateTimePicker();
-      this.dtpFromTime = new System.Windows.Forms.DateTimePicker();
       this.label6 = new System.Windows.Forms.Label();
       this.btSaveRecurrence = new System.Windows.Forms.Button();
@@ -74,8 +72,6 @@
       this.gbAppointment.Controls.Add(this.chbade);
       this.gbAppointment.Controls.Add(this.dtpEnd);
-      this.gbAppointment.Controls.Add(this.dtpToTime);
       this.gbAppointment.Controls.Add(this.label5);
       this.gbAppointment.Controls.Add(this.dtpStart);
-      this.gbAppointment.Controls.Add(this.dtpFromTime);
       this.gbAppointment.Controls.Add(this.label6);
       this.gbAppointment.Location = new System.Drawing.Point(2, 2);
@@ -98,19 +94,10 @@
       // dtpEnd
       // 
-      this.dtpEnd.Format = System.Windows.Forms.DateTimePickerFormat.Short;
+      this.dtpEnd.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpEnd.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpEnd.Location = new System.Drawing.Point(113, 56);
       this.dtpEnd.Name = "dtpEnd";
-      this.dtpEnd.Size = new System.Drawing.Size(91, 20);
+      this.dtpEnd.Size = new System.Drawing.Size(175, 20);
       this.dtpEnd.TabIndex = 29;
-      // 
-      // dtpToTime
-      // 
-      this.dtpToTime.CustomFormat = "HH:mm";
-      this.dtpToTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.dtpToTime.Location = new System.Drawing.Point(226, 56);
-      this.dtpToTime.Name = "dtpToTime";
-      this.dtpToTime.ShowUpDown = true;
-      this.dtpToTime.Size = new System.Drawing.Size(73, 20);
-      this.dtpToTime.TabIndex = 38;
       // 
       // label5
@@ -126,18 +113,9 @@
       // 
       this.dtpStart.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpStart.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpStart.Location = new System.Drawing.Point(113, 19);
       this.dtpStart.Name = "dtpStart";
-      this.dtpStart.Size = new System.Drawing.Size(89, 20);
+      this.dtpStart.Size = new System.Drawing.Size(175, 20);
       this.dtpStart.TabIndex = 28;
-      // 
-      // dtpFromTime
-      // 
-      this.dtpFromTime.CustomFormat = "HH:mm";
-      this.dtpFromTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.dtpFromTime.Location = new System.Drawing.Point(226, 19);
-      this.dtpFromTime.Name = "dtpFromTime";
-      this.dtpFromTime.ShowUpDown = true;
-      this.dtpFromTime.Size = new System.Drawing.Size(73, 20);
-      this.dtpFromTime.TabIndex = 30;
       // 
       // label6
@@ -307,6 +285,4 @@
     private System.Windows.Forms.Button btSaveRecurrence;
     private System.Windows.Forms.Button btCancelRecurrence;
-    private System.Windows.Forms.DateTimePicker dtpFromTime;
-    private System.Windows.Forms.DateTimePicker dtpToTime;
     private System.Windows.Forms.CheckBox chbade;
     private System.Windows.Forms.GroupBox gbWeekly;
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/Recurrence.cs	(revision 17059)
@@ -46,6 +46,6 @@
       //check if valid
       if (InputIsValid()) {
-        dateFrom = DateTime.Parse(dtpStart.Text + " " + dtpFromTime.Text);
-        dateTo = DateTime.Parse(dtpEnd.Text + " " + dtpToTime.Text);
+        dateFrom = DateTime.Parse(dtpStart.Text);
+        dateTo = DateTime.Parse(dtpEnd.Text);
 
         RecurrentEvent recurrentEvent = new RecurrentEvent() {
@@ -89,6 +89,6 @@
       DateTime dateFrom, dateTo;
 
-      dateFrom = DateTime.Parse(dtpStart.Text + " " + dtpFromTime.Text);
-      dateTo = DateTime.Parse(dtpEnd.Text + " " + dtpToTime.Text);
+      dateFrom = DateTime.Parse(dtpStart.Text);
+      dateTo = DateTime.Parse(dtpEnd.Text);
 
       if (chbade.Checked && dateFrom < dateTo) {
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 17059)
@@ -0,0 +1,171 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ResourceView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.idLabel = new System.Windows.Forms.Label();
+      this.idTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
+      this.heartbeatIntervalLabel = new System.Windows.Forms.Label();
+      this.heartbeatIntervalNumericUpDown = new Hive.Views.Extensions.FixedNumericUpDown();
+      this.publicLabel = new System.Windows.Forms.Label();
+      this.publicCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // idLabel
+      // 
+      this.idLabel.AutoSize = true;
+      this.idLabel.Location = new System.Drawing.Point(3, 11);
+      this.idLabel.Name = "idLabel";
+      this.idLabel.Size = new System.Drawing.Size(19, 13);
+      this.idLabel.TabIndex = 0;
+      this.idLabel.Text = "Id:";
+      // 
+      // idTextBox
+      // 
+      this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.idTextBox.Location = new System.Drawing.Point(130, 8);
+      this.idTextBox.Name = "idTextBox";
+      this.idTextBox.ReadOnly = true;
+      this.idTextBox.Size = new System.Drawing.Size(397, 20);
+      this.idTextBox.TabIndex = 1;
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 37);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 2;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(130, 34);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(397, 20);
+      this.nameTextBox.TabIndex = 3;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 63);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 4;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(130, 60);
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(397, 20);
+      this.descriptionTextBox.TabIndex = 5;
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
+      // 
+      // heartbeatIntervalLabel
+      // 
+      this.heartbeatIntervalLabel.AutoSize = true;
+      this.heartbeatIntervalLabel.Location = new System.Drawing.Point(3, 89);
+      this.heartbeatIntervalLabel.Name = "heartbeatIntervalLabel";
+      this.heartbeatIntervalLabel.Size = new System.Drawing.Size(121, 13);
+      this.heartbeatIntervalLabel.TabIndex = 8;
+      this.heartbeatIntervalLabel.Text = "Heartbeat Interval [sec]:";
+      // 
+      // heartbeatIntervalNumericUpDown
+      // 
+      this.heartbeatIntervalNumericUpDown.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.heartbeatIntervalNumericUpDown.Location = new System.Drawing.Point(130, 86);
+      this.heartbeatIntervalNumericUpDown.Name = "heartbeatIntervalNumericUpDown";
+      this.heartbeatIntervalNumericUpDown.Size = new System.Drawing.Size(397, 20);
+      this.heartbeatIntervalNumericUpDown.TabIndex = 9;
+      this.heartbeatIntervalNumericUpDown.Minimum = 0;
+      this.heartbeatIntervalNumericUpDown.Maximum = 120;
+      this.heartbeatIntervalNumericUpDown.ValueChanged += new System.EventHandler(this.heartbeatIntervalNumericUpDown_ValueChanged);
+      // 
+      // publicLabel
+      // 
+      this.publicLabel.AutoSize = true;
+      this.publicLabel.Location = new System.Drawing.Point(3, 115);
+      this.publicLabel.Name = "publicLabel";
+      this.publicLabel.Size = new System.Drawing.Size(39, 13);
+      this.publicLabel.TabIndex = 12;
+      this.publicLabel.Text = "Public:";
+      // 
+      // publicCheckBox
+      // 
+      this.publicCheckBox.AutoSize = true;
+      this.publicCheckBox.Location = new System.Drawing.Point(130, 114);
+      this.publicCheckBox.Name = "publicCheckBox";
+      this.publicCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.publicCheckBox.TabIndex = 13;
+      this.publicCheckBox.UseVisualStyleBackColor = true;
+      this.publicCheckBox.CheckedChanged += new System.EventHandler(this.publicCheckBox_CheckedChanged);
+      // 
+      // SlaveGroupView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.publicCheckBox);
+      this.Controls.Add(this.publicLabel);
+      this.Controls.Add(this.heartbeatIntervalNumericUpDown);
+      this.Controls.Add(this.heartbeatIntervalLabel);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.idTextBox);
+      this.Controls.Add(this.idLabel);
+      this.Name = "SlaveGroupView";
+      this.Size = new System.Drawing.Size(530, 350);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label idLabel;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.Label heartbeatIntervalLabel;
+    private System.Windows.Forms.Label publicLabel;
+    protected System.Windows.Forms.TextBox nameTextBox;
+    protected System.Windows.Forms.TextBox idTextBox;
+    protected System.Windows.Forms.TextBox descriptionTextBox;
+    protected HeuristicLab.Clients.Hive.Views.Extensions.FixedNumericUpDown heartbeatIntervalNumericUpDown;
+    protected System.Windows.Forms.CheckBox publicCheckBox;
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 17059)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 17059)
@@ -0,0 +1,92 @@
+﻿#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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("Resource View")]
+  [Content(typeof(Resource), IsDefaultView = true)]
+  public partial class ResourceView : ItemView {
+    public new Resource Content {
+      get { return (Resource)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ResourceView() {
+      InitializeComponent();
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        idTextBox.Clear();
+        nameTextBox.Clear();
+        descriptionTextBox.Clear();
+        heartbeatIntervalNumericUpDown.Value = 0;
+        publicCheckBox.Checked = false;
+      } else {
+        idTextBox.Text = Content.Id.ToString();
+        nameTextBox.Text = Content.Name;
+        descriptionTextBox.Text = Content.Description;
+        heartbeatIntervalNumericUpDown.Value = Content.HbInterval;
+        publicCheckBox.Checked = Content.OwnerUserId == null;
+      }
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      idTextBox.ReadOnly = true;
+      nameTextBox.ReadOnly = !enabled;
+      descriptionTextBox.ReadOnly = !enabled;
+      heartbeatIntervalNumericUpDown.ReadOnly = !enabled;
+      publicCheckBox.Enabled = enabled;
+    }
+    #endregion
+
+    #region Event Handlers
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text)
+        Content.Name = nameTextBox.Text;
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text)
+        Content.Description = descriptionTextBox.Text;
+    }
+
+    private void heartbeatIntervalNumericUpDown_ValueChanged(object sender, EventArgs e) {
+      if (Content != null && Content.HbInterval != (int)heartbeatIntervalNumericUpDown.Value)
+        Content.HbInterval = (int)heartbeatIntervalNumericUpDown.Value;
+    }
+
+    private void publicCheckBox_CheckedChanged(object sender, EventArgs e) {
+      var newOwnerUserId = publicCheckBox.Checked ? null : (Guid?)UserInformation.Instance.User.Id;
+      if (Content != null && Content.OwnerUserId != newOwnerUserId)
+        Content.OwnerUserId = newOwnerUserId;
+    }
+    #endregion
+  }
+}
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 17059)
@@ -37,9 +37,4 @@
         components.Dispose();
       }
-      HiveAdminClient.Instance.Refreshing -= new EventHandler(Instance_Refreshing);
-      HiveAdminClient.Instance.Refreshed -= new EventHandler(Instance_Refreshed);
-
-      Access.AccessClient.Instance.Refreshing -= new EventHandler(AccessClient_Refreshing);
-      Access.AccessClient.Instance.Refreshed -= new EventHandler(AccessClient_Refreshed);
       base.Dispose(disposing);
     }
@@ -56,18 +51,14 @@
       this.splitSlaves = new System.Windows.Forms.SplitContainer();
       this.btnRefresh = new System.Windows.Forms.Button();
-      this.progressBar = new System.Windows.Forms.ProgressBar();
       this.btnSave = new System.Windows.Forms.Button();
       this.btnRemoveGroup = new System.Windows.Forms.Button();
       this.btnAddGroup = new System.Windows.Forms.Button();
-      this.btnPermissionsSave = new System.Windows.Forms.Button();
-      this.treeSlaveGroup = new System.Windows.Forms.TreeView();
+      this.treeView = new Hive.Views.TreeView.NoDoubleClickTreeView();
       this.tabSlaveGroup = new System.Windows.Forms.TabControl();
       this.tabDetails = new System.Windows.Forms.TabPage();
-      this.slaveView = new HeuristicLab.Clients.Hive.Administrator.Views.SlaveView();
+      this.viewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
       this.tabSchedule = new System.Windows.Forms.TabPage();
       this.scheduleView = new HeuristicLab.Clients.Hive.Administrator.Views.ScheduleView();
       this.toolTip = new System.Windows.Forms.ToolTip(this.components);
-      this.tabPermissions = new System.Windows.Forms.TabPage();
-      this.permissionView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
       ((System.ComponentModel.ISupportInitialize)(this.splitSlaves)).BeginInit();
       this.splitSlaves.Panel1.SuspendLayout();
@@ -77,5 +68,4 @@
       this.tabDetails.SuspendLayout();
       this.tabSchedule.SuspendLayout();
-      this.tabPermissions.SuspendLayout();
       this.SuspendLayout();
       // 
@@ -88,6 +78,6 @@
       // splitSlaves
       // 
-      this.splitSlaves.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.splitSlaves.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.splitSlaves.Location = new System.Drawing.Point(3, 3);
@@ -97,9 +87,8 @@
       // 
       this.splitSlaves.Panel1.Controls.Add(this.btnRefresh);
-      this.splitSlaves.Panel1.Controls.Add(this.progressBar);
       this.splitSlaves.Panel1.Controls.Add(this.btnSave);
       this.splitSlaves.Panel1.Controls.Add(this.btnRemoveGroup);
       this.splitSlaves.Panel1.Controls.Add(this.btnAddGroup);
-      this.splitSlaves.Panel1.Controls.Add(this.treeSlaveGroup);
+      this.splitSlaves.Panel1.Controls.Add(this.treeView);
       // 
       // splitSlaves.Panel2
@@ -117,24 +106,16 @@
       this.btnRefresh.Size = new System.Drawing.Size(24, 24);
       this.btnRefresh.TabIndex = 8;
-      this.toolTip.SetToolTip(this.btnRefresh, "Fetch list from server");
+      this.toolTip.SetToolTip(this.btnRefresh, "Fetch list from server.");
       this.btnRefresh.UseVisualStyleBackColor = true;
       this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
       // 
-      // progressBar
-      // 
-      this.progressBar.Location = new System.Drawing.Point(123, 4);
-      this.progressBar.Name = "progressBar";
-      this.progressBar.Size = new System.Drawing.Size(123, 23);
-      this.progressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
-      this.progressBar.TabIndex = 7;
-      // 
       // btnSave
       // 
-      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.btnSave.Location = new System.Drawing.Point(93, 3);
       this.btnSave.Name = "btnSave";
       this.btnSave.Size = new System.Drawing.Size(24, 24);
       this.btnSave.TabIndex = 5;
-      this.toolTip.SetToolTip(this.btnSave, "Store slave and group configuration on the server");
+      this.toolTip.SetToolTip(this.btnSave, "Store slave and group configuration on the server.");
       this.btnSave.UseVisualStyleBackColor = true;
       this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
@@ -147,5 +128,5 @@
       this.btnRemoveGroup.Size = new System.Drawing.Size(24, 24);
       this.btnRemoveGroup.TabIndex = 2;
-      this.toolTip.SetToolTip(this.btnRemoveGroup, "Delete a slave or a group");
+      this.toolTip.SetToolTip(this.btnRemoveGroup, "Remove a slave or a group.");
       this.btnRemoveGroup.UseVisualStyleBackColor = true;
       this.btnRemoveGroup.Click += new System.EventHandler(this.btnRemoveGroup_Click);
@@ -158,36 +139,38 @@
       this.btnAddGroup.Size = new System.Drawing.Size(24, 24);
       this.btnAddGroup.TabIndex = 1;
-      this.toolTip.SetToolTip(this.btnAddGroup, "Add a new group");
+      this.toolTip.SetToolTip(this.btnAddGroup, "Add a new group.");
       this.btnAddGroup.UseVisualStyleBackColor = true;
       this.btnAddGroup.Click += new System.EventHandler(this.btnAddGroup_Click);
       // 
-      // treeSlaveGroup
-      // 
-      this.treeSlaveGroup.AllowDrop = true;
-      this.treeSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.treeSlaveGroup.ImageIndex = 0;
-      this.treeSlaveGroup.ImageList = this.imageListSlaveGroups;
-      this.treeSlaveGroup.Location = new System.Drawing.Point(3, 33);
-      this.treeSlaveGroup.Name = "treeSlaveGroup";
-      this.treeSlaveGroup.SelectedImageIndex = 0;
-      this.treeSlaveGroup.Size = new System.Drawing.Size(243, 511);
-      this.treeSlaveGroup.TabIndex = 0;
-      this.treeSlaveGroup.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
-      this.treeSlaveGroup.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterSelect);
-      this.treeSlaveGroup.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
-      this.treeSlaveGroup.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnter);
-      this.treeSlaveGroup.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragOver);
-      this.treeSlaveGroup.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.treeSlaveGroup_QueryContinueDrag);
+      // treeView
+      // 
+      this.treeView.AllowDrop = true;
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageListSlaveGroups;
+      this.treeView.Location = new System.Drawing.Point(3, 33);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(243, 511);
+      this.treeView.TabIndex = 0;
+      this.treeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
+      this.treeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeSlaveGroup_MouseDown);
+      this.treeView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeSlaveGroup_BeforeSelect);
+      this.treeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
+      this.treeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
+      this.treeView.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeSlaveGroup_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterCheck);
       // 
       // tabSlaveGroup
       // 
-      this.tabSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.tabSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.tabSlaveGroup.Controls.Add(this.tabDetails);
       this.tabSlaveGroup.Controls.Add(this.tabSchedule);
-      this.tabSlaveGroup.Controls.Add(this.tabPermissions);
       this.tabSlaveGroup.Location = new System.Drawing.Point(3, 3);
       this.tabSlaveGroup.Name = "tabSlaveGroup";
@@ -195,9 +178,10 @@
       this.tabSlaveGroup.Size = new System.Drawing.Size(585, 541);
       this.tabSlaveGroup.TabIndex = 1;
-      this.tabSlaveGroup.SelectedIndexChanged += new System.EventHandler(this.tabSlaveGroup_SelectedIndexChanged);
+      //this.tabSlaveGroup.TabIndexChanged += TabSlaveGroup_TabIndexChanged;
+      this.tabSlaveGroup.Selected += TabSlaveGroup_Selected;
       // 
       // tabDetails
       // 
-      this.tabDetails.Controls.Add(this.slaveView);
+      this.tabDetails.Controls.Add(this.viewHost);
       this.tabDetails.Location = new System.Drawing.Point(4, 22);
       this.tabDetails.Name = "tabDetails";
@@ -208,16 +192,19 @@
       this.tabDetails.UseVisualStyleBackColor = true;
       // 
-      // slaveView
-      // 
-      this.slaveView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.slaveView.Caption = "SlaveView";
-      this.slaveView.Content = null;
-      this.slaveView.Location = new System.Drawing.Point(6, 6);
-      this.slaveView.Name = "slaveView";
-      this.slaveView.ReadOnly = false;
-      this.slaveView.Size = new System.Drawing.Size(565, 503);
-      this.slaveView.TabIndex = 0;
+      // viewHost
+      // 
+      this.viewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.viewHost.Caption = "View";
+      this.viewHost.Content = null;
+      this.viewHost.Enabled = true;
+      this.viewHost.Location = new System.Drawing.Point(6, 6);
+      this.viewHost.Name = "viewHost";
+      this.viewHost.ReadOnly = false;
+      this.viewHost.Size = new System.Drawing.Size(565, 503);
+      this.viewHost.TabIndex = 0;
+      this.viewHost.ViewsLabelVisible = true;
+      this.viewHost.ViewType = null;
       // 
       // tabSchedule
@@ -234,6 +221,6 @@
       // scheduleView
       // 
-      this.scheduleView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      this.scheduleView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.scheduleView.Caption = "ScheduleView";
@@ -244,42 +231,4 @@
       this.scheduleView.Size = new System.Drawing.Size(565, 503);
       this.scheduleView.TabIndex = 0;
-      // 
-      // tabPermissions
-      // 
-      this.tabPermissions.Controls.Add(this.btnPermissionsSave);
-      this.tabPermissions.Controls.Add(this.permissionView);
-      this.tabPermissions.Location = new System.Drawing.Point(4, 22);
-      this.tabPermissions.Name = "tabPermissions";
-      this.tabPermissions.Padding = new System.Windows.Forms.Padding(3);
-      this.tabPermissions.Size = new System.Drawing.Size(577, 515);
-      this.tabPermissions.TabIndex = 2;
-      this.tabPermissions.Text = "Permissions";
-      this.tabPermissions.UseVisualStyleBackColor = true;
-      // 
-      // btnPermissionsSave
-      // 
-      this.btnPermissionsSave.Enabled = false;
-      this.btnPermissionsSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
-      this.btnPermissionsSave.Location = new System.Drawing.Point(39, 9);
-      this.btnPermissionsSave.Name = "btnPermissionsSave";
-      this.btnPermissionsSave.Size = new System.Drawing.Size(24, 24);
-      this.btnPermissionsSave.TabIndex = 1;
-      this.toolTip.SetToolTip(this.btnPermissionsSave, "Store slave and group sharing permissions on the server");
-      this.btnPermissionsSave.UseVisualStyleBackColor = true;
-      this.btnPermissionsSave.Click += new System.EventHandler(this.btnPermissionsSave_Click);
-      // 
-      // permissionView
-      // 
-      this.permissionView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.permissionView.Caption = "RefreshableLightweightUser View";
-      this.permissionView.Content = null;
-      this.permissionView.FetchSelectedUsers = null;
-      this.permissionView.Location = new System.Drawing.Point(6, 6);
-      this.permissionView.Name = "permissionView";
-      this.permissionView.ReadOnly = false;
-      this.permissionView.Size = new System.Drawing.Size(565, 503);
-      this.permissionView.TabIndex = 0;
       // 
       // ResourcesView
@@ -297,9 +246,7 @@
       this.tabDetails.ResumeLayout(false);
       this.tabSchedule.ResumeLayout(false);
-      this.tabPermissions.ResumeLayout(false);
       this.ResumeLayout(false);
 
     }
-
     #endregion
 
@@ -307,18 +254,14 @@
     private System.Windows.Forms.Button btnRemoveGroup;
     private System.Windows.Forms.Button btnAddGroup;
-    private System.Windows.Forms.TreeView treeSlaveGroup;
+    private HeuristicLab.Clients.Hive.Views.TreeView.NoDoubleClickTreeView treeView;
     private System.Windows.Forms.TabControl tabSlaveGroup;
     private System.Windows.Forms.TabPage tabDetails;
     private System.Windows.Forms.TabPage tabSchedule;
-    private SlaveView slaveView;
     private System.Windows.Forms.ImageList imageListSlaveGroups;
     private ScheduleView scheduleView;
     private System.Windows.Forms.Button btnSave;
-    private System.Windows.Forms.ProgressBar progressBar;
     private System.Windows.Forms.Button btnRefresh;
     private System.Windows.Forms.ToolTip toolTip;
-    private System.Windows.Forms.TabPage tabPermissions;
-    private Access.Views.RefreshableLightweightUserView permissionView;
-    private System.Windows.Forms.Button btnPermissionsSave;
+    private MainForm.WindowsForms.ViewHost viewHost;
   }
 }
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 17059)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -22,16 +22,15 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
 using System.Linq;
-using System.ServiceModel.Security;
-using System.Threading;
-using System.Threading.Tasks;
 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;
-using TS = System.Threading.Tasks;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
@@ -39,4 +38,31 @@
   [Content(typeof(IItemList<Resource>), false)]
   public partial class ResourcesView : ItemView, IDisposable {
+    private const int slaveImageIndex = 0;
+    private const int slaveGroupImageIndex = 1;
+    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
+    public const string UNGROUPED_GROUP_DESCRIPTION = "Contains slaves that are not assigned to any group.";
+    private const string SELECTED_TAG = ""; // " [selected]";
+    private const string NOT_STORED_TAG = "*"; // " [not stored]";
+    private const string CHANGES_NOT_STORED_TAG = "*"; // " [changes not stored]";
+
+    private readonly Color changedColor = Color.FromArgb(255, 87, 191, 193); // #57bfc1
+    private readonly Color selectedBackColor = Color.DodgerBlue;
+    private readonly Color selectedForeColor = Color.White;
+    private readonly Color calculatingColor = Color.FromArgb(255, 58, 114, 35); // #3a7223
+    private readonly Color offlineColor = Color.FromArgb(255, 187, 36, 36); // #bb2424
+    private readonly Color grayTextColor = SystemColors.GrayText;
+
+
+
+    private TreeNode ungroupedGroupNode;
+
+    private Resource selectedResource = null;
+    public Resource SelectedResource {
+      get { return selectedResource; }
+      set { if (selectedResource != value) ChangeSelectedResource(value); }
+    }
+
+    private readonly object locker = new object();
+
     public new IItemList<Resource> Content {
       get { return (IItemList<Resource>)base.Content; }
@@ -44,223 +70,475 @@
     }
 
-    public const string UngroupedGroupName = "UNGROUPED";
-    private const int slaveImageIndex = 0;
-    private const int slaveGroupImageIndex = 1;
-    private readonly Color ownedResourceColor = Color.LightGreen;
-    private TS.Task progressTask;
-    private bool stopProgressTask;
-    private bool currentlyAuthorized;
-
-
     public ResourcesView() {
       InitializeComponent();
-      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
-      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
-
-      HiveAdminClient.Instance.Refreshing += new EventHandler(Instance_Refreshing);
-      HiveAdminClient.Instance.Refreshed += new EventHandler(Instance_Refreshed);
-
-      Access.AccessClient.Instance.Refreshing += new EventHandler(AccessClient_Refreshing);
-      Access.AccessClient.Instance.Refreshed += new EventHandler(AccessClient_Refreshed);
-    }
-
-    private void UpdateProgress() {
-      while (!stopProgressTask) {
-        int diff = (progressBar.Maximum - progressBar.Minimum) / 10;
-
-        if (progressBar.InvokeRequired) {
-          progressBar.Invoke(new Action(delegate() { progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum; }));
-        } else {
-          progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum;
-        }
-
-        //ok, this is not very clever...
-        Thread.Sleep(500);
-      }
-      if (progressBar.InvokeRequired) {
-        progressBar.Invoke(new Action(delegate() { progressBar.Value = progressBar.Minimum; }));
-      } else {
-        progressBar.Value = progressBar.Minimum;
-      }
-    }
-
-    void Instance_Refreshing(object sender, EventArgs e) {
-      stopProgressTask = false;
-      progressTask = new TS.Task(UpdateProgress);
-      progressTask.Start();
-      SetEnabledStateOfControls();
-    }
-
-    void Instance_Refreshed(object sender, EventArgs e) {
-      stopProgressTask = true;
-      SetEnabledStateOfControls();
-    }
-
-    void AccessClient_Refreshing(object sender, EventArgs e) {
-      stopProgressTask = false;
-      progressTask = new TS.Task(UpdateProgress);
-      progressTask.Start();
-      SetEnabledStateOfControls();
-      btnPermissionsSave.Enabled = false;
-    }
-
-    void AccessClient_Refreshed(object sender, EventArgs e) {
-      stopProgressTask = true;
-      SetEnabledStateOfControls();
-    }
-
-    #region Register Content Events
-    protected override void DeregisterContentEvents() {
-      Content.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
-      Content.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
-      base.DeregisterContentEvents();
-    }
+
+      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 += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
-      Content.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
-    }
-    #endregion
+      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) {
-        slaveView.Content = null;
+        treeView.Nodes.Clear();
+        viewHost.Content = null;
         scheduleView.Content = null;
-        permissionView.Content = null;
-        permissionView.FetchSelectedUsers = null;
-        treeSlaveGroup.Nodes.Clear();
       } else {
-        permissionView.Content = Access.AccessClient.Instance;
-        treeSlaveGroup.Nodes.Clear();
-
-        //rebuild
-        TreeNode ungrp = new TreeNode(UngroupedGroupName);
-        ungrp.ImageIndex = slaveGroupImageIndex;
-        ungrp.SelectedImageIndex = ungrp.ImageIndex;
-        var newGroup = new SlaveGroup();
-        newGroup.Name = UngroupedGroupName;
-        newGroup.Id = Guid.NewGuid();
-        newGroup.Description = "Contains slaves which are in no group";
-        ungrp.Tag = newGroup;
-
-        foreach (Resource g in Content) {
-          if (g.GetType() == typeof(SlaveGroup)) {
-            //root node
-            if (g.ParentResourceId == null) {
-              TreeNode tn = new TreeNode();
-              tn.ImageIndex = slaveGroupImageIndex;
-              tn.SelectedImageIndex = tn.ImageIndex;
-
-              tn.Tag = g;
-              tn.Text = g.Name;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) tn.BackColor = ownedResourceColor;
-
-              BuildSlaveGroupTree(g, tn);
-              treeSlaveGroup.Nodes.Add(tn);
-            }
-          } else if (g.GetType() == typeof(Slave)) {
-            if (g.ParentResourceId == null) {
-              var stn = new TreeNode(g.Name);
-              stn.ImageIndex = slaveImageIndex;
-              stn.SelectedImageIndex = stn.ImageIndex;
-              stn.Tag = g;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-              ungrp.Nodes.Add(stn);
-            }
-          }
-        }
-        treeSlaveGroup.Nodes.Add(ungrp);
-      }
-    }
-
-    private void BuildSlaveGroupTree(Resource g, TreeNode tn) {
-      foreach (Resource r in Content.Where(s => s.ParentResourceId != null && s.ParentResourceId == g.Id)) {
-        TreeNode stn = new TreeNode(r.Name);
-        if (r is Slave) {
-          stn.ImageIndex = slaveImageIndex;
-        } else if (r is SlaveGroup) {
-          stn.ImageIndex = slaveGroupImageIndex;
-        }
-        stn.SelectedImageIndex = stn.ImageIndex;
-        stn.Tag = r;
-        if (r.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-        tn.Nodes.Add(stn);
-
-        BuildSlaveGroupTree(r, stn);
-      }
+        BuildResourceTree(Content);
+      }
+      SetEnabledStateOfControls();
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      if (Content == null) {
-        btnAddGroup.Enabled = false;
+
+      bool locked = Content == null || Locked || ReadOnly;
+      bool addLocked = locked
+                    || !IsAdmin()
+                    || (selectedResource is Slave && selectedResource.ParentResourceId != null)
+                    || (selectedResource != null && selectedResource.Id == Guid.Empty);
+
+      HashSet<Guid> descendantResources = null;
+      bool selectedRDeleteLocked = selectedResource == null
+                              || (selectedResource.Id != Guid.Empty && (!HiveAdminClient.Instance.ResourceDescendants.TryGetValue(selectedResource.Id, out descendantResources) || descendantResources.Any()));
+
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      var checkedResources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+      bool checkedRDeleteLocked = false;
+      for (int i = 0; !checkedRDeleteLocked && i < checkedResources.Count; i++) {
+        if (checkedResources[i].Id != Guid.Empty &&
+            (!HiveAdminClient.Instance.ResourceDescendants.TryGetValue(checkedResources[i].Id, out descendantResources) ||
+             descendantResources.Any()))
+          checkedRDeleteLocked = true;
+      }
+
+      bool deleteLocked = locked
+                          || !IsAdmin()
+                          || !Content.Any()
+                          || checkedResources.Any() && checkedRDeleteLocked
+                          || !checkedResources.Any() && selectedRDeleteLocked;
+
+      bool saveLocked = locked
+                       || !IsAdmin()
+                       || !Content.Any()
+                       || selectedResource == null;
+
+      btnAddGroup.Enabled = !addLocked;
+      btnRemoveGroup.Enabled = !deleteLocked;
+      btnSave.Enabled = !saveLocked;
+      viewHost.Locked = locked || !IsAdmin();
+      scheduleView.Locked = locked || !IsAdmin();
+    }
+    #endregion
+
+    #region Event Handlers
+    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsAdded, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Resource>> e) {
+      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<IndexedItem<Resource>>>)Content_ItemsRemoved, sender, e);
+      else {
+        OnContentChanged();
+      }
+    }
+
+    private void SlaveViewContent_PropertyChanged(object sender, PropertyChangedEventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, PropertyChangedEventArgs>)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<object, EventArgs>)HiveAdminClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void HiveAdminClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)HiveAdminClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.RemoveOperationProgressFromView(this);
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshing(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshing, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        mainForm.AddOperationProgressToView(this, "Refreshing ...");
+        SetEnabledStateOfControls();
+      }
+    }
+
+    private void AccessClient_Instance_Refreshed(object sender, EventArgs e) {
+      if (InvokeRequired) Invoke((Action<object, EventArgs>)AccessClient_Instance_Refreshed, sender, e);
+      else {
+        var mainForm = MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>();
+        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 parentResourceId = selectedResource is SlaveGroup ? selectedResource.Id : (Guid?)null;
+
+      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) {      
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      var checkedResources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+      if (selectedResource == null && !checkedResources.Any()) return;
+
+      lock (locker) {
+        if (!btnRemoveGroup.Enabled) return;
         btnRemoveGroup.Enabled = false;
+      }
+
+      if (checkedResources.Count > 0) {
+        var result = MessageBox.Show(
+          "Do you really want to delete all " + checkedResources.Count + " checked resources?",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+        if (result == DialogResult.Yes) {
+          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+            action: () => {
+              RemoveResource(checkedResources);
+            });
+        }
+      } else {
+        var res = checkedResources.Any() ? checkedResources.First() : selectedResource;
+        var result = MessageBox.Show(
+          "Do you really want to delete the selected resource " + res.Name + "?",
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+        if (result == DialogResult.Yes) {
+          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+            action: () => {
+              RemoveResource(res);
+            });
+        }
+      }
+
+      OnContentChanged();
+      SetEnabledStateOfControls();
+    }
+
+    private async void btnSave_Click(object sender, EventArgs e) {
+      lock (locker) {
+        if (!btnSave.Enabled) return;
         btnSave.Enabled = false;
-        btnPermissionsSave.Enabled = false;
-        permissionView.Enabled = false;
-        scheduleView.SetEnabledStateOfSchedule(false);
-        btnPermissionsSave.Enabled = false;
-        permissionView.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();
+        });
+
+      OnContentChanged();
+      SetEnabledStateOfControls();
+    }
+
+    private void treeSlaveGroup_MouseDown(object sender, MouseEventArgs e) {
+      var node = treeView.GetNodeAt(e.Location);
+      if (node == null || node == ungroupedGroupNode) return;
+      var r = (Resource)node.Tag;
+      if (!HiveAdminClient.Instance.DisabledParentResources.Contains(r)) ChangeSelectedResourceNode(node);
+    }
+
+    private void treeSlaveGroup_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
+      e.Cancel = true;
+    }
+
+    private void treeSlaveGroup_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      if (!IsAdmin() || e.Node == ungroupedGroupNode) {
+        e.Cancel = true;
       } else {
-        btnAddGroup.Enabled = true;
-        btnRemoveGroup.Enabled = true;
-        btnSave.Enabled = true;
-        scheduleView.SetEnabledStateOfSchedule(IsAuthorized(slaveView.Content));
-        btnPermissionsSave.Enabled = permissionView.FetchSelectedUsers != null;
-        permissionView.Enabled = permissionView.FetchSelectedUsers != null;
-      }
-    }
-
-    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 treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
-      if (e.Action != TreeViewAction.Unknown) {
-        Resource selectedResource = ((Resource)e.Node.Tag);
-        currentlyAuthorized = IsAuthorized(selectedResource);
-        if (currentlyAuthorized) {
-          permissionView.FetchSelectedUsers = new Func<List<Guid>>(() => {
-            return HiveServiceLocator.Instance.CallHiveService<List<ResourcePermission>>(service => {
-              return service.GetResourcePermissions(selectedResource.Id);
-            }).Select(x => x.GrantedUserId).ToList();
-          });
-          if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
+        var r = (Resource)e.Node.Tag;
+        if (HiveAdminClient.Instance.DisabledParentResources.Contains(r)) {
+          e.Cancel = true;
+        }
+      }
+    }
+
+    private void treeSlaveGroup_AfterCheck(object sender, TreeViewEventArgs e) {
+      SetEnabledStateOfControls();
+    }
+
+    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
+      if (e.Effect == DragDropEffects.None) return;
+
+      var targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
+      var targetResource = (targetNode != null) ? (Resource)targetNode.Tag : null;
+      var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+
+      foreach (var r in resources) {
+        r.ParentResourceId = targetResource != null ? targetResource.Id : (Guid?)null;
+      }
+
+      // TODO
+      //HiveAdminClient.Instance.UpdateResourceGenealogy(Content);
+      OnContentChanged();
+    }
+
+    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
+      if (!IsAdmin()) return;
+
+      var nodes = GetCheckedNodes(treeView.Nodes).ToList();
+      TreeNode sourceNode = (TreeNode)e.Item;
+      if (!sourceNode.Checked) nodes.Add(sourceNode);
+      nodes.Remove(ungroupedGroupNode);
+      ungroupedGroupNode.Checked = false;
+      var resources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+
+      if (resources.Count > 0) {
+        DataObject data = new DataObject();
+        data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, resources);
+        var action = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
+        if (action.HasFlag(DragDropEffects.Move)) {
+          foreach (var node in nodes) node.Remove();
+          StyleTreeNode(ungroupedGroupNode, (Resource)ungroupedGroupNode.Tag, resources);
+        }
+      }
+    }
+
+    private IEnumerable<TreeNode> GetCheckedNodes(TreeNodeCollection nodes) {
+      if (nodes != null) {
+        foreach (var node in nodes.OfType<TreeNode>()) {
+          if (node.Checked && node != ungroupedGroupNode) yield return node;
+          foreach (var child in GetCheckedNodes(node.Nodes))
+            yield return child;
+        }
+      }
+    }
+
+    private void treeSlaveGroup_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.Move;
+      var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+      var targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
+      var targetResource = (targetNode != null ? targetNode.Tag : null) as Resource;
+
+      if (!IsAdmin()
+        || resources == null
+        || !resources.Any()
+        || resources.Any(x => !HiveAdminClient.Instance.CheckParentChange(x, targetResource))
+        || (targetNode != null && (targetNode == ungroupedGroupNode || targetNode.Parent == ungroupedGroupNode))) {
+        e.Effect = DragDropEffects.None;
+      }
+    }
+
+    private void TabSlaveGroup_TabIndexChanged(object sender, EventArgs e) {
+      throw new NotImplementedException();
+    }
+
+    private async void TabSlaveGroup_Selected(object sender, System.Windows.Forms.TabControlEventArgs e) {
+      if (e.TabPage == tabSchedule) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => UpdateSchedule(),
+          finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
+      }
+      SetEnabledStateOfControls();
+    }
+    #endregion
+
+    #region Helpers
+    private void BuildResourceTree(IEnumerable<Resource> resources) {
+      treeView.Nodes.Clear();
+      if (!resources.Any()) return;
+
+      var disabledParentResources = HiveAdminClient.Instance.DisabledParentResources;
+      var mainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+        .Where(x => x.ParentResourceId == null));
+      //var parentedMainResources = new HashSet<Resource>(resources.OfType<SlaveGroup>()
+      //  .Where(x => x.ParentResourceId.HasValue && !resources.Select(y => y.Id).Contains(x.ParentResourceId.Value)));
+      //mainResources.UnionWith(parentedMainResources);
+      var mainDisabledParentResources = new HashSet<Resource>(disabledParentResources.Where(x => x.ParentResourceId == null || x.ParentResourceId == Guid.Empty));
+      mainResources.UnionWith(mainDisabledParentResources);
+      var subResources = new HashSet<Resource>(resources.Union(disabledParentResources).Except(mainResources).OrderByDescending(x => x.Name));
+
+      var stack = new Stack<Resource>(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 };
+        StyleTreeNode(newNode, newResource, resources);
+
+        if (selectedResource == null && !disabledParentResources.Contains(newResource)) {
+          SelectedResource = newResource;
+        }
+        if (!nodeSelected && selectedResource != null && newResource.Id == selectedResource.Id) {
+          newNode.BackColor = selectedBackColor;
+          newNode.ForeColor = selectedForeColor;
+          newNode.Text += SELECTED_TAG;
+          nodeSelected = true;
+        }
+
+        if (disabledParentResources.Contains(newResource)) {
+          newNode.Checked = false;
+          newNode.ForeColor = grayTextColor;
+        }
+
+        // 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 {
-          permissionView.FetchSelectedUsers = null;
-          btnPermissionsSave.Enabled = false;
-          if (selectedResource.Id == Guid.Empty) {
-            if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
-          } else tabSlaveGroup.TabPages.Remove(tabPermissions);
-        }
-
-        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
-          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-
-        slaveView.Content = selectedResource;
-        HiveAdminClient.Instance.DowntimeForResourceId = selectedResource.Id;
-
-        if (selectedResource is SlaveGroup) {
-          slaveView.Content.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-
-        if (tabSlaveGroup.SelectedIndex == 1) {
-          UpdateScheduleAsync();
-        } else if (tabSlaveGroup.SelectedIndex == 2) {
-          UpdatePermissionsAsync();
-        }
-      }
-    }
-
-    void SlaveViewContent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
-      OnContentChanged();
-      if (e.PropertyName == "HbInterval") {
-        UpdateChildHbIntervall(slaveView.Content);
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (newResource is SlaveGroup) {
+          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;
+      }
+
+      // collapse slave-only nodes
+      foreach (TreeNode n in treeView.Nodes) {
+        CollapseSlaveOnlyNodes(n);
+      }
+
+      ungroupedGroupNode = new TreeNode(UNGROUPED_GROUP_NAME) {
+        ForeColor = SystemColors.GrayText,
+        ImageIndex = slaveGroupImageIndex,
+        Tag = new SlaveGroup() {
+          Name = UNGROUPED_GROUP_NAME,
+          Description = UNGROUPED_GROUP_DESCRIPTION
+        }
+      };
+
+      foreach (var slave in subResources.OfType<Slave>().OrderBy(x => x.Name)) {
+        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+        StyleTreeNode(slaveNode, slave, resources);
+        ungroupedGroupNode.Nodes.Add(slaveNode);
+        if (selectedResource == null) {
+          SelectedResource = slave;
+        }
+
+        if (slave.Id == selectedResource.Id && !nodeSelected) {
+          slaveNode.BackColor = selectedBackColor;
+          slaveNode.ForeColor = selectedForeColor;
+          slaveNode.Text += SELECTED_TAG;
+          nodeSelected = true;
+        }
+      }
+
+      if (ungroupedGroupNode.Nodes.Count > 0) {
+        ungroupedGroupNode.Text += " [" + ungroupedGroupNode.Nodes.Count.ToString() + "]";
+        ungroupedGroupNode.Expand();
+        treeView.Nodes.Add(ungroupedGroupNode);
+      }
+    }
+
+    private void CollapseSlaveOnlyNodes(TreeNode tn) {
+      Resource r = (Resource)tn.Tag;
+      var descendants = GetResourceDescendants();
+      if (descendants.ContainsKey(r.Id)) {
+        if (descendants[r.Id].OfType<SlaveGroup>().Any()) {
+          tn.Expand();
+          foreach (TreeNode n in tn.Nodes) CollapseSlaveOnlyNodes(n);
+        } else {
+          tn.Collapse();
+        }
+      }
+    }
+
+    private void ExpandResourceNodesOfInterest(TreeNodeCollection nodes) {
+      foreach (TreeNode n in nodes) {
+        Resource r = (Resource)n.Tag;
+        if (n.Nodes.Count > 0) {
+          if (HiveAdminClient.Instance.GetAvailableResourceDescendants(r.Id).OfType<SlaveGroup>().Any()) {
+            n.Expand();
+            ExpandResourceNodesOfInterest(n.Nodes);
+          } else {
+            n.Collapse();
+          }
+        } else {
+          n.Collapse();
+        }
       }
     }
@@ -275,259 +553,221 @@
     }
 
-    private void btnAddGroup_Click(object sender, EventArgs e) {
-      SlaveGroup newGroup = new SlaveGroup();
-      newGroup.Name = "New Group";
-      newGroup.OwnerUserId = UserInformation.Instance.User.Id;
-      Content.Add(newGroup);
-    }
-
-    void Content_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
-      OnContentChanged();
-    }
-
-    void Content_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
-      OnContentChanged();
-    }
-
-    private void btnRemoveGroup_Click(object sender, EventArgs e) {
-      if (treeSlaveGroup.SelectedNode != null && treeSlaveGroup.SelectedNode.Tag != null) {
-        Resource res = (Resource)treeSlaveGroup.SelectedNode.Tag;
-
-        DialogResult diagRes = MessageBox.Show("Do you really want to delete " + res.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
-        if (diagRes == DialogResult.Yes) {
-          if (res is Slave) {
-            Content.Remove(res);
-            HiveAdminClient.Delete(res);
-          } else if (res is SlaveGroup) {
-            //only delete empty groups
-            if (Content.Where(s => s.ParentResourceId == res.Id).Count() < 1) {
-              Content.Remove(res);
-              HiveAdminClient.Delete(res);
-            } else {
-              MessageBox.Show("Only empty groups can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            }
+    private void UpdateResources() {
+      try {
+        HiveAdminClient.Instance.Refresh();
+        Content = HiveAdminClient.Instance.Resources;
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveResource(Resource resource) {
+      if (resource == null) return;
+
+      try {
+        if (resource.Id != Guid.Empty) {
+          SelectedResource = HiveAdminClient.Instance.GetAvailableResourceAncestors(resource.Id).LastOrDefault();
+
+          // deal with all new, but not yet saved resources
+          var newResources = Content.Where(x => x.ParentResourceId == resource.Id).ToList();
+          if (newResources.Any(x => x.Id != Guid.Empty)) return;
+          foreach (var nr in newResources) Content.Remove(nr);
+
+          HiveAdminClient.Delete(resource);          
+          UpdateResources();
+        } else {
+          SelectedResource = Content.FirstOrDefault(x => x.Id == resource.ParentResourceId);
+          Content.Remove(resource);
+        }
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void RemoveResource(IEnumerable<Resource> resources) {
+      if (resources == null || !resources.Any()) return;
+
+      var ids = resources.Select(x => x.Id).ToList();      
+      try {
+        bool update = false;
+        foreach (var r in resources) {          
+          if (r.Id != Guid.Empty)  {
+            if(r.Id == SelectedResource.Id)
+              SelectedResource = HiveAdminClient.Instance.GetAvailableResourceAncestors(r.Id).LastOrDefault();
+
+            // deal with all new, but not yet saved resources
+            var newResources = Content.Where(x => x.ParentResourceId == r.Id).ToList();
+            if (newResources.Any(x => x.Id != Guid.Empty)) return;
+            foreach (var nr in newResources) Content.Remove(nr);
+
+            HiveAdminClient.Delete(r);
+            update = true;
+          } else {
+            if (r.Id == SelectedResource.Id)
+              SelectedResource = Content.FirstOrDefault(x => x.Id == r.ParentResourceId);
+            Content.Remove(r);
           }
         }
-      }
-    }
-
-    private void btnSave_Click(object sender, EventArgs e) {
-      foreach (Resource res in Content) {
-        if (res is SlaveGroup && res.Id == Guid.Empty) {
-          SlaveGroup slaveGroup = (SlaveGroup)res;
-          slaveGroup.Store();
-        } else if (res.Id != Guid.Empty && res.Modified) {
-          res.Store();
-        }
-      }
-    }
-
-    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
-      if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
-        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
-        TreeNode destNode = ((TreeView)sender).GetNodeAt(pt);
-        TreeNode newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
-
-        if (destNode.TreeView == newNode.TreeView) {
-          if (destNode.Text == UngroupedGroupName || (destNode.Parent != null && destNode.Parent.Text == UngroupedGroupName)) {
-            MessageBox.Show(string.Format("You can't drag items to the group \"{0}\".{1}This group only contains slaves which haven't yet been assigned to a real group.",
-              UngroupedGroupName, Environment.NewLine), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information);
-            return;
-          }
-
-          SlaveGroup sgrp = null;
-          TreeNode parentNode = null;
-          if (destNode.Tag != null && destNode.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Tag;
-            parentNode = destNode;
-          } else if (destNode.Parent != null && destNode.Parent.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Parent.Tag;
-            parentNode = destNode.Parent;
-          }
-
-          if (newNode.Tag is SlaveGroup && CheckParentsEqualsMovedNode(parentNode, newNode)) {
-            return;
-          }
-
-          SlaveGroup parent = (SlaveGroup)parentNode.Tag;
-
-          if (parent.OwnerUserId != null && !IsAuthorized(parent)) {
-            MessageBox.Show(string.Format("You don't have the permissions to drag items to the group \"{0}\".", ((Resource)parentNode.Tag).Name),
-              "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            return;
-          }
-
-          if (sgrp != null && newNode.Tag != null) {
-            //save parent group to get an id
-            if (sgrp.Id == Guid.Empty) {
-              sgrp.Store();
-            }
-
-            if (newNode.Tag is Slave) {
-              Slave slave = (Slave)newNode.Tag;
-              if (slave.ParentResourceId == null || (slave.ParentResourceId != null && slave.ParentResourceId != sgrp.Id)) {
-                slave.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            } else if (newNode.Tag is SlaveGroup) {
-              SlaveGroup slaveGroup = (SlaveGroup)newNode.Tag;
-              if (slaveGroup.ParentResourceId == null || (slaveGroup.ParentResourceId != null && slaveGroup.ParentResourceId != sgrp.Id)) {
-                slaveGroup.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            }
-          }
-        }
-      }
-    }
-
-    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 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;
-    }
-
-    void ResetView() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ResetView));
+        if (update) UpdateResources();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void UpdateSchedule() {
+      try {
+        HiveAdminClient.Instance.RefreshCalendar();
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private bool IsAdmin() {
+      return HiveRoles.CheckAdminUserPermissions();
+    }
+
+    private void StyleTreeNode(TreeNode n, Resource r, IEnumerable<Resource> resources) {
+      n.Text = r.Name;
+      n.BackColor = Color.Transparent;
+      n.ForeColor = Color.Black;
+
+      if (HiveAdminClient.Instance.DisabledParentResources.Select(x => x.Id).Contains(r.Id)) {
+        n.ForeColor = grayTextColor;
+      } else if (r.Id == Guid.Empty && n != ungroupedGroupNode /*!r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        // not stored (i.e. new)
+        n.Text += NOT_STORED_TAG;
+      } else if (r.Modified && n != ungroupedGroupNode /*!r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        // changed
+        n.Text += CHANGES_NOT_STORED_TAG;
+      }
+
+      // slave count
+      int childSlavesCount = 0;
+      if (r.Id != Guid.Empty && r is SlaveGroup) {
+        var descendants = GetResourceDescendants();
+        if (descendants.ContainsKey(r.Id)) {
+          childSlavesCount = resources
+            .OfType<Slave>()
+            .Where(x => descendants[r.Id].Select(y => y.Id)
+              .Contains(x.Id))
+            .Count();
+        }
+      } else if (n == ungroupedGroupNode /*|| r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        childSlavesCount = resources
+          .OfType<Slave>()
+          .Where(x => x.ParentResourceId == null
+            || (x.ParentResourceId.HasValue && x.ParentResourceId.Value == Guid.Empty))
+          .Count();
+      }
+      if (childSlavesCount > 0)
+        n.Text += " [" + childSlavesCount.ToString() + "]";
+
+      // slave image index, state, utilization
+      if (r is Slave) {
+        n.ImageIndex = slaveImageIndex;
+        var s = r as Slave;
+        if (s.SlaveState == SlaveState.Calculating) {
+          n.ForeColor = calculatingColor;
+          n.Text += " [" + s.CpuUtilization.ToString("N2") + "%]";
+        } else if (s.SlaveState == SlaveState.Offline) {
+          n.ForeColor = offlineColor;
+          if (s.LastHeartbeat.HasValue)
+            n.Text += " [" + (s.LastHeartbeat != null ? s.LastHeartbeat.Value.ToString("g") : null) + "]";
+        }
       } else {
-        treeSlaveGroup.Nodes.Clear();
-
-        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
-          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
-        }
-        slaveView.Content = null;
+        n.ImageIndex = slaveGroupImageIndex;
+      }
+
+      // ungrouped
+      if (n == ungroupedGroupNode /*r.Name.StartsWith(UNGROUPED_GROUP_NAME)*/) {
+        n.ForeColor = SystemColors.GrayText;
+      }
+    }
+
+    private void ResetTreeNodes(TreeNodeCollection nodes, IEnumerable<Resource> resources) {
+      foreach (TreeNode n in nodes) {
+        StyleTreeNode(n, (Resource)n.Tag, resources);
+        if (n.Nodes.Count > 0) {
+          ResetTreeNodes(n.Nodes, resources);
+        }
+      }
+    }
+
+    private async void ChangeSelectedResource(Resource resource) {
+      selectedResource = resource;
+      viewHost.Content = selectedResource;
+
+      HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty;
+      if (tabSlaveGroup.SelectedTab == tabSchedule) {
+        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+          action: () => UpdateSchedule(),
+          finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
+      }
+
+      SetEnabledStateOfControls();
+    }
+
+    private void ChangeSelectedResourceNode(TreeNode resourceNode) {
+      if (resourceNode == null) return;
+      SelectedResource = (Resource)resourceNode.Tag;
+      ResetTreeNodes(treeView.Nodes, Content);
+      resourceNode.BackColor = selectedBackColor;
+      resourceNode.ForeColor = selectedForeColor;
+      resourceNode.Text += SELECTED_TAG;
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+
+    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() {
-      ResetView();
-
-      try {
-        if (!Access.UserInformation.Instance.UserExists) {
-          //do a refresh just in case that the user has changed his usr and pwd in between
-          Access.UserInformation.Instance.Refresh();
-        }
-        HiveAdminClient.Instance.Refresh();
-        Content = HiveAdminClient.Instance.Resources;
-      }
-      catch (MessageSecurityException) {
-        ShowMessageSecurityException();
-      }
-      catch (AnonymousUserException) {
-        ShowHiveInformationDialog();
-      }
-    }
-
-    private void ShowMessageSecurityException() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ShowMessageSecurityException));
-      } else {
-        MessageBox.Show("A Message Security error has occured. This normally means that your user name or password is wrong.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-      }
-    }
-
-    private void ShowHiveInformationDialog() {
-      if (this.InvokeRequired) {
-        Invoke(new Action(ShowHiveInformationDialog));
-      } else {
-        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
-          dialog.ShowDialog(this);
-        }
-      }
-    }
-
-    private void UpdateResourcesAsync() {
-      TS.Task.Factory.StartNew(UpdateResources).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-    private void UpdateSchedule() {
-      HiveAdminClient.Instance.RefreshCalendar();
-      scheduleView.Invoke(new Action(() => {
-        scheduleView.Content = HiveAdminClient.Instance.Downtimes;
-        SetEnabledStateOfControls();
-      }));
-    }
-
-    private void UpdateScheduleAsync() {
-      TS.Task.Factory.StartNew(UpdateSchedule).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-    private void UpdatePermissions() {
-      if (permissionView.Content != null && permissionView.FetchSelectedUsers != null)
-        permissionView.Invoke(new Action(() => permissionView.ManualRefresh()));
-    }
-
-    private void UpdatePermissionsAsync() {
-      TS.Task.Factory.StartNew(UpdatePermissions).ContinueWith((t) => {
-        DisplayError(t.Exception);
-      }, TaskContinuationOptions.OnlyOnFaulted);
-    }
-
-
-    private void DisplayError(Exception ex) {
-      MessageBox.Show(string.Format("An error occured while updating: {0} {1}", Environment.NewLine, ex.Message), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-    }
-
-    private void tabSlaveGroup_SelectedIndexChanged(object sender, EventArgs e) {
-      if (tabSlaveGroup.SelectedIndex == 1) {
-        UpdateScheduleAsync();
-      } else if (tabSlaveGroup.SelectedIndex == 2) {
-        UpdatePermissionsAsync();
-      }
-    }
-
-    private void btnRefresh_Click(object sender, EventArgs e) {
-      UpdateResourcesAsync();
-    }
-
-    private void ResourcesView_Load(object sender, EventArgs e) {
-      UpdateResourcesAsync();
-    }
-
-    private void btnPermissionsSave_Click(object sender, EventArgs e) {
-      SetEnabledStateOfControls();
-      HiveServiceLocator.Instance.CallHiveService(service => {
-        service.GrantResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetAddedUsers().Select(x => x.Id).ToList());
-        service.RevokeResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetDeletedUsers().Select(x => x.Id).ToList());
-      });
-      SetEnabledStateOfControls();
-    }
+
+    private Dictionary<Guid, HashSet<Resource>> GetResourceDescendants() {
+      var resourceDescendants = new Dictionary<Guid, HashSet<Resource>>();
+      //var resources = Content.Where(x => x.Id != Guid.Empty).Union(HiveAdminClient.Instance.DisabledParentResources).ToList();      
+      var resources = Content.Union(HiveAdminClient.Instance.DisabledParentResources).ToList();
+
+      foreach (var r in resources) {
+        if(!resourceDescendants.ContainsKey(r.Id))
+          resourceDescendants.Add(r.Id, new HashSet<Resource>());
+      }
+      foreach (var r in resources) {
+        var parentResourceId = r.ParentResourceId;
+        while (parentResourceId != null) {
+          var parent = resources.SingleOrDefault(x => x.Id == parentResourceId);
+          if (parent != null) {
+            resourceDescendants[parent.Id].Add(r);
+            parentResourceId = parent.ParentResourceId;
+          } else {
+            parentResourceId = null;
+          }
+        }
+      }
+      return resourceDescendants;
+    }
+
+    #endregion
   }
 }
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.Designer.cs	(revision 17059)
@@ -48,6 +48,4 @@
       Calendar.DrawTool drawTool1 = new Calendar.DrawTool();
       this.dvOnline = new Calendar.DayView();
-      this.txttimeTo = new System.Windows.Forms.DateTimePicker();
-      this.txttimeFrom = new System.Windows.Forms.DateTimePicker();
       this.dtpTo = new System.Windows.Forms.DateTimePicker();
       this.groupBox1 = new System.Windows.Forms.GroupBox();
@@ -94,30 +92,11 @@
       this.dvOnline.OnSelectionChanged += new System.EventHandler<System.EventArgs>(this.dvOnline_OnSelectionChanged);
       // 
-      // txttimeTo
-      // 
-      this.txttimeTo.CustomFormat = "HH:mm";
-      this.txttimeTo.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.txttimeTo.Location = new System.Drawing.Point(164, 45);
-      this.txttimeTo.Name = "txttimeTo";
-      this.txttimeTo.ShowUpDown = true;
-      this.txttimeTo.Size = new System.Drawing.Size(73, 20);
-      this.txttimeTo.TabIndex = 40;
-      // 
-      // txttimeFrom
-      // 
-      this.txttimeFrom.CustomFormat = "HH:mm";
-      this.txttimeFrom.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
-      this.txttimeFrom.Location = new System.Drawing.Point(164, 12);
-      this.txttimeFrom.Name = "txttimeFrom";
-      this.txttimeFrom.ShowUpDown = true;
-      this.txttimeFrom.Size = new System.Drawing.Size(73, 20);
-      this.txttimeFrom.TabIndex = 39;
-      // 
       // dtpTo
       // 
       this.dtpTo.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpTo.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpTo.Location = new System.Drawing.Point(72, 45);
       this.dtpTo.Name = "dtpTo";
-      this.dtpTo.Size = new System.Drawing.Size(89, 20);
+      this.dtpTo.Size = new System.Drawing.Size(175, 20);
       this.dtpTo.TabIndex = 33;
       // 
@@ -125,6 +104,4 @@
       // 
       this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.groupBox1.Controls.Add(this.txttimeTo);
-      this.groupBox1.Controls.Add(this.txttimeFrom);
       this.groupBox1.Controls.Add(this.dtpTo);
       this.groupBox1.Controls.Add(this.dtpFrom);
@@ -144,7 +121,8 @@
       // 
       this.dtpFrom.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
+      this.dtpFrom.CustomFormat = "ddd, dd.MM.yyyy, HH:mm:ss";
       this.dtpFrom.Location = new System.Drawing.Point(72, 12);
       this.dtpFrom.Name = "dtpFrom";
-      this.dtpFrom.Size = new System.Drawing.Size(89, 20);
+      this.dtpFrom.Size = new System.Drawing.Size(175, 20);
       this.dtpFrom.TabIndex = 32;
       // 
@@ -157,6 +135,6 @@
       this.chbade.TabIndex = 31;
       this.chbade.Text = "All Day Event";
+      this.chbade.CheckedChanged += new System.EventHandler(this.chbade_CheckedChanged);
       this.chbade.UseVisualStyleBackColor = true;
-      this.chbade.CheckedChanged += new System.EventHandler(this.chbade_CheckedChanged);
       // 
       // btnRecurrence
@@ -220,5 +198,5 @@
       // 
       this.btnSaveCal.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-      this.btnSaveCal.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.btnSaveCal.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.btnSaveCal.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
       this.btnSaveCal.Location = new System.Drawing.Point(640, 41);
@@ -273,7 +251,5 @@
     #endregion
 
-    private System.Windows.Forms.DateTimePicker txttimeTo;
     private Calendar.DayView dvOnline;
-    private System.Windows.Forms.DateTimePicker txttimeFrom;
     private System.Windows.Forms.DateTimePicker dtpTo;
     private System.Windows.Forms.GroupBox groupBox1;
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ScheduleView.cs	(revision 17059)
@@ -92,13 +92,10 @@
 
       if (!string.IsNullOrEmpty(dtpFrom.Text) && !string.IsNullOrEmpty(dtpTo.Text)) {
-        if (chbade.Checked) {
+        if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to) {
           //whole day appointment, only dates are visible
-          if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to)
-            offlineTimes.Add(CreateDowntime(from, to.AddDays(1), true, dtType));
-          else
-            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-        } else if (!string.IsNullOrEmpty(txttimeFrom.Text) && !string.IsNullOrEmpty(txttimeTo.Text)) {
-          //Timeframe appointment
-          if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to) {
+          if (chbade.Checked) {
+            offlineTimes.Add(CreateDowntime(new DateTime(from.Year, from.Month, from.Day), (new DateTime(to.Year, to.Month, to.Day)).AddDays(1), true, dtType));
+          } else {
+            //Timeframe appointment
             if (from.Date == to.Date)
               offlineTimes.Add(CreateDowntime(from, to, false, dtType));
@@ -111,7 +108,11 @@
               offlineTimes.Add(CreateDowntime(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false, dtType));
             }
-          } else
-            MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-        }
+          }
+
+        } else {
+          MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+        }
+
+
         dvOnline.Invalidate();
         return true;
@@ -156,9 +157,12 @@
 
     private void ChangeRecurrenceDowntime(Guid recurringId) {
-      int hourfrom = int.Parse(txttimeFrom.Text.Substring(0, txttimeFrom.Text.IndexOf(':')));
-      int hourTo = int.Parse(txttimeTo.Text.Substring(0, txttimeTo.Text.IndexOf(':')));
-      List<HiveDowntime> recurringDowntimes = offlineTimes.Where(appointment => ((HiveDowntime)appointment).RecurringId == recurringId).ToList();
-      recurringDowntimes.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, hourfrom, 0, 0));
-      recurringDowntimes.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, hourTo, 0, 0));
+      DateTime from, to;
+      if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to) {
+        List<HiveDowntime> recurringDowntimes = offlineTimes.Where(appointment => ((HiveDowntime)appointment).RecurringId == recurringId).ToList();
+        recurringDowntimes.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, from.Hour, 0, 0));
+        recurringDowntimes.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, to.Hour, 0, 0));
+      } else {
+        MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+      }
     }
 
@@ -222,16 +226,25 @@
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-    }
-
-    public virtual void SetEnabledStateOfSchedule(bool state) {
-      if (InvokeRequired) {
-        Invoke(new Action(() => SetEnabledStateOfSchedule(state)));
-      } else {
-        if (Content == null) state = false;
-        groupBox1.Enabled = state;
-        btnClearCal.Enabled = state;
-        btnSaveCal.Enabled = state;
-      }
-    }
+      bool enabled = Content != null && !Locked;
+      dtpFrom.Enabled = enabled;
+      dtpTo.Enabled = enabled;
+      chbade.Enabled = enabled;
+      btCreate.Enabled = enabled;
+      btbDelete.Enabled = enabled;      
+      btnRecurrence.Enabled = enabled;
+      btnClearCal.Enabled = enabled;
+      btnSaveCal.Enabled = enabled;
+    }
+
+    //public virtual void SetEnabledStateOfSchedule(bool state) {
+    //  if (InvokeRequired) {
+    //    Invoke(new Action(() => SetEnabledStateOfSchedule(state)));
+    //  } else {
+    //    if (Content == null || Locked || ReadOnly) state = false;
+    //    //groupBox1.Enabled = state;
+    //    btnClearCal.Enabled = state;
+    //    btnSaveCal.Enabled = state;
+    //  }
+    //}
 
     private void btnClearCal_Click(object sender, System.EventArgs e) {
@@ -243,22 +256,20 @@
 
     private void chbade_CheckedChanged(object sender, EventArgs e) {
-      txttimeFrom.Visible = !chbade.Checked;
-      txttimeTo.Visible = !chbade.Checked;
+      if(chbade.Checked) {
+        dtpFrom.Value = new DateTime(dtpFrom.Value.Year, dtpFrom.Value.Month, dtpFrom.Value.Day);
+        dtpTo.Value = new DateTime(dtpTo.Value.Year, dtpTo.Value.Month, dtpTo.Value.Day);
+      }
     }
 
     private void dvOnline_OnSelectionChanged(object sender, EventArgs e) {
       if (dvOnline.Selection == SelectionType.DateRange) {
-        dtpFrom.Text = dvOnline.SelectionStart.ToShortDateString();
-        dtpTo.Text = dvOnline.SelectionEnd.Date.ToShortDateString();
-        txttimeFrom.Text = dvOnline.SelectionStart.ToShortTimeString();
-        txttimeTo.Text = dvOnline.SelectionEnd.ToShortTimeString();
+        dtpFrom.Value = dvOnline.SelectionStart;
+        dtpTo.Value = dvOnline.SelectionEnd.Date;
         btCreate.Text = "Create Downtime";
       }
 
       if (dvOnline.Selection == SelectionType.Appointment) {
-        dtpFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortDateString();
-        dtpTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortDateString();
-        txttimeFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortTimeString();
-        txttimeTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortTimeString();
+        dtpFrom.Value = dvOnline.SelectedAppointment.StartDate;
+        dtpTo.Value = dvOnline.SelectedAppointment.EndDate;
 
         if (dvOnline.SelectedAppointment.Recurring)
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 17059)
@@ -45,290 +45,171 @@
     /// </summary>
     private void InitializeComponent() {
-      this.txtSlaveState = new System.Windows.Forms.TextBox();
-      this.txtOS = new System.Windows.Forms.TextBox();
-      this.label15 = new System.Windows.Forms.Label();
-      this.label14 = new System.Windows.Forms.Label();
-      this.txtDetailsDescription = new System.Windows.Forms.TextBox();
-      this.label13 = new System.Windows.Forms.Label();
-      this.txtName = new System.Windows.Forms.TextBox();
-      this.txtCPU = new System.Windows.Forms.TextBox();
-      this.txtMemory = new System.Windows.Forms.TextBox();
-      this.txtLastHeartbeat = new System.Windows.Forms.TextBox();
-      this.label12 = new System.Windows.Forms.Label();
-      this.label11 = new System.Windows.Forms.Label();
-      this.label10 = new System.Windows.Forms.Label();
-      this.label9 = new System.Windows.Forms.Label();
-      this.label1 = new System.Windows.Forms.Label();
-      this.txtFreeMemory = new System.Windows.Forms.TextBox();
-      this.txtId = new System.Windows.Forms.TextBox();
-      this.label2 = new System.Windows.Forms.Label();
-      this.label3 = new System.Windows.Forms.Label();
-      this.txtHbIntervall = new System.Windows.Forms.TextBox();
-      this.cbxDisposable = new System.Windows.Forms.CheckBox();
-      this.label4 = new System.Windows.Forms.Label();
-      this.cbxPublic = new System.Windows.Forms.CheckBox();
-      this.label5 = new System.Windows.Forms.Label();
+      this.cpuLabel = new System.Windows.Forms.Label();
+      this.cpuTextBox = new System.Windows.Forms.TextBox();
+      this.memoryLabel = new System.Windows.Forms.Label();
+      this.memoryTextBox = new System.Windows.Forms.TextBox();
+      this.operatingSystemLabel = new System.Windows.Forms.Label();
+      this.operatingSystemTextBox = new System.Windows.Forms.TextBox();
+      this.stateLabel = new System.Windows.Forms.Label();
+      this.stateTextBox = new System.Windows.Forms.TextBox();
+      this.lastHeartbeatLabel = new System.Windows.Forms.Label();
+      this.lastHeartbeatTextBox = new System.Windows.Forms.TextBox();
+      this.disposableLabel = new System.Windows.Forms.Label();
+      this.disposableCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
       this.SuspendLayout();
       // 
-      // txtSlaveState
-      // 
-      this.txtSlaveState.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtSlaveState.Enabled = false;
-      this.txtSlaveState.Location = new System.Drawing.Point(146, 242);
-      this.txtSlaveState.Name = "txtSlaveState";
-      this.txtSlaveState.Size = new System.Drawing.Size(390, 20);
-      this.txtSlaveState.TabIndex = 27;
-      // 
-      // txtOS
-      // 
-      this.txtOS.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtOS.Enabled = false;
-      this.txtOS.Location = new System.Drawing.Point(146, 216);
-      this.txtOS.Name = "txtOS";
-      this.txtOS.Size = new System.Drawing.Size(390, 20);
-      this.txtOS.TabIndex = 26;
-      // 
-      // label15
-      // 
-      this.label15.AutoSize = true;
-      this.label15.Location = new System.Drawing.Point(3, 245);
-      this.label15.Name = "label15";
-      this.label15.Size = new System.Drawing.Size(35, 13);
-      this.label15.TabIndex = 25;
-      this.label15.Text = "State:";
-      // 
-      // label14
-      // 
-      this.label14.AutoSize = true;
-      this.label14.Location = new System.Drawing.Point(3, 219);
-      this.label14.Name = "label14";
-      this.label14.Size = new System.Drawing.Size(93, 13);
-      this.label14.TabIndex = 24;
-      this.label14.Text = "Operating System:";
-      // 
-      // txtDetailsDescription
-      // 
-      this.txtDetailsDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtDetailsDescription.Enabled = false;
-      this.txtDetailsDescription.Location = new System.Drawing.Point(146, 112);
-      this.txtDetailsDescription.Name = "txtDetailsDescription";
-      this.txtDetailsDescription.Size = new System.Drawing.Size(390, 20);
-      this.txtDetailsDescription.TabIndex = 23;
-      // 
-      // label13
-      // 
-      this.label13.AutoSize = true;
-      this.label13.Location = new System.Drawing.Point(3, 115);
-      this.label13.Name = "label13";
-      this.label13.Size = new System.Drawing.Size(63, 13);
-      this.label13.TabIndex = 22;
-      this.label13.Text = "Description:";
-      // 
-      // txtName
-      // 
-      this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtName.Location = new System.Drawing.Point(146, 8);
-      this.txtName.Name = "txtName";
-      this.txtName.Size = new System.Drawing.Size(390, 20);
-      this.txtName.TabIndex = 21;
-      this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
-      // 
-      // txtCPU
-      // 
-      this.txtCPU.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtCPU.Enabled = false;
-      this.txtCPU.Location = new System.Drawing.Point(146, 138);
-      this.txtCPU.Name = "txtCPU";
-      this.txtCPU.Size = new System.Drawing.Size(390, 20);
-      this.txtCPU.TabIndex = 20;
-      // 
-      // txtMemory
-      // 
-      this.txtMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtMemory.Enabled = false;
-      this.txtMemory.Location = new System.Drawing.Point(146, 164);
-      this.txtMemory.Name = "txtMemory";
-      this.txtMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtMemory.TabIndex = 19;
-      // 
-      // txtLastHeartbeat
-      // 
-      this.txtLastHeartbeat.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtLastHeartbeat.Enabled = false;
-      this.txtLastHeartbeat.Location = new System.Drawing.Point(146, 268);
-      this.txtLastHeartbeat.Name = "txtLastHeartbeat";
-      this.txtLastHeartbeat.Size = new System.Drawing.Size(390, 20);
-      this.txtLastHeartbeat.TabIndex = 18;
-      // 
-      // label12
-      // 
-      this.label12.AutoSize = true;
-      this.label12.Location = new System.Drawing.Point(3, 271);
-      this.label12.Name = "label12";
-      this.label12.Size = new System.Drawing.Size(80, 13);
-      this.label12.TabIndex = 17;
-      this.label12.Text = "Last Heartbeat:";
-      // 
-      // label11
-      // 
-      this.label11.AutoSize = true;
-      this.label11.Location = new System.Drawing.Point(3, 167);
-      this.label11.Name = "label11";
-      this.label11.Size = new System.Drawing.Size(47, 13);
-      this.label11.TabIndex = 16;
-      this.label11.Text = "Memory:";
-      // 
-      // label10
-      // 
-      this.label10.AutoSize = true;
-      this.label10.Location = new System.Drawing.Point(3, 141);
-      this.label10.Name = "label10";
-      this.label10.Size = new System.Drawing.Size(32, 13);
-      this.label10.TabIndex = 15;
-      this.label10.Text = "CPU:";
-      // 
-      // label9
-      // 
-      this.label9.AutoSize = true;
-      this.label9.Location = new System.Drawing.Point(3, 11);
-      this.label9.Name = "label9";
-      this.label9.Size = new System.Drawing.Size(38, 13);
-      this.label9.TabIndex = 14;
-      this.label9.Text = "Name:";
-      // 
-      // label1
-      // 
-      this.label1.AutoSize = true;
-      this.label1.Location = new System.Drawing.Point(3, 193);
-      this.label1.Name = "label1";
-      this.label1.Size = new System.Drawing.Size(70, 13);
-      this.label1.TabIndex = 28;
-      this.label1.Text = "Free memory:";
-      // 
-      // txtFreeMemory
-      // 
-      this.txtFreeMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtFreeMemory.Enabled = false;
-      this.txtFreeMemory.Location = new System.Drawing.Point(146, 190);
-      this.txtFreeMemory.Name = "txtFreeMemory";
-      this.txtFreeMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtFreeMemory.TabIndex = 29;
-      // 
-      // txtId
-      // 
-      this.txtId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtId.Enabled = false;
-      this.txtId.Location = new System.Drawing.Point(146, 86);
-      this.txtId.Name = "txtId";
-      this.txtId.Size = new System.Drawing.Size(390, 20);
-      this.txtId.TabIndex = 30;
-      // 
-      // label2
-      // 
-      this.label2.AutoSize = true;
-      this.label2.Location = new System.Drawing.Point(3, 89);
-      this.label2.Name = "label2";
-      this.label2.Size = new System.Drawing.Size(19, 13);
-      this.label2.TabIndex = 31;
-      this.label2.Text = "Id:";
-      // 
-      // label3
-      // 
-      this.label3.AutoSize = true;
-      this.label3.Location = new System.Drawing.Point(3, 37);
-      this.label3.Name = "label3";
-      this.label3.Size = new System.Drawing.Size(137, 13);
-      this.label3.TabIndex = 32;
-      this.label3.Text = "Heartbeat Intervall (in sec) :";
-      // 
-      // txtHbIntervall
-      // 
-      this.txtHbIntervall.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtHbIntervall.Location = new System.Drawing.Point(146, 34);
-      this.txtHbIntervall.Name = "txtHbIntervall";
-      this.txtHbIntervall.Size = new System.Drawing.Size(390, 20);
-      this.txtHbIntervall.TabIndex = 33;
-      this.txtHbIntervall.TextChanged += new System.EventHandler(this.txtHbIntervall_TextChanged);
-      // 
-      // cbxDisposable
-      // 
-      this.cbxDisposable.AutoSize = true;
-      this.cbxDisposable.Enabled = false;
-      this.cbxDisposable.Location = new System.Drawing.Point(146, 297);
-      this.cbxDisposable.Name = "cbxDisposable";
-      this.cbxDisposable.Size = new System.Drawing.Size(15, 14);
-      this.cbxDisposable.TabIndex = 34;
-      this.cbxDisposable.UseVisualStyleBackColor = true;
-      this.cbxDisposable.CheckedChanged += new System.EventHandler(this.cbxDisposable_CheckedChanged);
-      // 
-      // label4
-      // 
-      this.label4.AutoSize = true;
-      this.label4.Location = new System.Drawing.Point(3, 297);
-      this.label4.Name = "label4";
-      this.label4.Size = new System.Drawing.Size(62, 13);
-      this.label4.TabIndex = 35;
-      this.label4.Text = "Disposable:";
-      // 
-      // cbxPublic
-      // 
-      this.cbxPublic.AutoSize = true;
-      this.cbxPublic.Enabled = false;
-      this.cbxPublic.Location = new System.Drawing.Point(146, 63);
-      this.cbxPublic.Name = "cbxPublic";
-      this.cbxPublic.Size = new System.Drawing.Size(15, 14);
-      this.cbxPublic.TabIndex = 36;
-      this.cbxPublic.UseVisualStyleBackColor = true;
-      this.cbxPublic.CheckedChanged += new System.EventHandler(this.cbxPublic_CheckedChanged);
-      // 
-      // label5
-      // 
-      this.label5.AutoSize = true;
-      this.label5.Location = new System.Drawing.Point(3, 63);
-      this.label5.Name = "label5";
-      this.label5.Size = new System.Drawing.Size(39, 13);
-      this.label5.TabIndex = 37;
-      this.label5.Text = "Public:";
+      // nameTextBox
+      // 
+      this.nameTextBox.ReadOnly = true;
+      // 
+      // cpuLabel
+      // 
+      this.cpuLabel.AutoSize = true;
+      this.cpuLabel.Location = new System.Drawing.Point(3, 141);
+      this.cpuLabel.Name = "cpuLabel";
+      this.cpuLabel.Size = new System.Drawing.Size(32, 13);
+      this.cpuLabel.TabIndex = 16;
+      this.cpuLabel.Text = "CPU:";
+      // 
+      // cpuTextBox
+      // 
+      this.cpuTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.cpuTextBox.Location = new System.Drawing.Point(130, 138);
+      this.cpuTextBox.Name = "cpuTextBox";
+      this.cpuTextBox.ReadOnly = true;
+      this.cpuTextBox.Size = new System.Drawing.Size(397, 20);
+      this.cpuTextBox.TabIndex = 17;
+      // 
+      // memoryLabel
+      // 
+      this.memoryLabel.AutoSize = true;
+      this.memoryLabel.Location = new System.Drawing.Point(3, 167);
+      this.memoryLabel.Name = "memoryLabel";
+      this.memoryLabel.Size = new System.Drawing.Size(47, 13);
+      this.memoryLabel.TabIndex = 18;
+      this.memoryLabel.Text = "Memory:";
+      // 
+      // memoryTextBox
+      // 
+      this.memoryTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.memoryTextBox.Location = new System.Drawing.Point(130, 164);
+      this.memoryTextBox.Name = "memoryTextBox";
+      this.memoryTextBox.ReadOnly = true;
+      this.memoryTextBox.Size = new System.Drawing.Size(397, 20);
+      this.memoryTextBox.TabIndex = 19;
+      // 
+      // operatingSystemLabel
+      // 
+      this.operatingSystemLabel.AutoSize = true;
+      this.operatingSystemLabel.Location = new System.Drawing.Point(3, 193);
+      this.operatingSystemLabel.Name = "operatingSystemLabel";
+      this.operatingSystemLabel.Size = new System.Drawing.Size(93, 13);
+      this.operatingSystemLabel.TabIndex = 20;
+      this.operatingSystemLabel.Text = "Operating System:";
+      // 
+      // operatingSystemTextBox
+      // 
+      this.operatingSystemTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.operatingSystemTextBox.Location = new System.Drawing.Point(130, 190);
+      this.operatingSystemTextBox.Name = "operatingSystemTextBox";
+      this.operatingSystemTextBox.ReadOnly = true;
+      this.operatingSystemTextBox.Size = new System.Drawing.Size(397, 20);
+      this.operatingSystemTextBox.TabIndex = 21;
+      // 
+      // stateLabel
+      // 
+      this.stateLabel.AutoSize = true;
+      this.stateLabel.Location = new System.Drawing.Point(3, 219);
+      this.stateLabel.Name = "stateLabel";
+      this.stateLabel.Size = new System.Drawing.Size(35, 13);
+      this.stateLabel.TabIndex = 22;
+      this.stateLabel.Text = "State:";
+      // 
+      // stateTextBox
+      // 
+      this.stateTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.stateTextBox.Location = new System.Drawing.Point(130, 216);
+      this.stateTextBox.Name = "stateTextBox";
+      this.stateTextBox.ReadOnly = true;
+      this.stateTextBox.Size = new System.Drawing.Size(397, 20);
+      this.stateTextBox.TabIndex = 23;
+      // 
+      // lastHeartbeatLabel
+      // 
+      this.lastHeartbeatLabel.AutoSize = true;
+      this.lastHeartbeatLabel.Location = new System.Drawing.Point(3, 245);
+      this.lastHeartbeatLabel.Name = "lastHeartbeatLabel";
+      this.lastHeartbeatLabel.Size = new System.Drawing.Size(80, 13);
+      this.lastHeartbeatLabel.TabIndex = 24;
+      this.lastHeartbeatLabel.Text = "Last Heartbeat:";
+      // 
+      // lastHeartbeatTextBox
+      // 
+      this.lastHeartbeatTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.lastHeartbeatTextBox.Location = new System.Drawing.Point(130, 242);
+      this.lastHeartbeatTextBox.Name = "lastHeartbeatTextBox";
+      this.lastHeartbeatTextBox.ReadOnly = true;
+      this.lastHeartbeatTextBox.Size = new System.Drawing.Size(397, 20);
+      this.lastHeartbeatTextBox.TabIndex = 25;
+      // 
+      // disposableLabel
+      // 
+      this.disposableLabel.AutoSize = true;
+      this.disposableLabel.Location = new System.Drawing.Point(3, 271);
+      this.disposableLabel.Name = "disposableLabel";
+      this.disposableLabel.Size = new System.Drawing.Size(62, 13);
+      this.disposableLabel.TabIndex = 28;
+      this.disposableLabel.Text = "Disposable:";
+      // 
+      // disposableCheckBox
+      // 
+      this.disposableCheckBox.AutoSize = true;
+      this.disposableCheckBox.Location = new System.Drawing.Point(130, 270);
+      this.disposableCheckBox.Name = "disposableCheckBox";
+      this.disposableCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.disposableCheckBox.TabIndex = 29;
+      this.disposableCheckBox.UseVisualStyleBackColor = true;
+      this.disposableCheckBox.CheckedChanged += new System.EventHandler(this.disposableCheckBox_CheckedChanged);
       // 
       // SlaveView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
-      this.Controls.Add(this.label5);
-      this.Controls.Add(this.cbxPublic);
-      this.Controls.Add(this.label4);
-      this.Controls.Add(this.cbxDisposable);
-      this.Controls.Add(this.txtHbIntervall);
-      this.Controls.Add(this.label3);
-      this.Controls.Add(this.label2);
-      this.Controls.Add(this.txtId);
-      this.Controls.Add(this.txtFreeMemory);
-      this.Controls.Add(this.label1);
-      this.Controls.Add(this.txtSlaveState);
-      this.Controls.Add(this.txtOS);
-      this.Controls.Add(this.label15);
-      this.Controls.Add(this.label14);
-      this.Controls.Add(this.txtDetailsDescription);
-      this.Controls.Add(this.label13);
-      this.Controls.Add(this.txtName);
-      this.Controls.Add(this.txtCPU);
-      this.Controls.Add(this.txtMemory);
-      this.Controls.Add(this.txtLastHeartbeat);
-      this.Controls.Add(this.label12);
-      this.Controls.Add(this.label11);
-      this.Controls.Add(this.label10);
-      this.Controls.Add(this.label9);
+      this.Controls.Add(this.disposableCheckBox);
+      this.Controls.Add(this.disposableLabel);
+      this.Controls.Add(this.lastHeartbeatTextBox);
+      this.Controls.Add(this.lastHeartbeatLabel);
+      this.Controls.Add(this.stateTextBox);
+      this.Controls.Add(this.stateLabel);
+      this.Controls.Add(this.operatingSystemTextBox);
+      this.Controls.Add(this.operatingSystemLabel);
+      this.Controls.Add(this.memoryTextBox);
+      this.Controls.Add(this.memoryLabel);
+      this.Controls.Add(this.cpuTextBox);
+      this.Controls.Add(this.cpuLabel);
       this.Name = "SlaveView";
-      this.Size = new System.Drawing.Size(539, 407);
+      this.Controls.SetChildIndex(this.idTextBox, 0);
+      this.Controls.SetChildIndex(this.descriptionTextBox, 0);
+      this.Controls.SetChildIndex(this.heartbeatIntervalNumericUpDown, 0);
+      this.Controls.SetChildIndex(this.publicCheckBox, 0);
+      this.Controls.SetChildIndex(this.nameTextBox, 0);
+      this.Controls.SetChildIndex(this.cpuLabel, 0);
+      this.Controls.SetChildIndex(this.cpuTextBox, 0);
+      this.Controls.SetChildIndex(this.memoryLabel, 0);
+      this.Controls.SetChildIndex(this.memoryTextBox, 0);
+      this.Controls.SetChildIndex(this.operatingSystemLabel, 0);
+      this.Controls.SetChildIndex(this.operatingSystemTextBox, 0);
+      this.Controls.SetChildIndex(this.stateLabel, 0);
+      this.Controls.SetChildIndex(this.stateTextBox, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatLabel, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatTextBox, 0);
+      this.Controls.SetChildIndex(this.disposableLabel, 0);
+      this.Controls.SetChildIndex(this.disposableCheckBox, 0);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
       this.ResumeLayout(false);
       this.PerformLayout();
@@ -338,28 +219,16 @@
     #endregion
 
-    private System.Windows.Forms.TextBox txtSlaveState;
-    private System.Windows.Forms.TextBox txtOS;
-    private System.Windows.Forms.Label label15;
-    private System.Windows.Forms.Label label14;
-    private System.Windows.Forms.TextBox txtDetailsDescription;
-    private System.Windows.Forms.Label label13;
-    private System.Windows.Forms.TextBox txtName;
-    private System.Windows.Forms.TextBox txtCPU;
-    private System.Windows.Forms.TextBox txtMemory;
-    private System.Windows.Forms.TextBox txtLastHeartbeat;
-    private System.Windows.Forms.Label label12;
-    private System.Windows.Forms.Label label11;
-    private System.Windows.Forms.Label label10;
-    private System.Windows.Forms.Label label9;
-    private System.Windows.Forms.Label label1;
-    private System.Windows.Forms.TextBox txtFreeMemory;
-    private System.Windows.Forms.TextBox txtId;
-    private System.Windows.Forms.Label label2;
-    private System.Windows.Forms.Label label3;
-    private System.Windows.Forms.TextBox txtHbIntervall;
-    private System.Windows.Forms.CheckBox cbxDisposable;
-    private System.Windows.Forms.Label label4;
-    private System.Windows.Forms.CheckBox cbxPublic;
-    private System.Windows.Forms.Label label5;
+    private System.Windows.Forms.Label cpuLabel;
+    private System.Windows.Forms.TextBox cpuTextBox;
+    private System.Windows.Forms.Label memoryLabel;
+    private System.Windows.Forms.TextBox memoryTextBox;
+    private System.Windows.Forms.Label operatingSystemLabel;
+    private System.Windows.Forms.TextBox operatingSystemTextBox;
+    private System.Windows.Forms.Label stateLabel;
+    private System.Windows.Forms.TextBox stateTextBox;
+    private System.Windows.Forms.Label lastHeartbeatLabel;
+    private System.Windows.Forms.TextBox lastHeartbeatTextBox;
+    private System.Windows.Forms.Label disposableLabel;
+    private System.Windows.Forms.CheckBox disposableCheckBox;
   }
 }
Index: stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs
===================================================================
--- stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 15587)
+++ stable/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 17059)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -22,15 +22,13 @@
 using System;
 using System.Windows.Forms;
-using HeuristicLab.Clients.Access;
-using HeuristicLab.Core.Views;
 using HeuristicLab.MainForm;
 using HeuristicLab.MainForm.WindowsForms;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
-  [View("SlaveView")]
-  [Content(typeof(Resource), IsDefaultView = true)]
-  public partial class SlaveView : ItemView {
-    public new Resource Content {
-      get { return (Resource)base.Content; }
+  [View("Slave View")]
+  [Content(typeof(Slave), IsDefaultView = true)]
+  public sealed partial class SlaveView : ResourceView {
+    public new Slave Content {
+      get { return (Slave)base.Content; }
       set { base.Content = value; }
     }
@@ -40,123 +38,37 @@
     }
 
-    #region Register Content Events
-    protected override void DeregisterContentEvents() {
-      base.DeregisterContentEvents();
-    }
-    protected override void RegisterContentEvents() {
-      base.RegisterContentEvents();
-    }
-    #endregion
-
+    #region Overrides
     protected override void OnContentChanged() {
       base.OnContentChanged();
       if (Content == null) {
-        ShowSlaveUI(true);
-        txtName.Clear();
-        txtCPU.Clear();
-        txtDetailsDescription.Clear();
-        txtMemory.Clear();
-        txtOS.Clear();
-        txtSlaveState.Clear();
-        txtLastHeartbeat.Clear();
-        txtFreeMemory.Clear();
-        txtId.Clear();
-        txtHbIntervall.Clear();
-        cbxDisposable.Checked = false;
-        cbxPublic.Checked = false;
+        cpuTextBox.Clear();
+        memoryTextBox.Clear();
+        operatingSystemTextBox.Clear();
+        stateTextBox.Clear();
+        lastHeartbeatTextBox.Clear();
+        disposableCheckBox.Checked = false;
       } else {
-        if (Content.GetType() == typeof(Slave)) {
-          ShowSlaveUI(true);
-          Slave ct = (Slave)Content;
-          bool authorized = UserInformation.Instance.UserExists && (ct.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = authorized;
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          txtCPU.Text = string.Format("{0} Cores @ {1} Mhz, Arch.: {2}", ct.Cores.ToString(), ct.CpuSpeed.ToString(), ct.CpuArchitecture.ToString());
-          txtDetailsDescription.Text = ct.Description;
-          txtMemory.Text = ct.Memory.ToString();
-          txtOS.Text = ct.OperatingSystem;
-          txtSlaveState.Text = ct.SlaveState.ToString();
-          txtLastHeartbeat.Text = ct.LastHeartbeat.ToString();
-          txtFreeMemory.Text = ct.FreeMemory.ToString();
-          txtId.Text = ct.Id.ToString();
-          cbxDisposable.Enabled = authorized;
-          cbxDisposable.Checked = ct.IsDisposable.GetValueOrDefault();
-        } else if (Content.GetType() == typeof(SlaveGroup)) {
-          SlaveGroup ct = (SlaveGroup)Content;
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = ct.Name != "UNGROUPED" && HiveRoles.CheckAdminUserPermissions();
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          ShowSlaveUI(false);
-        } else {
-          throw new Exception("Unknown Resource in SlaveView");
-        }
+        cpuTextBox.Text = string.Format("{0} Cores @ {1} MHz, Arch.: {2}", Content.Cores, Content.CpuSpeed, Content.CpuArchitecture);
+        memoryTextBox.Text = string.Format("{0} ({1} Free)", Content.Memory, Content.FreeMemory);
+        operatingSystemTextBox.Text = Content.OperatingSystem;
+        stateTextBox.Text = Content.SlaveState.ToString();
+        lastHeartbeatTextBox.Text = Content.LastHeartbeat.ToString();
+        disposableCheckBox.Checked = Content.IsDisposable.GetValueOrDefault();
       }
-    }
-
-    private void ShowSlaveUI(bool show) {
-      label1.Visible = show;
-      label2.Visible = show;
-      label4.Visible = show;
-      label10.Visible = show;
-      label11.Visible = show;
-      label12.Visible = show;
-      label13.Visible = show;
-      label14.Visible = show;
-      label15.Visible = show;
-      txtCPU.Visible = show;
-      txtDetailsDescription.Visible = show;
-      txtMemory.Visible = show;
-      txtOS.Visible = show;
-      txtSlaveState.Visible = show;
-      txtLastHeartbeat.Visible = show;
-      txtFreeMemory.Visible = show;
-      txtId.Visible = show;
-      txtName.Enabled = !show;
-      cbxDisposable.Visible = show;
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
+      bool enabled = Content != null && !Locked;
+      disposableCheckBox.Enabled = enabled;
     }
+    #endregion
 
-    private void txtName_TextChanged(object sender, EventArgs e) {
-      if (Content != null && Content is SlaveGroup) {
-        Content.Name = txtName.Text;
-      }
+    #region Event Handlers
+    private void disposableCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (Content != null)
+        Content.IsDisposable = disposableCheckBox.Checked;
     }
-
-    private void txtHbIntervall_TextChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        if (txtHbIntervall.Text.Length > 0) {
-          try {
-            int interval = int.Parse(txtHbIntervall.Text);
-            Content.HbInterval = interval;
-          }
-          catch (Exception) {
-            MessageBox.Show("Please enter a numeric value for the Heartbeat Interval.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            txtHbIntervall.Text = "10";
-          }
-        }
-      }
-    }
-
-    private void cbxDisposable_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        ((Slave)Content).IsDisposable = cbxDisposable.Checked;
-      }
-    }
-
-    private void cbxPublic_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        Content.OwnerUserId = cbxPublic.Checked ? null : new Guid?(UserInformation.Instance.User.Id);
-      }
-    }
+    #endregion
   }
 }
