1 // Aseprite
2 // Copyright (C) 2016  David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "app/tools/active_tool.h"
12 
13 #include "app/pref/preferences.h"
14 #include "app/tools/active_tool_observer.h"
15 #include "app/tools/ink.h"
16 #include "app/tools/pointer.h"
17 #include "app/tools/tool_box.h"
18 #include "app/ui/color_bar.h"
19 
20 namespace app {
21 namespace tools {
22 
23 class ActiveToolChangeTrigger {
24 public:
ActiveToolChangeTrigger(ActiveToolManager * manager)25   ActiveToolChangeTrigger(ActiveToolManager* manager)
26     : m_manager(manager)
27     , m_oldTool(manager->activeTool()) {
28   }
29 
~ActiveToolChangeTrigger()30   ~ActiveToolChangeTrigger() {
31     Tool* newTool = m_manager->activeTool();
32     if (m_oldTool != newTool) {
33       m_manager->notify_observers(
34         &ActiveToolObserver::onActiveToolChange, newTool);
35     }
36   }
37 
38 private:
39   ActiveToolManager* m_manager;
40   Tool* m_oldTool;
41 };
42 
ActiveToolManager(ToolBox * toolbox)43 ActiveToolManager::ActiveToolManager(ToolBox* toolbox)
44   : m_toolbox(toolbox)
45   , m_quickTool(nullptr)
46   , m_rightClick(false)
47   , m_rightClickTool(nullptr)
48   , m_rightClickInk(nullptr)
49   , m_proximityTool(nullptr)
50   , m_selectedTool(m_toolbox->getToolById(WellKnownTools::Pencil)) // "pencil" is the active tool by default
51 {
52 }
53 
activeTool() const54 Tool* ActiveToolManager::activeTool() const
55 {
56   if (m_quickTool)
57     return m_quickTool;
58 
59   if (m_rightClickTool)
60     return m_rightClickTool;
61 
62   if (m_proximityTool)
63     return m_proximityTool;
64 
65   // Active tool should never returns null
66   ASSERT(m_selectedTool);
67   return m_selectedTool;
68 }
69 
activeInk() const70 Ink* ActiveToolManager::activeInk() const
71 {
72   if (!m_quickTool && m_rightClickInk)
73     return m_rightClickInk;
74 
75   Tool* tool = activeTool();
76   Ink* ink = tool->getInk(m_rightClick ? 1: 0);
77   if (ink->isPaint() && !ink->isEffect()) {
78     tools::InkType inkType = Preferences::instance().tool(tool).ink();
79     const char* id = nullptr;
80 
81     switch (inkType) {
82 
83       case tools::InkType::SIMPLE: {
84         id = tools::WellKnownInks::Paint;
85 
86         ColorBar* colorbar = ColorBar::instance();
87         app::Color color = (m_rightClick ? colorbar->getBgColor():
88                                            colorbar->getFgColor());
89         if (color.getAlpha() == 0)
90           id = tools::WellKnownInks::PaintCopy;
91         break;
92       }
93 
94       case tools::InkType::ALPHA_COMPOSITING:
95         id = tools::WellKnownInks::Paint;
96         break;
97       case tools::InkType::COPY_COLOR:
98         id = tools::WellKnownInks::PaintCopy;
99         break;
100       case tools::InkType::LOCK_ALPHA:
101         id = tools::WellKnownInks::PaintLockAlpha;
102         break;
103       case tools::InkType::SHADING:
104         id = tools::WellKnownInks::Shading;
105         break;
106     }
107 
108     if (id)
109       ink = m_toolbox->getInkById(id);
110   }
111 
112   return ink;
113 }
114 
quickTool() const115 Tool* ActiveToolManager::quickTool() const
116 {
117   return m_quickTool;
118 }
119 
selectedTool() const120 Tool* ActiveToolManager::selectedTool() const
121 {
122   return m_selectedTool;
123 }
124 
newToolSelectedInToolBar(Tool * tool)125 void ActiveToolManager::newToolSelectedInToolBar(Tool* tool)
126 {
127   ActiveToolChangeTrigger trigger(this);
128   m_selectedTool = tool;
129 }
130 
newQuickToolSelectedFromEditor(Tool * tool)131 void ActiveToolManager::newQuickToolSelectedFromEditor(Tool* tool)
132 {
133   ActiveToolChangeTrigger trigger(this);
134   m_quickTool = tool;
135 }
136 
regularTipProximity()137 void ActiveToolManager::regularTipProximity()
138 {
139   if (m_proximityTool != nullptr) {
140     ActiveToolChangeTrigger trigger(this);
141     m_proximityTool = nullptr;
142   }
143 }
144 
eraserTipProximity()145 void ActiveToolManager::eraserTipProximity()
146 {
147   Tool* eraser = m_toolbox->getToolById(WellKnownTools::Eraser);
148   if (m_proximityTool != eraser) {
149     ActiveToolChangeTrigger trigger(this);
150     m_proximityTool = eraser;
151   }
152 }
153 
pressButton(const Pointer & pointer)154 void ActiveToolManager::pressButton(const Pointer& pointer)
155 {
156   ActiveToolChangeTrigger trigger(this);
157   Tool* tool = nullptr;
158   Ink* ink = nullptr;
159 
160   if (pointer.button() == Pointer::Right) {
161     m_rightClick = true;
162 
163     if (isToolAffectedByRightClickMode(activeTool())) {
164       switch (Preferences::instance().editor.rightClickMode()) {
165         case app::gen::RightClickMode::PAINT_BGCOLOR:
166           // Do nothing, use the active tool
167           break;
168         case app::gen::RightClickMode::PICK_FGCOLOR:
169           tool = m_toolbox->getToolById(WellKnownTools::Eyedropper);
170           ink = m_toolbox->getInkById(tools::WellKnownInks::PickFg);
171           break;
172         case app::gen::RightClickMode::ERASE:
173           tool = m_toolbox->getToolById(WellKnownTools::Eraser);
174           ink = m_toolbox->getInkById(tools::WellKnownInks::Eraser);
175           break;
176         case app::gen::RightClickMode::SCROLL:
177           tool = m_toolbox->getToolById(WellKnownTools::Hand);
178           ink = m_toolbox->getInkById(tools::WellKnownInks::Scroll);
179           break;
180         case app::gen::RightClickMode::RECTANGULAR_MARQUEE:
181           tool = m_toolbox->getToolById(WellKnownTools::RectangularMarquee);
182           ink = m_toolbox->getInkById(tools::WellKnownInks::Selection);
183           break;
184         case app::gen::RightClickMode::LASSO:
185           tool = m_toolbox->getToolById(WellKnownTools::Lasso);
186           ink = m_toolbox->getInkById(tools::WellKnownInks::Selection);
187           break;
188       }
189     }
190   }
191   else {
192     m_rightClick = false;
193   }
194 
195   m_rightClickTool = tool;
196   m_rightClickInk = ink;
197 }
198 
releaseButtons()199 void ActiveToolManager::releaseButtons()
200 {
201   ActiveToolChangeTrigger trigger(this);
202 
203   m_rightClick = false;
204   m_rightClickTool = nullptr;
205   m_rightClickInk = nullptr;
206 }
207 
setSelectedTool(Tool * tool)208 void ActiveToolManager::setSelectedTool(Tool* tool)
209 {
210   ActiveToolChangeTrigger trigger(this);
211 
212   m_selectedTool = tool;
213   notify_observers(&ActiveToolObserver::onSelectedToolChange, tool);
214 }
215 
216 // static
isToolAffectedByRightClickMode(Tool * tool)217 bool ActiveToolManager::isToolAffectedByRightClickMode(Tool* tool)
218 {
219   bool shadingMode = (Preferences::instance().tool(tool).ink() == InkType::SHADING);
220   return
221     ((tool->getInk(0)->isPaint() && !shadingMode) ||
222      (tool->getInk(0)->isEffect())) &&
223     (!tool->getInk(0)->isEraser()) &&
224     (!tool->getInk(0)->isSelection());
225 }
226 
227 } // namespace tools
228 } // namespace app
229