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