diff --git a/editors/ckeditor.inc b/editors/ckeditor.inc
index 8489512..cac77ec 100644
--- a/editors/ckeditor.inc
+++ b/editors/ckeditor.inc
@@ -28,6 +28,10 @@ function wysiwyg_ckeditor_editor() {
         ),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_ckeditor_version',
     'themes callback' => 'wysiwyg_ckeditor_themes',
     'settings callback' => 'wysiwyg_ckeditor_settings',
@@ -186,52 +190,71 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) {
     $settings['toolbarLocation'] = $config['toolbar_loc'];
   }
 
-  if (!empty($config['buttons'])) {
-    $extra_plugins = array();
-    $settings['toolbar'] = array();
-    $plugins = wysiwyg_get_plugins($editor['name']);
-    foreach ($config['buttons'] as $plugin => $buttons) {
-      foreach ($buttons as $button => $enabled) {
-        // Iterate separately over buttons and extensions properties.
-        foreach (array('buttons', 'extensions') as $type) {
-          // Skip unavailable plugins.
-          if (!isset($plugins[$plugin][$type][$button])) {
-            continue;
-          }
-          // Add buttons.
-          if ($type == 'buttons') {
-            $settings['toolbar'][] = $button;
-          }
-          // Add external Drupal plugins to the list of extensions.
-          if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) {
-            $extra_plugins[] = $button;
-          }
-          // Add external plugins to the list of extensions.
-          elseif ($type == 'buttons' && empty($plugins[$plugin]['internal'])) {
-            $extra_plugins[] = $plugin;
-          }
-          // Add internal buttons that also need to be loaded as extension.
-          elseif ($type == 'buttons' && !empty($plugins[$plugin]['load'])) {
-            $extra_plugins[] = $plugin;
-          }
-          // Add plain extensions.
-          elseif ($type == 'extensions' && !empty($plugins[$plugin]['load'])) {
-            $extra_plugins[] = $plugin;
-          }
-          // Allow plugins to add or override global configuration settings.
-          if (!empty($plugins[$plugin]['options'])) {
-            $settings = array_merge($settings, $plugins[$plugin]['options']);
-          }
-        }
+  $extra_plugins = array();
+  $toolbar = array();
+  $plugins = wysiwyg_get_plugins($editor['name']);
+
+  foreach ((array)$config['extensions'] as $plugin => $buttons) {
+    foreach ($buttons as $button => $enabled) {
+      if (!isset($plugins[$plugin]['extensions'][$button])) {
+        continue;
+      }
+
+      // Add plain extensions.
+      if (!empty($plugins[$plugin]['load'])) {
+        $extra_plugins[] = $plugin;
+      }
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
       }
     }
-    if (!empty($extra_plugins)) {
-      $settings['extraPlugins'] = implode(',', $extra_plugins);
-    }
-    // For now, all buttons are placed into one row.
-    if (!empty($settings['toolbar'])) {
-      $settings['toolbar'] = array($settings['toolbar']);
+  }
+
+  foreach ((array)$config['toolbar'] as $buttons) {
+    $group = array();
+
+    foreach ($buttons as $button_config) {
+      $plugin = $button_config['plugin'];
+      $button = $button_config['button'];
+
+      if ($button == WYSIWYG_SEPARATOR) {
+        $group[] = '-';
+        continue;
+      }
+      if (!isset($plugins[$plugin]['buttons'][$button])) {
+        continue;
+      }
+
+      $group[] = $button;
+
+      // Add external Drupal plugins to the list of extensions.
+      if (!empty($plugins[$plugin]['proxy']) || empty($plugins[$plugin]['internal']) || !empty($plugins[$plugin]['load'])) {
+        $extra_plugins[] = $button;
+      }
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
+      }
     }
+
+    $toolbar[] = $group;
+    $toolbar[] = '/'; // toolbar line break
+  }
+
+  // Remove the last '/'.
+  if (end($toolbar) == '/') {
+    array_pop($toolbar);
+  }
+
+  if (!empty($extra_plugins)) {
+    $settings['extraPlugins'] = implode(',', array_unique($extra_plugins));
+  }
+
+  if (!empty($toolbar)) {
+    $settings['toolbar'] = $toolbar;
   }
 
   return $settings;
diff --git a/editors/fckeditor.inc b/editors/fckeditor.inc
index 6fb5a1f..1c0820d 100644
--- a/editors/fckeditor.inc
+++ b/editors/fckeditor.inc
@@ -20,6 +20,10 @@ function wysiwyg_fckeditor_editor() {
         'files' => array('fckeditor.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_fckeditor_version',
     'themes callback' => 'wysiwyg_fckeditor_themes',
     'settings callback' => 'wysiwyg_fckeditor_settings',
@@ -129,40 +133,63 @@ function wysiwyg_fckeditor_settings($editor, $config, $theme) {
     if ($config['css_setting'] == 'theme') {
       $settings['EditorAreaCSS'] = implode(',', wysiwyg_get_css());
     }
-    else if ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+    elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
       $settings['EditorAreaCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme()));
     }
   }
 
-  if (!empty($config['buttons'])) {
-    // Use our custom toolbar set.
-    $settings['ToolbarSet'] = 'Wysiwyg';
-    // Populate our custom toolbar set for fckeditor.config.js.
-    $settings['buttons'] = array();
-    $plugins = wysiwyg_get_plugins($editor['name']);
-    foreach ($config['buttons'] as $plugin => $buttons) {
-      foreach ($buttons as $button => $enabled) {
-        // Iterate separately over buttons and extensions properties.
-        foreach (array('buttons', 'extensions') as $type) {
-          // Skip unavailable plugins.
-          if (!isset($plugins[$plugin][$type][$button])) {
-            continue;
-          }
-          // Add buttons.
-          if ($type == 'buttons') {
-            $settings['buttons'][] = $button;
-          }
-          // Allow plugins to add or override global configuration settings.
-          if (!empty($plugins[$plugin]['options'])) {
-            $settings = array_merge($settings, $plugins[$plugin]['options']);
-          }
-        }
+  $plugins = wysiwyg_get_plugins($editor['name']);
+  $toolbar = array();
+
+  foreach ((array)$config['extensions'] as $plugin => $buttons) {
+    foreach ($buttons as $button => $enabled) {
+      if (!isset($plugins[$plugin]['extensions'][$button])) {
+        continue;
+      }
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
       }
     }
-    // For now, all buttons are placed into one row.
-    if (!empty($settings['buttons'])) {
-      $settings['buttons'] = array($settings['buttons']);
+  }
+
+  foreach ((array)$config['toolbar'] as $buttons) {
+    $group = array();
+
+    foreach ($buttons as $button_config) {
+      $plugin = $button_config['plugin'];
+      $button = $button_config['button'];
+
+      if ($button == WYSIWYG_SEPARATOR) {
+        $group[] = '-';
+        continue;
+      }
+      if (!isset($plugins[$plugin]['buttons'][$button])) {
+        continue;
+      }
+
+      $group[] = $button;
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
+      }
     }
+
+    $toolbar[] = $group;
+    $toolbar[] = '/'; // toolbar line break
+  }
+
+  // Remove the last '/'.
+  if (end($toolbar) == '/') {
+    array_pop($toolbar);
+  }
+
+  if (!empty($toolbar)) {
+    // Use our custom toolbar set.
+    $settings['ToolbarSet'] = 'DrupalWysiwyg';
+    $settings['ToolbarSets']['DrupalWysiwyg'] = $toolbar;
   }
 
   return $settings;
diff --git a/editors/js/fckeditor.config.js b/editors/js/fckeditor.config.js
index 2c3415f..77ca0eb 100644
--- a/editors/js/fckeditor.config.js
+++ b/editors/js/fckeditor.config.js
@@ -17,27 +17,18 @@ var pluginSettings = Drupal.settings.wysiwyg.plugins[wysiwygFormat];
  * Apply format-specific settings.
  */
 for (var setting in wysiwygSettings) {
-  if (setting == 'buttons') {
-    // Apply custom Wysiwyg toolbar for this format.
-    // FCKConfig.ToolbarSets['Wysiwyg'] = wysiwygSettings.buttons;
+  FCKConfig[setting] = wysiwygSettings[setting];
+}
 
-    // Temporarily stack buttons into multiple button groups and remove
-    // separators until #277954 is solved.
-    FCKConfig.ToolbarSets['Wysiwyg'] = [];
-    for (var i = 0; i < wysiwygSettings.buttons[0].length; i++) {
-      FCKConfig.ToolbarSets['Wysiwyg'].push([wysiwygSettings.buttons[0][i]]);
-    }
-    FCKTools.AppendStyleSheet(document, '#xToolbar .TB_Start { display:none; }');
-    // Set valid height of select element in silver and office2003 skins.
-    if (FCKConfig.SkinPath.match(/\/office2003\/$/)) {
-      FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 24px; } #xToolbar .TB_End { display: none; }');
-    }
-    else if (FCKConfig.SkinPath.match(/\/silver\/$/)) {
-      FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 27px; }');
-    }
+// Add custom stylesheet to editor toolbar.
+FCKTools.AppendStyleSheet(document, '#xToolbar .TB_Start { display:none; }');
+if (FCKConfig.ToolbarSet == 'DrupalWysiwyg') {
+  // Set valid height of select element in silver and office2003 skins.
+  if (FCKConfig.SkinPath.match(/\/office2003\/$/)) {
+    FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 24px; } #xToolbar .TB_End { display: none; }');
   }
-  else {
-    FCKConfig[setting] = wysiwygSettings[setting];
+  else if (FCKConfig.SkinPath.match(/\/silver\/$/)) {
+    FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 27px; }');
   }
 }
 
diff --git a/editors/js/tinymce-3.js b/editors/js/tinymce-3.js
index c90ffd5..833b1ec 100644
--- a/editors/js/tinymce-3.js
+++ b/editors/js/tinymce-3.js
@@ -55,15 +55,7 @@ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
   ed.onEvent.add(function(ed, e) {
     Drupal.wysiwyg.activeId = ed.id;
   });
-  // Make toolbar buttons wrappable (required for IE).
-  ed.onPostRender.add(function (ed) {
-    var $toolbar = $('<div class="wysiwygToolbar"></div>');
-    $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function () {
-      $('<div></div>').addClass(this.className).append($(this).children()).appendTo($toolbar);
-    });
-    $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar);
-    $('#' + ed.editorContainer + ' table.mceToolbar').remove();
-  });
+
   // Attach editor.
   ed.render();
 };
diff --git a/editors/markitup.inc b/editors/markitup.inc
index c9650ba..ad83438 100644
--- a/editors/markitup.inc
+++ b/editors/markitup.inc
@@ -25,6 +25,10 @@ function wysiwyg_markitup_editor() {
         'files' => array('jquery.markitup.pack.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => FALSE,
+      'separator' => FALSE,
+    ),
     'version callback' => 'wysiwyg_markitup_version',
     'themes callback' => 'wysiwyg_markitup_themes',
     'settings callback' => 'wysiwyg_markitup_settings',
@@ -144,12 +148,12 @@ function wysiwyg_markitup_settings($editor, $config, $theme) {
       'call' => 'preview',
     ),
   );
-  if (!empty($config['buttons'])) {
-    foreach ($config['buttons'] as $plugin) {
-      foreach ($plugin as $button => $enabled) {
-        if (isset($default_buttons[$button])) {
-          $settings['markupSet'][$button] = $default_buttons[$button];
-        }
+  if (!empty($config['toolbar'])) {
+    $settings['markupSet'] = array();
+
+    foreach ($config['toolbar'] as $group) {
+      foreach ($group as $button) {
+        $settings['markupSet'][$button['button']] = $default_buttons[$button['button']];
       }
     }
   }
diff --git a/editors/nicedit.inc b/editors/nicedit.inc
index 30ce42d..7bd813d 100644
--- a/editors/nicedit.inc
+++ b/editors/nicedit.inc
@@ -20,6 +20,10 @@ function wysiwyg_nicedit_editor() {
         'files' => array('nicEdit.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => FALSE,
+      'separator' => FALSE,
+    ),
     'version callback' => 'wysiwyg_nicedit_version',
     'settings callback' => 'wysiwyg_nicedit_settings',
     'plugin callback' => 'wysiwyg_nicedit_plugins',
@@ -66,12 +70,14 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) {
   );
 
   // Add configured buttons or all available.
-  if (!empty($config['buttons'])) {
+  if (!empty($config['toolbar'])) {
     $buttons = array();
-    foreach ($config['buttons'] as $plugin) {
-      $buttons = array_merge($buttons, $plugin);
+    foreach ($config['toolbar'] as $group) {
+      foreach ($group as $button) {
+        $buttons[] = $button['button'];
+      }
     }
-    $settings['buttonList'] = array_keys($buttons);
+    $settings['buttonList'] = $buttons;
   }
   else {
     $settings['fullPanel'] = TRUE;
@@ -85,7 +91,7 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) {
         $settings['externalCSS'] = base_path() . $css;
       }
     }
-    else if ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+    elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
       $settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme()));
     }
   }
diff --git a/editors/tinymce.inc b/editors/tinymce.inc
index 12c4fd6..f7acc70 100644
--- a/editors/tinymce.inc
+++ b/editors/tinymce.inc
@@ -27,6 +27,10 @@ function wysiwyg_tinymce_editor() {
         'files' => array('tiny_mce_src.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi groups' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_tinymce_version',
     'themes callback' => 'wysiwyg_tinymce_themes',
     'settings callback' => 'wysiwyg_tinymce_settings',
@@ -188,75 +192,86 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) {
     if ($config['css_setting'] == 'theme') {
       $settings['content_css'] = implode(',', wysiwyg_get_css());
     }
-    else if ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+    elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
       $settings['content_css'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme()));
     }
   }
 
-  // Find the enabled buttons and the button row they belong on.
-  // Also map the plugin metadata for each button.
-  // @todo What follows is a pain; needs a rewrite.
-  if (!empty($config['buttons']) && is_array($config['buttons'])) {
-    // $settings['buttons'] are stacked into
-    // $settings['theme_advanced_buttons1'] later.
-    // @todo Add a toolbar designer based on jQuery UI.
-    $settings['buttons'] = array();
-    // Only array keys in $settings['extensions'] matter; added to
-    // $settings['plugins'] later.
-    $settings['extensions'] = array();
-    // $settings['extended_valid_elements'] are just stacked, unique'd later,
-    // and transformed into a comma-separated string in
-    // wysiwyg_add_editor_settings().
-    // @todo Needs a complete plugin API redesign using arrays for
-    //   tag => attributes definitions and array_merge_recursive().
-    $settings['extended_valid_elements'] = array();
+  $plugins = wysiwyg_get_plugins($editor['name']);
+  $settings['extended_valid_elements'] = array();
+  $toolbar = array();
+  $extensions = array();
 
-    $plugins = wysiwyg_get_plugins($editor['name']);
-    foreach ($config['buttons'] as $plugin => $buttons) {
-      foreach ($buttons as $button => $enabled) {
-        // Iterate separately over buttons and extensions properties.
-        foreach (array('buttons', 'extensions') as $type) {
-          // Skip unavailable plugins.
-          if (!isset($plugins[$plugin][$type][$button])) {
-            continue;
-          }
-          // Add buttons.
-          if ($type == 'buttons') {
-            $settings['buttons'][] = $button;
-          }
-          // Add external Drupal plugins to the list of extensions.
-          if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) {
-            $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $button)] = 1;
-          }
-          // Add external plugins to the list of extensions.
-          else if ($type == 'buttons' && empty($plugins[$plugin]['internal'])) {
-            $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1;
-          }
-          // Add internal buttons that also need to be loaded as extension.
-          else if ($type == 'buttons' && !empty($plugins[$plugin]['load'])) {
-            $settings['extensions'][$plugin] = 1;
-          }
-          // Add plain extensions.
-          else if ($type == 'extensions' && !empty($plugins[$plugin]['load'])) {
-            $settings['extensions'][$plugin] = 1;
-          }
-          // Allow plugins to add valid HTML elements.
-          if (!empty($plugins[$plugin]['extended_valid_elements'])) {
-            $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
-          }
-          // Allow plugins to add or override global configuration settings.
-          if (!empty($plugins[$plugin]['options'])) {
-            $settings = array_merge($settings, $plugins[$plugin]['options']);
-          }
-        }
+  foreach ((array)$config['extensions'] as $plugin => $buttons) {
+    foreach ($buttons as $button => $enabled) {
+      if (!isset($plugins[$plugin], $plugins[$plugin]['extensions'], $plugins[$plugin]['extensions'][$button])) {
+        continue;
+      }
+
+      // Add plain extensions.
+      if (!empty($plugins[$plugin]['load'])) {
+        $extensions[$plugin] = 1;
+      }
+
+      // Allow plugins to add valid HTML elements.
+      if (!empty($plugins[$plugin]['extended_valid_elements'])) {
+        $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
+      }
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
       }
     }
-    // Clean-up.
-    $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']);
-    if ($settings['extensions']) {
-      $settings['plugins'] = array_keys($settings['extensions']);
+  }
+
+  foreach ((array)$config['toolbar'] as $buttons) {
+    $group = array();
+
+    foreach ($buttons as $button_config) {
+      $plugin = $button_config['plugin'];
+      $button = $button_config['button'];
+
+      if ($button == WYSIWYG_SEPARATOR) {
+        $group[] = '|';
+        continue;
+      }
+      if (!isset($plugins[$plugin]['buttons'][$button])) {
+        continue;
+      }
+
+      $group[] = $button;
+
+      // Add external Drupal plugins to the list of extensions.
+      if (!empty($plugins[$plugin]['proxy'])) {
+        $extensions[_wysiwyg_tinymce_plugin_name('add', $button)] = 1;
+      }
+      // Add external plugins to the list of extensions.
+      elseif (empty($plugins[$plugin]['internal'])) {
+        $extensions[_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1;
+      }
+      // Add internal buttons that also need to be loaded as extension.
+      elseif (!empty($plugins[$plugin]['load'])) {
+        $extensions[$plugin] = 1;
+      }
+
+      // Allow plugins to add or override global configuration settings.
+      if (!empty($plugins[$plugin]['options'])) {
+        $settings = array_merge($settings, $plugins[$plugin]['options']);
+      }
+
+      // Allow plugins to add valid HTML elements.
+      if (!empty($plugins[$plugin]['extended_valid_elements'])) {
+        $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
+      }
     }
-    unset($settings['extensions']);
+
+    $toolbar[] = $group;
+  }
+
+  $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']);
+  if ($extensions) {
+    $settings['plugins'] = array_keys($extensions);
   }
 
   // Add theme-specific settings.
@@ -273,22 +288,17 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) {
       if (isset($config['block_formats'])) {
         $settings['theme_advanced_blockformats'] = $config['block_formats'];
       }
-      if (isset($settings['buttons'])) {
-        // These rows explicitly need to be set to be empty, otherwise TinyMCE
-        // loads its default buttons of the advanced theme for each row.
-        $settings += array(
-          'theme_advanced_buttons1' => array(),
-          'theme_advanced_buttons2' => array(),
-          'theme_advanced_buttons3' => array(),
-        );
-        // @todo Allow to sort/arrange editor buttons.
-        for ($i = 0; $i < count($settings['buttons']); $i++) {
-          $settings['theme_advanced_buttons1'][] = $settings['buttons'][$i];
+      if ($toolbar) {
+        for ($i = 0;$i < count($toolbar);$i++) {
+          $group = $toolbar[$i];
+
+          foreach ($group as $button) {
+            $settings['theme_advanced_buttons' . ($i+1)][] = $button;
+          }
         }
       }
       break;
   }
-  unset($settings['buttons']);
 
   // Convert the config values into the form expected by TinyMCE.
   foreach ($settings as $key => $value) {
diff --git a/images/add.png b/images/add.png
new file mode 100644
index 0000000..6332fef
Binary files /dev/null and b/images/add.png differ
diff --git a/images/draggable.png b/images/draggable.png
new file mode 100644
index 0000000..47e8a02
Binary files /dev/null and b/images/draggable.png differ
diff --git a/images/remove.png b/images/remove.png
new file mode 100644
index 0000000..1514d51
Binary files /dev/null and b/images/remove.png differ
diff --git a/wysiwyg-toolbar-designer.tpl.php b/wysiwyg-toolbar-designer.tpl.php
new file mode 100644
index 0000000..4a1a373
--- /dev/null
+++ b/wysiwyg-toolbar-designer.tpl.php
@@ -0,0 +1,28 @@
+<div id="wysiwyg-toolbar-designer">
+  <h4><?php print t('Toolbar'); ?> <a class="add-toolbar-row" href="javascript:;">&nbsp;</a></h4>
+  <div id="toolbar-rows"></div>
+
+  <div class="warning">
+    <span class="warning">*</span> <?php print t('Changes will not be saved until the form is submitted.'); ?>
+  </div>
+
+  <div id="toolbar-actions">
+    <input id="save-design" type="button" value="<?php print t('Save'); ?>"/>
+    <input id="reset-design" type="button" value="<?php print t('Reset'); ?>"/>
+    <div class="ahah-progress ahah-progress-throbber"><div class="throbber">&nbsp;</div></div>
+  </div>
+
+  <div class="toolbar-row-template">
+    <a href="javascript:;" class="row-handler handler">&nbsp;</a>
+    <a href="javascript:;" class="remove-row">&nbsp;</a>
+  </div>
+
+  <h4><?php print t('Buttons'); ?></h4>
+  <div id="toolbar-available-buttons">
+    <?php foreach ($buttons as $button): ?>
+    <span class="wysiwyg-button wysiwyg-button-<?php print $button['plugin'] . '-' . $button['button'];?>">
+      <a href="javascript:;" class="handler">&nbsp;</a><?php echo $button['title']; ?>
+    </span>
+    <?php endforeach;?>
+  </div>
+</div>
diff --git a/wysiwyg.admin.inc b/wysiwyg.admin.inc
index 26e3b6f..0d56635 100644
--- a/wysiwyg.admin.inc
+++ b/wysiwyg.admin.inc
@@ -7,9 +7,9 @@
  */
 
 /**
- * Form builder for Wysiwyg profile form.
+ * Prepare default properties for profile.
  */
-function wysiwyg_profile_form($form_state, $profile) {
+function wysiwyg_profile_default($profile) {
   // Merge in defaults.
   $profile = (array) $profile;
   $profile += array(
@@ -46,10 +46,121 @@ function wysiwyg_profile_form($form_state, $profile) {
   );
   $profile = (object) $profile;
 
+  return $profile;
+}
+
+/**
+ * Profile designer callback.
+ */
+function wysiwyg_profile_toolbar($profile) {
+  $profile = wysiwyg_profile_default($profile);
   $formats = filter_formats();
   $editor = wysiwyg_get_editor($profile->editor);
   drupal_set_title(t('%editor profile for %format', array('%editor' => $editor['title'], '%format' => $formats[$profile->format]->name)));
 
+  $plugins = wysiwyg_get_plugins($profile->editor);
+
+  // Default settings
+  $toolbar_support = array(
+    'multi rows' => FALSE,
+    'separator' => FALSE,
+  );
+
+  if (isset($editor['toolbar support'])) {
+    $toolbar_support = array_merge($toolbar_support, $editor['toolbar support']);
+  }
+
+  // Get all available buttons.
+  $buttons = array();
+
+  if (isset($toolbar_support['separator']) && $toolbar_support['separator']) {
+    // Separator button.
+    $buttons[] = array(
+      'plugin' => 'default',
+      'button' => WYSIWYG_SEPARATOR,
+      'title' => t('Separator'),
+    );
+  }
+
+  foreach ($plugins as $name => $meta) {
+    if (isset($meta['buttons']) && is_array($meta['buttons'])) {
+      foreach ($meta['buttons'] as $button => $title) {
+        $buttons[] = array(
+          'plugin' => $name,
+          'button' => $button,
+          'title' => $title,
+        );
+      }
+    }
+  }
+
+  $toolbar = (array)$profile->settings['toolbar'];
+
+  $toolbar_support = array_merge($toolbar_support, array(
+    'callbackUrl' => url('admin/settings/wysiwyg/profile/' . $profile->format . '/toolbar/save'),
+    'toolbar' => $toolbar,
+  ));
+
+  // Add necessary javascripts and stylesheets
+  drupal_add_js(array('wysiwyg_toolbar' => $toolbar_support), 'setting');
+  jquery_ui_add(array('ui.draggable', 'ui.droppable', 'ui.sortable'));
+  drupal_add_js(drupal_get_path('module', 'wysiwyg') . '/wysiwyg.admin_toolbar.js');
+  drupal_add_css(drupal_get_path('module', 'wysiwyg') . '/wysiwyg.admin_toolbar.css');
+
+  $output = theme('wysiwyg_profile_toolbar', $buttons, $toolbar, $toolbar_support);
+
+  return $output;
+}
+
+function wysiwyg_profile_toolbar_save($profile) {
+  $profile = wysiwyg_profile_default($profile);
+  $plugins = wysiwyg_get_plugins($profile->editor);
+  $format = $profile->format;
+
+  $raw_toolbar = explode("\n", trim($_POST['toolbar']));
+  $toolbar = array();
+
+  foreach ($raw_toolbar as $raw_row) {
+    $row = array();
+    $buttons = explode('|', $raw_row);
+
+    foreach ($buttons as $button) {
+      list($plugin, $name) = explode('.', $button, 2);
+
+      if ($name != WYSIWYG_SEPARATOR && !isset($plugins[$plugin], $plugins[$plugin]['buttons'][$name])) {
+        continue;
+      }
+
+      $row[] = array(
+        'button' => $name,
+        'plugin' => $plugin,
+      );
+    }
+
+    if (count($row)) {
+      $toolbar[] = $row;
+    }
+  }
+
+  $values = $profile->settings;
+  $values['toolbar'] = count($toolbar) ? $toolbar : NULL;
+  db_query("UPDATE {wysiwyg} SET settings = '%s' WHERE format = %d", serialize($values), $format);
+
+  echo drupal_json(array('status' => 'ok', 'toolbar' => $toolbar));
+}
+
+/**
+ * Form builder for Wysiwyg profile form.
+ */
+function wysiwyg_profile_form($form_state, $profile) {
+  $profile = wysiwyg_profile_default($profile);
+
+  $formats = filter_formats();
+  $editor = wysiwyg_get_editor($profile->editor);
+  drupal_set_title(t('%editor profile for %format', array('%editor' => $editor['title'], '%format' => $formats[$profile->format]->name)));
+
+  $form_state['storage']['profile'] = $profile;
+
   $form['format'] = array('#type' => 'value', '#value' => $profile->format);
   $form['input_format'] = array('#type' => 'value', '#value' => $formats[$profile->format]->name);
   $form['editor'] = array('#type' => 'value', '#value' => $profile->editor);
@@ -98,9 +209,9 @@ function wysiwyg_profile_form($form_state, $profile) {
     '#description' => t('The language to use for the editor interface. Language codes are based on the <a href="http://www.loc.gov/standards/iso639-2/englangn.html">ISO-639-2</a> format.'),
   );
 
-  $form['buttons'] = array(
+  $form['extensions'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Buttons and plugins'),
+    '#title' => t('Extensions'),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
     '#tree' => TRUE,
@@ -108,36 +219,14 @@ function wysiwyg_profile_form($form_state, $profile) {
   );
 
   $plugins = wysiwyg_get_plugins($profile->editor);
-  // Generate the button list.
+  // Generate the extension list
   foreach ($plugins as $name => $meta) {
-    if (isset($meta['buttons']) && is_array($meta['buttons'])) {
-      foreach ($meta['buttons'] as $button => $title) {
-        $icon = '';
-        if (!empty($meta['path'])) {
-          // @todo Button icon locations are different in editors, editor versions,
-          //   and contrib/custom plugins (like Image Assist, f.e.).
-          $img_src = $meta['path'] . "/images/$name.gif";
-          // Handle plugins that have more than one button.
-          if (!file_exists($img_src)) {
-            $img_src = $meta['path'] . "/images/$button.gif";
-          }
-          $icon = file_exists($img_src) ? '<img src="' . base_path() . $img_src . '" title="' . $button . '" style="border: 1px solid grey; vertical-align: middle;" />' : '';
-        }
-        $title = (isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title);
-        $title = (!empty($icon) ? $icon . ' ' . $title : $title);
-        $form['buttons'][$name][$button] = array(
-          '#type' => 'checkbox',
-          '#title' => $title,
-          '#default_value' => !empty($profile->settings['buttons'][$name][$button]) ? $profile->settings['buttons'][$name][$button] : FALSE,
-        );
-      }
-    }
-    else if (isset($meta['extensions']) && is_array($meta['extensions'])) {
+    if (isset($meta['extensions']) && is_array($meta['extensions'])) {
       foreach ($meta['extensions'] as $extension => $title) {
-        $form['buttons'][$name][$extension] = array(
+        $form['extensions'][$name][$extension] = array(
           '#type' => 'checkbox',
           '#title' => isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title,
-          '#default_value' => !empty($profile->settings['buttons'][$name][$extension]) ? $profile->settings['buttons'][$name][$extension] : FALSE,
+          '#default_value' => !empty($profile->settings['extensions'][$name][$extension]) ? $profile->settings['extensions'][$name][$extension] : FALSE,
         );
       }
     }
@@ -296,24 +385,25 @@ function wysiwyg_profile_form($form_state, $profile) {
  * @see wysiwyg_profile_form()
  */
 function wysiwyg_profile_form_submit($form, &$form_state) {
+  $profile = $form_state['storage']['profile'];
   $values = $form_state['values'];
-  if (isset($values['buttons'])) {
+  if (isset($values['extensions'])) {
     // Store only enabled buttons for each plugin.
-    foreach ($values['buttons'] as $plugin => $buttons) {
-      $values['buttons'][$plugin] = array_filter($values['buttons'][$plugin]);
+    foreach ($values['extensions'] as $plugin => $buttons) {
+      $values['extensions'][$plugin] = array_filter($values['extensions'][$plugin]);
     }
     // Store only enabled plugins.
-    $values['buttons'] = array_filter($values['buttons']);
+    $values['extensions'] = array_filter($values['extensions']);
   }
+
+  $values['extensions'] = (array)$values['extensions'];
+
   // Remove input format name.
   $format = $values['format'];
   $input_format = $values['input_format'];
   $editor = $values['editor'];
-  unset($values['format'], $values['input_format'], $values['editor']);
 
-  // Remove FAPI values.
-  // @see system_settings_form_submit()
-  unset($values['submit'], $values['form_id'], $values['op'], $values['form_token'], $values['form_build_id']);
+  $values = array_merge($profile->settings, wysiwyg_profile_clean($values));
 
   // Insert new profile data.
   db_query("UPDATE {wysiwyg} SET settings = '%s' WHERE format = %d", serialize($values), $format);
@@ -351,7 +441,12 @@ function theme_wysiwyg_admin_button_table($form) {
     $rows[] = $row;
   }
 
-  $output = theme('table', array(), $rows, array('width' => '100%'));
+  if (count($rows)) {
+    $output = theme('table', array(), $rows, array('width' => '100%'));
+  }
+  else {
+    $output = '';
+  }
 
   return $output;
 }
@@ -529,3 +624,15 @@ function wysiwyg_profile_delete($format) {
   db_query("DELETE FROM {wysiwyg} WHERE format = %d", $format);
 }
 
+/**
+ * Clean submitted setting values
+ */
+function wysiwyg_profile_clean($values) {
+  unset($values['format'], $values['input_format'], $values['editor']);
+
+  // Remove FAPI values.
+  // @see system_settings_form_submit()
+  unset($values['submit'], $values['form_id'], $values['op'], $values['form_token'], $values['form_build_id']);
+
+  return $values;
+}
diff --git a/wysiwyg.admin_toolbar.css b/wysiwyg.admin_toolbar.css
new file mode 100644
index 0000000..2ac30b5
--- /dev/null
+++ b/wysiwyg.admin_toolbar.css
@@ -0,0 +1,76 @@
+/* $Id$ */
+
+#toolbar-rows {
+  padding: 1em 1em 0 1em;
+}
+
+#toolbar-rows .toolbar-row {
+  border: 1px dashed gray;
+  padding: 0.2em;
+  min-height: 2em;
+  position: relative;
+  margin-bottom: 0.5em;
+}
+
+#toolbar-actions {
+  margin: 1em 0 1em 1em;
+}
+
+.toolbar-row-template {
+  display: none;
+}
+
+#toolbar-rows .row-handler {
+  margin-top: 0.2em;
+  float: left;
+}
+
+#toolbar-available-buttons {
+  border: 1px dashed silver;
+  padding: 1em;
+}
+
+#wysiwyg-toolbar-designer .add-toolbar-row {
+  display: inline-block;
+  background: url(images/add.png);
+  width: 16px;
+  height: 16px;
+  right: 0.2em;
+  top: 0.2em;
+  text-decoration: none;
+}
+
+#wysiwyg-toolbar-designer .remove-row {
+  display: block;
+  background: url(images/remove.png);
+  width: 16px;
+  height: 16px;
+  position: absolute;
+  right: 0.2em;
+  top: 0.2em;
+  text-decoration: none;
+}
+
+#wysiwyg-toolbar-designer .handler {
+  background: url(images/draggable.png) no-repeat 0 4px;
+  width: 16px;
+  height: 16px;
+  display: inline-block;
+  text-decoration: none;
+}
+
+#wysiwyg-toolbar-designer .wysiwyg-button {
+  border: 1px solid silver;
+  display: inline-block;
+  margin: 0.2em 0.2em 0.2em 0;
+  padding: 0 0.5em;
+  height: 20px;
+}
+
+#wysiwyg-toolbar-designer .ahah-progress, #wysiwyg-toolbar-designer div.warning {
+  display: none; /* hide by default */
+}
+
+.toolbar-hover {
+  border: 1px dashed black;
+}
diff --git a/wysiwyg.admin_toolbar.js b/wysiwyg.admin_toolbar.js
new file mode 100644
index 0000000..0efea5e
--- /dev/null
+++ b/wysiwyg.admin_toolbar.js
@@ -0,0 +1,189 @@
+// $Id$
+
+Drupal.behaviors.wysiwygToolbarDesigner = function(context) {
+  var settings = Drupal.settings.wysiwyg_toolbar;
+  var workspace = $('#wysiwyg-toolbar-designer');
+  var designArea = $('#toolbar-rows');
+  var changeNotification = $('#wysiwyg-toolbar-designer div.warning');
+  var availableButtons = $('#toolbar-available-buttons');
+  var separator = $('.wysiwyg-button-default-Separator',availableButtons);
+
+  var createRow = function(item) {
+    item.addClass('toolbar-row').sortable({
+      revert: true,
+      items: '.wysiwyg-button',
+      connectWith: '#toolbar-rows .toolbar-row',
+      stop: function(event,ui){
+        changeNotification.fadeIn();
+      }
+    });
+
+    item.droppable({
+      accept: '.template-button',
+      drop: function(event,ui) {
+        var button = ui.draggable.clone();
+        button.removeClass('template-button').addClass('toolbar-button');
+
+        // Disable this button in template area.
+        ui.draggable.hide();
+        separator.show();
+
+        $(this).append(button).sortable('refresh');
+        changeNotification.fadeIn();
+      }
+    });
+
+    item.find('.remove-row').click(function() {
+      if ($(this).parent().find('.wysiwyg-button').length == 0 || confirm(Drupal.t("Do you want to remove this row ?"))) {
+        // Enable buttons removed from this row.
+        $('.wysiwyg-button',$(this).parent()).each(function(i,button){
+          var button_class = /wysiwyg-button-[^-]+-[^\s]+/.exec($(button).attr('class'));
+          $('.' + button_class[0],availableButtons).show();
+        });
+        separator.show();
+
+        $(this).parent().remove();
+        changeNotification.fadeIn();
+      }
+    });
+
+    if (!settings['multi rows']) {
+      item.find('.remove-row').hide();
+    }
+  };
+
+  var reset = function() {
+    $('.toolbar-row',designArea).remove();
+
+    // Enable all buttons and then disable it later.
+    $('.wysiwyg-button',availableButtons).show();
+
+    for (var i in settings.toolbar) {
+      var buttons = settings.toolbar[i];
+
+      var row = $('.toolbar-row-template',workspace).clone().removeClass('toolbar-row-template');
+
+      for (var j in buttons) {
+        var buttonClass = '.wysiwyg-button-' + buttons[j].plugin + '-' + buttons[j].button;
+        var template_button = $(buttonClass,$('#toolbar-available-buttons'));
+
+        if (template_button.length) {
+
+          button = template_button.clone().show();
+          button.removeClass('template-button').addClass('toolbar-button');
+
+          row.append(button);
+
+          // Disable button in template area.
+          template_button.hide();
+        }
+      }
+
+      createRow(row);
+      designArea.append(row);
+    }
+
+    // Make sure we always have at least one row.
+    if (!settings['multi rows']) {
+      if ($('.toolbar-row',designArea).length <= 0) {
+        var row = $('.toolbar-row-template',workspace).clone().removeClass('toolbar-row-template');
+        createRow(row);
+        designArea.append(row);
+      }
+    }
+
+    separator.show();
+    changeNotification.fadeOut();
+  }
+
+  $('.add-toolbar-row',workspace).click(function(){
+    // clone from toolbar template
+    var row = $('.toolbar-row-template',workspace).clone().removeClass('toolbar-row-template');
+    createRow(row);
+
+    // Append row to design area.
+    designArea.append(row).sortable('refresh');
+    changeNotification.fadeIn();
+  });
+
+  $('.wysiwyg-button',availableButtons).addClass('template-button').draggable({
+    handle: '.handler',
+    helper: 'clone',
+    revert: 'invalid',
+    addClasses: false
+  });
+
+  availableButtons.droppable({
+    accept: '.toolbar-button',
+    drop: function(event, ui) {
+      ui.draggable.remove();
+      changeNotification.fadeIn();
+
+      // Enable button in template.
+      var button_id = /wysiwyg-button-([^-]+-[^\s]+)/.exec($(ui.draggable).attr('class'));
+      $('.wysiwyg-button-' + button_id[1]).show();
+    }
+  });
+
+  $('#toolbar-rows').sortable({
+    items: '.toolbar-row',
+    handle: '.row-handler',
+    stop: function(event, ui) {
+      changeNotification.fadeIn();
+    }
+  });
+
+  /* Design actions buttons. */
+  $('#reset-design').click(function() {
+    if (!changeNotification.is(':hidden') && confirm(Drupal.t('Do you want to reset the changes ?')))
+      reset();
+  });
+
+  $('#save-design').click(function() {
+    // Prepare toolbar data to submit.
+    var toolbar = "";
+
+    designArea.find('.toolbar-row').each(function(key,rowDom){
+      var row = "";
+
+      $('.wysiwyg-button',rowDom).each(function(key,button){
+        var cls = /wysiwyg-button-([^-]+)-([^\s]+)/.exec($(button).attr('class'));
+
+        row += cls[1] + "." + cls[2] + "|";
+      });
+
+      toolbar += row + "\n";
+    });
+
+    var button = $(this);
+    var progress = $('.ahah-progress',workspace);
+    button.attr('disabled','disabled');
+    progress.show();
+
+    $.ajax({
+      type: 'POST',
+      dataType: 'json',
+      url: settings.callbackUrl,
+      data: {toolbar: toolbar},
+      success: function(data){
+        button.removeAttr('disabled');
+        changeNotification.fadeOut();
+        settings.toolbar = data.toolbar;
+      },
+      complete: function(request,status) {
+        if (status == 'error' || status == 'parsererror') {
+          // TODO implement better error handler later.
+          alert("Error !!!");
+        }
+
+        progress.hide();
+      }
+    });
+  });
+
+  reset();
+
+  if (!settings['multi rows']) {
+    $('.add-toolbar-row').hide();
+  }
+};
diff --git a/wysiwyg.info b/wysiwyg.info
index e2ed9cc..7a164f7 100644
--- a/wysiwyg.info
+++ b/wysiwyg.info
@@ -4,6 +4,9 @@ description = Allows users to edit contents with client-side editors.
 package = User interface
 core = 6.x
 
+dependencies[] = jquery_ui
+dependencies[] = jquery_update
+
 ; Information added by drupal.org packaging script on 2010-03-12
 version = "6.x-2.x-dev"
 core = "6.x"
diff --git a/wysiwyg.module b/wysiwyg.module
index 660936b..4f4da31 100644
--- a/wysiwyg.module
+++ b/wysiwyg.module
@@ -6,6 +6,8 @@
  * Integrate client-side editors with Drupal.
  */
 
+define('WYSIWYG_SEPARATOR', 'Separator');
+
 /**
  * Implementation of hook_menu().
  */
@@ -32,6 +34,24 @@ function wysiwyg_menu() {
     'tab_parent' => 'admin/settings/wysiwyg/profile/%wysiwyg_profile',
     'type' => MENU_LOCAL_TASK,
   );
+  $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/toolbar'] = array(
+    'title' => 'Toolbar',
+    'page callback' => 'wysiwyg_profile_toolbar',
+    'page arguments' => array(4),
+    'access arguments' => array('administer filters'),
+    'file' => 'wysiwyg.admin.inc',
+    'tab_root' => 'admin/settings/wysiwyg/profile',
+    'tab_parent' => 'admin/settings/wysiwyg/profile/%wysiwyg_profile',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/toolbar/save'] = array(
+    'title' => 'Toolbar',
+    'page callback' => 'wysiwyg_profile_toolbar_save',
+    'page arguments' => array(4),
+    'access arguments' => array('administer filters'),
+    'file' => 'wysiwyg.admin.inc',
+    'type' => MENU_TASK,
+  );
   $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/delete'] = array(
     'title' => 'Remove',
     'page callback' => 'drupal_get_form',
@@ -64,6 +84,10 @@ function wysiwyg_theme() {
     'wysiwyg_profile_overview' => array(
       'arguments' => array('form' => NULL),
     ),
+    'wysiwyg_profile_toolbar' => array(
+      'arguments' => array('buttons' => NULL, 'toolbar' => NULL, 'toolbar_support' => NULL),
+      'template' => 'wysiwyg-toolbar-designer',
+    ),
     'wysiwyg_admin_button_table' => array(
       'arguments' => array('form' => NULL),
     ),
@@ -545,6 +569,7 @@ function wysiwyg_get_editor_config($profile, $theme) {
     $context = array('editor' => $editor, 'profile' => $profile, 'theme' => $theme);
     drupal_alter('wysiwyg_editor_settings', $settings, $context);
   }
+
   return $settings;
 }
 
