Index: branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs
===================================================================
--- branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 15630)
+++ branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 15631)
@@ -60,5 +60,4 @@
       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.tabSlaveGroup = new System.Windows.Forms.TabControl();
@@ -67,7 +66,8 @@
       this.tabSchedule = new System.Windows.Forms.TabPage();
       this.scheduleView = new HeuristicLab.Clients.Hive.Administrator.Views.ScheduleView();
+      this.tabPermissions = new System.Windows.Forms.TabPage();
+      this.btnPermissionsSave = new System.Windows.Forms.Button();
+      this.permissionView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
       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();
@@ -88,6 +88,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);
@@ -165,7 +165,8 @@
       // 
       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.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.CheckBoxes = true;
       this.treeSlaveGroup.ImageIndex = 0;
       this.treeSlaveGroup.ImageList = this.imageListSlaveGroups;
@@ -178,12 +179,11 @@
       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);
+      this.treeSlaveGroup.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
+      this.treeSlaveGroup.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnterOver);
       // 
       // 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);
@@ -210,6 +210,6 @@
       // slaveView
       // 
-      this.slaveView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      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";
@@ -234,6 +234,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";
@@ -271,6 +271,6 @@
       // permissionView
       // 
-      this.permissionView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-            | System.Windows.Forms.AnchorStyles.Left)
+      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";
Index: branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs
===================================================================
--- branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 15630)
+++ branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 15631)
@@ -44,5 +44,6 @@
     }
 
-    public const string UngroupedGroupName = "UNGROUPED";
+    public const string UNGROUPED_GROUP_NAME = "UNGROUPED";
+    private TreeNode ungroupedGroupNode;
     private const int slaveImageIndex = 0;
     private const int slaveGroupImageIndex = 1;
@@ -140,9 +141,8 @@
         var ungroupedSlaves = new SlaveGroup {
           Id = Guid.NewGuid(),
-          Name = UngroupedGroupName,
+          Name = UNGROUPED_GROUP_NAME,
           Description = "Slaves without a group"
         };
-        var ungroupedSlavesCount = 0;
-        var ungroupedNode = CreateTreeNodeFromResource(ungroupedSlaves);
+        ungroupedGroupNode = CreateTreeNode(ungroupedSlaves);
 
         var parentlessResources = from r in Content
@@ -152,15 +152,14 @@
         foreach (Resource r in parentlessResources) {
           if (r.GetType() == typeof(SlaveGroup)) {  // root node
-            var root = CreateTreeNodeFromResource(r);
+            var root = CreateTreeNode(r);
             treeSlaveGroup.Nodes.Add(root);
             BuildSlaveGroupTree((SlaveGroup)r, root);
           } else if (r.GetType() == typeof(Slave)) {  // ungrouped slaves
-            ungroupedNode.Nodes.Add(CreateTreeNodeFromResource(r));
-            ungroupedSlavesCount++;
+            ungroupedGroupNode.Nodes.Add(CreateTreeNode(r));
           }
         }
-        if (ungroupedSlavesCount > 0)
-          ungroupedNode.Text += "   [" + ungroupedSlavesCount.ToString() + "]";
-        treeSlaveGroup.Nodes.Add(ungroupedNode);
+        if (ungroupedGroupNode.Nodes.Count > 0)
+          ungroupedGroupNode.Text += "   [" + ungroupedGroupNode.Nodes.Count.ToString() + "]";
+        treeSlaveGroup.Nodes.Add(ungroupedGroupNode);
       }
     }
@@ -172,5 +171,5 @@
                         select r;
       foreach (var g in childGroups) {
-        var childNode = CreateTreeNodeFromResource(g);
+        var childNode = CreateTreeNode(g);
         node.Nodes.Add(childNode);
         BuildSlaveGroupTree(g, childNode);
@@ -183,5 +182,5 @@
       var childSlavesCount = 0;
       foreach (var s in childSlaves) {
-        node.Nodes.Add(CreateTreeNodeFromResource(s));
+        node.Nodes.Add(CreateTreeNode(s));
         childSlavesCount++;
       }
@@ -190,5 +189,5 @@
     }
 
-    private TreeNode CreateTreeNodeFromResource(Resource r) {
+    private TreeNode CreateTreeNode(Resource r) {
       var node = new TreeNode();
       node.Text = r.Name;
@@ -239,5 +238,5 @@
     private bool IsAuthorized(Resource resource) {
       return resource != null
-          && resource.Name != UngroupedGroupName
+          && resource != ungroupedGroupNode.Tag
           && resource.Id != Guid.Empty
           && UserInformation.Instance.UserExists
@@ -347,95 +346,56 @@
     }
 
+    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
+      var nodes = GetCheckedNodes(treeSlaveGroup.Nodes).ToList();
+      var draggedNode = (TreeNode)e.Item;
+      if (!draggedNode.Checked) nodes.Add(draggedNode);
+      nodes.Remove(ungroupedGroupNode);
+      var resources = nodes.Select(x => x.Tag).OfType<Resource>().ToList();
+
+      if ((resources.Count > 0) && resources.TrueForAll((r) => IsAuthorized(r))) {
+        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();
+          ungroupedGroupNode.Text = ((Resource)ungroupedGroupNode.Tag).Name;
+          if (ungroupedGroupNode.Nodes.Count > 0)
+            ungroupedGroupNode.Text += "   [" + ungroupedGroupNode.Nodes.Count.ToString() + "]";
+
+        }
+      }
+    }
+
+    private IEnumerable<TreeNode> GetCheckedNodes(TreeNodeCollection nodes) {
+      if (nodes != null) {
+        foreach (var node in nodes.OfType<TreeNode>()) {
+          if (node.Checked) yield return node;
+          foreach (var child in GetCheckedNodes(node.Nodes))
+            yield return child;
+        }
+      }
+    }
+
+    private void treeSlaveGroup_DragEnterOver(object sender, DragEventArgs e) {
+      e.Effect = DragDropEffects.None;
+      var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+      var node = treeSlaveGroup.GetNodeAt(treeSlaveGroup.PointToClient(new Point(e.X, e.Y)));
+      var group = node?.Tag as SlaveGroup;
+
+      if ((resources != null) && (node != null) && (group != null) && IsAuthorized(group) && (!resources.Contains(group))) {
+        if (e.AllowedEffect.HasFlag(DragDropEffects.Move)) e.Effect = DragDropEffects.Move;
+      }
+    }
+
     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;
+      if (e.Effect != DragDropEffects.None) {
+        var node = treeSlaveGroup.GetNodeAt(treeSlaveGroup.PointToClient(new Point(e.X, e.Y)));
+        var group = (SlaveGroup)node.Tag;  // drop is only allowed on slave groups, validity of drop target is check in treeSlaveGroup_DragEnterOver
+        var resources = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as IEnumerable<Resource>;
+
+        foreach (var r in resources) r.ParentResourceId = group.Id;
+        node.Nodes.Clear();
+        BuildSlaveGroupTree(group, node);
+      }
     }
 
