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