Revision
65
Author
emsmith
Date
2006-12-10 13:49:34 -0800 (Sun, 10 Dec 2006)

Log Message

IT WORKS - toolbar drag and drop customization is now working - still need to add "set to default" button and combo boxes for icon size and toolbar style to customize window - should all changes be reverted on close?

Modified Paths

Diff

Modified: desktop/trunk/lib/main.class.php (64 => 65)


--- desktop/trunk/lib/main.class.php	2006-12-10 18:09:56 UTC (rev 64)
+++ desktop/trunk/lib/main.class.php	2006-12-10 21:49:34 UTC (rev 65)
@@ -68,7 +68,7 @@
 			'file:quit',
 		),
 		'_View' => array(
-			'toolbar:toggle',
+			//'toolbar:toggle',
 			'view:fullscreen',
 		),
 		'_Help' => array(
@@ -101,6 +101,7 @@
 		'separator',
 		'help:help',
 		'help:website',
+		'expander',
 		'help:about'
 	);
 
@@ -240,7 +241,7 @@
 		$menu = $this->menu = new GtkMenuBar();
 		$actions = CC_Actions::instance();
 
-		foreach( $this->menubar as $name => $def)
+		foreach ($this->menubar as $name => $def)
 		{
 			$item = new GtkMenuItem(CC::i18n($name));
 			$menu->add($item);
@@ -248,7 +249,7 @@
 			$item->set_submenu($submenu);
 			foreach ($def as $name)
 			{
-				if( $name == 'separator')
+				if ($name == 'separator')
 				{
 					$submenu->append(new GtkSeparatorMenuItem());
 				}
@@ -258,6 +259,7 @@
 					$submenu->append($actions->create_menu_item($group, $action));
 				}
 			}
+
 		}
 		return;
 	}

Modified: desktop/trunk/lib/toolbar.class.php (64 => 65)


--- desktop/trunk/lib/toolbar.class.php	2006-12-10 18:09:56 UTC (rev 64)
+++ desktop/trunk/lib/toolbar.class.php	2006-12-10 21:49:34 UTC (rev 65)
@@ -26,42 +26,47 @@
  */
 class CC_Toolbar extends GtkToolbar
 {
-
 	/**
 	 * array of all actions available to put on a toolbar
 	 * @var $options 
 	 */
-	public $options = array();
+	protected $options = array();
 
 	/**
-	 * array of items for the current toolbar
+	 * default items for toolbar
+	 * @var $default array
+	 */
+	protected $default = array();
+
+	/**
+	 * current toolbar items
 	 * @var $current array
 	 */
 	protected $current = array();
 
 	/**
-	 * default items for window
-	 * @var $default array
+	 * popup context menu for the toolbar
+	 * @var $menu object instanceof GtkMenu
 	 */
-	public $default = array();
+	protected $menu;
 
 	/**
-	 * popup menu for the window
-	 * @var $menu object instanceof GtkMenu
+	 * entire liststore for customize
+	 * @var $model object instanceof GtkListstore
 	 */
-	public $menu;
+	protected $model;
 
 	/**
-	 * during customize dnd, highlighted button
-	 * @var $highlight object instanceof GtkToolItem
+	 * stores location of current
+	 * @var $iter object instanceof GtkTreeIter
 	 */
-	protected $highlight;
+	protected $iter;
 
 	/**
-	 * icon view for customize
-	 * @var $view object instanceof GtkIconView
+	 * if something's on the toolbar hide it (store it here)
+	 * @var $toolitem object instanceof GtkToolItem
 	 */
-	protected $view;
+	protected $toolitem;
 
 	/**
 	 * public function __construct
@@ -71,44 +76,180 @@
 	 * @param string $name name of specific toolbar
 	 * @return void
 	 */
-	public function __construct($name = 'Callicore')
+	public function __construct($name, array $default, array $options)
 	{
 		parent::__construct();
+
 		$this->set_name($name);
-		$this->connect('drag-motion', array($this, 'dragMotion'));
-		$this->connect('drag-drop', array($this, 'dragDrop'));
-		$this->connect('drag-leave', array($this, 'dragLeave'));
+		$this->default = $default;
+		$this->options = $options;
+
+		$this->register_actions();
+		$this->build_menu();
+		$this->build_toolbar();
+
+		$config = CC_Config::instance();
+		$actions = CC_Actions::instance();
+
+		$size = isset($config->{$name . '_toolbar_size'}) ? (string) $config->{$name . '_toolbar_size'} : 'small';
+		$style = isset($config->{$name . '_toolbar_style'}) ? (string) $config->{$name . '_toolbar_style'} : 'icon';
+		$active = isset($config->{$name . '_show_toolbar'}) ? (bool) $config->{$name . '_show_toolbar'} : true;
+
+		if ($style === 'icon')
+		{
+			$actions->get_action('toolbar', 'icon')->set_active(true);
+		}
+		elseif ($style === 'text')
+		{
+			$actions->get_action('toolbar', 'text')->set_active(true);
+		}
+		elseif ($style === 'both')
+		{
+			$actions->get_action('toolbar', 'both')->set_active(true);
+		}
+
+		if ($size === 'small')
+		{
+			$actions->get_action('toolbar', 'small')->set_active(true);
+		}
+		elseif ($size === 'medium')
+		{
+			$actions->get_action('toolbar', 'medium')->set_active(true);
+		}
+		elseif ($size === 'large')
+		{
+			$actions->get_action('toolbar', 'large')->set_active(true);
+		}
+
+		if ($active == false)
+		{
+			$this->set_no_show_all(true);
+			$this->set_visible(false);
+		}
+		else
+		{
+			$actions->get_action('toolbar', 'toggle')->set_active(true);
+		}
+
+		$this->connect('drag-motion', array($this, 'on_drag_motion'));
+		$this->connect('drag-drop', array($this, 'on_toolbar_drag_drop'));
+		$this->connect('drag-leave', array($this, 'on_drag_leave'));
+
 		return;
 	}
 
 	/**
-	 * public function buildToolbar
+	 * protected function register_actions
 	 *
-	 * takes current settings and recreates the toolbar
+	 * creates generic window actions
 	 *
+	 * @todo add additional generic actions
 	 * @return void
 	 */
-	public function buildToolbar()
+	protected function register_actions()
 	{
-		$name = $this->name;
-		$config = CC_Config::instance();
 		$actions = CC_Actions::instance();
 
-		$size = isset($config->{$name . '_toolbar_size'}) ? (string) $config->{$name . '_toolbar_size'} : 'small';
-		$style = isset($config->{$name . '_toolbar_style'}) ? (string) $config->{$name . '_toolbar_style'} : 'icon';
-		$active = isset($config->{$name . '_show_toolbar'}) ? $config->{$name . '_show_toolbar'} : TRUE;
-		$toolitems = $this->current = isset($config->{$name . 'toolbar_items'}) ? $config->{$name . 'toolbar_items'} : $this->default;
+		$actions->add_actions('toolbar', array(
+			array(
+				'type' => 'toggle',
+				'name' => 'toggle',
+				'label' => '_Show Toolbar',
+				'short-label' => '_Toolbar',
+				'tooltip' => 'Show and hide the toolbar',
+				'callback' => array($this, 'on_toggle_toolbar'),
+				'active' => $active,
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'small',
+				'label' => '_Small Icons',
+				'short-label' => '_Small',
+				'tooltip' => 'Display small icons on the toolbar',
+				'callback' => array($this, 'on_size_toolbar'),
+				'value' => 1,
+				'radio' => 'small',
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'medium',
+				'label' => '_Medium Icons',
+				'short-label' => '_Medium',
+				'tooltip' => 'Display medium icons on the toolbar',
+				'callback' => array($this, 'on_size_toolbar'),
+				'value' => 2,
+				'radio' => 'small',
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'large',
+				'label' => '_Large Icons',
+				'short-label' => '_Large',
+				'tooltip' => 'Display large icons on the toolbar',
+				'callback' => array($this, 'on_size_toolbar'),
+				'value' => 3,
+				'radio' => 'small',
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'icon',
+				'label' => '_Icons',
+				'short-label' => '_Icons',
+				'tooltip' => 'Display only icons on the toolbar',
+				'callback' => array($this, 'on_style_toolbar'),
+				'value' => 1,
+				'radio' => 'icon',
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'text',
+				'label' => '_Text',
+				'short-label' => '_Text',
+				'tooltip' => 'Display only text on the toolbar',
+				'callback' => array($this, 'on_style_toolbar'),
+				'value' => 2,
+				'radio' => 'icon',
+				),
+			array(
+				'type' => 'radio',
+				'name' => 'both',
+				'label' => '_Both',
+				'short-label' => '_Both',
+				'tooltip' => 'Display icons and text on the toolbar',
+				'callback' => array($this, 'on_style_toolbar'),
+				'value' => 3,
+				'radio' => 'icon',
+				),
+			array(
+				'type' => 'action',
+				'name' => 'customize',
+				'label' => '_Customize...',
+				'short-label' => '_Customize',
+				'tooltip' => 'Customize toolbar',
+				'callback' => array($this, 'on_customize_toolbar'),
+				'image' => 'gtk-properties',
+				),
+		));
 
-		$actions = CC_Actions::instance();
+		return;
+	}
 
-		// Create the popup menu
+	//----------------------------------------------------------------
+	//             UI building
+	//----------------------------------------------------------------
+
+	/**
+	 * public function build_menu
+	 *
+	 * build context menu
+	 *
+	 * @return void
+	 */
+	protected function build_menu()
+	{
 		$menu = $this->menu = new GtkMenu();
+		$actions = CC_Actions::instance();
 
-		$this->set_no_show_all(TRUE);
-		if($active == TRUE)
-		{
-			$actions->get_action('toolbar', 'toggle')->set_active(TRUE);
-		}
 		$menu->append($actions->create_menu_item('toolbar', 'toggle'));
 
 		$item = new GtkMenuItem(CC::i18n('Toolbar S_tyle'));
@@ -116,22 +257,8 @@
 		$submenu = new GtkMenu();
 		$item->set_submenu($submenu);
 
-		if($style === 'icon')
-		{
-			$actions->get_action('toolbar', 'icon')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'icon'));
-
-		if($style === 'text')
-		{
-			$actions->get_action('toolbar', 'text')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'text'));
-
-		if($style === 'both')
-		{
-			$actions->get_action('toolbar', 'both')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'both'));
 
 		$item = new GtkMenuItem(CC::i18n('Toolbar Si_ze'));
@@ -139,22 +266,8 @@
 		$submenu = new GtkMenu();
 		$item->set_submenu($submenu);
 
-		if($size === 'small')
-		{
-			$actions->get_action('toolbar', 'small')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'small'));
-
-		if($size === 'medium')
-		{
-			$actions->get_action('toolbar', 'medium')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'medium'));
-
-		if($size === 'large')
-		{
-			$actions->get_action('toolbar', 'large')->set_active(TRUE);
-		}
 		$submenu->append($actions->create_menu_item('toolbar', 'large'));
 
 		$menu->append(new GtkSeparatorMenuItem());
@@ -164,29 +277,131 @@
 		$menu->show_all();
 
 		$this->connect_simple('popup-context-menu', array($this, 'popup'));
+		return;
+	}
 
+	/**
+	 * public function build_toolbar
+	 *
+	 * actually builds the toolbar
+	 *
+	 * @return void
+	 */
+	protected function build_toolbar($default = false)
+	{
+		$config = CC_Config::instance();
+		$actions = CC_Actions::instance();
+
+		if ($default)
+		{
+			$this->current = $toolitems = $this->default;
+		}
+		else
+		{
+			$this->current = $toolitems = isset($config->{$this->name . '_toolbar_items'}) ? $config->{$this->name . '_toolbar_items'} : $this->default;
+		}
+
 		// populate toolbar
-		foreach($toolitems as $item)
+		foreach ($toolitems as $item)
 		{
-			if($item === 'separator')
+			if ($item === 'separator')
 			{
-				$this->insert($item = new GtkSeparatorToolItem(), -1);
-				$item->connect('drag-begin', array($this, 'dragBegin'));
+				$item = new GtkSeparatorToolItem();
 			}
+			elseif ($item === 'space')
+			{
+				$item = new GtkSeparatorToolItem();
+				$item->set_draw(false);
+			}
+			elseif ($item === 'expander')
+			{
+				$item = new GtkSeparatorToolItem();
+				$item->set_expand(true);
+				$item->set_draw(false);
+			}
 			else
 			{
 				list($group, $name) = explode(':', $item);
-				$this->insert($item = $actions->create_tool_item($group, $name), -1);
+				$item = $actions->create_tool_item($group, $name);
 				$item->child->set_events($item->get_events() | Gdk::BUTTON_PRESS_MASK);
-				$item->child->connect('button-press-event', array($this, 'doPopup'));
+				$item->child->connect('button-press-event', array($this, 'on_button_press_event'));
 				$item->child->connect_simple('popup-menu', array($this, 'popup'));
-				$item->connect('drag-begin', array($this, 'dragBegin'));
 			}
+			$item->connect('drag-begin', array($this, 'on_drag_begin'));
+			$this->insert($item, -1);
 		}
 		return;
 	}
 
 	/**
+	 * public function build_dialog
+	 *
+	 * builds a dialog for customizing toolbar
+	 *
+	 * @return void
+	 */
+	protected function build_dialog()
+	{
+		$window = new GtkDialog(CC::i18n('%s :: %s', CC::$program, 'Customize'),
+			$this->parent->parent, Gtk::DIALOG_DESTROY_WITH_PARENT | Gtk::DIALOG_NO_SEPARATOR);
+		$window->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
+		$window->set_default_size('300', '400');
+		$window->set_border_width(10);
+
+		$actions = CC_Actions::instance();
+		$label = new GtkLabel();
+		$label->set_markup(CC::i18n('<b><big>%s</big></b>',
+			'Add items by dragging them from this window onto the toolbar.' . "\n"
+			. 'Remove items by dragging them from the toolbar into this window.' . "\n"
+			. 'Reorder toolbar items by dragging them to their new position.'));
+		$window->vbox->pack_start($label, FALSE, FALSE);
+		$button = $window->add_button(Gtk::STOCK_CLOSE, Gtk::RESPONSE_CLOSE);
+		$button->connect_simple('clicked', array($window, 'destroy'));
+
+		// create iconview to hold all possible items
+		$this->view = $view = new GtkIconView();
+
+		if(empty($this->model))
+		{
+			$this->create_liststore();
+		}
+		$filter = new GtkTreeModelFilter($this->model); 
+		$filter->set_visible_column(4);
+		$view->set_model($filter);
+		$list = array_diff($this->options, $this->current);
+
+		$view->set_pixbuf_column(2);
+		$view->set_text_column(3);
+		$view->set_columns(0);
+		$view->set_selection_mode(Gtk::SELECTION_BROWSE);
+		$view->drag_source_set(Gdk::BUTTON1_MASK,
+				array(array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
+		$view->connect('drag-begin', array($this, 'on_drag_begin'));
+		$view->drag_dest_set(Gtk::DEST_DEFAULT_ALL,
+			array( array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
+		$view->connect('drag-drop', array($this, 'on_treeview_drag_drop'));
+		$view->select_path(0);
+
+		// scrolling window
+		$scroll = new GtkScrolledWindow();
+		$scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+		$scroll->add($view);
+		$window->vbox->add($scroll);
+
+		// getting this positioned is not fun
+		list($x, $y) = $this->get_parent_window()->get_origin();
+		$position = $this->get_allocation();
+		// y is y position toolbar height allocation + toolbar y position
+		$y += $position->height + $position->y;
+		$window->move($x, $y);
+		return $window;
+	}
+
+	//----------------------------------------------------------------
+	//             Popup Handling
+	//----------------------------------------------------------------
+
+	/**
 	 * public function popup
 	 *
 	 * pops up a context menu on right click
@@ -200,7 +415,7 @@
 	}
 
 	/**
-	 * public function doPopup
+	 * public function on_button_press_event
 	 *
 	 * pops up a popup menu on right click
 	 *
@@ -208,47 +423,30 @@
 	 * @param object $event GdkEvent
 	 * @return void
 	 */
-	public function doPopup($window, $event)
+	public function on_button_press_event($window, $event)
 	{
-		if($event->button == 3)
+		if ($event->button == 3)
 		{
 			$this->popup();
 		}
 		return;
 	}
 
-	/**
-	 * public function onToggleToolbar
-	 *
-	 * show-hide toolbar callback
-	 *
-	 * @return void
-	 */
-	public function onToggleToolbar($action)
-	{
-		$this->set_no_show_all(FALSE);
-		$state = $action->get_active();
-		$this->set_visible($state);
-		CC_Config::instance()->{$this->name . '_show_toolbar'} = (bool) $state;
-		$statusbar = CC_Writer::instance()->statusbar;
-		if(!is_null($statusbar))
-		{
-			$statusbar->label->set_label(CC::i18n('Toolbar visibility changed'));
-		}
-		return;
-	}
+	//----------------------------------------------------------------
+	//             Toolbar Style Callbacks
+	//----------------------------------------------------------------
 
 	/**
-	 * public function onStyleToolbar
+	 * public function on_style_toolbar
 	 *
 	 * change toolbar display type
 	 *
 	 * @return void
 	 */
-	public function onStyleToolbar($action)
+	public function on_style_toolbar($action)
 	{
 		$config = CC_Config::instance();
-		switch($action->get_current_value())
+		switch ($action->get_current_value())
 		{
 			case 3:
 			{
@@ -268,25 +466,21 @@
 				$this->set_toolbar_style(Gtk::TOOLBAR_ICONS);
 			}
 		}
-		$statusbar = CC_Writer::instance()->statusbar;
-		if(!is_null($statusbar))
-		{
-			$statusbar->label->set_label(CC::i18n('Toolbar style changed'));
-		}
+		$this->change_statusbar('Toolbar style changed');
 		return;
 	}
 
 	/**
-	 * public function onSizeToolbar
+	 * public function on_size_toolbar
 	 *
 	 * change toolbar icon display size
 	 *
 	 * @return void
 	 */
-	public function onSizeToolbar($action)
+	public function on_size_toolbar($action)
 	{
 		$config = CC_Config::instance();
-		switch($action->get_current_value())
+		switch ($action->get_current_value())
 		{
 			case 3:
 			{
@@ -306,26 +500,39 @@
 				$this->set_icon_size(CC::$BUTTON);
 			}
 		}
-		$statusbar = CC_Writer::instance()->statusbar;
-		if(!is_null($statusbar))
-		{
-			$statusbar->label->set_label(CC::i18n('Toolbar icon size changed'));
-		}
+		$this->change_statusbar('Toolbar icon size changed');
 		return;
 	}
 
 	/**
-	 * public function onCustomize
+	 * public function on_toggle_toolbar
 	 *
+	 * show-hide toolbar callback
+	 *
+	 * @return void
+	 */
+	public function on_toggle_toolbar($action)
+	{
+		$this->set_no_show_all(FALSE);
+		$state = $action->get_active();
+		$this->set_visible($state);
+		CC_Config::instance()->{$this->name . '_show_toolbar'} = (bool) $state;
+		$this->change_statusbar('Toolbar visibility changed');
+		return;
+	}
+
+	/**
+	 * public function on_customize_toolbar
+	 *
 	 * creates and runs a dialog to customize the toolbar
 	 *
 	 * @return void
 	 */
-	public function onCustomize()
+	public function on_customize_toolbar()
 	{
 		// make all buttons non-clickable and reorderable on the toolbar
 		$buttons = $this->get_children();
-		foreach($buttons as $button)
+		foreach ($buttons as $button)
 		{
 			$button->set_use_drag_window(TRUE);
 			$button->drag_source_set(Gdk::BUTTON1_MASK,
@@ -333,225 +540,352 @@
 		}
 		$this->drag_dest_set(Gtk::DEST_DEFAULT_ALL,
 			array( array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
-		
-		$window = $this->buildDialog();
+		$window = $this->build_dialog();
 		$window->show_all();
-		$window->connect('destroy', array($this, 'stopDrag'));
+		$window->connect('destroy', array($this, 'on_customize_end'));
 		return;
 	}
 
+	//----------------------------------------------------------------
+	//             Drag Callbacks
+	//----------------------------------------------------------------
+
 	/**
-	 * public function dragBegin
+	 * public function on_drag_begin
 	 *
-	 *  stores toolbutton item and sets appropriate dnd pixmap if needed
+	 * sets proper highlight pixmap
 	 *
 	 * @return void
 	 */
-	public function dragBegin($widget, $context)
+	public function on_drag_begin($widget, $context)
 	{
 		if($widget instanceof GtkIconView)
 		{
-			$iter = $widget->get_selected_items();
+			$selected = $widget->get_selected_items();
 			$model = $widget->get_model();
-			$iter = $model->get_iter($iter[0]);
-			$pixbuf = $model->get_value($iter, 2);
-			$widget->drag_source_set_icon_pixbuf($pixbuf);
-			$this->highlight = $iter;
-			return;
+			$iter = $model->convert_iter_to_child_iter($model->get_iter($selected[0]));
+			$this->toolitem = null;
 		}
-		elseif(!($widget instanceof GtkSeparatorToolItem))
+		else
 		{
-			$pixbuf = $this->render_icon($widget->get_property('stock-id'), Gtk::ICON_SIZE_DND);
-			$widget->drag_source_set_icon_pixbuf($pixbuf);
+			// right group and action
+			if($widget instanceof GtkSeparatorToolItem)
+			{
+				$group = '';
+				$action = 'separator';
+				if($widget->get_draw() == false)
+				{
+					$action = 'space';
+				}
+				if($widget->get_expand() == true)
+				{
+					$action = 'expander';
+				}
+			}
+			else
+			{
+				$group = $widget->get_data('group');
+				$action = $widget->get_data('action');
+			}
+			$iter = null;
+			foreach($this->model as $row)
+			{
+				if($row[0] == $group && $row[1] == $action)
+				{
+					$iter = $row->iter;
+					break;
+				}
+			}
+			$this->toolitem = $widget;
 		}
-		$this->highlight = $widget;
+		$this->iter = $iter;
+		// grab proper pixbuf and show it
+		$widget->drag_source_set_icon_pixbuf($this->model->get_value($iter, 2));
 		return;
 	}
 
 	/**
-	 * public function dragMotion
+	 * public function on_drag_motion
 	 *
 	 * sets the highlight button, and if needed removes the button we're moving
 	 *
 	 * @return void
 	 */
-	public function dragMotion($toolbar, $context, $x, $y, $time)
+	public function on_drag_motion($toolbar, $context, $x, $y, $time)
 	{
 		$index = $toolbar->get_drop_index($x, $y);
-		if($this->highlight instanceof GtkToolItem)
+		if($this->toolitem)
 		{
-			$this->highlight->hide();
-			$toolbar->set_drop_highlight_item(GtkToolButton::new_from_stock($this->highlight->get_property('stock-id')), $index);
+			$this->toolitem->hide();
 		}
-		else
-		{
-			$model = $this->view->get_model();
-			$action = CC_Actions::instance()->get_action($model->get_value($this->highlight, 0), $model->get_value($this->highlight, 1));
-			$toolbar->set_drop_highlight_item(GtkToolButton::new_from_stock($action->get_property('stock-id')), $index);
-		}
+		$toolbar->set_drop_highlight_item($this->model->get_value($this->iter, 5), $index);
 		return;
 	}
 
 	/**
-	 * public function dragBegin
+	 * public function on_drag_leave
 	 *
 	 * turns off highlighting and replace removed item in old location
 	 *
 	 * @return void
 	 */
-	public function dragLeave($toolbar, $context, $time)
+	public function on_drag_leave($toolbar, $context, $time)
 	{
 		$toolbar->set_drop_highlight_item(NULL, 0);
-		if($this->highlight instanceof GtkToolItem)
+		if($this->toolitem)
 		{
-			$this->highlight->show();
+			$this->toolitem->show();
 		}
 		return;
 	}
 
 	/**
-	 * public function removeButton
+	 * public function on_toolbar_drag_drop
 	 *
+	 * handles items dragged and dropped onto the toolbar
+	 *
+	 * @return void
+	 */
+	public function on_toolbar_drag_drop($toolbar, $context, $x, $y, $time)
+	{
+		$index = $toolbar->get_drop_index($x, $y);
+		if ($this->toolitem)
+		{
+			$current = $toolbar->get_item_index($this->toolitem);
+			$toolbar->remove($this->toolitem);
+			if($current < $index)
+			{
+				$index--;
+			}
+			$toolbar->insert($this->toolitem, $index);
+		}
+		else
+		{
+			$group = $this->model->get_value($this->iter, 0);
+			$action = $this->model->get_value($this->iter, 1);
+			if ($action === 'separator')
+			{
+				$widget = new GtkSeparatorToolItem();
+			}
+			elseif ($action === 'space')
+			{
+				$widget = new GtkSeparatorToolItem();
+				$widget->set_draw(false);
+			}
+			elseif ($action === 'expander')
+			{
+				$widget = new GtkSeparatorToolItem();
+				$widget->set_expand(true);
+				$widget->set_draw(false);
+			}
+			else
+			{
+				$widget = CC_Actions::instance()->create_tool_item($group, $action);
+				$widget->child->set_events($widget->get_events() | Gdk::BUTTON_PRESS_MASK);
+				$widget->child->connect('button-press-event', array($this, 'on_button_press_event'));
+				$widget->child->connect_simple('popup-menu', array($this, 'popup'));
+				$this->model->set($this->iter, 4, false);
+			}
+			$widget->show();
+			$widget->set_use_drag_window(TRUE);
+			$widget->drag_source_set(Gdk::BUTTON1_MASK,
+				array(array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
+			$widget->connect('drag-begin', array($this, 'on_drag_begin'));
+			$toolbar->insert($widget, $index);
+			$this->view->select_path(0);
+		}
+		return;
+	}
+
+	/**
+	 * public function on_treeview_drag_drop
+	 *
 	 * strip a button out of the toolbar and put it in the iconview
 	 *
 	 * @return void
 	 */
-	public function removeButton($view, $context, $x, $y, $time)
+	public function on_treeview_drag_drop($view, $context, $x, $y, $time)
 	{
-		$group = $this->highlight->get_data('group');
-		$action = $this->highlight->get_data('action');
-		$item = CC_Actions::instance()->get_action($group, $action);
-		$pixbuf = $this->render_icon($item->get_property('stock-id'),  CC::$DND);
-		$view->get_model()->append(array($group, $action, $pixbuf, preg_replace('/(?<!_)_|\.\.\./', '', $item->get_property('label'))));
-		$this->remove($this->highlight);
+		if($this->toolitem)
+		{
+			$this->remove($this->toolitem);
+			$this->model->set($this->iter, 4, true);
+			return;
+		}
+		$this->model->set($this->iter, 4, false);
 		return;
 	}
 
 	/**
-	 * public function stopDrag
+	 * public function on_customize_end
 	 *
-	 * turns draggable buttons back to normal
+	 * turns draggable buttons back to normal and saves toolbar
 	 *
 	 * @return void
 	 */
-	public function stopDrag()
+	public function on_customize_end()
 	{
 		// make all buttons non-clickable and reorderable on the toolbar
 		$buttons = $this->get_children();
-		foreach($buttons as $button)
+		$this->current = array();
+		foreach ($buttons as $button)
 		{
 			$button->set_use_drag_window(FALSE);
 			$button->drag_source_unset();
+			if($button instanceof GtkSeparatorToolItem)
+			{
+				$action = 'separator';
+				if($button->get_draw() == false)
+				{
+					$action = 'space';
+				}
+				if($button->get_expand() == true)
+				{
+					$action = 'expander';
+				}
+				$current[] = $action;
+			}
+			else
+			{
+				$current[] = $button->get_data('group') . ':' . $button->get_data('action');
+			}
 		}
+		$this->current = $current;
+		CC_Config::instance()->{$this->name . '_toolbar_items'} = $this->current;
 		$this->drag_dest_unset();
 		return;
 	}
 
+	//----------------------------------------------------------------
+	//             Internal Methods
+	//----------------------------------------------------------------
+
 	/**
-	 * public function dragDrop
+	 * protected function change_statusbar
 	 *
-	 * moves any dragged and dropped item to the new location and rewrites the
-	 * current array
+	 * attempts to retrieve a window with the same name as the toolbar from
+	 * CC_Wm and set the status label
 	 *
 	 * @return void
 	 */
-	public function dragDrop($toolbar, $context, $x, $y, $time)
+	protected function change_statusbar($message)
 	{
-		$index = $toolbar->get_drop_index($x, $y);
-		if($this->highlight instanceof GtkToolItem)
+		$window = CC_Wm::get_window($this->name);
+		if ($window instanceof CC_Main && $window->statusbar instanceof GtkStatusbar)
 		{
-			$current = $toolbar->get_item_index($this->highlight);
-			$toolbar->remove($this->highlight);
-			if($current < $index)
-			{
-				$index--;
-			}
-			$toolbar->insert($this->highlight, $index);
+			$window->statusbar->label->set_label(CC::i18n($message));
 		}
-		else
-		{
-			$model = $this->view->get_model();
-			// toolbar->highlight needs a button
-			$item = CC_Actions::instance()->create_tool_item($model->get_value($this->highlight, 0), $model->get_value($this->highlight, 1));
-			$item->child->set_events($item->get_events() | Gdk::BUTTON_PRESS_MASK);
-			$item->child->connect('button-press-event', array($this, 'doPopup'));
-			$item->child->connect_simple('popup-menu', array($this, 'popup'));
-			$item->set_use_drag_window(TRUE);
-			$item->drag_source_set(Gdk::BUTTON1_MASK,
-				array(array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
-			$item->connect('drag-begin', array($this, 'dragBegin'));
-			$toolbar->insert($item, $index);
-			$model->remove($this->highlight);
-			$this->view->select_path(0);
-		}
 		return;
 	}
 
 	/**
-	 * public function buildDialog
+	 * protected function create_liststore
 	 *
-	 * builds a dialog for customizing toolbar
+	 * builds the internal liststore that holds all available items
+	 * group, action, pixmap, name, button widget (or null), and boolean show item
+	 * this has to be called when icons are resized (yuck) or when toolbar style
+	 * is changed (double yuck)
 	 *
 	 * @return void
 	 */
-	protected function buildDialog()
+	protected function create_liststore()
 	{
-		$window = new GtkDialog(CC::i18n('%s :: %s', $this->parent->parent->program, 'Customize'),
-			$this->parent->parent, Gtk::DIALOG_DESTROY_WITH_PARENT | Gtk::DIALOG_NO_SEPARATOR);
-		$window->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
-		$window->set_default_size('300', '400');
-		$window->set_border_width(10);
+		if($this->model)
+		{
+			$this->model->clear();
+		}
+		else
+		{
+			$model = $this->model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING,
+				GdkPixbuf::gtype, Gtk::TYPE_STRING, Gtk::TYPE_BOOLEAN, Gtk::TYPE_PHP_VALUE);
+		}
+		$list = array_diff($this->options, $this->current);
+		// add separator
+		$model->append(array('', 'separator', $this->create_pixbuf('', 'separator'),
+			CC::i18n('Separator'), 1, new GtkSeparatorToolItem()));
+		// add space
+		$item = new GtkSeparatorToolItem();
+		$item->set_draw(false);
+		$model->append(array('', 'space', $this->create_pixbuf('', 'space'),
+			CC::i18n('Space'), 1, $item));
+		// expanding space
+		$item = new GtkSeparatorToolItem();
+		$item->set_draw(false);
+		$item->set_expand(true);
+		$model->append(array('', 'expander', $this->create_pixbuf('', 'expander'),
+			CC::i18n('Expanding Space'), 1, $item));
 
 		$actions = CC_Actions::instance();
-		$label = new GtkLabel();
-		$label->set_markup(CC::i18n('<b><big>%s</big></b>',
-			'Add items by dragging them from this window onto the toolbar.' . "\n"
-			. 'Remove items by dragging them from the toolbar into this window.' . "\n"
-			. 'Reorder toolbar items by dragging them to their new position.'));
-		$window->vbox->pack_start($label, FALSE, FALSE);
-		$button = $window->add_button(Gtk::STOCK_CLOSE, Gtk::RESPONSE_CLOSE);
-		$button->connect_simple('clicked', array($window, 'destroy'));
-
-		// create iconview to hold all possible items
-		$this->view = $view = new GtkIconView();
-
-		$model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING, GdkPixbuf::gtype, Gtk::TYPE_STRING);
-		$view->set_model($model);
-		$list = array_diff($this->options, $this->current);
-
-		// fill the model with default actions
-		foreach($list as $item)
+		// fill the model with available buttons
+		foreach ($this->options as $item)
 		{
 			list($group, $action) = explode(':', $item);
-			$item = $actions->get_action($group, $action);
-			$pixbuf = $this->render_icon($item->get_property('stock-id'),  CC::$DND);
-			$model->append(array($group, $action, $pixbuf, preg_replace('/(?<!_)_|\.\.\./', '', $item->get_property('label'))));
+			$pixbuf = $this->create_pixbuf($group, $action);
+			$label = preg_replace('/(?<!_)_|\.\.\./', '',
+				CC::i18n($actions->get_action($group, $action)->get_property('label')));
+			$show = in_array($item, $this->current) ? false : true;
+			$model->append(array($group, $action, $pixbuf, $label, $show, $actions->create_tool_item($group, $action)));
 		}
-		$view->set_pixbuf_column(2);
-		$view->set_markup_column(3);
-		$view->set_columns(0);
-		$view->set_selection_mode(Gtk::SELECTION_BROWSE);
-		$view->drag_source_set(Gdk::BUTTON1_MASK,
-				array(array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
-		$view->connect('drag-begin', array($this, 'dragBegin'));
-		$view->drag_dest_set(Gtk::DEST_DEFAULT_ALL,
-			array( array('application/x-toolbar-item', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
-		$view->connect('drag-drop', array($this, 'removeButton'));
-		$view->select_path(0);
+		return;
+	}
 
-		// scrolling window
-		$scroll = new GtkScrolledWindow();
-		$scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
-		$scroll->add($view);
-		$window->vbox->add($scroll);
-
-		// getting this positioned is not fun
-		list($x, $y) = $this->get_parent_window()->get_origin();
-		$position = $this->get_allocation();
-		// y is y position toolbar height allocation + toolbar y position
-		$y += $position->height + $position->y;
-		$window->move($x, $y);
-		return $window;
+	/**
+	 * protected function create_pixbuf
+	 *
+	 * create a new window, add a single toolbar with the current settings,
+	 * add the requested widget, take a snapshot, kill the window, return the
+	 * pixbuf - the results will be CACHED
+	 *
+	 * @return void
+	 */
+	protected function create_pixbuf($group, $action)
+	{
+		// create temporary window
+		$temp = new GtkWindow();
+		// attempt to force window size
+		list($width, $height) = Gtk::icon_size_lookup($this->get_icon_size());
+		$temp->set_default_size($width, $height);
+		// add appropriate toolbar
+		$temp->add($tool = new GtkToolbar());
+		$tool->set_toolbar_style($this->get_toolbar_style());
+		$tool->set_icon_size($this->get_icon_size());
+		// create widget
+		if ($action === 'separator')
+		{
+			$widget = new GtkSeparatorToolItem();
+		}
+		elseif ($action === 'space')
+		{
+			$widget = new GtkSeparatorToolItem();
+			$widget->set_draw(false);
+		}
+		elseif ($action === 'expander')
+		{
+			$widget = new GtkSeparatorToolItem();
+			$widget->set_expand(true);
+			$widget->set_draw(false);
+		}
+		else
+		{
+			$widget = CC_Actions::instance()->create_tool_item($group, $action);
+		}
+		// add widget to toolbar
+		$tool->insert($widget, -1);
+		// show all and force draw
+		$temp->show_all();
+		while (Gtk::events_pending())
+		{
+			Gtk::main_iteration();
+		}
+		// get current window size
+		$width = $widget->allocation->width;
+		$height = ($height > $widget->allocation->height) ? $height : $widget->allocation->height;
+		// take snapshot
+		$pixbuf = new GdkPixbuf(Gdk::COLORSPACE_RGB, TRUE, 8, $width, $height);
+		$pixbuf->get_from_drawable ($widget->window, $widget->window->get_colormap(),
+			0, 0, 0, 0, $width, $height);
+		$temp->destroy();
+		return $pixbuf;
 	}
 }
 ?>
\ No newline at end of file