Index: wysiwyg-toolbar-designer.tpl.php
===================================================================
RCS file: wysiwyg-toolbar-designer.tpl.php
diff -N wysiwyg-toolbar-designer.tpl.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg-toolbar-designer.tpl.php	7 Apr 2010 00:14:38 -0000
@@ -0,0 +1,37 @@
+<div id="wysiwyg-toolbar-designer">
+  <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'];?>">
+      <?php echo $button['title']; ?>
+    </span>
+    <?php endforeach;?>
+  </div>
+
+  <h4><?php print t('Toolbar'); ?></h4>
+
+  <div class="warning">
+    <span class="warning">*</span> <?php print t('Changes will not be saved until the form is submitted.'); ?>
+  </div>
+
+  <div id="toolbar-rows"></div>
+
+  <a title="<?php print t('Add new row') ;?>" class="add-toolbar-row" href="javascript:;">&nbsp;</a>
+
+  <div id="toolbar-actions">
+    <input id="reset-design" type="button" value="<?php print t('Reset'); ?>"/>
+    <input id="save-design" type="button" value="<?php print t('Save'); ?>"/>
+    <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="add-group">&nbsp;</a>
+    <a href="javascript:;" class="remove-row">&nbsp;</a>
+  </div>
+
+  <div class="toolbar-group-template">
+    <a href="javascript:;" class="group-handler handler">&nbsp;</a>
+    <a href="javascript:;" class="remove-group">&nbsp;</a>
+  </div>
+</div>
Index: wysiwyg.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.admin.inc,v
retrieving revision 1.15.2.9
diff -u -p -d -r1.15.2.9 wysiwyg.admin.inc
--- wysiwyg.admin.inc	13 Feb 2010 23:58:41 -0000	1.15.2.9
+++ wysiwyg.admin.inc	7 Apr 2010 00:14:39 -0000
@@ -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,136 @@ function wysiwyg_profile_form($form_stat
   );
   $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)));
+
+  $toolbar_support = isset($editor['toolbar support']) ? $editor['toolbar support'] : FALSE;
+
+  if ($toolbar_support === FALSE) {
+    drupal_set_message(t('This editor does not support custom toolbar feature.'), 'warning');
+
+    // Return a blank page.
+    return '';
+  }
+
+  if (!is_array($toolbar_support)) {
+    $toolbar_support = array();
+  }
+
+  // Default settings
+  $toolbar_support += array(
+    'multi rows' => FALSE,
+    'multi groups' => FALSE,
+    'separator' => FALSE,
+  );
+
+  // Get all available buttons.
+  $plugins = wysiwyg_get_plugins($profile->editor);
+  $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();
+    $raw_groups = explode('|', rtrim($raw_row, '|'));
+
+    foreach ($raw_groups as $raw_group) {
+      $buttons = explode(',', $raw_group);
+
+      $group = array();
+      foreach ($buttons as $button) {
+        list($plugin, $name) = explode('.', $button, 2);
+
+        if ($name != WYSIWYG_SEPARATOR && !isset($plugins[$plugin], $plugins[$plugin]['buttons'][$name])) {
+          continue;
+        }
+
+        $group[] = array(
+          'button' => $name,
+          'plugin' => $plugin,
+        );
+      }
+
+      $row[] = $group;
+    }
+
+
+    $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 +224,9 @@ function wysiwyg_profile_form($form_stat
     '#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 +234,14 @@ function wysiwyg_profile_form($form_stat
   );
 
   $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 +400,25 @@ function wysiwyg_profile_form($form_stat
  * @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 +456,12 @@ function theme_wysiwyg_admin_button_tabl
     $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 +639,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;
+}
Index: wysiwyg.admin_toolbar.css
===================================================================
RCS file: wysiwyg.admin_toolbar.css
diff -N wysiwyg.admin_toolbar.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.admin_toolbar.css	7 Apr 2010 00:14:39 -0000
@@ -0,0 +1,118 @@
+/* $Id$ */
+
+#toolbar-rows {
+  margin: 1em 0;
+}
+
+#toolbar-rows .toolbar-row {
+  border: 1px solid gray;
+  padding: 0.2em 0.2em 0.2em 20px;
+  min-height: 2em;
+  position: relative;
+  margin-bottom: 0.5em;
+}
+
+#toolbar-rows .single-group .toolbar-group {
+  border: 0;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+
+#toolbar-rows .single-group .group-handler {
+  display: none;
+}
+
+#toolbar-rows .toolbar-group {
+  border: 1px dotted silver;
+  display: inline-block;
+  min-width: 10em;
+  min-height: 27px;
+  padding: 0 1.5em 0 0.5em;
+  margin: 0 0.5em 0.1em 0;
+  position: relative;
+}
+
+#toolbar-actions {
+
+}
+
+.toolbar-row-template, .toolbar-group-template  {
+  display: none;
+}
+
+#toolbar-rows .row-handler {
+  position: absolute;
+  top: 0.2em;
+  left: 0.2em;
+}
+
+#toolbar-available-buttons {
+  border: 1px dashed silver;
+  padding: 0.5em;
+  margin-bottom: 1em;
+}
+
+#wysiwyg-toolbar-designer .add-group {
+  display: inline-block;
+  background: url(images/add.png);
+  width: 16px;
+  height: 16px;
+  right: 1.6em;
+  top: 0.2em;
+  text-decoration: none;
+  position: absolute;
+}
+
+#wysiwyg-toolbar-designer .add-toolbar-row {
+  display: inline-block;
+  background: url(images/add.png);
+  width: 16px;
+  height: 16px;
+  text-decoration: none;
+  outline: 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 .remove-group {
+  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;
+  cursor: pointer;
+}
+
+#wysiwyg-toolbar-designer .ahah-progress, #wysiwyg-toolbar-designer div.warning {
+  /* Hidden by default. */
+  display: none;
+}
Index: wysiwyg.admin_toolbar.js
===================================================================
RCS file: wysiwyg.admin_toolbar.js
diff -N wysiwyg.admin_toolbar.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.admin_toolbar.js	7 Apr 2010 00:14:39 -0000
@@ -0,0 +1,256 @@
+// $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(noGroup) {
+    var row = $('.toolbar-row-template',workspace).clone().removeClass('toolbar-row-template');
+    row.addClass('toolbar-row').sortable({
+      handle: '.group-handler',
+      revert: true,
+      items: '.toolbar-group',
+      connectWith: '#toolbar-rows .toolbar-row',
+      stop: function(event,ui) {
+        changeNotification.fadeIn();
+      }
+    });
+
+    row.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 groups']) {
+      row.find('.add-group').click(function() {
+        var group = createGroup();
+        row.append(group);
+        row.sortable('refresh');
+      });
+    }
+    else {
+      row.find('.add-group').hide();
+      row.addClass('single-group');
+    }
+
+    if (!settings['multi rows']) {
+      row.find('.remove-row').hide();
+    }
+
+    // add required group
+    if (!noGroup) {
+      var group = createGroup();
+      row.append(group);
+    }
+
+    return row;
+  };
+  
+  var createGroup = function() {
+    var group = $('.toolbar-group-template').clone().removeClass('toolbar-group-template');
+    group.addClass('toolbar-group');
+
+    group.sortable({
+      revert: true,
+      items: '.wysiwyg-button',
+      connectWith: '#toolbar-rows .toolbar-group',
+      stop: function(event,ui) {
+        changeNotification.fadeIn();
+      }
+    });
+
+    group.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();
+      }
+    });
+
+    if (settings['multi groups']) {
+      group.find('.remove-group').click(function() {
+        if ($(this).parent().find('.wysiwyg-button').length == 0 || confirm(Drupal.t("Do you want to remove this group ?"))) {
+          // 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();
+        }
+      });
+    }
+    else {
+      group.find('.remove-group').hide();
+    }
+
+    return group;
+  }
+
+  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 groups = settings.toolbar[i];
+      var row = createRow(true);
+
+      for (var j in groups) {
+        var group = createGroup();
+        var buttons = groups[j];
+
+        for (var k in buttons) {
+          var buttonClass = '.wysiwyg-button-' + buttons[k].plugin + '-' + buttons[k].button;
+          var template_button = $(buttonClass,$('#toolbar-available-buttons'));
+
+          if (template_button.length) {
+
+            button = template_button.clone().show();
+            button.removeClass('template-button').addClass('toolbar-button');
+
+            group.append(button);
+
+            // Disable button in template area.
+            template_button.hide();
+          }
+        }
+
+        row.append(group);
+      }
+
+      designArea.append(row);
+    }
+
+    // Make sure we always have at least one row.
+    if (!settings['multi rows']) {
+      if ($('.toolbar-row',designArea).length <= 0) {
+        var row = createRow();
+        designArea.append(row);
+      }
+    }
+
+    separator.show();
+    changeNotification.fadeOut();
+  }
+
+  $('.add-toolbar-row',workspace).click(function(){
+    // clone from toolbar template
+    var row = 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 = "";
+
+      $('.toolbar-group',rowDom).each(function(key,groupDom){
+        var group = "";
+
+        $('.wysiwyg-button',groupDom).each(function(key,button){
+          var cls = /wysiwyg-button-([^-]+)-([^\s]+)/.exec($(button).attr('class'));
+          group += cls[1] + "." + cls[2] + ",";
+        })
+
+        row += group + "|";
+      });
+
+      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();
+  }
+};
Index: wysiwyg.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.info,v
retrieving revision 1.1
diff -u -p -d -r1.1 wysiwyg.info
--- wysiwyg.info	14 Oct 2008 21:45:07 -0000	1.1
+++ wysiwyg.info	7 Apr 2010 00:14:39 -0000
@@ -3,3 +3,5 @@ name = Wysiwyg
 description = Allows users to edit contents with client-side editors.
 package = User interface
 core = 6.x
+dependencies[] = jquery_ui
+dependencies[] = jquery_update
\ No newline at end of file
Index: wysiwyg.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.module,v
retrieving revision 1.33.2.11
diff -u -p -d -r1.33.2.11 wysiwyg.module
--- wysiwyg.module	14 Feb 2010 01:59:47 -0000	1.33.2.11
+++ wysiwyg.module	7 Apr 2010 00:14:39 -0000
@@ -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),
     ),
@@ -414,16 +438,22 @@ function wysiwyg_add_plugin_settings($pr
   if (isset($editor['plugin settings callback'])) {
     // @todo Require PHP 5.1 in 3.x and use array_intersect_key().
     $profile_plugins_native = array();
-    foreach ($plugins[$editor['name']] as $plugin => $meta) {
-      // Skip Drupal plugins (handled below).
-      if ($plugin === $proxy) {
-        continue;
-      }
-      // Only keep native plugins that are enabled in this profile.
-      if (isset($profile->settings['buttons'][$plugin])) {
-        $profile_plugins_native[$plugin] = $meta;
+
+    foreach ((array)$profile->settings['toolbar'] as $row) {
+      foreach ($row as $group) {
+        foreach ($group as $button) {
+          $plugin = $button['plugin'];
+
+          // Skip Drupal plugins (handled below).
+          if ($plugin === $proxy) {
+            continue;
+          }
+
+          $profile_plugins_native[$plugin] = $plugins[$plugin];
+        }
       }
     }
+
     // Invoke the editor's plugin settings callback, so it can populate the
     // settings for native external plugins with required values.
     $settings_native = call_user_func($editor['plugin settings callback'], $editor, $profile, $profile_plugins_native);
@@ -433,26 +463,39 @@ function wysiwyg_add_plugin_settings($pr
 
   // Process Drupal plugins.
   if ($proxy && isset($editor['proxy plugin settings callback'])) {
+    $drupal_plugins = wysiwyg_get_all_plugins();
+
     $profile_plugins_drupal = array();
-    foreach (wysiwyg_get_all_plugins() as $plugin => $meta) {
-      if (isset($profile->settings['buttons'][$proxy][$plugin])) {
-        // JavaScript and plugin-specific settings for Drupal plugins must be
-        // loaded and processed only once. Plugin information is cached
-        // statically to pass it to the editor's proxy plugin settings callback.
-        if (!isset($processed_plugins[$proxy][$plugin])) {
-          $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin] = $meta;
-          // Load the Drupal plugin's JavaScript.
-          drupal_add_js($meta['js path'] . '/' . $meta['js file']);
-          // Add plugin-specific settings.
-          if (isset($meta['settings'])) {
-            drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin => $meta['settings'])))), 'setting');
+
+    foreach ((array)$profile->settings['toolbar'] as $row) {
+      foreach ($row as $group) {
+        foreach ($group as $button) {
+          $plugin = $button['plugin'];
+          $button_id = $button['button'];
+
+          if ($plugin == $proxy && isset($drupal_plugins[$button_id])) {
+            $meta = $drupal_plugins[$button_id];
+
+            // JavaScript and plugin-specific settings for Drupal plugins must be
+            // loaded and processed only once. Plugin information is cached
+            // statically to pass it to the editor's proxy plugin settings callback.
+            if (!isset($processed_plugins[$proxy][$button_id])) {
+              $profile_plugins_drupal[$button_id] = $processed_plugins[$proxy][$button_id] = $meta;
+              // Load the Drupal plugin's JavaScript.
+              drupal_add_js($meta['js path'] . '/' . $meta['js file']);
+              // Add plugin-specific settings.
+              if (isset($meta['settings'])) {
+                drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($button_id => $meta['settings'])))), 'setting');
+              }
+            }
+            else {
+              $profile_plugins_drupal[$button_id] = $processed_plugins[$proxy][$button_id];
+            }
           }
         }
-        else {
-          $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin];
-        }
       }
     }
+
     // Invoke the editor's proxy plugin settings callback, so it can populate
     // the settings for Drupal plugins with custom, required values.
     $settings_drupal = call_user_func($editor['proxy plugin settings callback'], $editor, $profile, $profile_plugins_drupal);
@@ -545,6 +588,7 @@ function wysiwyg_get_editor_config($prof
     $context = array('editor' => $editor, 'profile' => $profile, 'theme' => $theme);
     drupal_alter('wysiwyg_editor_settings', $settings, $context);
   }
+
   return $settings;
 }
 
Index: editors/ckeditor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/ckeditor.inc,v
retrieving revision 1.2.6.6
diff -u -p -d -r1.2.6.6 ckeditor.inc
--- editors/ckeditor.inc	2 Apr 2010 14:05:54 -0000	1.2.6.6
+++ editors/ckeditor.inc	7 Apr 2010 00:14:40 -0000
@@ -28,6 +28,11 @@ function wysiwyg_ckeditor_editor() {
         ),
       ),
     ),
+    'toolbar support' => array(
+      'multi groups' => TRUE,
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_ckeditor_version',
     'themes callback' => 'wysiwyg_ckeditor_themes',
     'settings callback' => 'wysiwyg_ckeditor_settings',
@@ -189,52 +194,74 @@ function wysiwyg_ckeditor_settings($edit
     $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 $row) {
+    foreach ($row 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;
Index: editors/fckeditor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/fckeditor.inc,v
retrieving revision 1.17.2.3
diff -u -p -d -r1.17.2.3 fckeditor.inc
--- editors/fckeditor.inc	2 Apr 2010 14:05:54 -0000	1.17.2.3
+++ editors/fckeditor.inc	7 Apr 2010 00:14:40 -0000
@@ -20,6 +20,11 @@ function wysiwyg_fckeditor_editor() {
         'files' => array('fckeditor.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi groups' => TRUE,
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_fckeditor_version',
     'themes callback' => 'wysiwyg_fckeditor_themes',
     'settings callback' => 'wysiwyg_fckeditor_settings',
@@ -132,40 +137,66 @@ function wysiwyg_fckeditor_settings($edi
     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 $row) {
+    foreach ($row 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[] = '/';
+  }
+
+  // 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;
Index: editors/markitup.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/markitup.inc,v
retrieving revision 1.7.2.2
diff -u -p -d -r1.7.2.2 markitup.inc
--- editors/markitup.inc	2 Apr 2010 14:05:54 -0000	1.7.2.2
+++ editors/markitup.inc	7 Apr 2010 00:14:40 -0000
@@ -25,6 +25,7 @@ function wysiwyg_markitup_editor() {
         'files' => array('jquery.markitup.pack.js'),
       ),
     ),
+    'toolbar support' => TRUE,
     'version callback' => 'wysiwyg_markitup_version',
     'themes callback' => 'wysiwyg_markitup_themes',
     'settings callback' => 'wysiwyg_markitup_settings',
@@ -147,11 +148,13 @@ function wysiwyg_markitup_settings($edit
       '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 $row) {
+      foreach ($row as $group) {
+        foreach ($group as $button) {
+          $settings['markupSet'][$button['button']] = $default_buttons[$button['button']];
         }
       }
     }
Index: editors/nicedit.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/nicedit.inc,v
retrieving revision 1.6.2.1
diff -u -p -d -r1.6.2.1 nicedit.inc
--- editors/nicedit.inc	3 Aug 2009 22:37:15 -0000	1.6.2.1
+++ editors/nicedit.inc	7 Apr 2010 00:14:40 -0000
@@ -20,6 +20,7 @@ function wysiwyg_nicedit_editor() {
         'files' => array('nicEdit.js'),
       ),
     ),
+    'toolbar support' => TRUE,
     'version callback' => 'wysiwyg_nicedit_version',
     'settings callback' => 'wysiwyg_nicedit_settings',
     'plugin callback' => 'wysiwyg_nicedit_plugins',
@@ -66,12 +67,16 @@ function wysiwyg_nicedit_settings($edito
   );
 
   // 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 $row) {
+      foreach ($row as $group) {
+        foreach ($group as $button) {
+          $buttons[] = $button['button'];
+        }
+      }
     }
-    $settings['buttonList'] = array_keys($buttons);
+    $settings['buttonList'] = $buttons;
   }
   else {
     $settings['fullPanel'] = TRUE;
@@ -85,7 +90,7 @@ function wysiwyg_nicedit_settings($edito
         $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()));
     }
   }
Index: editors/openwysiwyg.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/openwysiwyg.inc,v
retrieving revision 1.4.2.1
diff -u -p -d -r1.4.2.1 openwysiwyg.inc
--- editors/openwysiwyg.inc	2 Apr 2010 14:05:54 -0000	1.4.2.1
+++ editors/openwysiwyg.inc	7 Apr 2010 00:14:40 -0000
@@ -21,6 +21,10 @@ function wysiwyg_openwysiwyg_editor() {
         'files' => array('wysiwyg.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_openwysiwyg_version',
     'themes callback' => 'wysiwyg_openwysiwyg_themes',
     'settings callback' => 'wysiwyg_openwysiwyg_settings',
@@ -107,25 +111,36 @@ function wysiwyg_openwysiwyg_settings($e
     }
   }
 
-  if (!empty($config['buttons'])) {
+  if (!empty($config['toolbar'])) {
+    $toolbar = array();
     $plugins = wysiwyg_get_plugins($editor['name']);
-    foreach ($config['buttons'] as $plugin => $buttons) {
-      foreach ($buttons as $button => $enabled) {
-        foreach (array('buttons', 'extensions') as $type) {
+    foreach ($config['toolbar'] as $row) {
+      $toolbar_row = array();
+
+      foreach ($row as $buttons) {
+        foreach ($buttons as $button_config) {
           // Skip unavailable plugins.
-          if (!isset($plugins[$plugin][$type][$button])) {
-            continue;
+          $plugin = $button_config['plugin'];
+          $button = $button_config['button'];
+
+          if ($button == WYSIWYG_SEPARATOR) {
+            $toolbar_row[] = 'seperator';
           }
-          // Add buttons.
-          if ($type == 'buttons') {
-            $settings['Toolbar'][0][] = $button;
+          elseif (!isset($plugins[$plugin]['buttons'][$button])) {
+            continue;
           }
+
+          $toolbar_row[] = $button;
         }
       }
+
+      $toolbar[] = $toolbar_row;
     }
+
+    $settings['Toolbar'] = $toolbar;
   }
 
-  // @todo 
+  // @todo
 //  if (isset($config['block_formats'])) {
 //    $settings['DropDowns']['headings']['elements'] = explode(',', $config['block_formats']);
 //  }
Index: editors/tinymce.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/tinymce.inc,v
retrieving revision 1.35.2.4
diff -u -p -d -r1.35.2.4 tinymce.inc
--- editors/tinymce.inc	2 Apr 2010 14:05:54 -0000	1.35.2.4
+++ editors/tinymce.inc	7 Apr 2010 00:14:40 -0000
@@ -27,6 +27,10 @@ function wysiwyg_tinymce_editor() {
         'files' => array('tiny_mce_src.js'),
       ),
     ),
+    'toolbar support' => array(
+      'multi rows' => TRUE,
+      'separator' => TRUE,
+    ),
     'version callback' => 'wysiwyg_tinymce_version',
     'themes callback' => 'wysiwyg_tinymce_themes',
     'settings callback' => 'wysiwyg_tinymce_settings',
@@ -191,75 +195,88 @@ function wysiwyg_tinymce_settings($edito
     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 $row) {
+    $group = array();
+
+    foreach ($row as $buttons) {
+      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.
@@ -276,7 +293,7 @@ function wysiwyg_tinymce_settings($edito
       if (isset($config['block_formats'])) {
         $settings['theme_advanced_blockformats'] = $config['block_formats'];
       }
-      if (isset($settings['buttons'])) {
+      if ($toolbar) {
         // 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(
@@ -284,14 +301,17 @@ function wysiwyg_tinymce_settings($edito
           '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];
+
+        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) {
Index: editors/js/fckeditor.config.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/fckeditor.config.js,v
retrieving revision 1.6
diff -u -p -d -r1.6 fckeditor.config.js
--- editors/js/fckeditor.config.js	8 Jun 2009 22:20:38 -0000	1.6
+++ editors/js/fckeditor.config.js	7 Apr 2010 00:14:40 -0000
@@ -17,27 +17,18 @@ var pluginSettings = Drupal.settings.wys
  * 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; }');
   }
 }
 
Index: editors/js/tinymce-3.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/tinymce-3.js,v
retrieving revision 1.17.2.2
diff -u -p -d -r1.17.2.2 tinymce-3.js
--- editors/js/tinymce-3.js	13 Feb 2010 23:58:41 -0000	1.17.2.2
+++ editors/js/tinymce-3.js	7 Apr 2010 00:14:40 -0000
@@ -55,15 +55,7 @@ Drupal.wysiwyg.editor.attach.tinymce = f
   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();
 };
