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