From 2d64a1926e6119d6c38a7d12fdc2fb6bed82b640 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Tue, 15 Dec 2015 17:12:11 -0300
Subject: [PATCH 01/10] Improve BrushPopup to save custom brushes
 (type/size/angle params)

We moved custom brushes stuff from ContextBar to app::AppBrushes class.
And now we can access the list of brushes from app::App::brushes() member.
---
 src/app/CMakeLists.txt                      |   1 +
 src/app/app.h                               |   3 +
 src/app/app_brushes.cpp                     | 117 +++++++++
 src/app/app_brushes.h                       |  78 ++++++
 src/app/commands/cmd_keyboard_shortcuts.cpp |  24 +-
 src/app/commands/cmd_new_brush.cpp          |   3 +-
 src/app/ui/brush_popup.cpp                  | 252 +++++++++++++-------
 src/app/ui/brush_popup.h                    |  27 +--
 src/app/ui/button_set.cpp                   |  30 ++-
 src/app/ui/button_set.h                     |   5 +-
 src/app/ui/context_bar.cpp                  | 146 ++++--------
 src/app/ui/context_bar.h                    |  30 +--
 12 files changed, 478 insertions(+), 238 deletions(-)
 create mode 100644 src/app/app_brushes.cpp
 create mode 100644 src/app/app_brushes.h

diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 490c21cfd..275655bb6 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -90,6 +90,7 @@ endif()
 
 add_library(app-lib
   app.cpp
+  app_brushes.cpp
   app_menus.cpp
   app_options.cpp
   app_render.cpp
diff --git a/src/app/app.h b/src/app/app.h
index 3c1e7bc49..ba1ae1c65 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -9,6 +9,7 @@
 #define APP_APP_H_INCLUDED
 #pragma once
 
+#include "app/app_brushes.h"
 #include "base/signal.h"
 #include "base/string.h"
 #include "base/unique_ptr.h"
@@ -69,6 +70,7 @@ namespace app {
     RecentFiles* getRecentFiles() const;
     MainWindow* getMainWindow() const { return m_mainWindow; }
     Preferences& preferences() const;
+    AppBrushes& brushes() { return m_brushes; }
 
     void showNotification(INotificationDelegate* del);
     void updateDisplayTitleBar();
@@ -95,6 +97,7 @@ namespace app {
     base::UniquePtr<MainWindow> m_mainWindow;
     FileList m_files;
     base::UniquePtr<DocumentExporter> m_exporter;
+    AppBrushes m_brushes;
   };
 
   void app_refresh_screen();
diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp
new file mode 100644
index 000000000..a3a2addfb
--- /dev/null
+++ b/src/app/app_brushes.cpp
@@ -0,0 +1,117 @@
+// Aseprite
+// Copyright (C) 2001-2015  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/app_brushes.h"
+
+namespace app {
+
+using namespace doc;
+
+AppBrushes::AppBrushes()
+{
+  m_standard.push_back(BrushRef(new Brush(kCircleBrushType, 7, 0)));
+  m_standard.push_back(BrushRef(new Brush(kSquareBrushType, 7, 0)));
+  m_standard.push_back(BrushRef(new Brush(kLineBrushType, 7, 44)));
+}
+
+AppBrushes::slot_id AppBrushes::addCustomBrush(const BrushRef& brush)
+{
+  // Use an empty slot
+  for (size_t i=0; i<m_slots.size(); ++i) {
+    if (!m_slots[i].locked() ||
+        !m_slots[i].brush()) {
+      m_slots[i].setBrush(brush);
+      return i+1;
+    }
+  }
+
+  m_slots.push_back(BrushSlot(brush));
+  ItemsChange();
+  return (int)m_slots.size(); // Returns the slot
+}
+
+void AppBrushes::removeCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size()) {
+    m_slots[slot].setBrush(BrushRef(nullptr));
+
+    // Erase empty trailing slots
+    while (!m_slots.empty() &&
+           !m_slots[m_slots.size()-1].brush())
+      m_slots.erase(--m_slots.end());
+
+    ItemsChange();
+  }
+}
+
+void AppBrushes::removeAllCustomBrushes()
+{
+  while (!m_slots.empty())
+    m_slots.erase(--m_slots.end());
+
+  ItemsChange();
+}
+
+bool AppBrushes::hasCustomBrush(slot_id slot) const
+{
+  --slot;
+  return (slot >= 0 && slot < (int)m_slots.size() &&
+          m_slots[slot].brush());
+}
+
+BrushRef AppBrushes::getCustomBrush(slot_id slot) const
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size())
+    return m_slots[slot].brush();
+  else
+    return BrushRef();
+}
+
+Brushes AppBrushes::getCustomBrushes()
+{
+  Brushes brushes;
+  for (const auto& slot : m_slots)
+    brushes.push_back(slot.brush());
+  return brushes;
+}
+
+void AppBrushes::lockCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    m_slots[slot].setLocked(true);
+  }
+}
+
+void AppBrushes::unlockCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    m_slots[slot].setLocked(false);
+  }
+}
+
+bool AppBrushes::isCustomBrushLocked(slot_id slot) const
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    return m_slots[slot].locked();
+  }
+  else
+    return false;
+}
+
+} // namespace app
diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h
new file mode 100644
index 000000000..12662ed20
--- /dev/null
+++ b/src/app/app_brushes.h
@@ -0,0 +1,78 @@
+// Aseprite
+// Copyright (C) 2001-2015  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_APP_BRUSHES_H_INCLUDED
+#define APP_APP_BRUSHES_H_INCLUDED
+#pragma once
+
+#include "base/signal.h"
+#include "doc/brush.h"
+#include "doc/brushes.h"
+
+#include <vector>
+
+namespace app {
+
+  class AppBrushes {
+  public:
+    // Number of slot (a range from 1 to AppBrushes::size() inclusive)
+    typedef int slot_id;
+
+    AppBrushes();
+
+    // Adds a new brush and returns the slot number where the brush
+    // is now available.
+    slot_id addCustomBrush(const doc::BrushRef& brush);
+    void removeCustomBrush(slot_id slot);
+    void removeAllCustomBrushes();
+    bool hasCustomBrush(slot_id slot) const;
+    const doc::Brushes& getStandardBrushes() { return m_standard; }
+    doc::BrushRef getCustomBrush(slot_id slot) const;
+    doc::Brushes getCustomBrushes();
+
+    void lockCustomBrush(slot_id slot);
+    void unlockCustomBrush(slot_id slot);
+    bool isCustomBrushLocked(slot_id slot) const;
+
+    base::Signal0<void> ItemsChange;
+
+  private:
+    // Custom brush slot
+    class BrushSlot {
+    public:
+      BrushSlot(const doc::BrushRef& brush)
+        : m_locked(false)
+        , m_brush(brush) {
+      }
+
+      // True if this is a standard brush.
+      bool standard() const { return m_standard; }
+
+      // True if the user locked the brush using the shortcut key to
+      // access it.
+      bool locked() const { return m_locked; }
+      void setLocked(bool locked) { m_locked = locked; }
+
+      // Can be null if the user deletes the brush.
+      doc::BrushRef brush() const { return m_brush; }
+      void setBrush(const doc::BrushRef& brush) { m_brush = brush; }
+
+    private:
+      bool m_standard;
+      bool m_locked;
+      doc::BrushRef m_brush;
+    };
+
+    typedef std::vector<BrushSlot> BrushSlots;
+
+    doc::Brushes m_standard;
+    BrushSlots m_slots;
+  };
+
+} // namespace app
+
+#endif
diff --git a/src/app/commands/cmd_keyboard_shortcuts.cpp b/src/app/commands/cmd_keyboard_shortcuts.cpp
index e2ffa7982..acd25cd79 100644
--- a/src/app/commands/cmd_keyboard_shortcuts.cpp
+++ b/src/app/commands/cmd_keyboard_shortcuts.cpp
@@ -287,7 +287,8 @@ private:
 
 class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
 public:
-  KeyboardShortcutsWindow() : m_searchChange(false) {
+  KeyboardShortcutsWindow(const std::string& searchText)
+    : m_searchChange(false) {
     setAutoRemap(false);
 
     section()->addChild(new ListItem("Menus"));
@@ -302,6 +303,11 @@ public:
     resetButton()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onReset, this));
 
     fillAllLists();
+
+    if (!searchText.empty()) {
+      search()->setText(searchText);
+      onSearchChange();
+    }
   }
 
   void restoreKeys() {
@@ -523,7 +529,11 @@ public:
   Command* clone() const override { return new KeyboardShortcutsCommand(*this); }
 
 protected:
+  void onLoadParams(const Params& params) override;
   void onExecute(Context* context) override;
+
+private:
+  std::string m_search;
 };
 
 KeyboardShortcutsCommand::KeyboardShortcutsCommand()
@@ -533,9 +543,19 @@ KeyboardShortcutsCommand::KeyboardShortcutsCommand()
 {
 }
 
+void KeyboardShortcutsCommand::onLoadParams(const Params& params)
+{
+  m_search = params.get("search");
+}
+
 void KeyboardShortcutsCommand::onExecute(Context* context)
 {
-  KeyboardShortcutsWindow window;
+  // Here we copy the m_search field because
+  // KeyboardShortcutsWindow::fillAllLists() modifies this same
+  // KeyboardShortcutsCommand instance (so m_search will be "")
+  // TODO Seeing this, we need a complete new way to handle UI commands execution
+  std::string neededSearchCopy = m_search;
+  KeyboardShortcutsWindow window(neededSearchCopy);
 
   window.setBounds(gfx::Rect(0, 0, ui::display_w()*3/4, ui::display_h()*3/4));
   g_sep = window.bounds().w / 2;
diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp
index aa7b71a8b..d082d0ae1 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -149,9 +149,8 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
   brush->setImage(image.get());
   brush->setPatternOrigin(mask->bounds().origin());
 
-  // TODO add a active stock property in app::Context
   ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
-  int slot = ctxBar->addBrush(brush);
+  auto slot = App::instance()->brushes().addCustomBrush(brush);
   ctxBar->setActiveBrush(brush);
 
   // Get the shortcut for this brush and show it to the user
diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 238f06075..42d7e3c64 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -11,12 +11,18 @@
 
 #include "app/ui/brush_popup.h"
 
+#include "app/app.h"
+#include "app/app_brushes.h"
+#include "app/commands/command.h"
 #include "app/commands/commands.h"
+#include "app/modules/gui.h"
 #include "app/modules/palettes.h"
 #include "app/ui/app_menuitem.h"
 #include "app/ui/button_set.h"
+#include "app/ui/context_bar.h"
 #include "app/ui/keyboard_shortcuts.h"
 #include "app/ui/skin/skin_theme.h"
+#include "app/ui_context.h"
 #include "base/bind.h"
 #include "base/convert_to.h"
 #include "doc/brush.h"
@@ -28,10 +34,12 @@
 #include "she/scoped_surface_lock.h"
 #include "she/surface.h"
 #include "she/system.h"
+#include "ui/button.h"
+#include "ui/link_label.h"
+#include "ui/listitem.h"
 #include "ui/menu.h"
 #include "ui/message.h"
 #include "ui/separator.h"
-#include "ui/tooltips.h"
 
 namespace app {
 
@@ -41,11 +49,10 @@ using namespace ui;
 
 namespace {
 
-class Item : public ButtonSet::Item {
+class SelectBrushItem : public ButtonSet::Item {
 public:
-  Item(BrushPopup* popup, BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
-    : m_popup(popup)
-    , m_delegate(delegate)
+  SelectBrushItem(BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
+    : m_delegate(delegate)
     , m_brush(brush)
     , m_slot(slot) {
     SkinPartPtr icon(new SkinPart);
@@ -57,35 +64,84 @@ public:
     return m_brush;
   }
 
-protected:
-  bool onProcessMessage(Message* msg) override {
-    if (msg->type() == kMouseUpMessage && m_slot > 0) {
-      MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
-      if (mouseMsg->buttons() == kButtonRight) {
-        Menu menu;
-        AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
-        AppMenuItem deleteItem("Delete");
-        AppMenuItem deleteAllItem("Delete All");
-        lockItem.Click.connect(&Item::onLockBrush, this);
-        deleteItem.Click.connect(&Item::onDeleteBrush, this);
-        deleteAllItem.Click.connect(&Item::onDeleteAllBrushes, this);
-        menu.addChild(&lockItem);
-        menu.addChild(&deleteItem);
-        menu.addChild(new MenuSeparator);
-        menu.addChild(&deleteAllItem);
+private:
 
-        // Here we make the popup window temporaly floating, so it's
-        // not closed by the popup menu.
-        m_popup->makeFloating();
-        menu.showPopup(mouseMsg->position());
-        m_popup->makeFixed();
-        m_popup->closeWindow(nullptr);
-      }
-    }
-    return ButtonSet::Item::onProcessMessage(msg);
+  void onClick() override {
+    if (m_slot >= 0)
+      m_delegate->onSelectBrushSlot(m_slot);
+    else
+      m_delegate->onSelectBrush(m_brush);
   }
 
 private:
+
+  BrushPopupDelegate* m_delegate;
+  BrushRef m_brush;
+  int m_slot;
+};
+
+class BrushShortcutItem : public ButtonSet::Item {
+public:
+  BrushShortcutItem(const std::string& text, int slot)
+    : m_slot(slot) {
+    setText(text);
+  }
+
+private:
+  void onClick() override {
+    Params params;
+    params.set("change", "custom");
+    params.set("slot", base::convert_to<std::string>(m_slot).c_str());
+    Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::ChangeBrush);
+    cmd->loadParams(params);
+    std::string search = cmd->friendlyName();
+    if (!search.empty()) {
+      params.clear();
+      params.set("search", search.c_str());
+      cmd = CommandsModule::instance()->getCommandByName(CommandId::KeyboardShortcuts);
+      ASSERT(cmd);
+      if (cmd)
+        UIContext::instance()->executeCommand(cmd, params);
+    }
+  }
+
+  int m_slot;
+};
+
+class BrushOptionsItem : public ButtonSet::Item {
+public:
+  BrushOptionsItem(BrushPopup* popup, BrushPopupDelegate* delegate, int slot)
+    : m_popup(popup)
+    , m_delegate(delegate)
+    , m_slot(slot) {
+    setIcon(SkinTheme::instance()->parts.iconArrowDown(), true);
+  }
+
+private:
+
+  void onClick() override {
+    Menu menu;
+    AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
+    AppMenuItem deleteItem("Delete");
+    AppMenuItem deleteAllItem("Delete All");
+    lockItem.Click.connect(&BrushOptionsItem::onLockBrush, this);
+    deleteItem.Click.connect(&BrushOptionsItem::onDeleteBrush, this);
+    deleteAllItem.Click.connect(&BrushOptionsItem::onDeleteAllBrushes, this);
+    menu.addChild(&lockItem);
+    menu.addChild(&deleteItem);
+    menu.addChild(new MenuSeparator);
+    menu.addChild(&deleteAllItem);
+
+    // Here we make the popup window temporaly floating, so it's
+    // not closed by the popup menu.
+    m_popup->makeFloating();
+    menu.showPopup(gfx::Point(origin().x, origin().y+bounds().h));
+    m_popup->makeFixed();
+    m_popup->closeWindow(nullptr);
+  }
+
+private:
+
   void onLockBrush() {
     if (m_delegate->onIsBrushSlotLocked(m_slot))
       m_delegate->onUnlockBrushSlot(m_slot);
@@ -107,98 +163,122 @@ private:
   int m_slot;
 };
 
-} // anonymous namespace
+class NewCustomBrushItem : public ButtonSet::Item {
+public:
+  NewCustomBrushItem() {
+    setText("Save Brush");
+  }
 
-static BrushRef defBrushes[3];
+private:
+  void onClick() override {
+    AppBrushes& brushes = App::instance()->brushes();
+    auto slot = brushes.addCustomBrush(
+      ContextBar::createBrushFromPreferences());
+    brushes.lockCustomBrush(slot);
+  }
+};
+
+} // anonymous namespace
 
 BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
   : PopupWindow("", ClickBehavior::CloseOnClickInOtherWindow)
+  , m_tooltipManager(nullptr)
+  , m_standardBrushes(3)
+  , m_customBrushes(nullptr)
   , m_delegate(delegate)
 {
   setAutoRemap(false);
-  setBorder(gfx::Border(0));
+  setBorder(gfx::Border(2)*guiscale());
   setChildSpacing(0);
+  m_box.noBorderNoChildSpacing();
+
+  addChild(&m_box);
+
+  HBox* top = new HBox;
+  top->addChild(&m_standardBrushes);
+  top->addChild(new BoxFiller);
+
+  m_box.addChild(top);
+  m_box.addChild(new Separator("", HORIZONTAL));
+
+  const doc::Brushes& brushes = App::instance()->brushes().getStandardBrushes();
+  for (const auto& brush : brushes)
+    m_standardBrushes.addItem(new SelectBrushItem(m_delegate, brush));
+
+  m_standardBrushes.setTransparent(true);
+  m_standardBrushes.setBgColor(gfx::ColorNone);
+
+  App::instance()->brushes()
+    .ItemsChange.connect(&BrushPopup::onBrushChanges, this);
 }
 
 void BrushPopup::setBrush(Brush* brush)
 {
-  for (auto child : m_buttons->children()) {
-    Item* item = static_cast<Item*>(child);
+  for (auto child : m_standardBrushes.children()) {
+    SelectBrushItem* item = static_cast<SelectBrushItem*>(child);
 
     // Same type and same image
     if (item->brush() &&
         item->brush()->type() == brush->type() &&
         (brush->type() != kImageBrushType ||
          item->brush()->image() == brush->image())) {
-      m_buttons->setSelectedItem(item);
-      break;
+      m_standardBrushes.setSelectedItem(item);
+      return;
     }
   }
 }
 
-void BrushPopup::regenerate(const gfx::Rect& box, const Brushes& brushes)
+void BrushPopup::regenerate(const gfx::Rect& box)
 {
-  int columns = 3;
+  const doc::Brushes& brushes = App::instance()->brushes().getCustomBrushes();
 
-  if (m_buttons) {
-    for (auto child : m_buttons->children())
-      m_tooltipManager->removeTooltipFor(child);
-    removeChild(m_buttons.get());
-    m_buttons.reset();
+  if (m_customBrushes) {
+    // As BrushPopup::regenerate() can be called when a
+    // "m_customBrushes" button is clicked we cannot delete
+    // "m_customBrushes" right now.
+    m_customBrushes->parent()->removeChild(m_customBrushes);
+    m_customBrushes->deferDelete();
   }
 
-  if (!defBrushes[0]) {
-    defBrushes[0].reset(new Brush(kCircleBrushType, 7, 0));
-    defBrushes[1].reset(new Brush(kSquareBrushType, 7, 0));
-    defBrushes[2].reset(new Brush(kLineBrushType, 7, 44));
-  }
+  m_customBrushes = new ButtonSet(3);
+  m_customBrushes->setTriggerOnMouseUp(true);
 
-  m_buttons.reset(new ButtonSet(columns));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[0]));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[1]));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[2]));
-
-  int slot = 1;
+  auto& parts = SkinTheme::instance()->parts;
+  int slot = 0;
   for (const auto& brush : brushes) {
-    Item* item = new Item(this, m_delegate, brush, slot);
-    m_buttons->addItem(item);
+    ++slot;
 
-    Params params;
-    params.set("change", "custom");
-    params.set("slot", base::convert_to<std::string>(slot).c_str());
-    Key* key = KeyboardShortcuts::instance()->command(
-      CommandId::ChangeBrush, params);
-    if (key && !key->accels().empty()) {
-      std::string tooltip;
-      tooltip += "Shortcut: ";
-      tooltip += key->accels().front().toString();
-      m_tooltipManager->addTooltipFor(item, tooltip, TOP);
+    // Get shortcut
+    std::string shortcut;
+    {
+      Params params;
+      params.set("change", "custom");
+      params.set("slot", base::convert_to<std::string>(slot).c_str());
+      Key* key = KeyboardShortcuts::instance()->command(
+        CommandId::ChangeBrush, params);
+      if (key && !key->accels().empty())
+        shortcut = key->accels().front().toString();
     }
-    slot++;
+    m_customBrushes->addItem(new SelectBrushItem(m_delegate, brush, slot));
+    m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot));
+    m_customBrushes->addItem(new BrushOptionsItem(this, m_delegate, slot));
   }
-  // Add empty spaces
-  while (((slot-1) % columns) > 0)
-    m_buttons->addItem(new Item(this, m_delegate, BrushRef(nullptr), slot++));
 
-  m_buttons->ItemChange.connect(base::Bind<void>(&BrushPopup::onButtonChange, this));
-  m_buttons->setTransparent(true);
-  m_buttons->setBgColor(gfx::ColorNone);
-  addChild(m_buttons.get());
+  m_customBrushes->addItem(new NewCustomBrushItem, 3, 1);
+  m_customBrushes->setExpansive(true);
+  m_box.addChild(m_customBrushes);
 
-  gfx::Rect rc = box;
-  int buttons = m_buttons->children().size();
-  int rows = (buttons/columns + ((buttons%columns) > 0 ? 1: 0));
-  rc.w *= columns;
-  rc.h = rows * (rc.h-2*guiscale()) + 2*guiscale();
-
-  setBounds(rc);
+  // Resize the window and change the hot region.
+  setBounds(gfx::Rect(box.origin(), sizeHint()));
+  setHotRegion(gfx::Region(bounds()));
 }
 
-void BrushPopup::onButtonChange()
+void BrushPopup::onBrushChanges()
 {
-  Item* item = static_cast<Item*>(m_buttons->getItem(m_buttons->selectedItem()));
-  if (item->brush())
-    BrushChange(item->brush());
+  if (isVisible()) {
+    regenerate(bounds());
+    invalidate();
+  }
 }
 
 // static
diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h
index ccd1c2715..00f2cad6f 100644
--- a/src/app/ui/brush_popup.h
+++ b/src/app/ui/brush_popup.h
@@ -9,26 +9,20 @@
 #define APP_UI_BRUSH_POPUP_H_INCLUDED
 #pragma once
 
+#include "app/ui/button_set.h"
 #include "base/shared_ptr.h"
-#include "base/signal.h"
 #include "doc/brushes.h"
+#include "ui/box.h"
 #include "ui/popup_window.h"
-
-namespace doc {
-  class Brush;
-}
-
-namespace ui {
-  class Menu;
-  class TooltipManager;
-}
+#include "ui/tooltips.h"
 
 namespace app {
-  class ButtonSet;
 
   class BrushPopupDelegate {
   public:
     virtual ~BrushPopupDelegate() { }
+    virtual void onSelectBrush(const doc::BrushRef& brush) = 0;
+    virtual void onSelectBrushSlot(int slot) = 0;
     virtual void onDeleteBrushSlot(int slot) = 0;
     virtual void onDeleteAllBrushes() = 0;
     virtual bool onIsBrushSlotLocked(int slot) const = 0;
@@ -41,21 +35,22 @@ namespace app {
     BrushPopup(BrushPopupDelegate* delegate);
 
     void setBrush(doc::Brush* brush);
-    void regenerate(const gfx::Rect& box, const doc::Brushes& brushes);
+    void regenerate(const gfx::Rect& box);
 
     void setupTooltips(ui::TooltipManager* tooltipManager) {
       m_tooltipManager = tooltipManager;
     }
 
-    base::Signal1<void, const doc::BrushRef&> BrushChange;
-
     static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
 
   private:
-    void onButtonChange();
+    void onStandardBrush();
+    void onBrushChanges();
 
-    base::SharedPtr<ButtonSet> m_buttons;
     ui::TooltipManager* m_tooltipManager;
+    ui::VBox m_box;
+    ButtonSet m_standardBrushes;
+    ButtonSet* m_customBrushes;
     BrushPopupDelegate* m_delegate;
   };
 
diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp
index 910802cc1..60d727785 100644
--- a/src/app/ui/button_set.cpp
+++ b/src/app/ui/button_set.cpp
@@ -50,9 +50,10 @@ ButtonSet::Item::Item()
   setFocusStop(true);
 }
 
-void ButtonSet::Item::setIcon(const SkinPartPtr& icon)
+void ButtonSet::Item::setIcon(const SkinPartPtr& icon, bool mono)
 {
   m_icon = icon;
+  m_mono = mono;
   invalidate();
 }
 
@@ -123,11 +124,16 @@ void ButtonSet::Item::onPaint(ui::PaintEvent& ev)
   theme->drawRect(g, rc, nw.get(), bg);
 
   if (m_icon) {
+    she::Surface* bmp = m_icon->bitmap(0);
+
     if (isSelected() && hasCapture())
-      g->drawColoredRgbaSurface(m_icon->bitmap(0), theme->colors.buttonSelectedText(),
+      g->drawColoredRgbaSurface(bmp, theme->colors.buttonSelectedText(),
+                                iconRc.x, iconRc.y);
+    else if (m_mono)
+      g->drawColoredRgbaSurface(bmp, theme->colors.buttonNormalText(),
                                 iconRc.x, iconRc.y);
     else
-      g->drawRgbaSurface(m_icon->bitmap(0), iconRc.x, iconRc.y);
+      g->drawRgbaSurface(bmp, iconRc.x, iconRc.y);
   }
 
   if (hasText()) {
@@ -160,7 +166,7 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
         if (mnemonicPressed ||
             (hasFocus() && keymsg->scancode() == kKeySpace)) {
           buttonSet()->setSelectedItem(this);
-          buttonSet()->onItemChange(this);
+          onClick();
         }
       }
       break;
@@ -172,7 +178,7 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
       if (static_cast<MouseMessage*>(msg)->left() &&
           !buttonSet()->m_triggerOnMouseUp) {
-        buttonSet()->onItemChange(this);
+        onClick();
       }
       break;
 
@@ -183,10 +189,10 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
         if (static_cast<MouseMessage*>(msg)->left()) {
           if (buttonSet()->m_triggerOnMouseUp)
-            buttonSet()->onItemChange(this);
+            onClick();
         }
         else if (static_cast<MouseMessage*>(msg)->right()) {
-          buttonSet()->onRightClick(this);
+          onRightClick();
         }
       }
       break;
@@ -232,6 +238,16 @@ void ButtonSet::Item::onSizeHint(ui::SizeHintEvent& ev)
   ev.setSizeHint(sz);
 }
 
+void ButtonSet::Item::onClick()
+{
+  buttonSet()->onItemChange(this);
+}
+
+void ButtonSet::Item::onRightClick()
+{
+  buttonSet()->onRightClick(this);
+}
+
 ButtonSet::ButtonSet(int columns)
   : Grid(columns, false)
   , m_offerCapture(true)
diff --git a/src/app/ui/button_set.h b/src/app/ui/button_set.h
index 968551e7a..40f71534c 100644
--- a/src/app/ui/button_set.h
+++ b/src/app/ui/button_set.h
@@ -22,15 +22,18 @@ namespace app {
     class Item : public ui::Widget {
     public:
       Item();
-      void setIcon(const skin::SkinPartPtr& icon);
+      void setIcon(const skin::SkinPartPtr& icon, bool mono = false);
       skin::SkinPartPtr icon() const { return m_icon; }
       ButtonSet* buttonSet();
     protected:
       void onPaint(ui::PaintEvent& ev) override;
       bool onProcessMessage(ui::Message* msg) override;
       void onSizeHint(ui::SizeHintEvent& ev) override;
+      virtual void onClick();
+      virtual void onRightClick();
     private:
       skin::SkinPartPtr m_icon;
+      bool m_mono;
     };
 
     ButtonSet(int columns);
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 7e89e301e..56807f64f 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -12,6 +12,7 @@
 #include "app/ui/context_bar.h"
 
 #include "app/app.h"
+#include "app/app_brushes.h"
 #include "app/app_menus.h"
 #include "app/color_utils.h"
 #include "app/commands/commands.h"
@@ -77,13 +78,13 @@ public:
   BrushTypeField(ContextBar* owner)
     : ButtonSet(1)
     , m_owner(owner)
+    , m_brushes(App::instance()->brushes())
     , m_popupWindow(this) {
     SkinPartPtr part(new SkinPart);
     part->setBitmap(
       0, BrushPopup::createSurfaceForBrush(BrushRef(nullptr)));
 
     addItem(part);
-    m_popupWindow.BrushChange.connect(&BrushTypeField::onBrushChange, this);
   }
 
   ~BrushTypeField() {
@@ -103,6 +104,10 @@ public:
     m_popupWindow.setupTooltips(tooltipManager);
   }
 
+  void showPopupAndHighlightSlot(int slot) {
+    openPopup();
+  }
+
 protected:
   void onItemChange(Item* item) override {
     ButtonSet::onItemChange(item);
@@ -118,24 +123,55 @@ protected:
   }
 
   // BrushPopupDelegate impl
+  void onSelectBrush(const BrushRef& brush) {
+    if (brush->type() == kImageBrushType)
+      m_owner->setActiveBrush(brush);
+    else {
+      Tool* tool = App::instance()->activeTool();
+      ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
+
+      brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
+
+      m_owner->setActiveBrush(
+        ContextBar::createBrushFromPreferences(&brushPref));
+    }
+  }
+
+  void onSelectBrushSlot(int slot) {
+    doc::BrushRef brush = App::instance()->brushes().getCustomBrush(slot);
+
+    // Is this an empty custom brush slot?
+    if (!brush)
+      return;
+
+    Tool* tool = App::instance()->activeTool();
+    ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
+
+    brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
+    brushPref.size(brush->size());
+    brushPref.angle(brush->angle());
+
+    m_owner->setActiveBrush(brush);
+  }
+
   void onDeleteBrushSlot(int slot) override {
-    m_owner->removeBrush(slot);
+    m_brushes.removeCustomBrush(slot);
   }
 
   void onDeleteAllBrushes() override {
-    m_owner->removeAllBrushes();
+    m_brushes.removeAllCustomBrushes();
   }
 
   bool onIsBrushSlotLocked(int slot) const override {
-    return m_owner->isBrushSlotLocked(slot);
+    return m_brushes.isCustomBrushLocked(slot);
   }
 
   void onLockBrushSlot(int slot) override {
-    m_owner->lockBrushSlot(slot);
+    m_brushes.lockCustomBrush(slot);
   }
 
   void onUnlockBrushSlot(int slot) override {
-    m_owner->unlockBrushSlot(slot);
+    m_brushes.unlockCustomBrush(slot);
   }
 
 private:
@@ -151,7 +187,7 @@ private:
   void openPopup() {
     doc::BrushRef brush = m_owner->activeBrush();
 
-    m_popupWindow.regenerate(getPopupBox(), m_owner->getBrushes());
+    m_popupWindow.regenerate(getPopupBox());
     m_popupWindow.setBrush(brush.get());
 
     Region rgn(m_popupWindow.bounds().createUnion(bounds()));
@@ -165,21 +201,8 @@ private:
     deselectItems();
   }
 
-  void onBrushChange(const BrushRef& brush) {
-    if (brush->type() == kImageBrushType)
-      m_owner->setActiveBrush(brush);
-    else {
-      Tool* tool = App::instance()->activeTool();
-      ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
-
-      brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
-
-      m_owner->setActiveBrush(
-        ContextBar::createBrushFromPreferences(&brushPref));
-    }
-  }
-
   ContextBar* m_owner;
+  AppBrushes& m_brushes;
   BrushPopup m_popupWindow;
 };
 
@@ -1633,87 +1656,20 @@ void ContextBar::updateAutoSelectLayer(bool state)
   m_autoSelectLayer->setSelected(state);
 }
 
-int ContextBar::addBrush(const doc::BrushRef& brush)
-{
-  // Use an empty slot
-  for (size_t i=0; i<m_brushes.size(); ++i) {
-    if (!m_brushes[i].locked ||
-        !m_brushes[i].brush) {
-      m_brushes[i].brush = brush;
-      return i+1;
-    }
-  }
-
-  m_brushes.push_back(BrushSlot(brush));
-  return (int)m_brushes.size(); // Returns the slot
-}
-
-void ContextBar::removeBrush(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size()) {
-    m_brushes[slot].brush.reset();
-
-    // Erase empty trailing slots
-    while (!m_brushes.empty() &&
-           !m_brushes[m_brushes.size()-1].brush)
-      m_brushes.erase(--m_brushes.end());
-  }
-}
-
-void ContextBar::removeAllBrushes()
-{
-  while (!m_brushes.empty())
-    m_brushes.erase(--m_brushes.end());
-}
-
 void ContextBar::setActiveBrushBySlot(int slot)
 {
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = true;
-    setActiveBrush(m_brushes[slot].brush);
-  }
-}
+  AppBrushes& brushes = App::instance()->brushes();
 
-Brushes ContextBar::getBrushes()
-{
-  Brushes brushes;
-  for (const auto& slot : m_brushes)
-    brushes.push_back(slot.brush);
-  return brushes;
-}
-
-void ContextBar::lockBrushSlot(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = true;
+  if (brushes.hasCustomBrush(slot)) {
+    brushes.lockCustomBrush(slot);
+    setActiveBrush(brushes.getCustomBrush(slot));
   }
-}
-
-void ContextBar::unlockBrushSlot(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = false;
+  else {
+    updateForTool(App::instance()->activeTool());
+    m_brushType->showPopupAndHighlightSlot(slot);
   }
 }
 
-bool ContextBar::isBrushSlotLocked(int slot) const
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    return m_brushes[slot].locked;
-  }
-  else
-    return false;
-}
-
 void ContextBar::setActiveBrush(const doc::BrushRef& brush)
 {
   m_activeBrush = brush;
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index 097cb166c..0d30e4501 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -16,7 +16,6 @@
 #include "base/connection.h"
 #include "base/observable.h"
 #include "doc/brush.h"
-#include "doc/brushes.h"
 #include "ui/box.h"
 
 #include <vector>
@@ -53,21 +52,10 @@ namespace app {
     void updateAutoSelectLayer(bool state);
 
     void setActiveBrush(const doc::BrushRef& brush);
+    void setActiveBrushBySlot(int slot);
     doc::BrushRef activeBrush(tools::Tool* tool = nullptr) const;
     void discardActiveBrush();
 
-    // Adds a new brush and returns the slot number where the brush
-    // is now available.
-    int addBrush(const doc::BrushRef& brush);
-    void removeBrush(int slot);
-    void removeAllBrushes();
-    void setActiveBrushBySlot(int slot);
-    doc::Brushes getBrushes();
-
-    void lockBrushSlot(int slot);
-    void unlockBrushSlot(int slot);
-    bool isBrushSlotLocked(int slot) const;
-
     static doc::BrushRef createBrushFromPreferences(
       ToolPreferences::Brush* brushPref = nullptr);
 
@@ -91,21 +79,6 @@ namespace app {
     void onSymmetryModeChange();
     void onDropPixels(ContextBarObserver::DropAction action);
 
-    struct BrushSlot {
-      // True if the user locked the brush using the shortcut key to
-      // access it.
-      bool locked;
-
-      // Can be null if the user deletes the brush.
-      doc::BrushRef brush;
-
-      BrushSlot(const doc::BrushRef& brush)
-        : locked(false), brush(brush) {
-      }
-    };
-
-    typedef std::vector<BrushSlot> BrushSlots;
-
     class BrushTypeField;
     class BrushAngleField;
     class BrushSizeField;
@@ -155,7 +128,6 @@ namespace app {
     RotAlgorithmField* m_rotAlgo;
     DropPixelsField* m_dropPixels;
     doc::BrushRef m_activeBrush;
-    BrushSlots m_brushes;
     ui::Label* m_selectBoxHelp;
     SymmetryField* m_symmetry;
     base::ScopedConnection m_sizeConn;

From 6ff62c899e6acd5a898440e308e2a5d566006916 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Tue, 15 Dec 2015 17:58:55 -0300
Subject: [PATCH 02/10] Add "Save Brush Here" option to brush slot popup

---
 src/app/app_brushes.cpp    |  9 +++++++++
 src/app/app_brushes.h      |  1 +
 src/app/ui/brush_popup.cpp | 11 +++++++++++
 3 files changed, 21 insertions(+)

diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp
index a3a2addfb..0cdba53bf 100644
--- a/src/app/app_brushes.cpp
+++ b/src/app/app_brushes.cpp
@@ -77,6 +77,15 @@ BrushRef AppBrushes::getCustomBrush(slot_id slot) const
     return BrushRef();
 }
 
+void AppBrushes::setCustomBrush(slot_id slot, const doc::BrushRef& brush)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size()) {
+    m_slots[slot].setBrush(brush);
+    ItemsChange();
+  }
+}
+
 Brushes AppBrushes::getCustomBrushes()
 {
   Brushes brushes;
diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h
index 12662ed20..aa011035d 100644
--- a/src/app/app_brushes.h
+++ b/src/app/app_brushes.h
@@ -32,6 +32,7 @@ namespace app {
     bool hasCustomBrush(slot_id slot) const;
     const doc::Brushes& getStandardBrushes() { return m_standard; }
     doc::BrushRef getCustomBrush(slot_id slot) const;
+    void setCustomBrush(slot_id slot, const doc::BrushRef& brush);
     doc::Brushes getCustomBrushes();
 
     void lockCustomBrush(slot_id slot);
diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 42d7e3c64..fa2c8f5c0 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -121,12 +121,16 @@ private:
 
   void onClick() override {
     Menu menu;
+    AppMenuItem save("Save Brush Here");
     AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
     AppMenuItem deleteItem("Delete");
     AppMenuItem deleteAllItem("Delete All");
+    save.Click.connect(&BrushOptionsItem::onSaveBrush, this);
     lockItem.Click.connect(&BrushOptionsItem::onLockBrush, this);
     deleteItem.Click.connect(&BrushOptionsItem::onDeleteBrush, this);
     deleteAllItem.Click.connect(&BrushOptionsItem::onDeleteAllBrushes, this);
+    menu.addChild(&save);
+    menu.addChild(new MenuSeparator);
     menu.addChild(&lockItem);
     menu.addChild(&deleteItem);
     menu.addChild(new MenuSeparator);
@@ -142,6 +146,13 @@ private:
 
 private:
 
+  void onSaveBrush() {
+    AppBrushes& brushes = App::instance()->brushes();
+    brushes.setCustomBrush(
+      m_slot, ContextBar::createBrushFromPreferences());
+    brushes.lockCustomBrush(m_slot);
+  }
+
   void onLockBrush() {
     if (m_delegate->onIsBrushSlotLocked(m_slot))
       m_delegate->onUnlockBrushSlot(m_slot);

From 53b8eb7c4d06c97be282f51881d07000b36e90c0 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Tue, 15 Dec 2015 18:17:21 -0300
Subject: [PATCH 03/10] Don't use ContextBar from BrushPopup

---
 src/app/ui/brush_popup.cpp | 10 ++++++----
 src/app/ui/brush_popup.h   |  1 +
 src/app/ui/context_bar.cpp |  4 ++++
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index fa2c8f5c0..c27ca0474 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -19,7 +19,6 @@
 #include "app/modules/palettes.h"
 #include "app/ui/app_menuitem.h"
 #include "app/ui/button_set.h"
-#include "app/ui/context_bar.h"
 #include "app/ui/keyboard_shortcuts.h"
 #include "app/ui/skin/skin_theme.h"
 #include "app/ui_context.h"
@@ -149,7 +148,7 @@ private:
   void onSaveBrush() {
     AppBrushes& brushes = App::instance()->brushes();
     brushes.setCustomBrush(
-      m_slot, ContextBar::createBrushFromPreferences());
+      m_slot, m_delegate->onCreateBrushFromActivePreferences());
     brushes.lockCustomBrush(m_slot);
   }
 
@@ -176,7 +175,8 @@ private:
 
 class NewCustomBrushItem : public ButtonSet::Item {
 public:
-  NewCustomBrushItem() {
+  NewCustomBrushItem(BrushPopupDelegate* delegate)
+    : m_delegate(delegate) {
     setText("Save Brush");
   }
 
@@ -184,9 +184,11 @@ private:
   void onClick() override {
     AppBrushes& brushes = App::instance()->brushes();
     auto slot = brushes.addCustomBrush(
-      ContextBar::createBrushFromPreferences());
+      m_delegate->onCreateBrushFromActivePreferences());
     brushes.lockCustomBrush(slot);
   }
+
+  BrushPopupDelegate* m_delegate;
 };
 
 } // anonymous namespace
diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h
index 00f2cad6f..f7a2356a3 100644
--- a/src/app/ui/brush_popup.h
+++ b/src/app/ui/brush_popup.h
@@ -21,6 +21,7 @@ namespace app {
   class BrushPopupDelegate {
   public:
     virtual ~BrushPopupDelegate() { }
+    virtual doc::BrushRef onCreateBrushFromActivePreferences() = 0;
     virtual void onSelectBrush(const doc::BrushRef& brush) = 0;
     virtual void onSelectBrushSlot(int slot) = 0;
     virtual void onDeleteBrushSlot(int slot) = 0;
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 56807f64f..044bd56fa 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -123,6 +123,10 @@ protected:
   }
 
   // BrushPopupDelegate impl
+  BrushRef onCreateBrushFromActivePreferences() {
+    return ContextBar::createBrushFromPreferences();
+  }
+
   void onSelectBrush(const BrushRef& brush) {
     if (brush->type() == kImageBrushType)
       m_owner->setActiveBrush(brush);

From 3024bc76042431c71765d197bff5d17be066f408 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Tue, 15 Dec 2015 18:17:42 -0300
Subject: [PATCH 04/10] Fix redrawing issue when we "delete all brushes"

---
 src/app/ui/brush_popup.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index c27ca0474..4f205c719 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -289,8 +289,13 @@ void BrushPopup::regenerate(const gfx::Rect& box)
 void BrushPopup::onBrushChanges()
 {
   if (isVisible()) {
+    gfx::Region rgn;
+    getDrawableRegion(rgn, DrawableRegionFlags(kCutTopWindows | kUseChildArea));
+
     regenerate(bounds());
     invalidate();
+
+    parent()->invalidateRegion(rgn);
   }
 }
 

From fd8e8686d59e55419ef24ae14452e5723f69f2e5 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Tue, 15 Dec 2015 19:29:12 -0300
Subject: [PATCH 05/10] Fix NewCustomBrushItem construction

---
 src/app/ui/brush_popup.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 4f205c719..a333d8d2d 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -277,7 +277,7 @@ void BrushPopup::regenerate(const gfx::Rect& box)
     m_customBrushes->addItem(new BrushOptionsItem(this, m_delegate, slot));
   }
 
-  m_customBrushes->addItem(new NewCustomBrushItem, 3, 1);
+  m_customBrushes->addItem(new NewCustomBrushItem(m_delegate), 3, 1);
   m_customBrushes->setExpansive(true);
   m_box.addChild(m_customBrushes);
 

From bd99a301469fcc9856085c953526d0fab331e33a Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Wed, 16 Dec 2015 10:02:52 -0300
Subject: [PATCH 06/10] Avoid closing BrushPopup when we click the menu popup
 option

---
 src/app/ui/brush_popup.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index a333d8d2d..bcd2fa2a6 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -138,9 +138,17 @@ private:
     // Here we make the popup window temporaly floating, so it's
     // not closed by the popup menu.
     m_popup->makeFloating();
+
     menu.showPopup(gfx::Point(origin().x, origin().y+bounds().h));
+
+    // Add the menu popup region to the hot region so the BrushPopup (m_popup)
+    // isn't closed after we click the menu popup.
     m_popup->makeFixed();
-    m_popup->closeWindow(nullptr);
+
+    gfx::Region rgn;
+    rgn.createUnion(gfx::Region(m_popup->bounds()),
+                    gfx::Region(menu.bounds()));
+    m_popup->setHotRegion(rgn);
   }
 
 private:

From 0f35102a4bd68b61f0ab96ee270813f567c28bc9 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Wed, 16 Dec 2015 10:49:15 -0300
Subject: [PATCH 07/10] Add show_popup_menu() internal function in
 brush_popup.cpp

---
 src/app/ui/brush_popup.cpp | 35 +++++++++++++++++++++--------------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index bcd2fa2a6..3336751f6 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -48,6 +48,25 @@ using namespace ui;
 
 namespace {
 
+void show_popup_menu(PopupWindow* popupWindow, Menu* popupMenu,
+                     const gfx::Point& pt)
+{
+  // Here we make the popup window temporaly floating, so it's
+  // not closed by the popup menu.
+  popupWindow->makeFloating();
+
+  popupMenu->showPopup(pt);
+
+  // Add the menu popup region to the window popup hot region so it's
+  // not closed after we close the menu.
+  popupWindow->makeFixed();
+
+  gfx::Region rgn;
+  rgn.createUnion(gfx::Region(popupWindow->bounds()),
+                  gfx::Region(popupMenu->bounds()));
+  popupWindow->setHotRegion(rgn);
+}
+
 class SelectBrushItem : public ButtonSet::Item {
 public:
   SelectBrushItem(BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
@@ -135,20 +154,8 @@ private:
     menu.addChild(new MenuSeparator);
     menu.addChild(&deleteAllItem);
 
-    // Here we make the popup window temporaly floating, so it's
-    // not closed by the popup menu.
-    m_popup->makeFloating();
-
-    menu.showPopup(gfx::Point(origin().x, origin().y+bounds().h));
-
-    // Add the menu popup region to the hot region so the BrushPopup (m_popup)
-    // isn't closed after we click the menu popup.
-    m_popup->makeFixed();
-
-    gfx::Region rgn;
-    rgn.createUnion(gfx::Region(m_popup->bounds()),
-                    gfx::Region(menu.bounds()));
-    m_popup->setHotRegion(rgn);
+    show_popup_menu(m_popup, &menu,
+                    gfx::Point(origin().x, origin().y+bounds().h));
   }
 
 private:

From 1f513a1ed5ff610c90f349eaec4efa55ce5de9e9 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Wed, 16 Dec 2015 16:55:49 -0300
Subject: [PATCH 08/10] Restore selected ButtonSet item when we offer the mouse
 capture

When a ButtonSet triggers an onClick() when a mouse up message is received
(ButtonSet::m_multipleSelection == true), in case the mouse capture is
offered to other ButtonSet, we've to restore the selected item when we
capture the mouse, because we've never generated the onClick() event
in the first place.
---
 src/app/ui/brush_popup.cpp |  1 +
 src/app/ui/button_set.cpp  | 31 +++++++++++++++++++++++++++++--
 src/ui/widget.cpp          |  4 +++-
 src/ui/widget.h            |  5 +++--
 4 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 3336751f6..3eec4aa31 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -219,6 +219,7 @@ BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
   setBorder(gfx::Border(2)*guiscale());
   setChildSpacing(0);
   m_box.noBorderNoChildSpacing();
+  m_standardBrushes.setTriggerOnMouseUp(true);
 
   addChild(&m_box);
 
diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp
index 60d727785..16f2d20b8 100644
--- a/src/app/ui/button_set.cpp
+++ b/src/app/ui/button_set.cpp
@@ -33,6 +33,10 @@ namespace app {
 using namespace ui;
 using namespace app::skin;
 
+// Last selected item for ButtonSet activated on mouse up when the
+// mouse capture is get.
+static int g_itemBeforeCapture = -1;
+
 WidgetType buttonset_item_type()
 {
   static WidgetType type = kGenericWidget;
@@ -172,6 +176,15 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
       break;
 
     case ui::kMouseDownMessage:
+      // Only for single-item and trigerred on mouse up ButtonSets: We
+      // save the current selected item to restore it just in case the
+      // user leaves the ButtonSet without releasing the mouse button
+      // and the mouse capture if offered to other ButtonSet.
+      if (buttonSet()->m_triggerOnMouseUp) {
+        ASSERT(g_itemBeforeCapture < 0);
+        g_itemBeforeCapture = buttonSet()->selectedItem();
+      }
+
       captureMouse();
       buttonSet()->setSelectedItem(this);
       invalidate();
@@ -184,6 +197,9 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
     case ui::kMouseUpMessage:
       if (hasCapture()) {
+        if (g_itemBeforeCapture >= 0)
+          g_itemBeforeCapture = -1;
+
         releaseMouse();
         invalidate();
 
@@ -199,8 +215,19 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
     case ui::kMouseMoveMessage:
       if (hasCapture()) {
-        if (buttonSet()->m_offerCapture)
-          offerCapture(static_cast<ui::MouseMessage*>(msg), buttonset_item_type());
+        if (buttonSet()->m_offerCapture) {
+          if (offerCapture(static_cast<ui::MouseMessage*>(msg), buttonset_item_type())) {
+            // Only for ButtonSets trigerred on mouse up.
+            if (buttonSet()->m_triggerOnMouseUp &&
+                g_itemBeforeCapture >= 0) {
+              // As we never received a kMouseUpMessage (so we never
+              // called onClick()), we have to restore the selected
+              // item at the point when we received the mouse capture.
+              buttonSet()->setSelectedItem(g_itemBeforeCapture);
+              g_itemBeforeCapture = -1;
+            }
+          }
+        }
       }
       break;
 
diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp
index de2033bc5..5266fa0e8 100644
--- a/src/ui/widget.cpp
+++ b/src/ui/widget.cpp
@@ -1236,7 +1236,7 @@ void Widget::releaseMouse()
   }
 }
 
-void Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
+bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
 {
   if (hasCapture()) {
     Widget* pick = manager()->pick(mouseMsg->position());
@@ -1250,8 +1250,10 @@ void Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
         mouseMsg->position());
       mouseMsg2->addRecipient(pick);
       manager()->enqueueMessage(mouseMsg2);
+      return true;
     }
   }
+  return false;
 }
 
 bool Widget::hasFocus()
diff --git a/src/ui/widget.h b/src/ui/widget.h
index c41839d29..0d48ac78b 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -340,8 +340,9 @@ namespace ui {
     bool hasMouseOver();
     bool hasCapture();
 
-    // Offer the capture to widgets of the given type
-    void offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
+    // Offer the capture to widgets of the given type. Returns true if
+    // the capture was passed to other widget.
+    bool offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
 
     // Returns lower-case letter that represet the mnemonic of the widget
     // (the underscored character, i.e. the letter after & symbol).

From 0cdc71bf05b47af0f66447082b31422b462909c5 Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Wed, 16 Dec 2015 17:27:04 -0300
Subject: [PATCH 09/10] Add possibility to save brush type/size/angle, fg/bg
 colors, ink type/opacity, shade, etc. into brush slots

Fix #904
---
 data/pref.xml                      |  11 +++
 src/app/app_brushes.cpp            |  44 ++++------
 src/app/app_brushes.h              |  51 +++--------
 src/app/brush_slot.h               | 104 +++++++++++++++++++++++
 src/app/commands/cmd_new_brush.cpp |   3 +-
 src/app/ui/brush_popup.cpp         | 130 ++++++++++++++++++++++++-----
 src/app/ui/brush_popup.h           |   4 +-
 src/app/ui/context_bar.cpp         | 118 ++++++++++++++++++++------
 src/app/ui/context_bar.h           |   2 +
 9 files changed, 353 insertions(+), 114 deletions(-)
 create mode 100644 src/app/brush_slot.h

diff --git a/data/pref.xml b/data/pref.xml
index aea4a0424..1cd907dc4 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -128,6 +128,17 @@
     <section id="brush">
       <option id="pattern" type="doc::BrushPattern" default="doc::BrushPattern::DEFAULT" />
     </section>
+    <section id="save_brush">
+      <option id="brush_type" type="bool" default="true" />
+      <option id="brush_size" type="bool" default="true" />
+      <option id="brush_angle" type="bool" default="true" />
+      <option id="fg_color" type="bool" default="false" />
+      <option id="bg_color" type="bool" default="false" />
+      <option id="ink_type" type="bool" default="true" />
+      <option id="ink_opacity" type="bool" default="true" />
+      <option id="shade" type="bool" default="true" />
+      <option id="pixel_perfect" type="bool" default="false" />
+    </section>
     <section id="selection">
       <option id="mode" type="app::tools::SelectionMode" default="app::tools::SelectionMode::DEFAULT" />
       <option id="pivot_visibility" type="bool" default="false" />
diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp
index 0cdba53bf..715433ff3 100644
--- a/src/app/app_brushes.cpp
+++ b/src/app/app_brushes.cpp
@@ -22,27 +22,27 @@ AppBrushes::AppBrushes()
   m_standard.push_back(BrushRef(new Brush(kLineBrushType, 7, 44)));
 }
 
-AppBrushes::slot_id AppBrushes::addCustomBrush(const BrushRef& brush)
+AppBrushes::slot_id AppBrushes::addBrushSlot(const BrushSlot& brush)
 {
   // Use an empty slot
   for (size_t i=0; i<m_slots.size(); ++i) {
     if (!m_slots[i].locked() ||
         !m_slots[i].brush()) {
-      m_slots[i].setBrush(brush);
+      m_slots[i] = brush;
       return i+1;
     }
   }
 
-  m_slots.push_back(BrushSlot(brush));
+  m_slots.push_back(brush);
   ItemsChange();
-  return (int)m_slots.size(); // Returns the slot
+  return slot_id(m_slots.size()); // Returns the slot
 }
 
-void AppBrushes::removeCustomBrush(slot_id slot)
+void AppBrushes::removeBrushSlot(slot_id slot)
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size()) {
-    m_slots[slot].setBrush(BrushRef(nullptr));
+    m_slots[slot] = BrushSlot();
 
     // Erase empty trailing slots
     while (!m_slots.empty() &&
@@ -53,7 +53,7 @@ void AppBrushes::removeCustomBrush(slot_id slot)
   }
 }
 
-void AppBrushes::removeAllCustomBrushes()
+void AppBrushes::removeAllBrushSlots()
 {
   while (!m_slots.empty())
     m_slots.erase(--m_slots.end());
@@ -61,40 +61,32 @@ void AppBrushes::removeAllCustomBrushes()
   ItemsChange();
 }
 
-bool AppBrushes::hasCustomBrush(slot_id slot) const
+bool AppBrushes::hasBrushSlot(slot_id slot) const
 {
   --slot;
   return (slot >= 0 && slot < (int)m_slots.size() &&
-          m_slots[slot].brush());
+          !m_slots[slot].isEmpty());
 }
 
-BrushRef AppBrushes::getCustomBrush(slot_id slot) const
+BrushSlot AppBrushes::getBrushSlot(slot_id slot) const
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size())
-    return m_slots[slot].brush();
+    return m_slots[slot];
   else
-    return BrushRef();
+    return BrushSlot();
 }
 
-void AppBrushes::setCustomBrush(slot_id slot, const doc::BrushRef& brush)
+void AppBrushes::setBrushSlot(slot_id slot, const BrushSlot& brush)
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size()) {
-    m_slots[slot].setBrush(brush);
+    m_slots[slot] = brush;
     ItemsChange();
   }
 }
 
-Brushes AppBrushes::getCustomBrushes()
-{
-  Brushes brushes;
-  for (const auto& slot : m_slots)
-    brushes.push_back(slot.brush());
-  return brushes;
-}
-
-void AppBrushes::lockCustomBrush(slot_id slot)
+void AppBrushes::lockBrushSlot(slot_id slot)
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size() &&
@@ -103,7 +95,7 @@ void AppBrushes::lockCustomBrush(slot_id slot)
   }
 }
 
-void AppBrushes::unlockCustomBrush(slot_id slot)
+void AppBrushes::unlockBrushSlot(slot_id slot)
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size() &&
@@ -112,11 +104,11 @@ void AppBrushes::unlockCustomBrush(slot_id slot)
   }
 }
 
-bool AppBrushes::isCustomBrushLocked(slot_id slot) const
+bool AppBrushes::isBrushSlotLocked(slot_id slot) const
 {
   --slot;
   if (slot >= 0 && slot < (int)m_slots.size() &&
-      m_slots[slot].brush()) {
+      !m_slots[slot].isEmpty()) {
     return m_slots[slot].locked();
   }
   else
diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h
index aa011035d..dc451c2b4 100644
--- a/src/app/app_brushes.h
+++ b/src/app/app_brushes.h
@@ -9,8 +9,8 @@
 #define APP_APP_BRUSHES_H_INCLUDED
 #pragma once
 
+#include "app/brush_slot.h"
 #include "base/signal.h"
-#include "doc/brush.h"
 #include "doc/brushes.h"
 
 #include <vector>
@@ -21,55 +21,28 @@ namespace app {
   public:
     // Number of slot (a range from 1 to AppBrushes::size() inclusive)
     typedef int slot_id;
+    typedef std::vector<BrushSlot> BrushSlots;
 
     AppBrushes();
 
     // Adds a new brush and returns the slot number where the brush
     // is now available.
-    slot_id addCustomBrush(const doc::BrushRef& brush);
-    void removeCustomBrush(slot_id slot);
-    void removeAllCustomBrushes();
-    bool hasCustomBrush(slot_id slot) const;
+    slot_id addBrushSlot(const BrushSlot& brush);
+    void removeBrushSlot(slot_id slot);
+    void removeAllBrushSlots();
+    bool hasBrushSlot(slot_id slot) const;
     const doc::Brushes& getStandardBrushes() { return m_standard; }
-    doc::BrushRef getCustomBrush(slot_id slot) const;
-    void setCustomBrush(slot_id slot, const doc::BrushRef& brush);
-    doc::Brushes getCustomBrushes();
+    BrushSlot getBrushSlot(slot_id slot) const;
+    void setBrushSlot(slot_id slot, const BrushSlot& brush);
+    const BrushSlots& getBrushSlots() const { return m_slots; }
 
-    void lockCustomBrush(slot_id slot);
-    void unlockCustomBrush(slot_id slot);
-    bool isCustomBrushLocked(slot_id slot) const;
+    void lockBrushSlot(slot_id slot);
+    void unlockBrushSlot(slot_id slot);
+    bool isBrushSlotLocked(slot_id slot) const;
 
     base::Signal0<void> ItemsChange;
 
   private:
-    // Custom brush slot
-    class BrushSlot {
-    public:
-      BrushSlot(const doc::BrushRef& brush)
-        : m_locked(false)
-        , m_brush(brush) {
-      }
-
-      // True if this is a standard brush.
-      bool standard() const { return m_standard; }
-
-      // True if the user locked the brush using the shortcut key to
-      // access it.
-      bool locked() const { return m_locked; }
-      void setLocked(bool locked) { m_locked = locked; }
-
-      // Can be null if the user deletes the brush.
-      doc::BrushRef brush() const { return m_brush; }
-      void setBrush(const doc::BrushRef& brush) { m_brush = brush; }
-
-    private:
-      bool m_standard;
-      bool m_locked;
-      doc::BrushRef m_brush;
-    };
-
-    typedef std::vector<BrushSlot> BrushSlots;
-
     doc::Brushes m_standard;
     BrushSlots m_slots;
   };
diff --git a/src/app/brush_slot.h b/src/app/brush_slot.h
new file mode 100644
index 000000000..c76041f23
--- /dev/null
+++ b/src/app/brush_slot.h
@@ -0,0 +1,104 @@
+// Aseprite
+// Copyright (C) 2001-2015  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_BRUSH_SLOT_H_INCLUDED
+#define APP_BRUSH_SLOT_H_INCLUDED
+#pragma once
+
+#include "app/color.h"
+#include "app/shade.h"
+#include "app/tools/ink_type.h"
+#include "doc/brush.h"
+
+namespace app {
+
+// Custom brush slot
+class BrushSlot {
+public:
+  enum class Flags {
+    Locked       = 0x0001,
+    BrushType    = 0x0002,
+    BrushSize    = 0x0004,
+    BrushAngle   = 0x0008,
+    FgColor      = 0x0010,
+    BgColor      = 0x0020,
+    InkType      = 0x0040,
+    InkOpacity   = 0x0080,
+    Shade        = 0x0100,
+    PixelPerfect = 0x0200
+  };
+
+  BrushSlot(Flags flags = Flags(0),
+            const doc::BrushRef& brush = doc::BrushRef(nullptr),
+            const app::Color& fgColor = app::Color::fromMask(),
+            const app::Color& bgColor = app::Color::fromMask(),
+            tools::InkType inkType = tools::InkType::DEFAULT,
+            int inkOpacity = 255,
+            const Shade& shade = Shade(),
+            bool pixelPerfect = false)
+    : m_flags(flags)
+    , m_brush(brush)
+    , m_fgColor(fgColor)
+    , m_bgColor(bgColor)
+    , m_inkType(inkType)
+    , m_inkOpacity(inkOpacity)
+    , m_shade(shade)
+    , m_pixelPerfect(pixelPerfect) {
+  }
+
+  bool isEmpty() const {
+    return int(m_flags) == 0;
+  }
+
+  bool hasFlag(Flags flag) const {
+    return ((int(m_flags) & int(flag)) == int(flag));
+  }
+
+  bool hasBrush() const {
+    return
+      (brush() &&
+       (hasFlag(Flags::BrushType) ||
+        hasFlag(Flags::BrushSize) ||
+        hasFlag(Flags::BrushAngle)));
+  }
+
+  // Can be null if the user deletes the brush.
+  doc::BrushRef brush() const { return m_brush; }
+  app::Color fgColor() const { return m_fgColor; }
+  app::Color bgColor() const { return m_bgColor; }
+  tools::InkType inkType() const { return m_inkType; }
+  int inkOpacity() const { return m_inkOpacity; }
+  const Shade& shade() const { return m_shade; }
+  bool pixelPerfect() const { return m_pixelPerfect; }
+
+  // True if the user locked the brush using the shortcut key to
+  // access it.
+  bool locked() const {
+    return hasFlag(Flags::Locked);
+  }
+
+  void setLocked(bool locked) {
+    if (locked)
+      m_flags = static_cast<Flags>(int(m_flags) | int(Flags::Locked));
+    else
+      m_flags = static_cast<Flags>(int(m_flags) & ~int(Flags::Locked));
+  }
+
+private:
+  Flags m_flags;
+  doc::BrushRef m_brush;
+  app::Color m_fgColor;
+  app::Color m_bgColor;
+  tools::InkType m_inkType;
+  int m_inkOpacity;
+  Shade m_shade;
+  bool m_pixelPerfect;
+};
+
+} // namespace app
+
+#endif
diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp
index d082d0ae1..165770367 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -150,7 +150,8 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
   brush->setPatternOrigin(mask->bounds().origin());
 
   ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
-  auto slot = App::instance()->brushes().addCustomBrush(brush);
+  int slot = App::instance()->brushes().addBrushSlot(
+    BrushSlot(BrushSlot::Flags::BrushType, brush));
   ctxBar->setActiveBrush(brush);
 
   // Get the shortcut for this brush and show it to the user
diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 3eec4aa31..0efa988f8 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -13,10 +13,12 @@
 
 #include "app/app.h"
 #include "app/app_brushes.h"
+#include "app/brush_slot.h"
 #include "app/commands/command.h"
 #include "app/commands/commands.h"
 #include "app/modules/gui.h"
 #include "app/modules/palettes.h"
+#include "app/pref/preferences.h"
 #include "app/ui/app_menuitem.h"
 #include "app/ui/button_set.h"
 #include "app/ui/keyboard_shortcuts.h"
@@ -69,16 +71,18 @@ void show_popup_menu(PopupWindow* popupWindow, Menu* popupMenu,
 
 class SelectBrushItem : public ButtonSet::Item {
 public:
-  SelectBrushItem(BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
+  SelectBrushItem(BrushPopupDelegate* delegate, const BrushSlot& brush, int slot = -1)
     : m_delegate(delegate)
     , m_brush(brush)
     , m_slot(slot) {
-    SkinPartPtr icon(new SkinPart);
-    icon->setBitmap(0, BrushPopup::createSurfaceForBrush(brush));
-    setIcon(icon);
+    if (m_brush.hasBrush()) {
+      SkinPartPtr icon(new SkinPart);
+      icon->setBitmap(0, BrushPopup::createSurfaceForBrush(m_brush.brush()));
+      setIcon(icon);
+    }
   }
 
-  const BrushRef& brush() const {
+  const BrushSlot& brush() const {
     return m_brush;
   }
 
@@ -87,14 +91,14 @@ private:
   void onClick() override {
     if (m_slot >= 0)
       m_delegate->onSelectBrushSlot(m_slot);
-    else
-      m_delegate->onSelectBrush(m_brush);
+    else if (m_brush.hasBrush())
+      m_delegate->onSelectBrush(m_brush.brush());
   }
 
 private:
 
   BrushPopupDelegate* m_delegate;
-  BrushRef m_brush;
+  BrushSlot m_brush;
   int m_slot;
 };
 
@@ -162,9 +166,9 @@ private:
 
   void onSaveBrush() {
     AppBrushes& brushes = App::instance()->brushes();
-    brushes.setCustomBrush(
-      m_slot, m_delegate->onCreateBrushFromActivePreferences());
-    brushes.lockCustomBrush(m_slot);
+    brushes.setBrushSlot(
+      m_slot, m_delegate->onCreateBrushSlotFromActivePreferences());
+    brushes.lockBrushSlot(m_slot);
   }
 
   void onLockBrush() {
@@ -198,14 +202,95 @@ public:
 private:
   void onClick() override {
     AppBrushes& brushes = App::instance()->brushes();
-    auto slot = brushes.addCustomBrush(
-      m_delegate->onCreateBrushFromActivePreferences());
-    brushes.lockCustomBrush(slot);
+    auto slot = brushes.addBrushSlot(
+      m_delegate->onCreateBrushSlotFromActivePreferences());
+    brushes.lockBrushSlot(slot);
   }
 
   BrushPopupDelegate* m_delegate;
 };
 
+class NewBrushOptionsItem : public ButtonSet::Item {
+public:
+  NewBrushOptionsItem() {
+    setIcon(SkinTheme::instance()->parts.iconArrowDown(), true);
+  }
+
+private:
+  void onClick() override {
+    Menu menu;
+
+    menu.addChild(new Separator("Parameters to Save", HORIZONTAL));
+
+    Grid* grid = new Grid(2, false);
+    ButtonSet* brushParams = new ButtonSet(3);
+    ButtonSet::Item* brushType = brushParams->addItem("Type");
+    ButtonSet::Item* brushSize = brushParams->addItem("Size");
+    ButtonSet::Item* brushAngle = brushParams->addItem("Angle");
+    brushParams->setMultipleSelection(true);
+
+    ButtonSet* colorParams = new ButtonSet(2);
+    ButtonSet::Item* fgColor = colorParams->addItem("Foreground");
+    ButtonSet::Item* bgColor = colorParams->addItem("Background");
+    colorParams->setMultipleSelection(true);
+
+    ButtonSet* inkParams = new ButtonSet(2);
+    ButtonSet::Item* inkType = inkParams->addItem("Type");
+    ButtonSet::Item* inkOpacity = inkParams->addItem("Opacity");
+    inkParams->setMultipleSelection(true);
+
+    ButtonSet* extrasParams = new ButtonSet(2);
+    ButtonSet::Item* shade = extrasParams->addItem("Shade");
+    ButtonSet::Item* pixelPerfect = extrasParams->addItem("Pixel-Perfect");
+    extrasParams->setMultipleSelection(true);
+
+    grid->addChildInCell(new Label("Brush:"), 1, 1, 0);
+    grid->addChildInCell(brushParams, 1, 1, 0);
+    grid->addChildInCell(new Label("Color:"), 1, 1, 0);
+    grid->addChildInCell(colorParams, 1, 1, 0);
+    grid->addChildInCell(new Label("Ink:"), 1, 1, 0);
+    grid->addChildInCell(inkParams, 1, 1, 0);
+    grid->addChildInCell(new Label("Extras:"), 1, 1, 0);
+    grid->addChildInCell(extrasParams, 1, 1, 0);
+    menu.addChild(grid);
+
+    // Load preferences
+    auto& saveBrush = Preferences::instance().saveBrush;
+    brushType->setSelected(saveBrush.brushType());
+    brushSize->setSelected(saveBrush.brushSize());
+    brushAngle->setSelected(saveBrush.brushAngle());
+    fgColor->setSelected(saveBrush.fgColor());
+    bgColor->setSelected(saveBrush.bgColor());
+    inkType->setSelected(saveBrush.inkType());
+    inkOpacity->setSelected(saveBrush.inkOpacity());
+    shade->setSelected(saveBrush.shade());
+    pixelPerfect->setSelected(saveBrush.pixelPerfect());
+
+    show_popup_menu(static_cast<PopupWindow*>(window()), &menu,
+                    gfx::Point(origin().x, origin().y+bounds().h));
+
+    // Save preferences
+    if (saveBrush.brushType() != brushType->isSelected())
+      saveBrush.brushType(brushType->isSelected());
+    if (saveBrush.brushSize() != brushSize->isSelected())
+      saveBrush.brushSize(brushSize->isSelected());
+    if (saveBrush.brushAngle() != brushAngle->isSelected())
+      saveBrush.brushAngle(brushAngle->isSelected());
+    if (saveBrush.fgColor() != fgColor->isSelected())
+      saveBrush.fgColor(fgColor->isSelected());
+    if (saveBrush.bgColor() != bgColor->isSelected())
+      saveBrush.bgColor(bgColor->isSelected());
+    if (saveBrush.inkType() != inkType->isSelected())
+      saveBrush.inkType(inkType->isSelected());
+    if (saveBrush.inkOpacity() != inkOpacity->isSelected())
+      saveBrush.inkOpacity(inkOpacity->isSelected());
+    if (saveBrush.shade() != shade->isSelected())
+      saveBrush.shade(shade->isSelected());
+    if (saveBrush.pixelPerfect() != pixelPerfect->isSelected())
+      saveBrush.pixelPerfect(pixelPerfect->isSelected());
+  }
+};
+
 } // anonymous namespace
 
 BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
@@ -232,7 +317,9 @@ BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
 
   const doc::Brushes& brushes = App::instance()->brushes().getStandardBrushes();
   for (const auto& brush : brushes)
-    m_standardBrushes.addItem(new SelectBrushItem(m_delegate, brush));
+    m_standardBrushes.addItem(
+      new SelectBrushItem(
+        m_delegate, BrushSlot(BrushSlot::Flags::BrushType, brush)));
 
   m_standardBrushes.setTransparent(true);
   m_standardBrushes.setBgColor(gfx::ColorNone);
@@ -247,10 +334,10 @@ void BrushPopup::setBrush(Brush* brush)
     SelectBrushItem* item = static_cast<SelectBrushItem*>(child);
 
     // Same type and same image
-    if (item->brush() &&
-        item->brush()->type() == brush->type() &&
+    if (item->brush().hasBrush() &&
+        item->brush().brush()->type() == brush->type() &&
         (brush->type() != kImageBrushType ||
-         item->brush()->image() == brush->image())) {
+         item->brush().brush()->image() == brush->image())) {
       m_standardBrushes.setSelectedItem(item);
       return;
     }
@@ -259,7 +346,7 @@ void BrushPopup::setBrush(Brush* brush)
 
 void BrushPopup::regenerate(const gfx::Rect& box)
 {
-  const doc::Brushes& brushes = App::instance()->brushes().getCustomBrushes();
+  auto& brushSlots = App::instance()->brushes().getBrushSlots();
 
   if (m_customBrushes) {
     // As BrushPopup::regenerate() can be called when a
@@ -274,7 +361,7 @@ void BrushPopup::regenerate(const gfx::Rect& box)
 
   auto& parts = SkinTheme::instance()->parts;
   int slot = 0;
-  for (const auto& brush : brushes) {
+  for (const auto& brush : brushSlots) {
     ++slot;
 
     // Get shortcut
@@ -293,7 +380,8 @@ void BrushPopup::regenerate(const gfx::Rect& box)
     m_customBrushes->addItem(new BrushOptionsItem(this, m_delegate, slot));
   }
 
-  m_customBrushes->addItem(new NewCustomBrushItem(m_delegate), 3, 1);
+  m_customBrushes->addItem(new NewCustomBrushItem(m_delegate), 2, 1);
+  m_customBrushes->addItem(new NewBrushOptionsItem);
   m_customBrushes->setExpansive(true);
   m_box.addChild(m_customBrushes);
 
diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h
index f7a2356a3..fd109802c 100644
--- a/src/app/ui/brush_popup.h
+++ b/src/app/ui/brush_popup.h
@@ -18,10 +18,12 @@
 
 namespace app {
 
+  class BrushSlot;
+
   class BrushPopupDelegate {
   public:
     virtual ~BrushPopupDelegate() { }
-    virtual doc::BrushRef onCreateBrushFromActivePreferences() = 0;
+    virtual BrushSlot onCreateBrushSlotFromActivePreferences() = 0;
     virtual void onSelectBrush(const doc::BrushRef& brush) = 0;
     virtual void onSelectBrushSlot(int slot) = 0;
     virtual void onDeleteBrushSlot(int slot) = 0;
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 044bd56fa..b5904dc12 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -123,11 +123,34 @@ protected:
   }
 
   // BrushPopupDelegate impl
-  BrushRef onCreateBrushFromActivePreferences() {
-    return ContextBar::createBrushFromPreferences();
+  BrushSlot onCreateBrushSlotFromActivePreferences() override {
+    auto& pref = Preferences::instance();
+    auto& saveBrush = pref.saveBrush;
+    auto& toolPref = pref.tool(App::instance()->activeTool());
+
+    int flags = 0;
+    if (saveBrush.brushType()) flags |= int(BrushSlot::Flags::BrushType);
+    if (saveBrush.brushSize()) flags |= int(BrushSlot::Flags::BrushSize);
+    if (saveBrush.brushAngle()) flags |= int(BrushSlot::Flags::BrushAngle);
+    if (saveBrush.fgColor()) flags |= int(BrushSlot::Flags::FgColor);
+    if (saveBrush.bgColor()) flags |= int(BrushSlot::Flags::BgColor);
+    if (saveBrush.inkType()) flags |= int(BrushSlot::Flags::InkType);
+    if (saveBrush.inkOpacity()) flags |= int(BrushSlot::Flags::InkOpacity);
+    if (saveBrush.shade()) flags |= int(BrushSlot::Flags::Shade);
+    if (saveBrush.pixelPerfect()) flags |= int(BrushSlot::Flags::PixelPerfect);
+
+    return BrushSlot(
+      BrushSlot::Flags(flags),
+      ContextBar::createBrushFromPreferences(),
+      pref.colorBar.fgColor(),
+      pref.colorBar.bgColor(),
+      toolPref.ink(),
+      toolPref.opacity(),
+      m_owner->getShade(),
+      toolPref.freehandAlgorithm() == tools::FreehandAlgorithm::PIXEL_PERFECT);
   }
 
-  void onSelectBrush(const BrushRef& brush) {
+  void onSelectBrush(const BrushRef& brush) override {
     if (brush->type() == kImageBrushType)
       m_owner->setActiveBrush(brush);
     else {
@@ -141,41 +164,28 @@ protected:
     }
   }
 
-  void onSelectBrushSlot(int slot) {
-    doc::BrushRef brush = App::instance()->brushes().getCustomBrush(slot);
-
-    // Is this an empty custom brush slot?
-    if (!brush)
-      return;
-
-    Tool* tool = App::instance()->activeTool();
-    ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
-
-    brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
-    brushPref.size(brush->size());
-    brushPref.angle(brush->angle());
-
-    m_owner->setActiveBrush(brush);
+  void onSelectBrushSlot(int slot) override {
+    m_owner->setActiveBrushBySlot(slot);
   }
 
   void onDeleteBrushSlot(int slot) override {
-    m_brushes.removeCustomBrush(slot);
+    m_brushes.removeBrushSlot(slot);
   }
 
   void onDeleteAllBrushes() override {
-    m_brushes.removeAllCustomBrushes();
+    m_brushes.removeAllBrushSlots();
   }
 
   bool onIsBrushSlotLocked(int slot) const override {
-    return m_brushes.isCustomBrushLocked(slot);
+    return m_brushes.isBrushSlotLocked(slot);
   }
 
   void onLockBrushSlot(int slot) override {
-    m_brushes.lockCustomBrush(slot);
+    m_brushes.lockBrushSlot(slot);
   }
 
   void onUnlockBrushSlot(int slot) override {
-    m_brushes.unlockCustomBrush(slot);
+    m_brushes.unlockBrushSlot(slot);
   }
 
 private:
@@ -723,6 +733,14 @@ public:
     return m_shade.createShadeRemap(left);
   }
 
+  Shade getShade() const {
+    return m_shade.getShade();
+  }
+
+  void setShade(const Shade& shade) {
+    m_shade.setShade(shade);
+  }
+
 private:
   void onShowMenu() {
     loadShades();
@@ -1663,10 +1681,53 @@ void ContextBar::updateAutoSelectLayer(bool state)
 void ContextBar::setActiveBrushBySlot(int slot)
 {
   AppBrushes& brushes = App::instance()->brushes();
+  BrushSlot brush = brushes.getBrushSlot(slot);
+  if (!brush.isEmpty()) {
+    brushes.lockBrushSlot(slot);
 
-  if (brushes.hasCustomBrush(slot)) {
-    brushes.lockCustomBrush(slot);
-    setActiveBrush(brushes.getCustomBrush(slot));
+    Tool* tool = App::instance()->activeTool();
+    Preferences& pref = Preferences::instance();
+    ToolPreferences& toolPref = pref.tool(tool);
+    ToolPreferences::Brush& brushPref = toolPref.brush;
+
+    if (brush.brush()) {
+      if (brush.brush()->type() == doc::kImageBrushType) {
+        setActiveBrush(brush.brush());
+      }
+      else {
+        setActiveBrush(ContextBar::createBrushFromPreferences());
+
+        if (brush.hasFlag(BrushSlot::Flags::BrushType))
+          brushPref.type(static_cast<app::gen::BrushType>(brush.brush()->type()));
+
+        if (brush.hasFlag(BrushSlot::Flags::BrushSize))
+          brushPref.size(brush.brush()->size());
+
+        if (brush.hasFlag(BrushSlot::Flags::BrushAngle))
+          brushPref.angle(brush.brush()->angle());
+      }
+    }
+
+    if (brush.hasFlag(BrushSlot::Flags::FgColor))
+      pref.colorBar.fgColor(brush.fgColor());
+
+    if (brush.hasFlag(BrushSlot::Flags::BgColor))
+      pref.colorBar.bgColor(brush.bgColor());
+
+    if (brush.hasFlag(BrushSlot::Flags::InkType))
+      setInkType(brush.inkType());
+
+    if (brush.hasFlag(BrushSlot::Flags::InkOpacity))
+      toolPref.opacity(brush.inkOpacity());
+
+    if (brush.hasFlag(BrushSlot::Flags::Shade))
+      m_inkShades->setShade(brush.shade());
+
+    if (brush.hasFlag(BrushSlot::Flags::PixelPerfect))
+      toolPref.freehandAlgorithm(
+        (brush.pixelPerfect() ?
+         tools::FreehandAlgorithm::PIXEL_PERFECT:
+         tools::FreehandAlgorithm::REGULAR));
   }
   else {
     updateForTool(App::instance()->activeTool());
@@ -1717,6 +1778,11 @@ doc::BrushRef ContextBar::createBrushFromPreferences(ToolPreferences::Brush* bru
   return brush;
 }
 
+Shade ContextBar::getShade() const
+{
+  return m_inkShades->getShade();
+}
+
 doc::Remap* ContextBar::createShadeRemap(bool left)
 {
   return m_inkShades->createShadeRemap(left);
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index 0d30e4501..df0082c73 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -10,6 +10,7 @@
 #pragma once
 
 #include "app/pref/preferences.h"
+#include "app/shade.h"
 #include "app/tools/ink_type.h"
 #include "app/tools/selection_mode.h"
 #include "app/ui/context_bar_observer.h"
@@ -61,6 +62,7 @@ namespace app {
 
     doc::Remap* createShadeRemap(bool left);
     void reverseShadeColors();
+    Shade getShade() const;
 
     void setInkType(tools::InkType type);
 

From 403e3ee72683e4c97130127f107a5d92775da77a Mon Sep 17 00:00:00 2001
From: David Capello <davidcapello@gmail.com>
Date: Wed, 16 Dec 2015 18:14:04 -0300
Subject: [PATCH 10/10] Add popup on each BrushSlot to enable/disable params

Converted BrushSlotParams into a xml widget.
---
 data/widgets/brush_slot_params.xml |  34 ++++++++
 src/app/brush_slot.h               |   3 +
 src/app/ui/brush_popup.cpp         | 130 ++++++++++++++++-------------
 src/app/widget_loader.cpp          |   6 ++
 src/gen/ui_class.cpp               |   5 ++
 5 files changed, 119 insertions(+), 59 deletions(-)
 create mode 100644 data/widgets/brush_slot_params.xml

diff --git a/data/widgets/brush_slot_params.xml b/data/widgets/brush_slot_params.xml
new file mode 100644
index 000000000..6ad243acd
--- /dev/null
+++ b/data/widgets/brush_slot_params.xml
@@ -0,0 +1,34 @@
+<!-- ASEPRITE -->
+<!-- Copyright (C) 2015 by David Capello -->
+<gui>
+  <vbox id="brush_slot_params">
+    <grid columns="2">
+
+      <label text="Brush:" />
+      <buttonset id="brush_params" columns="3" multiple="true">
+        <item id="brush_type" text="Type" />
+        <item id="brush_size" text="Size" />
+        <item id="brush_angle" text="Angle" />
+      </buttonset>
+
+      <label text="Color:" />
+      <buttonset id="color_params" columns="2" multiple="true">
+        <item id="fg_color" text="Foreground" />
+        <item id="bg_color" text="Background" />
+      </buttonset>
+
+      <label text="Ink:" />
+      <buttonset id="ink_params" columns="2" multiple="true">
+        <item id="ink_type" text="Type" />
+        <item id="ink_opacity" text="Opacity" />
+      </buttonset>
+
+      <label text="Extras:" />
+      <buttonset id="extra_params" columns="2" multiple="true">
+        <item id="shade" text="Shade" />
+        <item id="pixel_perfect" text="Pixel-Perfect" />
+      </buttonset>
+
+    </grid>
+  </vbox>
+</gui>
diff --git a/src/app/brush_slot.h b/src/app/brush_slot.h
index c76041f23..d07cdac6a 100644
--- a/src/app/brush_slot.h
+++ b/src/app/brush_slot.h
@@ -50,6 +50,9 @@ public:
     , m_pixelPerfect(pixelPerfect) {
   }
 
+  Flags flags() const { return m_flags; }
+  void setFlags(Flags flags) { m_flags = flags; }
+
   bool isEmpty() const {
     return int(m_flags) == 0;
   }
diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 0efa988f8..44c613743 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -42,6 +42,8 @@
 #include "ui/message.h"
 #include "ui/separator.h"
 
+#include "brush_slot_params.xml.h"
+
 namespace app {
 
 using namespace app::skin;
@@ -144,22 +146,61 @@ private:
   void onClick() override {
     Menu menu;
     AppMenuItem save("Save Brush Here");
-    AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
+    AppMenuItem lockItem("Locked");
     AppMenuItem deleteItem("Delete");
     AppMenuItem deleteAllItem("Delete All");
+
+    lockItem.setSelected(m_delegate->onIsBrushSlotLocked(m_slot));
+
     save.Click.connect(&BrushOptionsItem::onSaveBrush, this);
     lockItem.Click.connect(&BrushOptionsItem::onLockBrush, this);
     deleteItem.Click.connect(&BrushOptionsItem::onDeleteBrush, this);
     deleteAllItem.Click.connect(&BrushOptionsItem::onDeleteAllBrushes, this);
+
     menu.addChild(&save);
     menu.addChild(new MenuSeparator);
     menu.addChild(&lockItem);
     menu.addChild(&deleteItem);
     menu.addChild(new MenuSeparator);
     menu.addChild(&deleteAllItem);
+    menu.addChild(new Label(""));
+    menu.addChild(new Separator("Saved Parameters", HORIZONTAL));
+
+    app::gen::BrushSlotParams params;
+    menu.addChild(&params);
+
+    // Load preferences
+    AppBrushes& brushes = App::instance()->brushes();
+    BrushSlot brush = brushes.getBrushSlot(m_slot);
+
+    params.brushType()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushType));
+    params.brushSize()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushSize));
+    params.brushAngle()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushAngle));
+    params.fgColor()->setSelected(brush.hasFlag(BrushSlot::Flags::FgColor));
+    params.bgColor()->setSelected(brush.hasFlag(BrushSlot::Flags::BgColor));
+    params.inkType()->setSelected(brush.hasFlag(BrushSlot::Flags::InkType));
+    params.inkOpacity()->setSelected(brush.hasFlag(BrushSlot::Flags::InkOpacity));
+    params.shade()->setSelected(brush.hasFlag(BrushSlot::Flags::Shade));
+    params.pixelPerfect()->setSelected(brush.hasFlag(BrushSlot::Flags::PixelPerfect));
 
     show_popup_menu(m_popup, &menu,
                     gfx::Point(origin().x, origin().y+bounds().h));
+
+    int flags = 0;
+    if (params.brushType()->isSelected()) flags |= int(BrushSlot::Flags::BrushType);
+    if (params.brushSize()->isSelected()) flags |= int(BrushSlot::Flags::BrushSize);
+    if (params.brushAngle()->isSelected()) flags |= int(BrushSlot::Flags::BrushAngle);
+    if (params.fgColor()->isSelected()) flags |= int(BrushSlot::Flags::FgColor);
+    if (params.bgColor()->isSelected()) flags |= int(BrushSlot::Flags::BgColor);
+    if (params.inkType()->isSelected()) flags |= int(BrushSlot::Flags::InkType);
+    if (params.inkOpacity()->isSelected()) flags |= int(BrushSlot::Flags::InkOpacity);
+    if (params.shade()->isSelected()) flags |= int(BrushSlot::Flags::Shade);
+    if (params.pixelPerfect()->isSelected()) flags |= int(BrushSlot::Flags::PixelPerfect);
+
+    if (brush.flags() != BrushSlot::Flags(flags)) {
+      brush.setFlags(BrushSlot::Flags(flags));
+      brushes.setBrushSlot(m_slot, brush);
+    }
   }
 
 private:
@@ -222,72 +263,43 @@ private:
 
     menu.addChild(new Separator("Parameters to Save", HORIZONTAL));
 
-    Grid* grid = new Grid(2, false);
-    ButtonSet* brushParams = new ButtonSet(3);
-    ButtonSet::Item* brushType = brushParams->addItem("Type");
-    ButtonSet::Item* brushSize = brushParams->addItem("Size");
-    ButtonSet::Item* brushAngle = brushParams->addItem("Angle");
-    brushParams->setMultipleSelection(true);
-
-    ButtonSet* colorParams = new ButtonSet(2);
-    ButtonSet::Item* fgColor = colorParams->addItem("Foreground");
-    ButtonSet::Item* bgColor = colorParams->addItem("Background");
-    colorParams->setMultipleSelection(true);
-
-    ButtonSet* inkParams = new ButtonSet(2);
-    ButtonSet::Item* inkType = inkParams->addItem("Type");
-    ButtonSet::Item* inkOpacity = inkParams->addItem("Opacity");
-    inkParams->setMultipleSelection(true);
-
-    ButtonSet* extrasParams = new ButtonSet(2);
-    ButtonSet::Item* shade = extrasParams->addItem("Shade");
-    ButtonSet::Item* pixelPerfect = extrasParams->addItem("Pixel-Perfect");
-    extrasParams->setMultipleSelection(true);
-
-    grid->addChildInCell(new Label("Brush:"), 1, 1, 0);
-    grid->addChildInCell(brushParams, 1, 1, 0);
-    grid->addChildInCell(new Label("Color:"), 1, 1, 0);
-    grid->addChildInCell(colorParams, 1, 1, 0);
-    grid->addChildInCell(new Label("Ink:"), 1, 1, 0);
-    grid->addChildInCell(inkParams, 1, 1, 0);
-    grid->addChildInCell(new Label("Extras:"), 1, 1, 0);
-    grid->addChildInCell(extrasParams, 1, 1, 0);
-    menu.addChild(grid);
+    app::gen::BrushSlotParams params;
+    menu.addChild(&params);
 
     // Load preferences
     auto& saveBrush = Preferences::instance().saveBrush;
-    brushType->setSelected(saveBrush.brushType());
-    brushSize->setSelected(saveBrush.brushSize());
-    brushAngle->setSelected(saveBrush.brushAngle());
-    fgColor->setSelected(saveBrush.fgColor());
-    bgColor->setSelected(saveBrush.bgColor());
-    inkType->setSelected(saveBrush.inkType());
-    inkOpacity->setSelected(saveBrush.inkOpacity());
-    shade->setSelected(saveBrush.shade());
-    pixelPerfect->setSelected(saveBrush.pixelPerfect());
+    params.brushType()->setSelected(saveBrush.brushType());
+    params.brushSize()->setSelected(saveBrush.brushSize());
+    params.brushAngle()->setSelected(saveBrush.brushAngle());
+    params.fgColor()->setSelected(saveBrush.fgColor());
+    params.bgColor()->setSelected(saveBrush.bgColor());
+    params.inkType()->setSelected(saveBrush.inkType());
+    params.inkOpacity()->setSelected(saveBrush.inkOpacity());
+    params.shade()->setSelected(saveBrush.shade());
+    params.pixelPerfect()->setSelected(saveBrush.pixelPerfect());
 
     show_popup_menu(static_cast<PopupWindow*>(window()), &menu,
                     gfx::Point(origin().x, origin().y+bounds().h));
 
     // Save preferences
-    if (saveBrush.brushType() != brushType->isSelected())
-      saveBrush.brushType(brushType->isSelected());
-    if (saveBrush.brushSize() != brushSize->isSelected())
-      saveBrush.brushSize(brushSize->isSelected());
-    if (saveBrush.brushAngle() != brushAngle->isSelected())
-      saveBrush.brushAngle(brushAngle->isSelected());
-    if (saveBrush.fgColor() != fgColor->isSelected())
-      saveBrush.fgColor(fgColor->isSelected());
-    if (saveBrush.bgColor() != bgColor->isSelected())
-      saveBrush.bgColor(bgColor->isSelected());
-    if (saveBrush.inkType() != inkType->isSelected())
-      saveBrush.inkType(inkType->isSelected());
-    if (saveBrush.inkOpacity() != inkOpacity->isSelected())
-      saveBrush.inkOpacity(inkOpacity->isSelected());
-    if (saveBrush.shade() != shade->isSelected())
-      saveBrush.shade(shade->isSelected());
-    if (saveBrush.pixelPerfect() != pixelPerfect->isSelected())
-      saveBrush.pixelPerfect(pixelPerfect->isSelected());
+    if (saveBrush.brushType() != params.brushType()->isSelected())
+      saveBrush.brushType(params.brushType()->isSelected());
+    if (saveBrush.brushSize() != params.brushSize()->isSelected())
+      saveBrush.brushSize(params.brushSize()->isSelected());
+    if (saveBrush.brushAngle() != params.brushAngle()->isSelected())
+      saveBrush.brushAngle(params.brushAngle()->isSelected());
+    if (saveBrush.fgColor() != params.fgColor()->isSelected())
+      saveBrush.fgColor(params.fgColor()->isSelected());
+    if (saveBrush.bgColor() != params.bgColor()->isSelected())
+      saveBrush.bgColor(params.bgColor()->isSelected());
+    if (saveBrush.inkType() != params.inkType()->isSelected())
+      saveBrush.inkType(params.inkType()->isSelected());
+    if (saveBrush.inkOpacity() != params.inkOpacity()->isSelected())
+      saveBrush.inkOpacity(params.inkOpacity()->isSelected());
+    if (saveBrush.shade() != params.shade()->isSelected())
+      saveBrush.shade(params.shade()->isSelected());
+    if (saveBrush.pixelPerfect() != params.pixelPerfect()->isSelected())
+      saveBrush.pixelPerfect(params.pixelPerfect()->isSelected());
   }
 };
 
diff --git a/src/app/widget_loader.cpp b/src/app/widget_loader.cpp
index 9db8c9acc..af2ff07d3 100644
--- a/src/app/widget_loader.cpp
+++ b/src/app/widget_loader.cpp
@@ -434,6 +434,12 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
 
     if (!widget && columns)
       widget = new ButtonSet(strtol(columns, NULL, 10));
+
+    if (ButtonSet* buttonset = dynamic_cast<ButtonSet*>(widget)) {
+      bool multiple = bool_attr_is_true(elem, "multiple");
+      if (multiple)
+        buttonset->setMultipleSelection(multiple);
+    }
   }
   else if (elem_name == "item") {
     if (!parent)
diff --git a/src/gen/ui_class.cpp b/src/gen/ui_class.cpp
index 757745e3d..600ca6f17 100644
--- a/src/gen/ui_class.cpp
+++ b/src/gen/ui_class.cpp
@@ -49,6 +49,10 @@ static void collect_widgets_with_ids(TiXmlElement* elem, XmlElements& widgets)
 
 static std::string convert_type(const std::string& name)
 {
+  static std::string parent;
+  if (name != "item")
+    parent = name;
+
   if (name == "box") return "ui::Box";
   if (name == "button") return "ui::Button";
   if (name == "buttonset") return "app::ButtonSet";
@@ -59,6 +63,7 @@ static std::string convert_type(const std::string& name)
   if (name == "entry") return "ui::Entry";
   if (name == "grid") return "ui::Grid";
   if (name == "hbox") return "ui::HBox";
+  if (name == "item" && parent == "buttonset") return "app::ButtonSet::Item";
   if (name == "label") return "ui::Label";
   if (name == "link") return "ui::LinkLabel";
   if (name == "listbox") return "ui::ListBox";