1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup wm
19 *
20 * Experimental tool-system>
21 */
22
23 #include <string.h>
24
25 #include "CLG_log.h"
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_listbase.h"
30 #include "BLI_string.h"
31 #include "BLI_utildefines.h"
32
33 #include "DNA_ID.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_space_types.h"
37 #include "DNA_windowmanager_types.h"
38 #include "DNA_workspace_types.h"
39
40 #include "BKE_brush.h"
41 #include "BKE_context.h"
42 #include "BKE_idprop.h"
43 #include "BKE_lib_id.h"
44 #include "BKE_main.h"
45 #include "BKE_paint.h"
46 #include "BKE_workspace.h"
47
48 #include "RNA_access.h"
49 #include "RNA_enum_types.h"
50
51 #include "WM_api.h"
52 #include "WM_message.h"
53 #include "WM_toolsystem.h" /* own include */
54 #include "WM_types.h"
55
56 static void toolsystem_reinit_with_toolref(bContext *C,
57 WorkSpace *UNUSED(workspace),
58 bToolRef *tref);
59 static bToolRef *toolsystem_reinit_ensure_toolref(bContext *C,
60 WorkSpace *workspace,
61 const bToolKey *tkey,
62 const char *default_tool);
63 static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
64 WorkSpace *workspace,
65 bToolRef *tref);
66
67 /* -------------------------------------------------------------------- */
68 /** \name Tool Reference API
69 * \{ */
70
WM_toolsystem_ref_from_context(struct bContext * C)71 struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
72 {
73 WorkSpace *workspace = CTX_wm_workspace(C);
74 ViewLayer *view_layer = CTX_data_view_layer(C);
75 ScrArea *area = CTX_wm_area(C);
76 if ((area == NULL) || ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
77 return NULL;
78 }
79 const bToolKey tkey = {
80 .space_type = area->spacetype,
81 .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
82 };
83 bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
84 /* We could return 'area->runtime.tool' in this case. */
85 if (area->runtime.is_tool_set) {
86 BLI_assert(tref == area->runtime.tool);
87 }
88 return tref;
89 }
90
WM_toolsystem_runtime_from_context(struct bContext * C)91 struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C)
92 {
93 bToolRef *tref = WM_toolsystem_ref_from_context(C);
94 return tref ? tref->runtime : NULL;
95 }
96
WM_toolsystem_ref_find(WorkSpace * workspace,const bToolKey * tkey)97 bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey)
98 {
99 BLI_assert((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK);
100 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
101 if ((tref->space_type == tkey->space_type) && (tref->mode == tkey->mode)) {
102 return tref;
103 }
104 }
105 return NULL;
106 }
107
WM_toolsystem_runtime_find(WorkSpace * workspace,const bToolKey * tkey)108 bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey)
109 {
110 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
111 return tref ? tref->runtime : NULL;
112 }
113
WM_toolsystem_ref_ensure(struct WorkSpace * workspace,const bToolKey * tkey,bToolRef ** r_tref)114 bool WM_toolsystem_ref_ensure(struct WorkSpace *workspace, const bToolKey *tkey, bToolRef **r_tref)
115 {
116 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
117 if (tref) {
118 *r_tref = tref;
119 return false;
120 }
121 tref = MEM_callocN(sizeof(*tref), __func__);
122 BLI_addhead(&workspace->tools, tref);
123 tref->space_type = tkey->space_type;
124 tref->mode = tkey->mode;
125 *r_tref = tref;
126 return true;
127 }
128
129 /** \} */
130
toolsystem_unlink_ref(bContext * C,WorkSpace * UNUSED (workspace),bToolRef * tref)131 static void toolsystem_unlink_ref(bContext *C, WorkSpace *UNUSED(workspace), bToolRef *tref)
132 {
133 bToolRef_Runtime *tref_rt = tref->runtime;
134
135 if (tref_rt->gizmo_group[0]) {
136 wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false);
137 if (gzgt != NULL) {
138 Main *bmain = CTX_data_main(C);
139 WM_gizmo_group_remove_by_tool(C, bmain, gzgt, tref);
140 }
141 }
142 }
WM_toolsystem_unlink(bContext * C,WorkSpace * workspace,const bToolKey * tkey)143 void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
144 {
145 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
146 if (tref && tref->runtime) {
147 toolsystem_unlink_ref(C, workspace, tref);
148 }
149 }
150
toolsystem_ref_link(bContext * C,WorkSpace * workspace,bToolRef * tref)151 static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref)
152 {
153 bToolRef_Runtime *tref_rt = tref->runtime;
154 if (tref_rt->gizmo_group[0]) {
155 const char *idname = tref_rt->gizmo_group;
156 wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
157 if (gzgt != NULL) {
158 if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) == 0) {
159 if (!WM_gizmo_group_type_ensure_ptr(gzgt)) {
160 /* Even if the group-type was has been linked, it's possible the space types
161 * were not previously using it. (happens with multiple windows.) */
162 wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
163 WM_gizmoconfig_update_tag_group_type_init(gzmap_type, gzgt);
164 }
165 }
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 if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) {
176 const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
177 const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
178 if (i != -1) {
179 const int value = items[i].value;
180 wmWindowManager *wm = bmain->wm.first;
181 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
182 if (workspace == WM_window_get_active_workspace(win)) {
183 Scene *scene = WM_window_get_active_scene(win);
184 ToolSettings *ts = scene->toolsettings;
185 ts->particle.brushtype = value;
186 }
187 }
188 }
189 }
190 else {
191 const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
192 BLI_assert(paint_mode != PAINT_MODE_INVALID);
193 const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
194 BLI_assert(items != NULL);
195
196 const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
197 if (i != -1) {
198 const int slot_index = items[i].value;
199 wmWindowManager *wm = bmain->wm.first;
200 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
201 if (workspace == WM_window_get_active_workspace(win)) {
202 Scene *scene = WM_window_get_active_scene(win);
203 BKE_paint_ensure_from_paintmode(scene, paint_mode);
204 Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
205 struct Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
206 if (brush == NULL) {
207 /* Could make into a function. */
208 brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
209 if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
210 /* pass */
211 }
212 else {
213 brush = BKE_brush_add(bmain, items[i].name, paint->runtime.ob_mode);
214 BKE_brush_tool_set(brush, paint, slot_index);
215 }
216 BKE_paint_brush_set(paint, brush);
217 }
218 BKE_paint_brush_set(paint, brush);
219 }
220 }
221 }
222 }
223 }
224 }
225
toolsystem_refresh_ref(bContext * C,WorkSpace * workspace,bToolRef * tref)226 static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
227 {
228 if (tref->runtime == NULL) {
229 return;
230 }
231 /* currently same operation. */
232 toolsystem_ref_link(C, workspace, tref);
233 }
WM_toolsystem_refresh(bContext * C,WorkSpace * workspace,const bToolKey * tkey)234 void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
235 {
236 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
237 if (tref) {
238 toolsystem_refresh_ref(C, workspace, tref);
239 }
240 }
241
toolsystem_reinit_ref(bContext * C,WorkSpace * workspace,bToolRef * tref)242 static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
243 {
244 toolsystem_reinit_with_toolref(C, workspace, tref);
245 }
WM_toolsystem_reinit(bContext * C,WorkSpace * workspace,const bToolKey * tkey)246 void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
247 {
248 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
249 if (tref) {
250 toolsystem_reinit_ref(C, workspace, tref);
251 }
252 }
253
254 /* Operate on all active tools. */
WM_toolsystem_unlink_all(struct bContext * C,struct WorkSpace * workspace)255 void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace)
256 {
257 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
258 tref->tag = 0;
259 }
260
261 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
262 if (tref->runtime) {
263 if (tref->tag == 0) {
264 toolsystem_unlink_ref(C, workspace, tref);
265 tref->tag = 1;
266 }
267 }
268 }
269 }
270
WM_toolsystem_refresh_all(struct bContext * C,struct WorkSpace * workspace)271 void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
272 {
273 BLI_assert(0);
274 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
275 toolsystem_refresh_ref(C, workspace, tref);
276 }
277 }
WM_toolsystem_reinit_all(struct bContext * C,wmWindow * win)278 void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
279 {
280 bScreen *screen = WM_window_get_active_screen(win);
281 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
282 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
283 if (((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
284 continue;
285 }
286
287 WorkSpace *workspace = WM_window_get_active_workspace(win);
288 const bToolKey tkey = {
289 .space_type = area->spacetype,
290 .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
291 };
292 bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
293 if (tref) {
294 if (tref->tag == 0) {
295 toolsystem_reinit_ref(C, workspace, tref);
296 tref->tag = 1;
297 }
298 }
299 }
300 }
301
WM_toolsystem_ref_set_from_runtime(struct bContext * C,struct WorkSpace * workspace,bToolRef * tref,const bToolRef_Runtime * tref_rt,const char * idname)302 void WM_toolsystem_ref_set_from_runtime(struct bContext *C,
303 struct WorkSpace *workspace,
304 bToolRef *tref,
305 const bToolRef_Runtime *tref_rt,
306 const char *idname)
307 {
308 Main *bmain = CTX_data_main(C);
309
310 if (tref->runtime) {
311 toolsystem_unlink_ref(C, workspace, tref);
312 }
313
314 STRNCPY(tref->idname, idname);
315
316 if (tref->runtime == NULL) {
317 tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__);
318 }
319
320 if (tref_rt != tref->runtime) {
321 *tref->runtime = *tref_rt;
322 }
323
324 /* Ideally Python could check this gizmo group flag and not
325 * pass in the argument to begin with. */
326 bool use_fallback_keymap = false;
327
328 if (tref->idname_fallback[0] || tref->runtime->keymap_fallback[0]) {
329 if (tref_rt->gizmo_group[0]) {
330 wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false);
331 if (gzgt) {
332 if (gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) {
333 use_fallback_keymap = true;
334 }
335 }
336 }
337 }
338 if (use_fallback_keymap == false) {
339 tref->idname_fallback[0] = '\0';
340 tref->runtime->keymap_fallback[0] = '\0';
341 }
342
343 toolsystem_ref_link(C, workspace, tref);
344
345 toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
346
347 /* Set the cursor if possible, if not - it's fine as entering the region will refresh it. */
348 {
349 wmWindow *win = CTX_wm_window(C);
350 if (win != NULL) {
351 win->addmousemove = true;
352 win->tag_cursor_refresh = true;
353 }
354 }
355
356 {
357 struct wmMsgBus *mbus = CTX_wm_message_bus(C);
358 WM_msg_publish_rna_prop(mbus, &workspace->id, workspace, WorkSpace, tools);
359 }
360 }
361
362 /**
363 * Sync the internal active state of a tool back into the tool system,
364 * this is needed for active brushes where the real active state is not stored in the tool system.
365 *
366 * \see #toolsystem_ref_link
367 */
WM_toolsystem_ref_sync_from_context(Main * bmain,WorkSpace * workspace,bToolRef * tref)368 void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToolRef *tref)
369 {
370 bToolRef_Runtime *tref_rt = tref->runtime;
371 if ((tref_rt == NULL) || (tref_rt->data_block[0] == '\0')) {
372 return;
373 }
374 wmWindowManager *wm = bmain->wm.first;
375 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
376 if (workspace != WM_window_get_active_workspace(win)) {
377 continue;
378 }
379
380 Scene *scene = WM_window_get_active_scene(win);
381 ToolSettings *ts = scene->toolsettings;
382 const ViewLayer *view_layer = WM_window_get_active_view_layer(win);
383 const Object *ob = OBACT(view_layer);
384 if (ob == NULL) {
385 /* pass */
386 }
387 if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) {
388 if (ob->mode & OB_MODE_PARTICLE_EDIT) {
389 const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
390 const int i = RNA_enum_from_value(items, ts->particle.brushtype);
391 const EnumPropertyItem *item = &items[i];
392 if (!STREQ(tref_rt->data_block, item->identifier)) {
393 STRNCPY(tref_rt->data_block, item->identifier);
394 SNPRINTF(tref->idname, "builtin_brush.%s", item->name);
395 }
396 }
397 }
398 else {
399 const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
400 Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
401 const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
402 if (paint && paint->brush && items) {
403 const ID *brush = (ID *)paint->brush;
404 const char tool_type = BKE_brush_tool_get((struct Brush *)brush, paint);
405 const int i = RNA_enum_from_value(items, tool_type);
406 /* Possible when loading files from the future. */
407 if (i != -1) {
408 const char *name = items[i].name;
409 const char *identifier = items[i].identifier;
410 if (!STREQ(tref_rt->data_block, identifier)) {
411 STRNCPY(tref_rt->data_block, identifier);
412 SNPRINTF(tref->idname, "builtin_brush.%s", name);
413 }
414 }
415 }
416 }
417 }
418 }
419
WM_toolsystem_init(bContext * C)420 void WM_toolsystem_init(bContext *C)
421 {
422 Main *bmain = CTX_data_main(C);
423
424 BLI_assert(CTX_wm_window(C) == NULL);
425
426 LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
427 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
428 MEM_SAFE_FREE(tref->runtime);
429 }
430 }
431
432 /* Rely on screen initialization for gizmos. */
433 }
434
toolsystem_key_ensure_check(const bToolKey * tkey)435 static bool toolsystem_key_ensure_check(const bToolKey *tkey)
436 {
437 switch (tkey->space_type) {
438 case SPACE_VIEW3D:
439 return true;
440 case SPACE_IMAGE:
441 if (ELEM(tkey->mode, SI_MODE_PAINT, SI_MODE_UV)) {
442 return true;
443 }
444 break;
445 case SPACE_NODE:
446 return true;
447 case SPACE_SEQ:
448 return true;
449 }
450 return false;
451 }
452
WM_toolsystem_mode_from_spacetype(ViewLayer * view_layer,ScrArea * area,int space_type)453 int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *area, int space_type)
454 {
455 int mode = -1;
456 switch (space_type) {
457 case SPACE_VIEW3D: {
458 /* 'area' may be NULL in this case. */
459 Object *obact = OBACT(view_layer);
460 if (obact != NULL) {
461 Object *obedit = OBEDIT_FROM_OBACT(obact);
462 mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode);
463 }
464 else {
465 mode = CTX_MODE_OBJECT;
466 }
467 break;
468 }
469 case SPACE_IMAGE: {
470 SpaceImage *sima = area->spacedata.first;
471 mode = sima->mode;
472 break;
473 }
474 case SPACE_NODE: {
475 mode = 0;
476 break;
477 }
478 case SPACE_SEQ: {
479 SpaceSeq *sseq = area->spacedata.first;
480 mode = sseq->view;
481 break;
482 }
483 }
484 return mode;
485 }
486
WM_toolsystem_key_from_context(ViewLayer * view_layer,ScrArea * area,bToolKey * tkey)487 bool WM_toolsystem_key_from_context(ViewLayer *view_layer, ScrArea *area, bToolKey *tkey)
488 {
489 int space_type = SPACE_EMPTY;
490 int mode = -1;
491
492 if (area != NULL) {
493 space_type = area->spacetype;
494 mode = WM_toolsystem_mode_from_spacetype(view_layer, area, space_type);
495 }
496
497 if (mode != -1) {
498 tkey->space_type = space_type;
499 tkey->mode = mode;
500 return true;
501 }
502 return false;
503 }
504
505 /**
506 * Use to update the active tool (shown in the top bar) in the least disruptive way.
507 *
508 * This is a little involved since there may be multiple valid active tools
509 * depending on the mode and space type.
510 *
511 * Used when undoing since the active mode may have changed.
512 */
WM_toolsystem_refresh_active(bContext * C)513 void WM_toolsystem_refresh_active(bContext *C)
514 {
515 Main *bmain = CTX_data_main(C);
516 for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
517 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
518 WorkSpace *workspace = WM_window_get_active_workspace(win);
519 bScreen *screen = WM_window_get_active_screen(win);
520 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
521 /* Could skip loop for modes that don't depend on space type. */
522 int space_type_mask_handled = 0;
523 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
524 /* Don't change the space type of the active tool, only update its mode. */
525 const int space_type_mask = (1 << area->spacetype);
526 if ((space_type_mask & WM_TOOLSYSTEM_SPACE_MASK) &&
527 ((space_type_mask_handled & space_type_mask) == 0)) {
528 space_type_mask_handled |= space_type_mask;
529 const bToolKey tkey = {
530 .space_type = area->spacetype,
531 .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
532 };
533 bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
534 if (tref != area->runtime.tool) {
535 toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
536 }
537 }
538 }
539 }
540 }
541
542 BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT);
543
544 LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
545 if (workspace->id.tag & LIB_TAG_DOIT) {
546 workspace->id.tag &= ~LIB_TAG_DOIT;
547 /* Refresh to ensure data is initialized.
548 * This is needed because undo can load a state which no longer has the underlying DNA data
549 * needed for the tool (un-initialized paint-slots for eg), see: T64339. */
550 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
551 toolsystem_refresh_ref(C, workspace, tref);
552 }
553 }
554 }
555 }
556
WM_toolsystem_refresh_screen_area(WorkSpace * workspace,ViewLayer * view_layer,ScrArea * area)557 void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *area)
558 {
559 area->runtime.tool = NULL;
560 area->runtime.is_tool_set = true;
561 const int mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype);
562 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
563 if (tref->space_type == area->spacetype) {
564 if (tref->mode == mode) {
565 area->runtime.tool = tref;
566 break;
567 }
568 }
569 }
570 }
571
WM_toolsystem_refresh_screen_all(Main * bmain)572 void WM_toolsystem_refresh_screen_all(Main *bmain)
573 {
574 /* Update all ScrArea's tools */
575 for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
576 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
577 WorkSpace *workspace = WM_window_get_active_workspace(win);
578 bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0};
579 LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
580 space_type_has_tools[tref->space_type] = true;
581 }
582 bScreen *screen = WM_window_get_active_screen(win);
583 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
584 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
585 area->runtime.tool = NULL;
586 area->runtime.is_tool_set = true;
587 if (space_type_has_tools[area->spacetype]) {
588 WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
589 }
590 }
591 }
592 }
593 }
594
toolsystem_refresh_screen_from_active_tool(Main * bmain,WorkSpace * workspace,bToolRef * tref)595 static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
596 WorkSpace *workspace,
597 bToolRef *tref)
598 {
599 /* Update all ScrArea's tools */
600 for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
601 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
602 if (workspace == WM_window_get_active_workspace(win)) {
603 bScreen *screen = WM_window_get_active_screen(win);
604 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
605 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
606 if (area->spacetype == tref->space_type) {
607 int mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype);
608 if (mode == tref->mode) {
609 area->runtime.tool = tref;
610 area->runtime.is_tool_set = true;
611 }
612 }
613 }
614 }
615 }
616 }
617 }
618
WM_toolsystem_ref_set_by_id_ex(bContext * C,WorkSpace * workspace,const bToolKey * tkey,const char * name,bool cycle)619 bToolRef *WM_toolsystem_ref_set_by_id_ex(
620 bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *name, bool cycle)
621 {
622 wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_id", false);
623 /* On startup, Python operatores are not yet loaded. */
624 if (ot == NULL) {
625 return NULL;
626 }
627 PointerRNA op_props;
628 WM_operator_properties_create_ptr(&op_props, ot);
629 RNA_string_set(&op_props, "name", name);
630
631 BLI_assert((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK);
632
633 RNA_enum_set(&op_props, "space_type", tkey->space_type);
634 RNA_boolean_set(&op_props, "cycle", cycle);
635
636 WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
637 WM_operator_properties_free(&op_props);
638
639 bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
640
641 if (tref) {
642 Main *bmain = CTX_data_main(C);
643 toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
644 }
645
646 return (tref && STREQ(tref->idname, name)) ? tref : NULL;
647 }
648
WM_toolsystem_ref_set_by_id(bContext * C,const char * name)649 bToolRef *WM_toolsystem_ref_set_by_id(bContext *C, const char *name)
650 {
651 ViewLayer *view_layer = CTX_data_view_layer(C);
652 ScrArea *area = CTX_wm_area(C);
653 bToolKey tkey;
654 if (WM_toolsystem_key_from_context(view_layer, area, &tkey)) {
655 WorkSpace *workspace = CTX_wm_workspace(C);
656 return WM_toolsystem_ref_set_by_id_ex(C, workspace, &tkey, name, false);
657 }
658 return NULL;
659 }
660
toolsystem_reinit_with_toolref(bContext * C,WorkSpace * workspace,bToolRef * tref)661 static void toolsystem_reinit_with_toolref(bContext *C, WorkSpace *workspace, bToolRef *tref)
662 {
663 bToolKey tkey = {
664 .space_type = tref->space_type,
665 .mode = tref->mode,
666 };
667 WM_toolsystem_ref_set_by_id_ex(C, workspace, &tkey, tref->idname, false);
668 }
669
toolsystem_default_tool(const bToolKey * tkey)670 static const char *toolsystem_default_tool(const bToolKey *tkey)
671 {
672 switch (tkey->space_type) {
673 case SPACE_VIEW3D:
674 switch (tkey->mode) {
675 /* Use the names of the enums for each brush tool. */
676 case CTX_MODE_SCULPT:
677 case CTX_MODE_PAINT_VERTEX:
678 case CTX_MODE_PAINT_WEIGHT:
679 case CTX_MODE_PAINT_TEXTURE:
680 case CTX_MODE_PAINT_GPENCIL:
681 return "builtin_brush.Draw";
682 case CTX_MODE_SCULPT_GPENCIL:
683 return "builtin_brush.Push";
684 case CTX_MODE_WEIGHT_GPENCIL:
685 return "builtin_brush.Weight";
686 case CTX_MODE_VERTEX_GPENCIL:
687 return "builtin_brush.Draw";
688 /* end temporary hack. */
689
690 case CTX_MODE_PARTICLE:
691 return "builtin_brush.Comb";
692 case CTX_MODE_EDIT_TEXT:
693 return "builtin.cursor";
694 }
695 break;
696 case SPACE_IMAGE:
697 switch (tkey->mode) {
698 case SI_MODE_PAINT:
699 return "builtin_brush.Draw";
700 }
701 break;
702 case SPACE_NODE: {
703 return "builtin.select_box";
704 }
705 case SPACE_SEQ: {
706 switch (tkey->mode) {
707 case SEQ_VIEW_SEQUENCE:
708 return "builtin.select";
709 case SEQ_VIEW_PREVIEW:
710 return "builtin.sample";
711 case SEQ_VIEW_SEQUENCE_PREVIEW:
712 return "builtin.select";
713 }
714 return "builtin.select_box";
715 }
716 }
717
718 return "builtin.select_box";
719 }
720
721 /**
722 * Run after changing modes.
723 */
toolsystem_reinit_ensure_toolref(bContext * C,WorkSpace * workspace,const bToolKey * tkey,const char * default_tool)724 static bToolRef *toolsystem_reinit_ensure_toolref(bContext *C,
725 WorkSpace *workspace,
726 const bToolKey *tkey,
727 const char *default_tool)
728 {
729 bToolRef *tref;
730 if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) {
731 if (default_tool == NULL) {
732 default_tool = toolsystem_default_tool(tkey);
733 }
734 STRNCPY(tref->idname, default_tool);
735 }
736 toolsystem_reinit_with_toolref(C, workspace, tref);
737 return tref;
738 }
739
wm_toolsystem_update_from_context_view3d_impl(bContext * C,WorkSpace * workspace)740 static void wm_toolsystem_update_from_context_view3d_impl(bContext *C, WorkSpace *workspace)
741 {
742 ViewLayer *view_layer = CTX_data_view_layer(C);
743 int space_type = SPACE_VIEW3D;
744 const bToolKey tkey = {
745 .space_type = space_type,
746 .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type),
747 };
748 toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
749 }
750
WM_toolsystem_update_from_context_view3d(bContext * C)751 void WM_toolsystem_update_from_context_view3d(bContext *C)
752 {
753 WorkSpace *workspace = CTX_wm_workspace(C);
754 wm_toolsystem_update_from_context_view3d_impl(C, workspace);
755
756 /* Multi window support. */
757 Main *bmain = CTX_data_main(C);
758 wmWindowManager *wm = bmain->wm.first;
759 if (!BLI_listbase_is_single(&wm->windows)) {
760 wmWindow *win_prev = CTX_wm_window(C);
761 ScrArea *area_prev = CTX_wm_area(C);
762 ARegion *region_prev = CTX_wm_region(C);
763
764 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
765 if (win != win_prev) {
766 WorkSpace *workspace_iter = WM_window_get_active_workspace(win);
767 if (workspace_iter != workspace) {
768
769 CTX_wm_window_set(C, win);
770
771 wm_toolsystem_update_from_context_view3d_impl(C, workspace_iter);
772
773 CTX_wm_window_set(C, win_prev);
774 CTX_wm_area_set(C, area_prev);
775 CTX_wm_region_set(C, region_prev);
776 }
777 }
778 }
779 }
780 }
781
WM_toolsystem_update_from_context(bContext * C,WorkSpace * workspace,ViewLayer * view_layer,ScrArea * area)782 void WM_toolsystem_update_from_context(bContext *C,
783 WorkSpace *workspace,
784 ViewLayer *view_layer,
785 ScrArea *area)
786 {
787 const bToolKey tkey = {
788 .space_type = area->spacetype,
789 .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
790 };
791 if (toolsystem_key_ensure_check(&tkey)) {
792 toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
793 }
794 }
795
796 /**
797 * For paint modes to support non-brush tools.
798 */
WM_toolsystem_active_tool_is_brush(const bContext * C)799 bool WM_toolsystem_active_tool_is_brush(const bContext *C)
800 {
801 bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
802 return tref_rt && (tref_rt->data_block[0] != '\0');
803 }
804
805 /* Follow wmMsgNotifyFn spec */
WM_toolsystem_do_msg_notify_tag_refresh(bContext * C,wmMsgSubscribeKey * UNUSED (msg_key),wmMsgSubscribeValue * msg_val)806 void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C,
807 wmMsgSubscribeKey *UNUSED(msg_key),
808 wmMsgSubscribeValue *msg_val)
809 {
810 ScrArea *area = msg_val->user_data;
811 Main *bmain = CTX_data_main(C);
812 wmWindow *win = ((wmWindowManager *)bmain->wm.first)->windows.first;
813 if (win->next != NULL) {
814 do {
815 bScreen *screen = WM_window_get_active_screen(win);
816 if (BLI_findindex(&screen->areabase, area) != -1) {
817 break;
818 }
819 } while ((win = win->next));
820 }
821
822 WorkSpace *workspace = WM_window_get_active_workspace(win);
823 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
824
825 const bToolKey tkey = {
826 .space_type = area->spacetype,
827 .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
828 };
829 WM_toolsystem_refresh(C, workspace, &tkey);
830 WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
831 }
832
WM_toolsystem_ref_properties_ensure_idprops(bToolRef * tref)833 IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref)
834 {
835 if (tref->properties == NULL) {
836 IDPropertyTemplate val = {0};
837 tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
838 }
839 return tref->properties;
840 }
841
WM_toolsystem_ref_properties_get_ex(bToolRef * tref,const char * idname,StructRNA * type,PointerRNA * r_ptr)842 bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref,
843 const char *idname,
844 StructRNA *type,
845 PointerRNA *r_ptr)
846 {
847 IDProperty *group = tref->properties;
848 IDProperty *prop = group ? IDP_GetPropertyFromGroup(group, idname) : NULL;
849 RNA_pointer_create(NULL, type, prop, r_ptr);
850 return (prop != NULL);
851 }
852
WM_toolsystem_ref_properties_ensure_ex(bToolRef * tref,const char * idname,StructRNA * type,PointerRNA * r_ptr)853 void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref,
854 const char *idname,
855 StructRNA *type,
856 PointerRNA *r_ptr)
857 {
858 IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref);
859 IDProperty *prop = IDP_GetPropertyFromGroup(group, idname);
860 if (prop == NULL) {
861 IDPropertyTemplate val = {0};
862 prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties");
863 STRNCPY(prop->name, idname);
864 IDP_ReplaceInGroup_ex(group, prop, NULL);
865 }
866 else {
867 BLI_assert(prop->type == IDP_GROUP);
868 }
869
870 RNA_pointer_create(NULL, type, prop, r_ptr);
871 }
872
WM_toolsystem_ref_properties_init_for_keymap(bToolRef * tref,PointerRNA * dst_ptr,PointerRNA * src_ptr,wmOperatorType * ot)873 void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref,
874 PointerRNA *dst_ptr,
875 PointerRNA *src_ptr,
876 wmOperatorType *ot)
877 {
878 *dst_ptr = *src_ptr;
879 if (dst_ptr->data) {
880 dst_ptr->data = IDP_CopyProperty(dst_ptr->data);
881 }
882 else {
883 IDPropertyTemplate val = {0};
884 dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
885 }
886 if (tref->properties != NULL) {
887 IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname);
888 if (prop) {
889 /* Important key-map items properties don't get overwritten by the tools.
890 * - When a key-map item doesn't set a property, the tool-systems is used.
891 * - When it does, it overrides the tool-system.
892 *
893 * This way the default action can be to follow the top-bar tool-settings &
894 * modifier keys can be used to perform different actions that aren't clobbered here.
895 */
896 IDP_MergeGroup(dst_ptr->data, prop, false);
897 }
898 }
899 }
900