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