33c58f8563da8051007f577605ed913b64840ac7
[blender.git] / source / blender / windowmanager / gizmo / intern / wm_gizmo_map.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  * The Original Code is Copyright (C) 2014 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup wm
22  */
23
24 #include <string.h>
25
26 #include "BLI_listbase.h"
27 #include "BLI_math.h"
28 #include "BLI_rect.h"
29 #include "BLI_ghash.h"
30
31 #include "BKE_context.h"
32 #include "BKE_global.h"
33 #include "BKE_main.h"
34
35 #include "ED_screen.h"
36 #include "ED_select_utils.h"
37 #include "ED_view3d.h"
38
39 #include "GPU_glew.h"
40 #include "GPU_matrix.h"
41 #include "GPU_select.h"
42 #include "GPU_state.h"
43
44 #include "MEM_guardedalloc.h"
45
46 #include "WM_api.h"
47 #include "WM_types.h"
48 #include "wm_event_system.h"
49
50 /* for tool-tips */
51 #include "UI_interface.h"
52
53 #include "DEG_depsgraph.h"
54
55 /* own includes */
56 #include "wm_gizmo_wmapi.h"
57 #include "wm_gizmo_intern.h"
58
59 /**
60  * Store all gizmo-maps here. Anyone who wants to register a gizmo for a certain
61  * area type can query the gizmo-map to do so.
62  */
63 static ListBase gizmomaptypes = {NULL, NULL};
64
65 /**
66  * Update when gizmo-map types change.
67  */
68 /* so operator removal can trigger update */
69 typedef enum eWM_GizmoFlagGroupTypeGlobalFlag {
70   WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
71   WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
72 } eWM_GizmoFlagGroupTypeGlobalFlag;
73
74 static eWM_GizmoFlagGroupTypeGlobalFlag wm_gzmap_type_update_flag = 0;
75
76 /**
77  * Gizmo-map update tagging.
78  */
79 enum {
80   /** #gizmomap_prepare_drawing has run */
81   GIZMOMAP_IS_PREPARE_DRAW = (1 << 0),
82   GIZMOMAP_IS_REFRESH_CALLBACK = (1 << 1),
83 };
84
85 /* -------------------------------------------------------------------- */
86 /** \name wmGizmoMap Selection Array API
87  *
88  * Just handle ``wm_gizmomap_select_array_*``, not flags or callbacks.
89  *
90  * \{ */
91
92 static void wm_gizmomap_select_array_ensure_len_alloc(wmGizmoMap *gzmap, int len)
93 {
94   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
95   if (len <= msel->len_alloc) {
96     return;
97   }
98   msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
99   msel->len_alloc = len;
100 }
101
102 void wm_gizmomap_select_array_clear(wmGizmoMap *gzmap)
103 {
104   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
105   MEM_SAFE_FREE(msel->items);
106   msel->len = 0;
107   msel->len_alloc = 0;
108 }
109
110 void wm_gizmomap_select_array_shrink(wmGizmoMap *gzmap, int len_subtract)
111 {
112   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
113   msel->len -= len_subtract;
114   if (msel->len <= 0) {
115     wm_gizmomap_select_array_clear(gzmap);
116   }
117   else {
118     if (msel->len < msel->len_alloc / 2) {
119       msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
120       msel->len_alloc = msel->len;
121     }
122   }
123 }
124
125 void wm_gizmomap_select_array_push_back(wmGizmoMap *gzmap, wmGizmo *gz)
126 {
127   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
128   BLI_assert(msel->len <= msel->len_alloc);
129   if (msel->len == msel->len_alloc) {
130     msel->len_alloc = (msel->len + 1) * 2;
131     msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
132   }
133   msel->items[msel->len++] = gz;
134 }
135
136 void wm_gizmomap_select_array_remove(wmGizmoMap *gzmap, wmGizmo *gz)
137 {
138   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
139   /* remove gizmo from selected_gizmos array */
140   for (int i = 0; i < msel->len; i++) {
141     if (msel->items[i] == gz) {
142       for (int j = i; j < (msel->len - 1); j++) {
143         msel->items[j] = msel->items[j + 1];
144       }
145       wm_gizmomap_select_array_shrink(gzmap, 1);
146       break;
147     }
148   }
149 }
150
151 /** \} */
152
153 /* -------------------------------------------------------------------- */
154 /** \name wmGizmoMap
155  *
156  * \{ */
157
158 static wmGizmoMap *wm_gizmomap_new_from_type_ex(struct wmGizmoMapType *gzmap_type,
159                                                 wmGizmoMap *gzmap)
160 {
161   gzmap->type = gzmap_type;
162   gzmap->is_init = true;
163   WM_gizmomap_tag_refresh(gzmap);
164
165   /* create all gizmo-groups for this gizmo-map. We may create an empty one
166    * too in anticipation of gizmos from operators etc */
167   for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first; gzgt_ref;
168        gzgt_ref = gzgt_ref->next) {
169     wm_gizmogroup_new_from_type(gzmap, gzgt_ref->type);
170   }
171
172   return gzmap;
173 }
174
175 /**
176  * Creates a gizmo-map with all registered gizmos for that type
177  */
178 wmGizmoMap *WM_gizmomap_new_from_type(const struct wmGizmoMapType_Params *gzmap_params)
179 {
180   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(gzmap_params);
181   wmGizmoMap *gzmap = MEM_callocN(sizeof(wmGizmoMap), "GizmoMap");
182   wm_gizmomap_new_from_type_ex(gzmap_type, gzmap);
183   return gzmap;
184 }
185
186 static void wm_gizmomap_free_data(wmGizmoMap *gzmap)
187 {
188   /* Clear first so further calls don't waste time trying to maintain correct array state. */
189   wm_gizmomap_select_array_clear(gzmap);
190
191   for (wmGizmoGroup *gzgroup = gzmap->groups.first, *gzgroup_next; gzgroup;
192        gzgroup = gzgroup_next) {
193     gzgroup_next = gzgroup->next;
194     BLI_assert(gzgroup->parent_gzmap == gzmap);
195     wm_gizmogroup_free(NULL, gzgroup);
196   }
197   BLI_assert(BLI_listbase_is_empty(&gzmap->groups));
198 }
199
200 void wm_gizmomap_remove(wmGizmoMap *gzmap)
201 {
202   wm_gizmomap_free_data(gzmap);
203   MEM_freeN(gzmap);
204 }
205
206 /** Re-create the gizmos (use when changing theme settings). */
207 void WM_gizmomap_reinit(wmGizmoMap *gzmap)
208 {
209   wmGizmoMapType *gzmap_type = gzmap->type;
210   wm_gizmomap_free_data(gzmap);
211   memset(gzmap, 0x0, sizeof(*gzmap));
212   wm_gizmomap_new_from_type_ex(gzmap_type, gzmap);
213 }
214
215 wmGizmoGroup *WM_gizmomap_group_find(struct wmGizmoMap *gzmap, const char *idname)
216 {
217   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
218   if (gzgt) {
219     return WM_gizmomap_group_find_ptr(gzmap, gzgt);
220   }
221   return NULL;
222 }
223
224 wmGizmoGroup *WM_gizmomap_group_find_ptr(struct wmGizmoMap *gzmap,
225                                          const struct wmGizmoGroupType *gzgt)
226 {
227   for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
228     if (gzgroup->type == gzgt) {
229       return gzgroup;
230     }
231   }
232   return NULL;
233 }
234
235 const ListBase *WM_gizmomap_group_list(wmGizmoMap *gzmap)
236 {
237   return &gzmap->groups;
238 }
239
240 bool WM_gizmomap_is_any_selected(const wmGizmoMap *gzmap)
241 {
242   return gzmap->gzmap_context.select.len != 0;
243 }
244
245 /**
246  * \note We could use a callback to define bounds, for now just use matrix location.
247  */
248 bool WM_gizmomap_minmax(const wmGizmoMap *gzmap,
249                         bool UNUSED(use_hidden),
250                         bool use_select,
251                         float r_min[3],
252                         float r_max[3])
253 {
254   if (use_select) {
255     int i;
256     for (i = 0; i < gzmap->gzmap_context.select.len; i++) {
257       minmax_v3v3_v3(r_min, r_max, gzmap->gzmap_context.select.items[i]->matrix_basis[3]);
258     }
259     return i != 0;
260   }
261   else {
262     bool ok = false;
263     BLI_assert(!"TODO");
264     return ok;
265   }
266 }
267
268 /**
269  * Creates and returns idname hash table for (visible) gizmos in \a gzmap
270  *
271  * \param poll: Polling function for excluding gizmos.
272  * \param data: Custom data passed to \a poll
273  *
274  * TODO(campbell): this uses unreliable order,
275  * best we use an iterator function instead of a hash.
276  */
277 static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C,
278                                          wmGizmoMap *gzmap,
279                                          bool (*poll)(const wmGizmo *, void *),
280                                          void *data,
281                                          const eWM_GizmoFlag flag_exclude)
282 {
283   GHash *hash = BLI_ghash_ptr_new(__func__);
284
285   /* collect gizmos */
286   for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
287     if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
288       for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
289         if (((flag_exclude == 0) || ((gz->flag & flag_exclude) == 0)) &&
290             (!poll || poll(gz, data))) {
291           BLI_ghash_insert(hash, gz, gz);
292         }
293       }
294     }
295   }
296
297   return hash;
298 }
299
300 void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
301 {
302   if (gzmap) {
303     /* We might want only to refresh some, for tag all steps. */
304     for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
305       gzmap->update_flag[i] |= (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK);
306     }
307   }
308 }
309
310 static bool gizmo_prepare_drawing(wmGizmoMap *gzmap,
311                                   wmGizmo *gz,
312                                   const bContext *C,
313                                   ListBase *draw_gizmos,
314                                   const eWM_GizmoFlagMapDrawStep drawstep)
315 {
316   int do_draw = wm_gizmo_is_visible(gz);
317   if (do_draw == 0) {
318     /* skip */
319   }
320   else {
321     /* Ensure we get RNA updates */
322     if (do_draw & WM_GIZMO_IS_VISIBLE_UPDATE) {
323       /* hover gizmos need updating, even if we don't draw them */
324       wm_gizmo_update(gz, C, (gzmap->update_flag[drawstep] & GIZMOMAP_IS_PREPARE_DRAW) != 0);
325     }
326     if (do_draw & WM_GIZMO_IS_VISIBLE_DRAW) {
327       BLI_addhead(draw_gizmos, BLI_genericNodeN(gz));
328     }
329     return true;
330   }
331
332   return false;
333 }
334
335 /**
336  * Update gizmos of \a gzmap to prepare for drawing. Adds all gizmos that
337  * should be drawn to list \a draw_gizmos, note that added items need freeing.
338  */
339 static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
340                                      const bContext *C,
341                                      ListBase *draw_gizmos,
342                                      const eWM_GizmoFlagMapDrawStep drawstep)
343 {
344   if (!gzmap || BLI_listbase_is_empty(&gzmap->groups)) {
345     return;
346   }
347
348   gzmap->is_init = false;
349
350   wmGizmo *gz_modal = gzmap->gzmap_context.modal;
351
352   /* only active gizmo needs updating */
353   if (gz_modal) {
354     if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) {
355       if (wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) {
356         if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) {
357           gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
358         }
359       }
360       /* don't draw any other gizmos */
361       return;
362     }
363   }
364
365   for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
366     /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
367     if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) ||
368         !WM_gizmo_group_type_poll(C, gzgroup->type)) {
369       continue;
370     }
371
372     /* needs to be initialized on first draw */
373     /* XXX weak: Gizmo-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
374     if (gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK) {
375       /* force refresh again. */
376       gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
377     }
378     /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
379     WM_gizmogroup_ensure_init(C, gzgroup);
380
381     /* prepare drawing */
382     if (gzgroup->type->draw_prepare) {
383       gzgroup->type->draw_prepare(C, gzgroup);
384     }
385
386     for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
387       gizmo_prepare_drawing(gzmap, gz, C, draw_gizmos, drawstep);
388     }
389   }
390
391   gzmap->update_flag[drawstep] &= ~(GIZMOMAP_IS_REFRESH_CALLBACK | GIZMOMAP_IS_PREPARE_DRAW);
392 }
393
394 /**
395  * Draw all visible gizmos in \a gzmap.
396  * Uses global draw_gizmos listbase.
397  */
398 static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBase *draw_gizmos)
399 {
400   /* Can be empty if we're dynamically added and removed. */
401   if ((gzmap == NULL) || BLI_listbase_is_empty(&gzmap->groups)) {
402     return;
403   }
404
405   /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
406   /*  const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */
407
408   bool is_depth_prev = false;
409
410   /* draw_gizmos contains all visible gizmos - draw them */
411   for (LinkData *link = draw_gizmos->first, *link_next; link; link = link_next) {
412     wmGizmo *gz = link->data;
413     link_next = link->next;
414
415     bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
416
417     /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
418     if (is_depth && (gz->state & WM_GIZMO_STATE_HIGHLIGHT)) {
419       is_depth = false;
420     }
421
422     if (is_depth == is_depth_prev) {
423       /* pass */
424     }
425     else {
426       if (is_depth) {
427         GPU_depth_test(true);
428       }
429       else {
430         GPU_depth_test(false);
431       }
432       is_depth_prev = is_depth;
433     }
434
435     /* XXX force AntiAlias Gizmos. */
436     GPU_line_smooth(true);
437     GPU_polygon_smooth(true);
438
439     gz->type->draw(C, gz);
440
441     GPU_line_smooth(false);
442     GPU_polygon_smooth(false);
443
444     /* free/remove gizmo link after drawing */
445     BLI_freelinkN(draw_gizmos, link);
446   }
447
448   if (is_depth_prev) {
449     GPU_depth_test(false);
450   }
451 }
452
453 void WM_gizmomap_draw(wmGizmoMap *gzmap,
454                       const bContext *C,
455                       const eWM_GizmoFlagMapDrawStep drawstep)
456 {
457   if (!WM_gizmo_context_check_drawstep(C, drawstep)) {
458     return;
459   }
460
461   ListBase draw_gizmos = {NULL};
462
463   gizmomap_prepare_drawing(gzmap, C, &draw_gizmos, drawstep);
464   gizmos_draw_list(gzmap, C, &draw_gizmos);
465   BLI_assert(BLI_listbase_is_empty(&draw_gizmos));
466 }
467
468 static void gizmo_draw_select_3D_loop(const bContext *C,
469                                       ListBase *visible_gizmos,
470                                       const wmGizmo *gz_stop)
471 {
472   int select_id = 0;
473   wmGizmo *gz;
474
475   /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
476   bool is_depth_prev = false;
477   bool is_depth_skip_prev = false;
478
479   for (LinkData *link = visible_gizmos->first; link; link = link->next, select_id++) {
480     gz = link->data;
481     if (gz == gz_stop) {
482       break;
483     }
484     if (gz->type->draw_select == NULL) {
485       continue;
486     }
487
488     bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
489     if (is_depth == is_depth_prev) {
490       /* pass */
491     }
492     else {
493       if (is_depth) {
494         GPU_depth_test(true);
495       }
496       else {
497         GPU_depth_test(false);
498       }
499       is_depth_prev = is_depth;
500     }
501     bool is_depth_skip = (gz->flag & WM_GIZMO_SELECT_BACKGROUND) != 0;
502     if (is_depth_skip == is_depth_skip_prev) {
503       /* pass */
504     }
505     else {
506       glDepthMask(!is_depth_skip);
507       is_depth_skip_prev = is_depth_skip;
508     }
509
510     /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */
511
512     gz->type->draw_select(C, gz, select_id << 8);
513   }
514
515   if (is_depth_prev) {
516     GPU_depth_test(false);
517   }
518   if (is_depth_skip_prev) {
519     glDepthMask(true);
520   }
521 }
522
523 static int gizmo_find_intersected_3d_intern(ListBase *visible_gizmos,
524                                             const bContext *C,
525                                             const int co[2],
526                                             const int hotspot,
527                                             const wmGizmo *gz_stop)
528 {
529   ScrArea *sa = CTX_wm_area(C);
530   ARegion *ar = CTX_wm_region(C);
531   View3D *v3d = sa->spacedata.first;
532   rcti rect;
533   /* Almost certainly overkill, but allow for many custom gizmos. */
534   GLuint buffer[MAXPICKBUF];
535   short hits;
536
537   BLI_rcti_init_pt_radius(&rect, co, hotspot);
538
539   ED_view3d_draw_setup_view(
540       CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
541
542   GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
543   /* do the drawing */
544   gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop);
545
546   hits = GPU_select_end();
547
548   if (hits > 0) {
549     GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
550     gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop);
551     GPU_select_end();
552   }
553
554   ED_view3d_draw_setup_view(
555       CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
556
557   const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
558
559   return hit_near ? hit_near[3] : -1;
560 }
561
562 /**
563  * Try to find a 3D gizmo at screen-space coordinate \a co. Uses OpenGL picking.
564  */
565 static wmGizmo *gizmo_find_intersected_3d(bContext *C,
566                                           const int co[2],
567                                           ListBase *visible_gizmos,
568                                           int *r_part)
569 {
570   wmGizmo *result = NULL;
571   int hit = -1;
572
573   *r_part = 0;
574
575   /* set up view matrices */
576   view3d_operator_needs_opengl(C);
577
578   /* Search for 3D gizmo's that use the 2D callback for checking intersections. */
579   bool has_3d = false;
580   {
581     int select_id = 0;
582     for (LinkData *link = visible_gizmos->first; link; link = link->next, select_id++) {
583       wmGizmo *gz = link->data;
584       /* With both defined, favor the 3D, incase the gizmo can be used in 2D or 3D views. */
585       if (gz->type->test_select && (gz->type->draw_select == NULL)) {
586         if ((*r_part = gz->type->test_select(C, gz, co)) != -1) {
587           hit = select_id;
588           result = gz;
589           break;
590         }
591       }
592       else {
593         has_3d = true;
594       }
595     }
596   }
597
598   /* Search for 3D intersections if they're before 2D that have been found (if any).
599    * This way we always use the first hit. */
600   if (has_3d) {
601     const int hotspot_radii[] = {
602         3 * U.pixelsize,
603         /* This runs on mouse move, careful doing too many tests! */
604         10 * U.pixelsize,
605     };
606     for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
607       hit = gizmo_find_intersected_3d_intern(visible_gizmos, C, co, hotspot_radii[i], result);
608       if (hit != -1) {
609         break;
610       }
611     }
612
613     if (hit != -1) {
614       LinkData *link = BLI_findlink(visible_gizmos, hit >> 8);
615       if (link != NULL) {
616         *r_part = hit & 255;
617         result = link->data;
618       }
619       else {
620         /* All gizmos should use selection ID they're given as part of the callback,
621          * if they don't it will attempt tp lookup non-existing index. */
622         BLI_assert(0);
623       }
624     }
625   }
626
627   return result;
628 }
629
630 /**
631  * Try to find a gizmo under the mouse position. 2D intersections have priority over
632  * 3D ones (could check for smallest screen-space distance but not needed right now).
633  */
634 wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
635                                     bContext *C,
636                                     const wmEvent *event,
637                                     int *r_part)
638 {
639   wmGizmo *gz = NULL;
640   ListBase visible_3d_gizmos = {NULL};
641   bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
642
643   for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
644     do_step[i] = WM_gizmo_context_check_drawstep(C, i);
645   }
646
647   for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
648
649     /* If it were important we could initialize here,
650      * but this only happens when events are handled before drawing,
651      * just skip to keep code-path for initializing gizmos simple. */
652     if ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0) {
653       continue;
654     }
655
656     if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
657       eWM_GizmoFlagMapDrawStep step;
658       if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
659         step = WM_GIZMOMAP_DRAWSTEP_3D;
660       }
661       else {
662         step = WM_GIZMOMAP_DRAWSTEP_2D;
663       }
664
665       if (do_step[step]) {
666         if ((gzmap->update_flag[step] & GIZMOMAP_IS_REFRESH_CALLBACK) &&
667             (gzgroup->type->refresh != NULL)) {
668           gzgroup->type->refresh(C, gzgroup);
669           /* cleared below */
670         }
671         if (step == WM_GIZMOMAP_DRAWSTEP_3D) {
672           wm_gizmogroup_intersectable_gizmos_to_list(gzgroup, &visible_3d_gizmos);
673         }
674         else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
675           if ((gz = wm_gizmogroup_find_intersected_gizmo(gzgroup, C, event, r_part))) {
676             break;
677           }
678         }
679       }
680     }
681   }
682
683   if (!BLI_listbase_is_empty(&visible_3d_gizmos)) {
684     /* 2D gizmos get priority. */
685     if (gz == NULL) {
686       gz = gizmo_find_intersected_3d(C, event->mval, &visible_3d_gizmos, r_part);
687     }
688     BLI_freelistN(&visible_3d_gizmos);
689   }
690
691   gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_3D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
692   gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_2D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
693
694   return gz;
695 }
696
697 void WM_gizmomap_add_handlers(ARegion *ar, wmGizmoMap *gzmap)
698 {
699   LISTBASE_FOREACH (wmEventHandler *, handler_base, &ar->handlers) {
700     if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
701       wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
702       if (handler->gizmo_map == gzmap) {
703         return;
704       }
705     }
706   }
707
708   wmEventHandler_Gizmo *handler = MEM_callocN(sizeof(*handler), __func__);
709   handler->head.type = WM_HANDLER_TYPE_GIZMO;
710   BLI_assert(gzmap == ar->gizmo_map);
711   handler->gizmo_map = gzmap;
712   BLI_addtail(&ar->handlers, handler);
713 }
714
715 void wm_gizmomaps_handled_modal_update(bContext *C, wmEvent *event, wmEventHandler_Op *handler)
716 {
717   const bool modal_running = (handler->op != NULL);
718
719   /* happens on render or when joining areas */
720   if (!handler->context.region || !handler->context.region->gizmo_map) {
721     return;
722   }
723
724   wmGizmoMap *gzmap = handler->context.region->gizmo_map;
725   wmGizmo *gz = wm_gizmomap_modal_get(gzmap);
726   ScrArea *area = CTX_wm_area(C);
727   ARegion *region = CTX_wm_region(C);
728
729   wm_gizmomap_handler_context_op(C, handler);
730
731   /* regular update for running operator */
732   if (modal_running) {
733     wmGizmoOpElem *gzop = gz ? WM_gizmo_operator_get(gz, gz->highlight_part) : NULL;
734     if (gz && gzop && (gzop->type != NULL) && (gzop->type == handler->op->type)) {
735       wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
736       if (modal_fn != NULL) {
737         int retval = modal_fn(C, gz, event, 0);
738         /* The gizmo is tried to the operator, we can't choose when to exit. */
739         BLI_assert(retval & OPERATOR_RUNNING_MODAL);
740         UNUSED_VARS_NDEBUG(retval);
741       }
742     }
743   }
744   /* operator not running anymore */
745   else {
746     wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
747     if (gz) {
748       /* This isn't defined if it ends because of success of cancel, we may want to change. */
749       bool cancel = true;
750       if (gz->type->exit) {
751         gz->type->exit(C, gz, cancel);
752       }
753       wm_gizmomap_modal_set(gzmap, C, gz, NULL, false);
754     }
755   }
756
757   /* restore the area */
758   CTX_wm_area_set(C, area);
759   CTX_wm_region_set(C, region);
760 }
761
762 /**
763  * Deselect all selected gizmos in \a gzmap.
764  * \return if selection has changed.
765  */
766 bool wm_gizmomap_deselect_all(wmGizmoMap *gzmap)
767 {
768   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
769
770   if (msel->items == NULL || msel->len == 0) {
771     return false;
772   }
773
774   for (int i = 0; i < msel->len; i++) {
775     wm_gizmo_select_set_ex(gzmap, msel->items[i], false, false, true);
776   }
777
778   wm_gizmomap_select_array_clear(gzmap);
779
780   /* always return true, we already checked
781    * if there's anything to deselect */
782   return true;
783 }
784
785 BLI_INLINE bool gizmo_selectable_poll(const wmGizmo *gz, void *UNUSED(data))
786 {
787   return (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SELECT);
788 }
789
790 /**
791  * Select all selectable gizmos in \a gzmap.
792  * \return if selection has changed.
793  */
794 static bool wm_gizmomap_select_all_intern(bContext *C, wmGizmoMap *gzmap)
795 {
796   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
797   /* GHash is used here to avoid having to loop over all gizmos twice (once to
798    * get tot_sel for allocating, once for actually selecting). Instead we collect
799    * selectable gizmos in hash table and use this to get tot_sel and do selection */
800
801   GHash *hash = WM_gizmomap_gizmo_hash_new(
802       C, gzmap, gizmo_selectable_poll, NULL, WM_GIZMO_HIDDEN | WM_GIZMO_HIDDEN_SELECT);
803   GHashIterator gh_iter;
804   int i;
805   bool changed = false;
806
807   wm_gizmomap_select_array_ensure_len_alloc(gzmap, BLI_ghash_len(hash));
808
809   GHASH_ITER_INDEX(gh_iter, hash, i)
810   {
811     wmGizmo *gz_iter = BLI_ghashIterator_getValue(&gh_iter);
812     WM_gizmo_select_set(gzmap, gz_iter, true);
813   }
814   /* highlight first gizmo */
815   wm_gizmomap_highlight_set(gzmap, C, msel->items[0], msel->items[0]->highlight_part);
816
817   BLI_assert(BLI_ghash_len(hash) == msel->len);
818
819   BLI_ghash_free(hash, NULL, NULL);
820   return changed;
821 }
822
823 /**
824  * Select/Deselect all selectable gizmos in \a gzmap.
825  * \return if selection has changed.
826  *
827  * TODO select all by type
828  */
829 bool WM_gizmomap_select_all(bContext *C, wmGizmoMap *gzmap, const int action)
830 {
831   bool changed = false;
832
833   switch (action) {
834     case SEL_SELECT:
835       changed = wm_gizmomap_select_all_intern(C, gzmap);
836       break;
837     case SEL_DESELECT:
838       changed = wm_gizmomap_deselect_all(gzmap);
839       break;
840     default:
841       BLI_assert(0);
842       break;
843   }
844
845   if (changed) {
846     WM_event_add_mousemove(C);
847   }
848
849   return changed;
850 }
851
852 /**
853  * Prepare context for gizmo handling (but only if area/region is
854  * part of screen). Version of #wm_handler_op_context for gizmos.
855  */
856 void wm_gizmomap_handler_context_op(bContext *C, wmEventHandler_Op *handler)
857 {
858   bScreen *screen = CTX_wm_screen(C);
859
860   if (screen) {
861     ScrArea *sa;
862
863     for (sa = screen->areabase.first; sa; sa = sa->next) {
864       if (sa == handler->context.area) {
865         break;
866       }
867     }
868     if (sa == NULL) {
869       /* when changing screen layouts with running modal handlers (like render display), this
870        * is not an error to print */
871       printf("internal error: modal gizmo-map handler has invalid area\n");
872     }
873     else {
874       ARegion *ar;
875       CTX_wm_area_set(C, sa);
876       for (ar = sa->regionbase.first; ar; ar = ar->next) {
877         if (ar == handler->context.region) {
878           break;
879         }
880       }
881       /* XXX no warning print here, after full-area and back regions are remade */
882       if (ar) {
883         CTX_wm_region_set(C, ar);
884       }
885     }
886   }
887 }
888
889 void wm_gizmomap_handler_context_gizmo(bContext *UNUSED(C), wmEventHandler_Gizmo *UNUSED(handler))
890 {
891   /* pass */
892 }
893
894 bool WM_gizmomap_cursor_set(const wmGizmoMap *gzmap, wmWindow *win)
895 {
896   wmGizmo *gz = gzmap->gzmap_context.highlight;
897   if (gz && gz->type->cursor_get) {
898     WM_cursor_set(win, gz->type->cursor_get(gz));
899     return true;
900   }
901
902   return false;
903 }
904
905 bool wm_gizmomap_highlight_set(wmGizmoMap *gzmap, const bContext *C, wmGizmo *gz, int part)
906 {
907   if ((gz != gzmap->gzmap_context.highlight) || (gz && part != gz->highlight_part)) {
908     if (gzmap->gzmap_context.highlight) {
909       gzmap->gzmap_context.highlight->state &= ~WM_GIZMO_STATE_HIGHLIGHT;
910       gzmap->gzmap_context.highlight->highlight_part = -1;
911     }
912
913     gzmap->gzmap_context.highlight = gz;
914
915     if (gz) {
916       gz->state |= WM_GIZMO_STATE_HIGHLIGHT;
917       gz->highlight_part = part;
918       gzmap->gzmap_context.last_cursor = -1;
919
920       if (C && gz->type->cursor_get) {
921         wmWindow *win = CTX_wm_window(C);
922         gzmap->gzmap_context.last_cursor = win->cursor;
923         WM_cursor_set(win, gz->type->cursor_get(gz));
924       }
925     }
926     else {
927       if (C && gzmap->gzmap_context.last_cursor != -1) {
928         wmWindow *win = CTX_wm_window(C);
929         WM_cursor_set(win, gzmap->gzmap_context.last_cursor);
930       }
931     }
932
933     /* tag the region for redraw */
934     if (C) {
935       ARegion *ar = CTX_wm_region(C);
936       ED_region_tag_redraw(ar);
937     }
938
939     return true;
940   }
941
942   return false;
943 }
944
945 wmGizmo *wm_gizmomap_highlight_get(wmGizmoMap *gzmap)
946 {
947   return gzmap->gzmap_context.highlight;
948 }
949
950 /**
951  * Caller should call exit when (enable == False).
952  */
953 void wm_gizmomap_modal_set(
954     wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, const wmEvent *event, bool enable)
955 {
956   if (enable) {
957     BLI_assert(gzmap->gzmap_context.modal == NULL);
958     wmWindow *win = CTX_wm_window(C);
959
960     WM_tooltip_clear(C, win);
961
962     /* Use even if we don't have invoke, so we can setup data before an operator runs. */
963     if (gz->parent_gzgroup->type->invoke_prepare) {
964       gz->parent_gzgroup->type->invoke_prepare(C, gz->parent_gzgroup, gz);
965     }
966
967     if (gz->type->invoke && (gz->type->modal || gz->custom_modal)) {
968       const int retval = gz->type->invoke(C, gz, event);
969       if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
970         return;
971       }
972     }
973
974     gz->state |= WM_GIZMO_STATE_MODAL;
975     gzmap->gzmap_context.modal = gz;
976
977     if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->is_motion_absolute == false)) {
978       WM_cursor_grab_enable(win, true, true, NULL);
979       copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x);
980       gzmap->gzmap_context.event_grabcursor = win->grabcursor;
981     }
982     else {
983       gzmap->gzmap_context.event_xy[0] = INT_MAX;
984     }
985
986     struct wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, gz->highlight_part);
987     if (gzop && gzop->type) {
988       const int retval = WM_gizmo_operator_invoke(C, gz, gzop);
989       if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
990         wm_gizmomap_modal_set(gzmap, C, gz, event, false);
991       }
992
993       /* we failed to hook the gizmo to the operator handler or operator was cancelled, return */
994       if (!gzmap->gzmap_context.modal) {
995         gz->state &= ~WM_GIZMO_STATE_MODAL;
996         MEM_SAFE_FREE(gz->interaction_data);
997       }
998       return;
999     }
1000   }
1001   else {
1002     BLI_assert(ELEM(gzmap->gzmap_context.modal, NULL, gz));
1003
1004     /* deactivate, gizmo but first take care of some stuff */
1005     if (gz) {
1006       gz->state &= ~WM_GIZMO_STATE_MODAL;
1007       MEM_SAFE_FREE(gz->interaction_data);
1008     }
1009     gzmap->gzmap_context.modal = NULL;
1010
1011     if (C) {
1012       wmWindow *win = CTX_wm_window(C);
1013       if (gzmap->gzmap_context.event_xy[0] != INT_MAX) {
1014         /* Check if some other part of Blender (typically operators)
1015          * have adjusted the grab mode since it was set.
1016          * If so: warp, so we have a predictable outcome. */
1017         if (gzmap->gzmap_context.event_grabcursor == win->grabcursor) {
1018           WM_cursor_grab_disable(win, gzmap->gzmap_context.event_xy);
1019         }
1020         else {
1021           WM_cursor_warp(win, UNPACK2(gzmap->gzmap_context.event_xy));
1022         }
1023       }
1024       ED_region_tag_redraw(CTX_wm_region(C));
1025       WM_event_add_mousemove(C);
1026     }
1027
1028     gzmap->gzmap_context.event_xy[0] = INT_MAX;
1029   }
1030 }
1031
1032 wmGizmo *wm_gizmomap_modal_get(wmGizmoMap *gzmap)
1033 {
1034   return gzmap->gzmap_context.modal;
1035 }
1036
1037 wmGizmo **wm_gizmomap_selected_get(wmGizmoMap *gzmap, int *r_selected_len)
1038 {
1039   *r_selected_len = gzmap->gzmap_context.select.len;
1040   return gzmap->gzmap_context.select.items;
1041 }
1042
1043 ListBase *wm_gizmomap_groups_get(wmGizmoMap *gzmap)
1044 {
1045   return &gzmap->groups;
1046 }
1047
1048 void WM_gizmomap_message_subscribe(bContext *C,
1049                                    wmGizmoMap *gzmap,
1050                                    ARegion *ar,
1051                                    struct wmMsgBus *mbus)
1052 {
1053   for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
1054     if ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0 ||
1055         !WM_gizmo_group_type_poll(C, gzgroup->type)) {
1056       continue;
1057     }
1058     for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
1059       if (gz->flag & WM_GIZMO_HIDDEN) {
1060         continue;
1061       }
1062       WM_gizmo_target_property_subscribe_all(gz, mbus, ar);
1063     }
1064     if (gzgroup->type->message_subscribe != NULL) {
1065       gzgroup->type->message_subscribe(C, gzgroup, mbus);
1066     }
1067   }
1068 }
1069
1070 /** \} */ /* wmGizmoMap */
1071
1072 /* -------------------------------------------------------------------- */
1073 /** \name Tooltip Handling
1074  *
1075  * \{ */
1076
1077 struct ARegion *WM_gizmomap_tooltip_init(struct bContext *C,
1078                                          struct ARegion *ar,
1079                                          int *UNUSED(r_pass),
1080                                          double *UNUSED(pass_delay),
1081                                          bool *r_exit_on_event)
1082 {
1083   wmGizmoMap *gzmap = ar->gizmo_map;
1084   *r_exit_on_event = true;
1085   if (gzmap) {
1086     wmGizmo *gz = gzmap->gzmap_context.highlight;
1087     if (gz) {
1088       return UI_tooltip_create_from_gizmo(C, gz);
1089     }
1090   }
1091   return NULL;
1092 }
1093
1094 /** \} */ /* wmGizmoMapType */
1095
1096 /* -------------------------------------------------------------------- */
1097 /** \name wmGizmoMapType
1098  *
1099  * \{ */
1100
1101 wmGizmoMapType *WM_gizmomaptype_find(const struct wmGizmoMapType_Params *gzmap_params)
1102 {
1103   for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type;
1104        gzmap_type = gzmap_type->next) {
1105     if (gzmap_type->spaceid == gzmap_params->spaceid &&
1106         gzmap_type->regionid == gzmap_params->regionid) {
1107       return gzmap_type;
1108     }
1109   }
1110
1111   return NULL;
1112 }
1113
1114 wmGizmoMapType *WM_gizmomaptype_ensure(const struct wmGizmoMapType_Params *gzmap_params)
1115 {
1116   wmGizmoMapType *gzmap_type = WM_gizmomaptype_find(gzmap_params);
1117
1118   if (gzmap_type) {
1119     return gzmap_type;
1120   }
1121
1122   gzmap_type = MEM_callocN(sizeof(wmGizmoMapType), "gizmotype list");
1123   gzmap_type->spaceid = gzmap_params->spaceid;
1124   gzmap_type->regionid = gzmap_params->regionid;
1125   BLI_addhead(&gizmomaptypes, gzmap_type);
1126
1127   return gzmap_type;
1128 }
1129
1130 void wm_gizmomaptypes_free(void)
1131 {
1132   for (wmGizmoMapType *gzmap_type = gizmomaptypes.first, *gzmap_type_next; gzmap_type;
1133        gzmap_type = gzmap_type_next) {
1134     gzmap_type_next = gzmap_type->next;
1135     for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_next; gzgt_ref;
1136          gzgt_ref = gzgt_next) {
1137       gzgt_next = gzgt_ref->next;
1138       WM_gizmomaptype_group_free(gzgt_ref);
1139     }
1140     MEM_freeN(gzmap_type);
1141   }
1142 }
1143
1144 /**
1145  * Initialize keymaps for all existing gizmo-groups
1146  */
1147 void wm_gizmos_keymap(wmKeyConfig *keyconf)
1148 {
1149   /* we add this item-less keymap once and use it to group gizmo-group keymaps into it */
1150   WM_keymap_ensure(keyconf, "Gizmos", 0, 0);
1151
1152   for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type;
1153        gzmap_type = gzmap_type->next) {
1154     for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first; gzgt_ref;
1155          gzgt_ref = gzgt_ref->next) {
1156       wm_gizmogrouptype_setup_keymap(gzgt_ref->type, keyconf);
1157     }
1158   }
1159 }
1160
1161 /** \} */ /* wmGizmoMapType */
1162
1163 /* -------------------------------------------------------------------- */
1164 /** \name Updates for Dynamic Type Registraion
1165  *
1166  * \{ */
1167
1168 void WM_gizmoconfig_update_tag_init(wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
1169 {
1170   /* tag for update on next use */
1171   gzmap_type->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
1172   gzgt->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
1173
1174   wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
1175 }
1176
1177 void WM_gizmoconfig_update_tag_remove(wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
1178 {
1179   /* tag for update on next use */
1180   gzmap_type->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1181   gzgt->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1182
1183   wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
1184 }
1185
1186 /**
1187  * Run incase new types have been added (runs often, early exit where possible).
1188  * Follows #WM_keyconfig_update concentions.
1189  */
1190 void WM_gizmoconfig_update(struct Main *bmain)
1191 {
1192   if (G.background) {
1193     return;
1194   }
1195
1196   if (wm_gzmap_type_update_flag == 0) {
1197     return;
1198   }
1199
1200   if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1201     for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type;
1202          gzmap_type = gzmap_type->next) {
1203       if (gzmap_type->type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1204         gzmap_type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1205         for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_ref_next;
1206              gzgt_ref;
1207              gzgt_ref = gzgt_ref_next) {
1208           gzgt_ref_next = gzgt_ref->next;
1209           if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_REMOVE) {
1210             gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1211             WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt_ref->type);
1212           }
1213         }
1214       }
1215     }
1216
1217     wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
1218   }
1219
1220   if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT) {
1221     for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type;
1222          gzmap_type = gzmap_type->next) {
1223       const uchar type_update_all = WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT;
1224       if (gzmap_type->type_update_flag & type_update_all) {
1225         gzmap_type->type_update_flag &= ~type_update_all;
1226         for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first; gzgt_ref;
1227              gzgt_ref = gzgt_ref->next) {
1228           if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_KEYMAP_INIT) {
1229             WM_gizmomaptype_group_init_runtime_keymap(bmain, gzgt_ref->type);
1230             gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_KEYMAP_INIT;
1231           }
1232
1233           if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_INIT) {
1234             WM_gizmomaptype_group_init_runtime(bmain, gzmap_type, gzgt_ref->type);
1235             gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_INIT;
1236           }
1237         }
1238       }
1239     }
1240
1241     wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
1242   }
1243 }
1244
1245 /** \} */
1246
1247 /* -------------------------------------------------------------------- */
1248 /** \name Recreate All Gizmos
1249  *
1250  * Use when adjusting themes.
1251  *
1252  * \{ */
1253
1254 void WM_reinit_gizmomap_all(Main *bmain)
1255 {
1256   for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
1257     for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
1258       for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
1259         ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
1260         for (ARegion *ar = regionbase->first; ar; ar = ar->next) {
1261           wmGizmoMap *gzmap = ar->gizmo_map;
1262           if ((gzmap != NULL) && (gzmap->is_init == false)) {
1263             WM_gizmomap_reinit(gzmap);
1264           }
1265         }
1266       }
1267     }
1268   }
1269 }
1270
1271 /** \} */