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