Tool System: per space/mode tool support
[blender.git] / source / blender / windowmanager / intern / wm_toolsystem.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/windowmanager/intern/wm_toolsystem.c
22  *  \ingroup wm
23  *
24  * Experimental tool-system>
25  */
26
27 #include <string.h>
28
29 #include "CLG_log.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_utildefines.h"
34 #include "BLI_string.h"
35 #include "BLI_listbase.h"
36
37 #include "DNA_ID.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_windowmanager_types.h"
41 #include "DNA_workspace_types.h"
42 #include "DNA_object_types.h"
43
44 #include "BKE_context.h"
45 #include "BKE_library.h"
46 #include "BKE_main.h"
47 #include "BKE_paint.h"
48 #include "BKE_workspace.h"
49
50 #include "RNA_access.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54 #include "WM_message.h"
55
56
57 /* -------------------------------------------------------------------- */
58 /** \name Tool Reference API
59  * \{ */
60
61 struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
62 {
63         WorkSpace *workspace = CTX_wm_workspace(C);
64         Scene *scene = CTX_data_scene(C);
65         ScrArea *sa = CTX_wm_area(C);
66         const bToolKey tkey = {
67                 .space_type = sa->spacetype,
68                 .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype),
69         };
70         return WM_toolsystem_ref_find(workspace, &tkey);
71 }
72
73 struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C)
74 {
75         bToolRef *tref = WM_toolsystem_ref_from_context(C);
76         return tref ? tref->runtime : NULL;
77 }
78
79 bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey)
80 {
81         LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
82                 if ((tref->space_type == tkey->space_type) &&
83                     (tref->mode == tkey->mode))
84                 {
85                         return tref;
86                 }
87         }
88         return NULL;
89 }
90
91 bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey)
92 {
93         bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
94         return tref ? tref->runtime : NULL;
95 }
96
97 bool WM_toolsystem_ref_ensure(
98         struct WorkSpace *workspace, const bToolKey *tkey,
99         bToolRef **r_tref)
100 {
101         bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
102         if (tref) {
103                 *r_tref = tref;
104                 return false;
105         }
106         tref = MEM_callocN(sizeof(*tref), __func__);
107         BLI_addhead(&workspace->tools, tref);
108         tref->space_type = tkey->space_type;
109         tref->mode = tkey->mode;
110         *r_tref = tref;
111         return true;
112 }
113
114 /** \} */
115
116
117 static void toolsystem_unlink_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
118 {
119         bToolRef_Runtime *tref_rt = tref->runtime;
120
121         if (tref_rt->manipulator_group[0]) {
122                 wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(tref_rt->manipulator_group, false);
123                 if (wgt != NULL) {
124                         bool found = false;
125
126                         /* TODO(campbell) */
127                         Main *bmain = CTX_data_main(C);
128 #if 0
129                         wmWindowManager *wm = bmain->wm.first;
130                         /* Check another workspace isn't using this tool. */
131                         for (wmWindow *win = wm->windows.first; win; win = win->next) {
132                                 const WorkSpace *workspace_iter = WM_window_get_active_workspace(win);
133                                 if (workspace != workspace_iter) {
134                                         if (STREQ(workspace->tool.manipulator_group, workspace_iter->tool.manipulator_group)) {
135                                                 found = true;
136                                                 break;
137                                         }
138                                 }
139                         }
140 #else
141                         UNUSED_VARS(workspace);
142 #endif
143                         if (!found) {
144                                 wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
145                                 WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt);
146                         }
147                 }
148         }
149 }
150 void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
151 {
152         bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
153         if (tref && tref->runtime) {
154                 toolsystem_unlink_ref(C, workspace, tref);
155         }
156 }
157
158 static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref)
159 {
160         bToolRef_Runtime *tref_rt = tref->runtime;
161         if (tref_rt->manipulator_group[0]) {
162                 const char *idname = tref_rt->manipulator_group;
163                 wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
164                 if (wgt != NULL) {
165                         WM_manipulator_group_type_ensure_ptr(wgt);
166                 }
167                 else {
168                         CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname);
169                 }
170         }
171
172         if (tref_rt->data_block[0]) {
173                 Main *bmain = CTX_data_main(C);
174
175                 /* Currently only brush data-blocks supported. */
176                 struct Brush *brush = (struct Brush *)BKE_libblock_find_name(ID_BR, tref_rt->data_block);
177
178                 if (brush) {
179                         wmWindowManager *wm = bmain->wm.first;
180                         for (wmWindow *win = wm->windows.first; win; win = win->next) {
181                                 if (workspace == WM_window_get_active_workspace(win)) {
182                                         Scene *scene = win->scene;
183                                         ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
184                                         Paint *paint = BKE_paint_get_active(scene, view_layer);
185                                         if (paint) {
186                                                 if (brush) {
187                                                         BKE_paint_brush_set(paint, brush);
188                                                 }
189                                         }
190                                 }
191                         }
192                 }
193         }
194 }
195 void WM_toolsystem_link(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
196 {
197         bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
198         if (tref) {
199                 toolsystem_ref_link(C, workspace, tref);
200         }
201 }
202
203 static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
204 {
205         /* currently same operation. */
206         toolsystem_ref_link(C, workspace, tref);
207 }
208 void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
209 {
210         bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
211         if (tref) {
212                 toolsystem_refresh_ref(C, workspace, tref);
213         }
214 }
215
216 /* Operate on all active tools. */
217 void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace)
218 {
219         LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
220                 if (tref->runtime) {
221                         toolsystem_unlink_ref(C, workspace, tref);
222                 }
223         }
224 }
225 void WM_toolsystem_link_all(struct bContext *C, struct WorkSpace *workspace)
226 {
227         LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
228                 if (tref->runtime) {
229                         toolsystem_ref_link(C, workspace, tref);
230                 }
231         }
232 }
233 void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
234 {
235         LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
236                 if (tref->runtime) {
237                         toolsystem_refresh_ref(C, workspace, tref);
238                 }
239         }
240 }
241
242 void WM_toolsystem_ref_set_from_runtime(
243         struct bContext *C, struct WorkSpace *workspace, bToolRef *tref,
244         const bToolRef_Runtime *tref_rt, const char *idname)
245 {
246         if (tref->runtime) {
247                 toolsystem_unlink_ref(C, workspace, tref);
248         }
249
250         STRNCPY(tref->idname, idname);
251
252         /* BAD DESIGN WARNING: used for topbar. */
253         workspace->tools_space_type = tref->space_type;
254         workspace->tools_mode = tref->mode;
255
256         if (tref->runtime == NULL) {
257                 tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__);
258         }
259
260         if (tref_rt != tref->runtime) {
261                 *tref->runtime = *tref_rt;
262         }
263
264         toolsystem_ref_link(C, workspace, tref);
265
266         /* TODO(campbell): fix message. */
267         {
268                 struct wmMsgBus *mbus = CTX_wm_message_bus(C);
269                 WM_msg_publish_rna_prop(
270                         mbus, &workspace->id, workspace, WorkSpace, tools);
271         }
272 }
273
274 void WM_toolsystem_init(bContext *C)
275 {
276         Main *bmain = CTX_data_main(C);
277         wmWindowManager *wm = bmain->wm.first;
278
279         for (wmWindow *win = wm->windows.first; win; win = win->next) {
280                 WorkSpace *workspace = WM_window_get_active_workspace(win);
281                 WM_toolsystem_link_all(C, workspace);
282         }
283 }
284
285 int WM_toolsystem_mode_from_spacetype(
286         WorkSpace *workspace, Scene *scene, ScrArea *sa,
287         int spacetype)
288 {
289         int mode = -1;
290         switch (spacetype) {
291                 case SPACE_VIEW3D:
292                 {
293                         /* 'sa' may be NULL in this case. */
294                         ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
295                         Object *obact = OBACT(view_layer);
296                         mode = obact ? obact->mode : OB_MODE_OBJECT;
297                         break;
298                 }
299                 case SPACE_IMAGE:
300                 {
301                         SpaceImage *sima = sa->spacedata.first;
302                         mode = sima->mode;
303                         break;
304                 }
305         }
306         return mode;
307 }
308
309 bool WM_toolsystem_key_from_context(
310         WorkSpace *workspace, Scene *scene, ScrArea *sa,
311         bToolKey *tkey)
312 {
313         int space_type = SPACE_EMPTY;
314         int mode = -1;
315
316         if (sa != NULL) {
317                 space_type = sa->spacetype;
318                 mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, space_type);
319         }
320
321         if (mode != -1) {
322                 tkey->space_type = space_type;
323                 tkey->mode = mode;
324                 return true;
325         }
326         return false;
327 }
328
329 /**
330  * Run after changing modes.
331  */
332 static void toolsystem_update_with_toolref(
333         bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool)
334 {
335         bToolRef *tref;
336         if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) {
337                 STRNCPY(tref->idname, default_tool);
338         }
339
340         wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
341         PointerRNA op_props;
342         WM_operator_properties_create_ptr(&op_props, ot);
343         RNA_string_set(&op_props, "name", tref->idname);
344         RNA_enum_set(&op_props, "space_type", tkey->space_type);
345         WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
346         WM_operator_properties_free(&op_props);
347 }
348
349 void WM_toolsystem_update_from_context_view3d(bContext *C)
350 {
351         WorkSpace *workspace = CTX_wm_workspace(C);
352         Scene *scene = CTX_data_scene(C);
353         int space_type = SPACE_VIEW3D;
354         const bToolKey tkey = {
355                 .space_type = space_type,
356                 .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, NULL, space_type),
357         };
358         toolsystem_update_with_toolref(C, workspace, &tkey, "Cursor");
359 }
360
361 /**
362  * For paint modes to support non-brush tools.
363  */
364 bool WM_toolsystem_active_tool_is_brush(const bContext *C)
365 {
366         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
367         return tref_rt->data_block[0] != '\0';
368 }
369
370 /* Follow wmMsgNotifyFn spec */
371 void WM_toolsystem_do_msg_notify_tag_refresh(
372         bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
373 {
374         WorkSpace *workspace = CTX_wm_workspace(C);
375         Scene *scene = CTX_data_scene(C);
376         ScrArea *sa = msg_val->user_data;
377         int space_type = sa->spacetype;
378         const bToolKey tkey = {
379                 .space_type = space_type,
380                 .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, sa, sa->spacetype),
381         };
382         WM_toolsystem_refresh(C, workspace, &tkey);
383 }