--- fieldgroup.module.orig	2008-04-29 01:50:29.000000000 +0200
+++ fieldgroup.module	2008-07-28 11:44:33.000000000 +0200
@@ -90,6 +90,16 @@ function fieldgroup_edit_group_form($con
     '#title' => 'Form settings',
     '#description' => t('These settings apply to the group in the node editing form'),
   );
+
+  $parent_groups_tree = _fieldgroup_get_tree($content_type['type']);
+  $parent_groups = _fieldgroup_plain_tree($parent_groups_tree);
+  $form['settings']['form']['parent_group_name'] = array(
+    '#type' => 'select',
+    '#title' => t('Parent'),
+    '#default_value' => $group['settings']['form']['parent_group_name'] ? $group['settings']['form']['parent_group_name'] : '',
+    '#options' => array('' => t('Top')) + $parent_groups,
+  );
+
   $form['settings']['form']['style'] = array(
     '#type' => 'radios',
     '#title' => t('Style'),
@@ -127,6 +137,7 @@ function fieldgroup_edit_group_form($con
   $form['weight'] = array('#type' => 'hidden', '#default_value' => $group['weight']);
   $form['group_name'] = array('#type' => 'hidden', '#default_value' => $group_name);
   $form['#submit'] = array('fieldgroup_edit_group_submit' => array($content_type, $action));
+  $form['#validate'] = array('fieldgroup_edit_group_validate' => array($content_type, $action));
   return $form;
 }
 
@@ -134,6 +145,11 @@ function fieldgroup_edit_group_form($con
  *  Group name validation for programmatic group addition.
  */
 function fieldgroup_edit_group_validate($form_id, $form_values, $content_type, $action) {
+
+  if ($form_values['group_name'] && ($form_values['settings']['form']['parent_group_name'] == $form_values['group_name'])) {
+      form_set_error('parent_group_name', t('The parent group cannot be the group itself.'));
+  }
+
   if (!empty($form_values['group_name']) && $action == 'add') {
 
     $groups = fieldgroup_groups($content_type['type']);
@@ -186,14 +202,14 @@ function fieldgroup_save_group($type_nam
         }
       }
     }
-    db_query("INSERT INTO {node_group} (type_name, group_name, label, settings, weight)
-             VALUES ('%s', '%s', '%s', '%s', %d)", $type_name, $group_name, $group['label'], serialize($group['settings']), $group['weight']);
+    db_query("INSERT INTO {node_group} (type_name, group_name, parent_group_name, label, settings, weight)
+             VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $type_name, $group_name, $group['settings']['form']['parent_group_name'], $group['label'], serialize($group['settings']), $group['weight']);
     return SAVED_NEW;
   }
   else {
-    db_query("UPDATE {node_group} SET label = '%s', settings = '%s', weight = %d ".
+    db_query("UPDATE {node_group} SET parent_group_name='%s', label = '%s', settings = '%s', weight = %d ".
              "WHERE type_name = '%s' AND group_name = '%s'",
-             $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']);
+             $group['settings']['form']['parent_group_name'], $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']);
     return SAVED_UPDATED;
   }
 }
@@ -225,7 +241,6 @@ function fieldgroup_remove_group_submit(
 }
 
 
-
 /*
  * Returns all groups for a content type
  */
@@ -269,7 +284,6 @@ function fieldgroup_groups($content_type
   return $sorted ? $groups_sorted[$content_type] : $groups[$content_type];
 }
 
-
 function _fieldgroup_groups_label($content_type) {
   $groups = fieldgroup_groups($content_type);
 
@@ -284,40 +298,99 @@ function _fieldgroup_field_get_group($co
   return db_result(db_query("SELECT group_name FROM {node_group_fields} WHERE type_name = '%s' AND field_name = '%s'", $content_type, $field_name));
 }
 
+function _fieldgroup_add_group_to_form(&$form, $element, $group_name, $group, $groups) {
+  $element = array(
+    '#type' => 'fieldset',
+    '#title' => t($group['label']),
+    '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed',
+    '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')),
+    '#weight' => $group['weight'],
+    '#description' => t($group['settings']['form']['description']),
+    '#attributes' => array('class' => strtr($group['group_name'], '_', '-')),
+  );
+  foreach ($group['fields'] as $field_name => $field) {
+    if (isset($form[$field_name])) {
+      $element[$field_name] = $form[$field_name];
+      unset($form[$field_name]);
+    }
+  }
+  if (!empty($group['fields']) && !element_children($form[$group_name])) {
+    //hide the fieldgroup, because the fields are hidden too
+    unset($element[$group_name]);
+  }
+
+  $parent_group_name = $group_name;
+  foreach ($groups as $group_name => $group) {
+    if ($group['parent_group_name'] == $parent_group_name) {
+      $element[$group_name] = _fieldgroup_add_group_to_form($form, $element[$group_name], $group_name, $group, $groups);
+    }
+  }
+
+  return $element;
+}
+
 /*
  * Implementation of hook_form_alter()
  */
 function fieldgroup_form_alter($form_id, &$form) {
   if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
-    foreach (fieldgroup_groups($form['type']['#value']) as $group_name => $group) {
-      $form[$group_name] = array(
-        '#type' => 'fieldset',
-        '#title' => t($group['label']),
-        '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed',
-        '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')),
-        '#weight' => $group['weight'],
-        '#description' => t($group['settings']['form']['description']),
-        '#attributes' => array('class' => strtr($group['group_name'], '_', '-')),
-      );
-      foreach ($group['fields'] as $field_name => $field) {
-        if (isset($form[$field_name])) {
-          $form[$group_name][$field_name] = $form[$field_name];
-          unset($form[$field_name]);
+    $groups_tree = _fieldgroup_get_tree($form['type']['#value']);
+    $tgroups = fieldgroup_groups($form['type']['#value']);
+    $groups = array();
+    foreach ($groups_tree as $key => $group_tree) {
+      $group_name = $group_tree->group_name;
+      $groups[$group_name] = $tgroups[$group_name];
+      $groups[$group_name]['depth'] = $group_tree->depth;
+    }
+    if ((arg(0)=='node' && arg(1)=='add') ||
+      (arg(0)=='node' && is_numeric(arg(1)) && arg(2)=='edit')
+    ) {
+      foreach ($groups as $group_name=>$group) {
+        $parent_group_name = $group['parent_group_name'];
+        if ($parent_group_name) {
+          $groups[$group_name]['weight'] = $groups[$parent_group_name]['weight'] + abs($groups[$group_name]['depth'] * .1) + abs($groups[$group_name]['weight'] * .01);
         }
       }
-      if (!empty($group['fields']) && !element_children($form[$group_name])) {
-        //hide the fieldgroup, because the fields are hidden too
-        unset($form[$group_name]);
+      foreach ($groups as $group_name => $group) {
+        $parent_group_name = $group['parent_group_name'];
+        if (!$parent_group_name) {
+          $form[$group_name] = _fieldgroup_add_group_to_form($form, $form[$group_name], $group_name, $group, $groups);
+        }
+      }
+    }
+    else {
+      //Sort groups by weight
+      foreach ($groups as $group_name => $group) {
+        $form[$group_name] = array(
+          '#type' => 'fieldset',
+          '#title' => t($group['label']),
+          '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed',
+          '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')),
+          '#weight' => $group['weight'],
+          '#description' => t($group['settings']['form']['description']),
+          '#attributes' => array('class' => strtr($group['group_name'], '_', '-')),
+        );
+        foreach ($group['fields'] as $field_name => $field) {
+          if (isset($form[$field_name])) {
+            $form[$group_name][$field_name] = $form[$field_name];
+            unset($form[$field_name]);
+          }
+        }
+        if (!empty($group['fields']) && !element_children($form[$group_name])) {
+          //hide the fieldgroup, because the fields are hidden too
+          unset($form[$group_name]);
+        }
       }
     }
-
   }
   else if ($form_id == '_content_admin_field') {
     $content_type = content_types($form['type_name']['#value']);
+    $groups_tree = _fieldgroup_get_tree($content_type['type']);
+    $groups = array(0 => t('No group')) + _fieldgroup_plain_tree($groups_tree);
     $form['widget']['group'] = array(
       '#type' => 'select',
       '#title' => t('Display in group'),
-      '#options' => _fieldgroup_groups_label($content_type['type']),
+      '#options' => $groups,
       '#default_value' => _fieldgroup_field_get_group($content_type['type'], $form['field_name']['#value']),
       '#description' => t('Select a group, in which the field will be displayed on the editing form.'),
       '#weight' => 5,
@@ -460,10 +533,22 @@ function fieldgroup_node_type($op, $info
  * Adds a own table to the "display fields" tab
  */
 function fieldgroup_display_overview_form($content_type) {
-  $groups = fieldgroup_groups($content_type);
+  $tgroups_tree = _fieldgroup_get_tree($content_type);
+  $tgroups = _fieldgroup_plain_tree($tgroups_tree);
+
+  $result = db_query("SELECT * FROM {node_group} WHERE type_name='%s' ORDER BY weight, group_name", $content_type);
+  $groups = array();
+  $groups_sorted = array();
+  while ($group = db_fetch_array($result)) {
+    $group['settings'] = unserialize($group['settings']);
+    $groups[$group['group_name']] = $group;
+  }
+
   $form = array();
   $form['#tree'] = TRUE;
-  foreach ($groups as $group_name => $group) {
+  foreach ($tgroups as $group_name => $group_label) {
+    $group = $groups[$group_name];
+    $group['label'] = $tgroups[$group_name];
     $form[$group_name] = fieldgroup_display_overview_form_row($group);
   }
 
@@ -665,4 +750,50 @@ function fieldgroup_panels_fieldgroup_ti
   $types = fieldgroup_groups(NULL, FALSE, FALSE);
   $type = $types[$conf['type_name']][$conf['group']];
   return t('"@s" fieldgroup @name', array('@s' => $context->identifier, '@name' => $type['label']));
-}
\ No newline at end of file
+}
+
+function _fieldgroup_get_tree($type_name, $parent_group_name = '', $depth = -1, $max_depth = NULL) {
+  static $children, $parents, $groups;
+
+  $depth++;
+  // We cache trees, so it's not CPU-intensive to call get_tree() on a term
+  // and its children, too.
+  if (!isset($children[$type_name])) {
+    $children[$type_name] = array();
+
+    $s = "SELECT * FROM {node_group} WHERE type_name='%s' ORDER BY weight";
+    $r = db_query($s, $type_name);
+    while ($group = db_fetch_object($r)) {
+      $children[$type_name][$group->parent_group_name][] = $group->group_name;
+      $parents[$type_name][$group->group_name][] = $group->parent_group_name;
+      $groups[$type_name][$group->group_name] = $group;
+    }
+  }
+
+  $max_depth = (is_null($max_depth)) ? count($children[$type_name]) : $max_depth;
+  if ($children[$type_name][$parent_group_name]) {
+    foreach ($children[$type_name][$parent_group_name] as $child_group_name) {
+      if ($max_depth > $depth) {
+        $group = drupal_clone($groups[$type_name][$child_group_name]);
+        $group->depth = $depth;
+        unset($group->parent_group_name);
+        $group->parents = $parents[$type_name][$child_group_name];
+        $tree[] = $group;
+        if ($children[$type_name][$child_group_name]) {
+          $tree = array_merge($tree, _fieldgroup_get_tree($type_name, $child_group_name, $depth, $max_depth));
+        }
+      }
+    }
+  }
+  return $tree ? $tree : array();
+}
+
+function _fieldgroup_plain_tree($tree) {
+  $rows = array();
+  foreach ($tree as $item) {
+    $group_name = $item->group_name;
+    $label = $item->label;
+    if ($group_name) $rows[$group_name] = str_repeat('--', $item->depth) .' '. $label;
+  }
+  return $rows;
+}
