a867a17f5291166ff8b2076bba1235622d16e5a8
[blender.git] / source / blender / windowmanager / intern / wm_toolsystem.c
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_utildefines.h"
30 #include "BLI_string.h"
31 #include "BLI_listbase.h"
32
33 #include "DNA_ID.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_space_types.h"
36 #include "DNA_windowmanager_types.h"
37 #include "DNA_workspace_types.h"
38 #include "DNA_object_types.h"
39
40 #include "BKE_brush.h"
41 #include "BKE_context.h"
42 #include "BKE_idprop.h"
43 #include "BKE_library.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_types.h"
53 #include "WM_message.h"
54 #include "WM_toolsystem.h" /* own include */
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
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 *sa = CTX_wm_area(C);
76   if (((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
77     return NULL;
78   }
79   const bToolKey tkey = {
80       .space_type = sa->spacetype,
81       .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
82   };
83   bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
84   /* We could return 'sa->runtime.tool' in this case. */
85   if (sa->runtime.is_tool_set) {
86     BLI_assert(tref == sa->runtime.tool);
87   }
88   return tref;
89 }
90
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
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
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
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
131 static void toolsystem_unlink_ref(bContext *C, WorkSpace *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       bool found = false;
139
140       /* TODO(campbell) */
141       Main *bmain = CTX_data_main(C);
142 #if 0
143       wmWindowManager *wm = bmain->wm.first;
144       /* Check another workspace isn't using this tool. */
145       for (wmWindow *win = wm->windows.first; win; win = win->next) {
146         const WorkSpace *workspace_iter = WM_window_get_active_workspace(win);
147         if (workspace != workspace_iter) {
148           if (STREQ(workspace->tool.gizmo_group, workspace_iter->tool.gizmo_group)) {
149             found = true;
150             break;
151           }
152         }
153       }
154 #else
155       UNUSED_VARS(workspace);
156 #endif
157       if (!found) {
158         wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
159         WM_gizmomaptype_group_unlink(C, bmain, gzmap_type, gzgt);
160       }
161     }
162   }
163 }
164 void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
165 {
166   bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
167   if (tref && tref->runtime) {
168     toolsystem_unlink_ref(C, workspace, tref);
169   }
170 }
171
172 static void toolsystem_ref_link__refresh_image_uv_sculpt(bContext *C, Scene *scene)
173 {
174   PointerRNA ptr;
175   RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &ptr);
176   PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_uv_sculpt");
177   RNA_property_update(C, &ptr, prop);
178 }
179
180 /**
181  * \see #toolsystem_ref_link
182  */
183 static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref)
184 {
185   bToolRef_Runtime *tref_rt = tref->runtime;
186   if (tref_rt->gizmo_group[0]) {
187     const char *idname = tref_rt->gizmo_group;
188     wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
189     if (gzgt != NULL) {
190       if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) == 0) {
191         WM_gizmo_group_type_ensure_ptr(gzgt);
192       }
193     }
194     else {
195       CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname);
196     }
197   }
198
199   if (tref_rt->data_block[0]) {
200     Main *bmain = CTX_data_main(C);
201
202     if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_SCULPT_GPENCIL)) {
203       const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items;
204       const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
205       if (i != -1) {
206         const int value = items[i].value;
207         wmWindowManager *wm = bmain->wm.first;
208         for (wmWindow *win = wm->windows.first; win; win = win->next) {
209           if (workspace == WM_window_get_active_workspace(win)) {
210             Scene *scene = WM_window_get_active_scene(win);
211             ToolSettings *ts = scene->toolsettings;
212             ts->gp_sculpt.brushtype = value;
213           }
214         }
215       }
216     }
217     else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_WEIGHT_GPENCIL)) {
218       const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items;
219       const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
220       if (i != -1) {
221         const int value = items[i].value;
222         wmWindowManager *wm = bmain->wm.first;
223         for (wmWindow *win = wm->windows.first; win; win = win->next) {
224           if (workspace == WM_window_get_active_workspace(win)) {
225             Scene *scene = WM_window_get_active_scene(win);
226             ToolSettings *ts = scene->toolsettings;
227             ts->gp_sculpt.weighttype = value;
228           }
229         }
230       }
231     }
232     else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) {
233       const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
234       const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
235       if (i != -1) {
236         const int value = items[i].value;
237         wmWindowManager *wm = bmain->wm.first;
238         for (wmWindow *win = wm->windows.first; win; win = win->next) {
239           if (workspace == WM_window_get_active_workspace(win)) {
240             Scene *scene = WM_window_get_active_scene(win);
241             ToolSettings *ts = scene->toolsettings;
242             ts->particle.brushtype = value;
243           }
244         }
245       }
246     }
247     else if ((tref->space_type == SPACE_IMAGE) && (tref->mode == SI_MODE_UV)) {
248       /* Note that switching uv-sculpt boolean is a hack at the moment.
249        * It would be best to make this either an operator or a higher level mode (like mesh-object sculpt mode). */
250       const EnumPropertyItem *items = rna_enum_uv_sculpt_tool_items;
251       const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
252       if (i != -1) {
253         const int value = items[i].value;
254         wmWindowManager *wm = bmain->wm.first;
255         for (wmWindow *win = wm->windows.first; win; win = win->next) {
256           if (workspace == WM_window_get_active_workspace(win)) {
257             Scene *scene = WM_window_get_active_scene(win);
258             ToolSettings *ts = scene->toolsettings;
259             ts->uv_sculpt_tool = value;
260
261             if (ts->use_uv_sculpt == false) {
262               ts->use_uv_sculpt = true;
263               toolsystem_ref_link__refresh_image_uv_sculpt(C, scene);
264             }
265           }
266         }
267       }
268     }
269     else {
270       const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
271       BLI_assert(paint_mode != PAINT_MODE_INVALID);
272       const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
273       BLI_assert(items != NULL);
274
275       const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
276       if (i != -1) {
277         const int slot_index = items[i].value;
278         wmWindowManager *wm = bmain->wm.first;
279         for (wmWindow *win = wm->windows.first; win; win = win->next) {
280           if (workspace == WM_window_get_active_workspace(win)) {
281             Scene *scene = WM_window_get_active_scene(win);
282             Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
283             struct Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
284             if (brush == NULL) {
285               /* Could make into a function. */
286               brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
287               if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
288                 /* pass */
289               }
290               else {
291                 brush = BKE_brush_add(bmain, items[i].name, paint->runtime.ob_mode);
292                 BKE_brush_tool_set(brush, paint, slot_index);
293               }
294               BKE_paint_brush_set(paint, brush);
295             }
296             BKE_paint_brush_set(paint, brush);
297           }
298         }
299       }
300     }
301   }
302   else {
303     /* XXX, this part is weak, disables uv_sculpt when non uv-tool set. */
304     if ((tref->space_type == SPACE_IMAGE) && (tref->mode == SI_MODE_UV)) {
305       Main *bmain = CTX_data_main(C);
306       wmWindowManager *wm = bmain->wm.first;
307       for (wmWindow *win = wm->windows.first; win; win = win->next) {
308         if (workspace == WM_window_get_active_workspace(win)) {
309           Scene *scene = WM_window_get_active_scene(win);
310           ToolSettings *ts = scene->toolsettings;
311           if (ts->use_uv_sculpt == true) {
312             ts->use_uv_sculpt = false;
313             toolsystem_ref_link__refresh_image_uv_sculpt(C, scene);
314           }
315         }
316       }
317     }
318   }
319 }
320
321 static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
322 {
323   if (tref->runtime == NULL) {
324     return;
325   }
326   /* currently same operation. */
327   toolsystem_ref_link(C, workspace, tref);
328 }
329 void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
330 {
331   bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
332   if (tref) {
333     toolsystem_refresh_ref(C, workspace, tref);
334   }
335 }
336
337 static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
338 {
339   toolsystem_reinit_with_toolref(C, workspace, tref);
340 }
341 void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
342 {
343   bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
344   if (tref) {
345     toolsystem_reinit_ref(C, workspace, tref);
346   }
347 }
348
349 /* Operate on all active tools. */
350 void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace)
351 {
352   LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
353     tref->tag = 0;
354   }
355
356   LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
357     if (tref->runtime) {
358       if (tref->tag == 0) {
359         toolsystem_unlink_ref(C, workspace, tref);
360         tref->tag = 1;
361       }
362     }
363   }
364 }
365
366 void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
367 {
368   BLI_assert(0);
369   LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
370     toolsystem_refresh_ref(C, workspace, tref);
371   }
372 }
373 void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
374 {
375   bScreen *screen = WM_window_get_active_screen(win);
376   ViewLayer *view_layer = WM_window_get_active_view_layer(win);
377   for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
378     if (((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
379       continue;
380     }
381
382     WorkSpace *workspace = WM_window_get_active_workspace(win);
383     const bToolKey tkey = {
384         .space_type = sa->spacetype,
385         .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
386     };
387     bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
388     if (tref) {
389       if (tref->tag == 0) {
390         toolsystem_reinit_ref(C, workspace, tref);
391         tref->tag = 1;
392       }
393     }
394   }
395 }
396
397 void WM_toolsystem_ref_set_from_runtime(struct bContext *C,
398                                         struct WorkSpace *workspace,
399                                         bToolRef *tref,
400                                         const bToolRef_Runtime *tref_rt,
401                                         const char *idname)
402 {
403   Main *bmain = CTX_data_main(C);
404
405   if (tref->runtime) {
406     toolsystem_unlink_ref(C, workspace, tref);
407   }
408
409   STRNCPY(tref->idname, idname);
410
411   /* BAD DESIGN WARNING: used for topbar. */
412   workspace->tools_space_type = tref->space_type;
413   workspace->tools_mode = tref->mode;
414
415   if (tref->runtime == NULL) {
416     tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__);
417   }
418
419   if (tref_rt != tref->runtime) {
420     *tref->runtime = *tref_rt;
421   }
422
423   toolsystem_ref_link(C, workspace, tref);
424
425   toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
426
427   {
428     struct wmMsgBus *mbus = CTX_wm_message_bus(C);
429     WM_msg_publish_rna_prop(mbus, &workspace->id, workspace, WorkSpace, tools);
430   }
431 }
432
433 /**
434  * Sync the internal active state of a tool back into the tool system,
435  * this is needed for active brushes where the real active state is not stored in the tool system.
436  *
437  * \see #toolsystem_ref_link
438  */
439 void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToolRef *tref)
440 {
441   bToolRef_Runtime *tref_rt = tref->runtime;
442   if ((tref_rt == NULL) || (tref_rt->data_block[0] == '\0')) {
443     return;
444   }
445   wmWindowManager *wm = bmain->wm.first;
446   for (wmWindow *win = wm->windows.first; win; win = win->next) {
447     if (workspace != WM_window_get_active_workspace(win)) {
448       continue;
449     }
450
451     Scene *scene = WM_window_get_active_scene(win);
452     ToolSettings *ts = scene->toolsettings;
453     const ViewLayer *view_layer = WM_window_get_active_view_layer(win);
454     const Object *ob = OBACT(view_layer);
455     if (ob == NULL) {
456       /* pass */
457     }
458     else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_SCULPT_GPENCIL)) {
459       if (ob->mode & OB_MODE_SCULPT_GPENCIL) {
460         const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items;
461         const int i = RNA_enum_from_value(items, ts->gp_sculpt.brushtype);
462         const EnumPropertyItem *item = &items[i];
463         if (!STREQ(tref_rt->data_block, item->identifier)) {
464           STRNCPY(tref_rt->data_block, item->identifier);
465           SNPRINTF(tref->idname, "builtin_brush.%s", item->name);
466         }
467       }
468     }
469     else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_WEIGHT_GPENCIL)) {
470       if (ob->mode & OB_MODE_WEIGHT_GPENCIL) {
471         const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items;
472         const int i = RNA_enum_from_value(items, ts->gp_sculpt.weighttype);
473         const EnumPropertyItem *item = &items[i];
474         if (!STREQ(tref_rt->data_block, item->identifier)) {
475           STRNCPY(tref_rt->data_block, item->identifier);
476           SNPRINTF(tref->idname, "builtin_brush.%s", item->name);
477         }
478       }
479     }
480     else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) {
481       if (ob->mode & OB_MODE_PARTICLE_EDIT) {
482         const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
483         const int i = RNA_enum_from_value(items, ts->particle.brushtype);
484         const EnumPropertyItem *item = &items[i];
485         if (!STREQ(tref_rt->data_block, item->identifier)) {
486           STRNCPY(tref_rt->data_block, item->identifier);
487           SNPRINTF(tref->idname, "builtin_brush.%s", item->name);
488         }
489       }
490     }
491     else if ((tref->space_type == SPACE_IMAGE) && (tref->mode == SI_MODE_UV)) {
492       if (ob->mode & OB_MODE_EDIT) {
493         const EnumPropertyItem *items = rna_enum_uv_sculpt_tool_items;
494         const int i = RNA_enum_from_value(items, ts->uv_sculpt_tool);
495         const EnumPropertyItem *item = &items[i];
496         if (!STREQ(tref_rt->data_block, item->identifier)) {
497           STRNCPY(tref_rt->data_block, item->identifier);
498           SNPRINTF(tref->idname, "builtin_brush.%s", item->name);
499         }
500       }
501     }
502     else {
503       const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
504       Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
505       const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
506       if (paint && paint->brush && items) {
507         const ID *brush = (ID *)paint->brush;
508         const char tool_type = BKE_brush_tool_get((struct Brush *)brush, paint);
509         const int i = RNA_enum_from_value(items, tool_type);
510         /* Possible when loading files from the future. */
511         if (i != -1) {
512           const char *name = items[i].name;
513           const char *identifier = items[i].identifier;
514           if (!STREQ(tref_rt->data_block, identifier)) {
515             STRNCPY(tref_rt->data_block, identifier);
516             SNPRINTF(tref->idname, "builtin_brush.%s", name);
517           }
518         }
519       }
520     }
521   }
522 }
523
524 void WM_toolsystem_init(bContext *C)
525 {
526   Main *bmain = CTX_data_main(C);
527
528   BLI_assert(CTX_wm_window(C) == NULL);
529
530   LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
531     LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
532       MEM_SAFE_FREE(tref->runtime);
533     }
534   }
535
536   /* Rely on screen initialization for gizmos. */
537 }
538
539 static bool toolsystem_key_ensure_check(const bToolKey *tkey)
540 {
541   switch (tkey->space_type) {
542     case SPACE_VIEW3D:
543       return true;
544     case SPACE_IMAGE:
545       if (ELEM(tkey->mode, SI_MODE_PAINT, SI_MODE_UV)) {
546         return true;
547       }
548       break;
549     case SPACE_NODE:
550       return true;
551   }
552   return false;
553 }
554
555 int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *sa, int spacetype)
556 {
557   int mode = -1;
558   switch (spacetype) {
559     case SPACE_VIEW3D: {
560       /* 'sa' may be NULL in this case. */
561       Object *obact = OBACT(view_layer);
562       if (obact != NULL) {
563         Object *obedit = OBEDIT_FROM_OBACT(obact);
564         mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode);
565       }
566       else {
567         mode = CTX_MODE_OBJECT;
568       }
569       break;
570     }
571     case SPACE_IMAGE: {
572       SpaceImage *sima = sa->spacedata.first;
573       mode = sima->mode;
574       break;
575     }
576     case SPACE_NODE: {
577       mode = 0;
578       break;
579     }
580   }
581   return mode;
582 }
583
584 bool WM_toolsystem_key_from_context(ViewLayer *view_layer, ScrArea *sa, bToolKey *tkey)
585 {
586   int space_type = SPACE_EMPTY;
587   int mode = -1;
588
589   if (sa != NULL) {
590     space_type = sa->spacetype;
591     mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, space_type);
592   }
593
594   if (mode != -1) {
595     tkey->space_type = space_type;
596     tkey->mode = mode;
597     return true;
598   }
599   return false;
600 }
601
602 /**
603  * Use to update the active tool (shown in the top bar) in the least disruptive way.
604  *
605  * This is a little involved since there may be multiple valid active tools depending on the mode and space type.
606  *
607  * Used when undoing since the active mode may have changed.
608  */
609 void WM_toolsystem_refresh_active(bContext *C)
610 {
611   Main *bmain = CTX_data_main(C);
612   for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
613     for (wmWindow *win = wm->windows.first; win; win = win->next) {
614       WorkSpace *workspace = WM_window_get_active_workspace(win);
615       bScreen *screen = WM_window_get_active_screen(win);
616       ViewLayer *view_layer = WM_window_get_active_view_layer(win);
617       int mode_other = 0;
618       enum { UNSET = -1, CHANGE = 0, MATCH = 1 } mode_match = UNSET;
619       /* Could skip loop for modes that don't depend on space type. */
620       for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
621         /* Don't change the space type of the active tool, only update it's mode. */
622         if (sa->spacetype == workspace->tools_space_type) {
623           const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
624           if (workspace->tools_mode == mode) {
625             mode_match = MATCH;
626             break;
627           }
628           else if (mode_match == -1) {
629             mode_match = CHANGE;
630             mode_other = mode;
631           }
632         }
633       }
634
635       if (mode_match == CHANGE) {
636         const bToolKey tkey = {
637             .space_type = workspace->tools_space_type,
638             .mode = mode_other,
639         };
640         toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
641       }
642     }
643   }
644 }
645
646 void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *sa)
647 {
648   sa->runtime.tool = NULL;
649   sa->runtime.is_tool_set = true;
650   const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
651   for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
652     if (tref->space_type == sa->spacetype) {
653       if (tref->mode == mode) {
654         sa->runtime.tool = tref;
655         break;
656       }
657     }
658   }
659 }
660
661 void WM_toolsystem_refresh_screen_all(Main *bmain)
662 {
663   /* Update all ScrArea's tools */
664   for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
665     for (wmWindow *win = wm->windows.first; win; win = win->next) {
666       WorkSpace *workspace = WM_window_get_active_workspace(win);
667       bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0};
668       for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
669         space_type_has_tools[tref->space_type] = true;
670       }
671       bScreen *screen = WM_window_get_active_screen(win);
672       ViewLayer *view_layer = WM_window_get_active_view_layer(win);
673       for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
674         sa->runtime.tool = NULL;
675         sa->runtime.is_tool_set = true;
676         if (space_type_has_tools[sa->spacetype]) {
677           WM_toolsystem_refresh_screen_area(workspace, view_layer, sa);
678         }
679       }
680     }
681   }
682 }
683
684 static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
685                                                        WorkSpace *workspace,
686                                                        bToolRef *tref)
687 {
688   /* Update all ScrArea's tools */
689   for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
690     for (wmWindow *win = wm->windows.first; win; win = win->next) {
691       if (workspace == WM_window_get_active_workspace(win)) {
692         bScreen *screen = WM_window_get_active_screen(win);
693         ViewLayer *view_layer = WM_window_get_active_view_layer(win);
694         for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
695           if (sa->spacetype == tref->space_type) {
696             int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
697             if (mode == tref->mode) {
698               sa->runtime.tool = tref;
699               sa->runtime.is_tool_set = true;
700             }
701           }
702         }
703       }
704     }
705   }
706 }
707
708 bToolRef *WM_toolsystem_ref_set_by_id(
709     bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *name, bool cycle)
710 {
711   wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_id", false);
712   /* On startup, Python operatores are not yet loaded. */
713   if (ot == NULL) {
714     return NULL;
715   }
716   PointerRNA op_props;
717   WM_operator_properties_create_ptr(&op_props, ot);
718   RNA_string_set(&op_props, "name", name);
719
720   /* Will get from context if not set. */
721   bToolKey tkey_from_context;
722   if (tkey == NULL) {
723     ViewLayer *view_layer = CTX_data_view_layer(C);
724     ScrArea *sa = CTX_wm_area(C);
725     WM_toolsystem_key_from_context(view_layer, sa, &tkey_from_context);
726     tkey = &tkey_from_context;
727   }
728
729   BLI_assert((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK);
730
731   RNA_enum_set(&op_props, "space_type", tkey->space_type);
732   RNA_boolean_set(&op_props, "cycle", cycle);
733
734   WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
735   WM_operator_properties_free(&op_props);
736
737   bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
738
739   if (tref) {
740     Main *bmain = CTX_data_main(C);
741     toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
742   }
743
744   return (tref && STREQ(tref->idname, name)) ? tref : NULL;
745 }
746
747 static void toolsystem_reinit_with_toolref(bContext *C, WorkSpace *workspace, bToolRef *tref)
748 {
749   bToolKey tkey = {
750       .space_type = tref->space_type,
751       .mode = tref->mode,
752   };
753   WM_toolsystem_ref_set_by_id(C, workspace, &tkey, tref->idname, false);
754 }
755
756 static const char *toolsystem_default_tool(const bToolKey *tkey)
757 {
758   switch (tkey->space_type) {
759     case SPACE_VIEW3D:
760       switch (tkey->mode) {
761         /* Use the names of the enums for each brush tool. */
762         case CTX_MODE_SCULPT:
763         case CTX_MODE_PAINT_VERTEX:
764         case CTX_MODE_PAINT_WEIGHT:
765         case CTX_MODE_WEIGHT_GPENCIL:
766         case CTX_MODE_PAINT_TEXTURE:
767         case CTX_MODE_PAINT_GPENCIL:
768           return "builtin_brush.Draw";
769         case CTX_MODE_SCULPT_GPENCIL:
770           return "builtin_brush.Push";
771           /* end temporary hack. */
772
773         case CTX_MODE_PARTICLE:
774           return "builtin_brush.Comb";
775         case CTX_MODE_EDIT_TEXT:
776           return "builtin.cursor";
777       }
778       break;
779     case SPACE_IMAGE:
780       switch (tkey->mode) {
781         case SI_MODE_PAINT:
782           return "builtin_brush.draw";
783       }
784       break;
785     case SPACE_NODE: {
786       /* 'Select Box' interferes with cut-links which is handy. */
787       return "builtin.select";
788     }
789   }
790
791   return "builtin.select_box";
792 }
793
794 /**
795  * Run after changing modes.
796  */
797 static bToolRef *toolsystem_reinit_ensure_toolref(bContext *C,
798                                                   WorkSpace *workspace,
799                                                   const bToolKey *tkey,
800                                                   const char *default_tool)
801 {
802   bToolRef *tref;
803   if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) {
804     if (default_tool == NULL) {
805       default_tool = toolsystem_default_tool(tkey);
806     }
807     STRNCPY(tref->idname, default_tool);
808   }
809   toolsystem_reinit_with_toolref(C, workspace, tref);
810   return tref;
811 }
812
813 void WM_toolsystem_update_from_context_view3d(bContext *C)
814 {
815   WorkSpace *workspace = CTX_wm_workspace(C);
816   ViewLayer *view_layer = CTX_data_view_layer(C);
817   int space_type = SPACE_VIEW3D;
818   const bToolKey tkey = {
819       .space_type = space_type,
820       .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type),
821   };
822   toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
823 }
824
825 void WM_toolsystem_update_from_context(bContext *C,
826                                        WorkSpace *workspace,
827                                        ViewLayer *view_layer,
828                                        ScrArea *sa)
829 {
830   const bToolKey tkey = {
831       .space_type = sa->spacetype,
832       .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
833   };
834   if (toolsystem_key_ensure_check(&tkey)) {
835     toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
836   }
837 }
838
839 /**
840  * For paint modes to support non-brush tools.
841  */
842 bool WM_toolsystem_active_tool_is_brush(const bContext *C)
843 {
844   bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
845   return tref_rt && (tref_rt->data_block[0] != '\0');
846 }
847
848 /* Follow wmMsgNotifyFn spec */
849 void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C,
850                                              wmMsgSubscribeKey *UNUSED(msg_key),
851                                              wmMsgSubscribeValue *msg_val)
852 {
853   WorkSpace *workspace = CTX_wm_workspace(C);
854   ViewLayer *view_layer = CTX_data_view_layer(C);
855   ScrArea *sa = msg_val->user_data;
856   int space_type = sa->spacetype;
857   const bToolKey tkey = {
858       .space_type = space_type,
859       .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
860   };
861   WM_toolsystem_refresh(C, workspace, &tkey);
862   WM_toolsystem_refresh_screen_area(workspace, view_layer, sa);
863 }
864
865 IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref)
866 {
867   if (tref->properties == NULL) {
868     IDPropertyTemplate val = {0};
869     tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
870   }
871   return tref->properties;
872 }
873
874 bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref,
875                                          const char *idname,
876                                          StructRNA *type,
877                                          PointerRNA *r_ptr)
878 {
879   IDProperty *group = tref->properties;
880   IDProperty *prop = group ? IDP_GetPropertyFromGroup(group, idname) : NULL;
881   RNA_pointer_create(NULL, type, prop, r_ptr);
882   return (prop != NULL);
883 }
884
885 void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref,
886                                             const char *idname,
887                                             StructRNA *type,
888                                             PointerRNA *r_ptr)
889 {
890   IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref);
891   IDProperty *prop = IDP_GetPropertyFromGroup(group, idname);
892   if (prop == NULL) {
893     IDPropertyTemplate val = {0};
894     prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties");
895     STRNCPY(prop->name, idname);
896     IDP_ReplaceInGroup_ex(group, prop, NULL);
897   }
898   else {
899     BLI_assert(prop->type == IDP_GROUP);
900   }
901
902   RNA_pointer_create(NULL, type, prop, r_ptr);
903 }
904
905 void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref,
906                                                   PointerRNA *dst_ptr,
907                                                   PointerRNA *src_ptr,
908                                                   wmOperatorType *ot)
909 {
910   *dst_ptr = *src_ptr;
911   if (dst_ptr->data) {
912     dst_ptr->data = IDP_CopyProperty(dst_ptr->data);
913   }
914   else {
915     IDPropertyTemplate val = {0};
916     dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
917   }
918   if (tref->properties != NULL) {
919     IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname);
920     if (prop) {
921       /* Important key-map items properties don't get overwritten by the tools.
922        * - When a key-map item doesn't set a property, the tool-systems is used.
923        * - When it does, it overrides the tool-system.
924        *
925        * This way the default action can be to follow the top-bar tool-settings &
926        * modifier keys can be used to perform different actions that aren't clobbered here.
927        */
928       IDP_MergeGroup(dst_ptr->data, prop, false);
929     }
930   }
931 }