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  * \name Gizmo-Group
24  *
25  * Gizmo-groups store and manage groups of gizmos. They can be
26  * attached to modal handlers and have own keymaps.
27  */
28 
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "BLI_buffer.h"
35 #include "BLI_listbase.h"
36 #include "BLI_rect.h"
37 #include "BLI_string.h"
38 
39 #include "BKE_context.h"
40 #include "BKE_main.h"
41 #include "BKE_report.h"
42 #include "BKE_workspace.h"
43 
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46 
47 #include "WM_api.h"
48 #include "WM_types.h"
49 #include "wm_event_system.h"
50 
51 #include "ED_screen.h"
52 #include "ED_undo.h"
53 
54 /* own includes */
55 #include "wm_gizmo_intern.h"
56 #include "wm_gizmo_wmapi.h"
57 
58 #ifdef WITH_PYTHON
59 #  include "BPY_extern.h"
60 #endif
61 
62 /* -------------------------------------------------------------------- */
63 /** \name wmGizmoGroup
64  *
65  * \{ */
66 
67 /**
68  * Create a new gizmo-group from \a gzgt.
69  */
wm_gizmogroup_new_from_type(wmGizmoMap * gzmap,wmGizmoGroupType * gzgt)70 wmGizmoGroup *wm_gizmogroup_new_from_type(wmGizmoMap *gzmap, wmGizmoGroupType *gzgt)
71 {
72   wmGizmoGroup *gzgroup = MEM_callocN(sizeof(*gzgroup), "gizmo-group");
73 
74   gzgroup->type = gzgt;
75   gzgroup->type->users += 1;
76 
77   /* keep back-link */
78   gzgroup->parent_gzmap = gzmap;
79 
80   BLI_addtail(&gzmap->groups, gzgroup);
81 
82   return gzgroup;
83 }
84 
wm_gizmogroup_find_by_type(const wmGizmoMap * gzmap,const wmGizmoGroupType * gzgt)85 wmGizmoGroup *wm_gizmogroup_find_by_type(const wmGizmoMap *gzmap, const wmGizmoGroupType *gzgt)
86 {
87   return BLI_findptr(&gzmap->groups, gzgt, offsetof(wmGizmoGroup, type));
88 }
89 
wm_gizmogroup_free(bContext * C,wmGizmoGroup * gzgroup)90 void wm_gizmogroup_free(bContext *C, wmGizmoGroup *gzgroup)
91 {
92   wmGizmoMap *gzmap = gzgroup->parent_gzmap;
93 
94   /* Similar to WM_gizmo_unlink, but only to keep gzmap state correct,
95    * we don't want to run callbacks. */
96   if (gzmap->gzmap_context.highlight &&
97       gzmap->gzmap_context.highlight->parent_gzgroup == gzgroup) {
98     wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
99   }
100   if (gzmap->gzmap_context.modal && gzmap->gzmap_context.modal->parent_gzgroup == gzgroup) {
101     wm_gizmomap_modal_set(gzmap, C, gzmap->gzmap_context.modal, NULL, false);
102   }
103 
104   for (wmGizmo *gz = gzgroup->gizmos.first, *gz_next; gz; gz = gz_next) {
105     gz_next = gz->next;
106     if (gzmap->gzmap_context.select.len) {
107       WM_gizmo_select_unlink(gzmap, gz);
108     }
109     WM_gizmo_free(gz);
110   }
111   BLI_listbase_clear(&gzgroup->gizmos);
112 
113 #ifdef WITH_PYTHON
114   if (gzgroup->py_instance) {
115     /* do this first in case there are any __del__ functions or
116      * similar that use properties */
117     BPY_DECREF_RNA_INVALIDATE(gzgroup->py_instance);
118   }
119 #endif
120 
121   if (gzgroup->reports && (gzgroup->reports->flag & RPT_FREE)) {
122     BKE_reports_clear(gzgroup->reports);
123     MEM_freeN(gzgroup->reports);
124   }
125 
126   if (gzgroup->customdata_free) {
127     gzgroup->customdata_free(gzgroup->customdata);
128   }
129   else {
130     MEM_SAFE_FREE(gzgroup->customdata);
131   }
132 
133   BLI_remlink(&gzmap->groups, gzgroup);
134 
135   if (gzgroup->tag_remove == false) {
136     gzgroup->type->users -= 1;
137   }
138 
139   MEM_freeN(gzgroup);
140 }
141 
WM_gizmo_group_tag_remove(wmGizmoGroup * gzgroup)142 void WM_gizmo_group_tag_remove(wmGizmoGroup *gzgroup)
143 {
144   if (gzgroup->tag_remove == false) {
145     gzgroup->tag_remove = true;
146     gzgroup->type->users -= 1;
147     BLI_assert(gzgroup->type->users >= 0);
148     WM_gizmoconfig_update_tag_group_remove(gzgroup->parent_gzmap);
149   }
150 }
151 
152 /**
153  * Add \a gizmo to \a gzgroup and make sure its name is unique within the group.
154  */
wm_gizmogroup_gizmo_register(wmGizmoGroup * gzgroup,wmGizmo * gz)155 void wm_gizmogroup_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz)
156 {
157   BLI_assert(BLI_findindex(&gzgroup->gizmos, gz) == -1);
158   BLI_addtail(&gzgroup->gizmos, gz);
159   gz->parent_gzgroup = gzgroup;
160 }
161 
WM_gizmo_cmp_temp_fl(const void * gz_a_ptr,const void * gz_b_ptr)162 int WM_gizmo_cmp_temp_fl(const void *gz_a_ptr, const void *gz_b_ptr)
163 {
164   const wmGizmo *gz_a = gz_a_ptr;
165   const wmGizmo *gz_b = gz_b_ptr;
166   if (gz_a->temp.f < gz_b->temp.f) {
167     return -1;
168   }
169   if (gz_a->temp.f > gz_b->temp.f) {
170     return 1;
171   }
172   return 0;
173 }
174 
WM_gizmo_cmp_temp_fl_reverse(const void * gz_a_ptr,const void * gz_b_ptr)175 int WM_gizmo_cmp_temp_fl_reverse(const void *gz_a_ptr, const void *gz_b_ptr)
176 {
177   const wmGizmo *gz_a = gz_a_ptr;
178   const wmGizmo *gz_b = gz_b_ptr;
179   if (gz_a->temp.f < gz_b->temp.f) {
180     return 1;
181   }
182   if (gz_a->temp.f > gz_b->temp.f) {
183     return -1;
184   }
185   return 0;
186 }
187 
wm_gizmo_keymap_uses_event_modifier(wmWindowManager * wm,const wmGizmoGroup * gzgroup,wmGizmo * gz,const int event_modifier,int * r_gzgroup_keymap_uses_modifier)188 static bool wm_gizmo_keymap_uses_event_modifier(wmWindowManager *wm,
189                                                 const wmGizmoGroup *gzgroup,
190                                                 wmGizmo *gz,
191                                                 const int event_modifier,
192                                                 int *r_gzgroup_keymap_uses_modifier)
193 {
194   if (gz->keymap) {
195     wmKeyMap *keymap = WM_keymap_active(wm, gz->keymap);
196     if (!WM_keymap_uses_event_modifier(keymap, event_modifier)) {
197       return false;
198     }
199   }
200   else if (gzgroup->type->keymap) {
201     if (*r_gzgroup_keymap_uses_modifier == -1) {
202       wmKeyMap *keymap = WM_keymap_active(wm, gzgroup->type->keymap);
203       *r_gzgroup_keymap_uses_modifier = WM_keymap_uses_event_modifier(keymap, event_modifier);
204     }
205     if (*r_gzgroup_keymap_uses_modifier == 0) {
206       return false;
207     }
208   }
209   return true;
210 }
211 
wm_gizmogroup_find_intersected_gizmo(wmWindowManager * wm,const wmGizmoGroup * gzgroup,bContext * C,const int event_modifier,const int mval[2],int * r_part)212 wmGizmo *wm_gizmogroup_find_intersected_gizmo(wmWindowManager *wm,
213                                               const wmGizmoGroup *gzgroup,
214                                               bContext *C,
215                                               const int event_modifier,
216                                               const int mval[2],
217                                               int *r_part)
218 {
219   int gzgroup_keymap_uses_modifier = -1;
220 
221   LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
222     if (gz->type->test_select && (gz->flag & (WM_GIZMO_HIDDEN | WM_GIZMO_HIDDEN_SELECT)) == 0) {
223 
224       if (!wm_gizmo_keymap_uses_event_modifier(
225               wm, gzgroup, gz, event_modifier, &gzgroup_keymap_uses_modifier)) {
226         continue;
227       }
228 
229       if ((*r_part = gz->type->test_select(C, gz, mval)) != -1) {
230         return gz;
231       }
232     }
233   }
234 
235   return NULL;
236 }
237 
238 /**
239  * Adds all gizmos of \a gzgroup that can be selected to the head of \a listbase.
240  * Added items need freeing!
241  */
wm_gizmogroup_intersectable_gizmos_to_list(wmWindowManager * wm,const wmGizmoGroup * gzgroup,const int event_modifier,BLI_Buffer * visible_gizmos)242 void wm_gizmogroup_intersectable_gizmos_to_list(wmWindowManager *wm,
243                                                 const wmGizmoGroup *gzgroup,
244                                                 const int event_modifier,
245                                                 BLI_Buffer *visible_gizmos)
246 {
247   int gzgroup_keymap_uses_modifier = -1;
248   for (wmGizmo *gz = gzgroup->gizmos.last; gz; gz = gz->prev) {
249     if ((gz->flag & (WM_GIZMO_HIDDEN | WM_GIZMO_HIDDEN_SELECT)) == 0) {
250       if (((gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) &&
251            (gz->type->draw_select || gz->type->test_select)) ||
252           ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) == 0 && gz->type->test_select)) {
253 
254         if (!wm_gizmo_keymap_uses_event_modifier(
255                 wm, gzgroup, gz, event_modifier, &gzgroup_keymap_uses_modifier)) {
256           continue;
257         }
258 
259         BLI_buffer_append(visible_gizmos, wmGizmo *, gz);
260       }
261     }
262   }
263 }
264 
WM_gizmogroup_ensure_init(const bContext * C,wmGizmoGroup * gzgroup)265 void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup)
266 {
267   /* prepare for first draw */
268   if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) {
269     gzgroup->type->setup(C, gzgroup);
270 
271     /* Not ideal, initialize keymap here, needed for RNA runtime generated gizmos. */
272     wmGizmoGroupType *gzgt = gzgroup->type;
273     if (gzgt->keymap == NULL) {
274       wmWindowManager *wm = CTX_wm_manager(C);
275       wm_gizmogrouptype_setup_keymap(gzgt, wm->defaultconf);
276       BLI_assert(gzgt->keymap != NULL);
277     }
278     gzgroup->init_flag |= WM_GIZMOGROUP_INIT_SETUP;
279   }
280 
281   /* Refresh may be called multiple times,
282    * this just ensures its called at least once before we draw. */
283   if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_REFRESH) == 0)) {
284     /* Clear the flag before calling refresh so the callback
285      * can postpone the refresh by clearing this flag. */
286     gzgroup->init_flag |= WM_GIZMOGROUP_INIT_REFRESH;
287     WM_gizmo_group_refresh(C, gzgroup);
288   }
289 }
290 
WM_gizmo_group_remove_by_tool(bContext * C,Main * bmain,const wmGizmoGroupType * gzgt,const bToolRef * tref)291 void WM_gizmo_group_remove_by_tool(bContext *C,
292                                    Main *bmain,
293                                    const wmGizmoGroupType *gzgt,
294                                    const bToolRef *tref)
295 {
296   wmGizmoMapType *gzmap_type = WM_gizmomaptype_find(&gzgt->gzmap_params);
297   for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
298     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
299       if (area->runtime.tool == tref) {
300         LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
301           wmGizmoMap *gzmap = region->gizmo_map;
302           if (gzmap && gzmap->type == gzmap_type) {
303             wmGizmoGroup *gzgroup, *gzgroup_next;
304             for (gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup_next) {
305               gzgroup_next = gzgroup->next;
306               if (gzgroup->type == gzgt) {
307                 BLI_assert(gzgroup->parent_gzmap == gzmap);
308                 wm_gizmogroup_free(C, gzgroup);
309                 ED_region_tag_redraw_editor_overlays(region);
310               }
311             }
312           }
313         }
314       }
315     }
316   }
317 }
318 
wm_gizmogroup_is_visible_in_drawstep(const wmGizmoGroup * gzgroup,const eWM_GizmoFlagMapDrawStep drawstep)319 bool wm_gizmogroup_is_visible_in_drawstep(const wmGizmoGroup *gzgroup,
320                                           const eWM_GizmoFlagMapDrawStep drawstep)
321 {
322   switch (drawstep) {
323     case WM_GIZMOMAP_DRAWSTEP_2D:
324       return (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) == 0;
325     case WM_GIZMOMAP_DRAWSTEP_3D:
326       return (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D);
327     default:
328       BLI_assert(0);
329       return false;
330   }
331 }
332 
wm_gizmogroup_is_any_selected(const wmGizmoGroup * gzgroup)333 bool wm_gizmogroup_is_any_selected(const wmGizmoGroup *gzgroup)
334 {
335   if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_SELECT) {
336     LISTBASE_FOREACH (const wmGizmo *, gz, &gzgroup->gizmos) {
337       if (gz->state & WM_GIZMO_STATE_SELECT) {
338         return true;
339       }
340     }
341   }
342   return false;
343 }
344 
345 /** \} */
346 
347 /** \name Gizmo operators
348  *
349  * Basic operators for gizmo interaction with user configurable keymaps.
350  *
351  * \{ */
352 
gizmo_select_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))353 static int gizmo_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
354 {
355   ARegion *region = CTX_wm_region(C);
356   wmGizmoMap *gzmap = region->gizmo_map;
357   wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
358   wmGizmo *highlight = gzmap->gzmap_context.highlight;
359 
360   bool extend = RNA_boolean_get(op->ptr, "extend");
361   bool deselect = RNA_boolean_get(op->ptr, "deselect");
362   bool toggle = RNA_boolean_get(op->ptr, "toggle");
363 
364   /* deselect all first */
365   if (extend == false && deselect == false && toggle == false) {
366     wm_gizmomap_deselect_all(gzmap);
367     BLI_assert(msel->items == NULL && msel->len == 0);
368     UNUSED_VARS_NDEBUG(msel);
369   }
370 
371   if (highlight) {
372     const bool is_selected = (highlight->state & WM_GIZMO_STATE_SELECT);
373     bool redraw = false;
374 
375     if (toggle) {
376       /* toggle: deselect if already selected, else select */
377       deselect = is_selected;
378     }
379 
380     if (deselect) {
381       if (is_selected && WM_gizmo_select_set(gzmap, highlight, false)) {
382         redraw = true;
383       }
384     }
385     else if (wm_gizmo_select_and_highlight(C, gzmap, highlight)) {
386       redraw = true;
387     }
388 
389     if (redraw) {
390       ED_region_tag_redraw_editor_overlays(region);
391     }
392 
393     return OPERATOR_FINISHED;
394   }
395 
396   BLI_assert(0);
397   return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
398 }
399 
GIZMOGROUP_OT_gizmo_select(wmOperatorType * ot)400 void GIZMOGROUP_OT_gizmo_select(wmOperatorType *ot)
401 {
402   /* identifiers */
403   ot->name = "Gizmo Select";
404   ot->description = "Select the currently highlighted gizmo";
405   ot->idname = "GIZMOGROUP_OT_gizmo_select";
406 
407   /* api callbacks */
408   ot->invoke = gizmo_select_invoke;
409 
410   ot->flag = OPTYPE_UNDO;
411 
412   WM_operator_properties_mouse_select(ot);
413 }
414 
415 typedef struct GizmoTweakData {
416   wmGizmoMap *gzmap;
417   wmGizmoGroup *gzgroup;
418   wmGizmo *gz_modal;
419 
420   int init_event; /* initial event type */
421   int flag;       /* tweak flags */
422 
423 } GizmoTweakData;
424 
gizmo_tweak_start(bContext * C,wmGizmoMap * gzmap,wmGizmo * gz,const wmEvent * event)425 static bool gizmo_tweak_start(bContext *C, wmGizmoMap *gzmap, wmGizmo *gz, const wmEvent *event)
426 {
427   /* activate highlighted gizmo */
428   wm_gizmomap_modal_set(gzmap, C, gz, event, true);
429 
430   return (gz->state & WM_GIZMO_STATE_MODAL);
431 }
432 
gizmo_tweak_start_and_finish(bContext * C,wmGizmoMap * gzmap,wmGizmo * gz,const wmEvent * event,bool * r_is_modal)433 static bool gizmo_tweak_start_and_finish(
434     bContext *C, wmGizmoMap *gzmap, wmGizmo *gz, const wmEvent *event, bool *r_is_modal)
435 {
436   wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, gz->highlight_part);
437   if (r_is_modal) {
438     *r_is_modal = false;
439   }
440   if (gzop && gzop->type) {
441 
442     /* Undo/Redo */
443     if (gzop->is_redo) {
444       wmWindowManager *wm = CTX_wm_manager(C);
445       wmOperator *op = WM_operator_last_redo(C);
446 
447       /* We may want to enable this, for now the gizmo can manage its own properties. */
448 #if 0
449       IDP_MergeGroup(gzop->ptr.data, op->properties, false);
450 #endif
451 
452       WM_operator_free_all_after(wm, op);
453       ED_undo_pop_op(C, op);
454     }
455 
456     /* XXX temporary workaround for modal gizmo operator
457      * conflicting with modal operator attached to gizmo */
458     if (gzop->type->modal) {
459       /* activate highlighted gizmo */
460       wm_gizmomap_modal_set(gzmap, C, gz, event, true);
461       if (r_is_modal) {
462         *r_is_modal = true;
463       }
464     }
465     else {
466       if (gz->parent_gzgroup->type->invoke_prepare) {
467         gz->parent_gzgroup->type->invoke_prepare(C, gz->parent_gzgroup, gz, event);
468       }
469       /* Allow for 'button' gizmos, single click to run an action. */
470       WM_gizmo_operator_invoke(C, gz, gzop);
471     }
472     return true;
473   }
474   return false;
475 }
476 
gizmo_tweak_finish(bContext * C,wmOperator * op,const bool cancel,bool clear_modal)477 static void gizmo_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
478 {
479   GizmoTweakData *mtweak = op->customdata;
480   if (mtweak->gz_modal->type->exit) {
481     mtweak->gz_modal->type->exit(C, mtweak->gz_modal, cancel);
482   }
483   if (clear_modal) {
484     /* The gizmo may have been removed. */
485     if ((BLI_findindex(&mtweak->gzmap->groups, mtweak->gzgroup) != -1) &&
486         (BLI_findindex(&mtweak->gzgroup->gizmos, mtweak->gz_modal) != -1)) {
487       wm_gizmomap_modal_set(mtweak->gzmap, C, mtweak->gz_modal, NULL, false);
488     }
489   }
490   MEM_freeN(mtweak);
491 }
492 
gizmo_tweak_modal(bContext * C,wmOperator * op,const wmEvent * event)493 static int gizmo_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
494 {
495   GizmoTweakData *mtweak = op->customdata;
496   wmGizmo *gz = mtweak->gz_modal;
497   int retval = OPERATOR_PASS_THROUGH;
498   bool clear_modal = true;
499 
500   if (gz == NULL) {
501     BLI_assert(0);
502     return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
503   }
504 
505   if (retval == OPERATOR_FINISHED) {
506     /* pass */
507   }
508   else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
509     retval = OPERATOR_FINISHED;
510   }
511   else if (event->type == EVT_MODAL_MAP) {
512     switch (event->val) {
513       case TWEAK_MODAL_CANCEL:
514         retval = OPERATOR_CANCELLED;
515         break;
516       case TWEAK_MODAL_CONFIRM:
517         retval = OPERATOR_FINISHED;
518         break;
519       case TWEAK_MODAL_PRECISION_ON:
520         mtweak->flag |= WM_GIZMO_TWEAK_PRECISE;
521         break;
522       case TWEAK_MODAL_PRECISION_OFF:
523         mtweak->flag &= ~WM_GIZMO_TWEAK_PRECISE;
524         break;
525 
526       case TWEAK_MODAL_SNAP_ON:
527         mtweak->flag |= WM_GIZMO_TWEAK_SNAP;
528         break;
529       case TWEAK_MODAL_SNAP_OFF:
530         mtweak->flag &= ~WM_GIZMO_TWEAK_SNAP;
531         break;
532     }
533   }
534 
535   if (retval != OPERATOR_PASS_THROUGH) {
536     gizmo_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
537     return retval;
538   }
539 
540   /* handle gizmo */
541   wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
542   if (modal_fn) {
543     /* Ugly hack to ensure Python won't get 'EVT_MODAL_MAP' which isn't supported, see T73727.
544      * note that we could move away from wrapping modal gizmos in a modal operator,
545      * since it's causing the need for code like this. */
546     wmEvent *evil_event = (wmEvent *)event;
547     short event_modal_val = 0;
548 
549     if (event->type == EVT_MODAL_MAP) {
550       event_modal_val = evil_event->val;
551       evil_event->type = evil_event->prevtype;
552       evil_event->val = evil_event->prevval;
553     }
554 
555     int modal_retval = modal_fn(C, gz, event, mtweak->flag);
556 
557     if (event_modal_val != 0) {
558       evil_event->type = EVT_MODAL_MAP;
559       evil_event->val = event_modal_val;
560     }
561 
562     if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
563       gizmo_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
564       return OPERATOR_FINISHED;
565     }
566 
567     /* Ugly hack to send gizmo events */
568     evil_event->type = EVT_GIZMO_UPDATE;
569   }
570 
571   /* always return PASS_THROUGH so modal handlers
572    * with gizmos attached can update */
573   BLI_assert(retval == OPERATOR_PASS_THROUGH);
574   return OPERATOR_PASS_THROUGH;
575 }
576 
gizmo_tweak_invoke(bContext * C,wmOperator * op,const wmEvent * event)577 static int gizmo_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
578 {
579   ARegion *region = CTX_wm_region(C);
580   wmGizmoMap *gzmap = region->gizmo_map;
581   wmGizmo *gz = gzmap->gzmap_context.highlight;
582 
583   /* Needed for single click actions which don't enter modal state. */
584   WM_tooltip_clear(C, CTX_wm_window(C));
585 
586   if (!gz) {
587     /* wm_handlers_do_intern shouldn't let this happen */
588     BLI_assert(0);
589     return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
590   }
591 
592   const int highlight_part_init = gz->highlight_part;
593 
594   if (gz->drag_part != -1) {
595     if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
596       gz->highlight_part = gz->drag_part;
597     }
598   }
599 
600   if (gizmo_tweak_start_and_finish(C, gzmap, gz, event, NULL)) {
601     return OPERATOR_FINISHED;
602   }
603 
604   if (!gizmo_tweak_start(C, gzmap, gz, event)) {
605     /* failed to start */
606     gz->highlight_part = highlight_part_init;
607     return OPERATOR_PASS_THROUGH;
608   }
609 
610   GizmoTweakData *mtweak = MEM_mallocN(sizeof(GizmoTweakData), __func__);
611 
612   mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
613   mtweak->gz_modal = gzmap->gzmap_context.highlight;
614   mtweak->gzgroup = mtweak->gz_modal->parent_gzgroup;
615   mtweak->gzmap = gzmap;
616   mtweak->flag = 0;
617 
618   op->customdata = mtweak;
619 
620   WM_event_add_modal_handler(C, op);
621 
622   return OPERATOR_RUNNING_MODAL;
623 }
624 
GIZMOGROUP_OT_gizmo_tweak(wmOperatorType * ot)625 void GIZMOGROUP_OT_gizmo_tweak(wmOperatorType *ot)
626 {
627   /* identifiers */
628   ot->name = "Gizmo Tweak";
629   ot->description = "Tweak the active gizmo";
630   ot->idname = "GIZMOGROUP_OT_gizmo_tweak";
631 
632   /* api callbacks */
633   ot->invoke = gizmo_tweak_invoke;
634   ot->modal = gizmo_tweak_modal;
635 
636   /* TODO(campbell): This causes problems tweaking settings for operators,
637    * need to find a way to support this. */
638 #if 0
639   ot->flag = OPTYPE_UNDO;
640 #endif
641 }
642 
643 /** \} */
644 
wm_gizmogroup_tweak_modal_keymap(wmKeyConfig * keyconf)645 wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf)
646 {
647   wmKeyMap *keymap;
648   char name[KMAP_MAX_NAME];
649 
650   static EnumPropertyItem modal_items[] = {
651       {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
652       {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
653       {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""},
654       {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""},
655       {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""},
656       {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""},
657       {0, NULL, 0, NULL, NULL},
658   };
659 
660   STRNCPY(name, "Generic Gizmo Tweak Modal Map");
661   keymap = WM_modalkeymap_find(keyconf, name);
662 
663   /* this function is called for each spacetype, only needs to add map once */
664   if (keymap && keymap->modal_items) {
665     return NULL;
666   }
667 
668   keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
669 
670   /* items for modal map */
671   WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
672   WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
673 
674   WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
675   WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
676 
677   WM_modalkeymap_add_item(
678       keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
679   WM_modalkeymap_add_item(
680       keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
681   WM_modalkeymap_add_item(keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
682   WM_modalkeymap_add_item(
683       keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
684 
685   WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
686   WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
687   WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
688   WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
689 
690   WM_modalkeymap_assign(keymap, "GIZMOGROUP_OT_gizmo_tweak");
691 
692   return keymap;
693 }
694 
695 /** \} */ /* wmGizmoGroup */
696 
697 /* -------------------------------------------------------------------- */
698 /** \name wmGizmoGroup (Key-map callbacks)
699  *
700  * \{ */
701 
WM_gizmogroup_setup_keymap_generic(const wmGizmoGroupType * UNUSED (gzgt),wmKeyConfig * kc)702 wmKeyMap *WM_gizmogroup_setup_keymap_generic(const wmGizmoGroupType *UNUSED(gzgt), wmKeyConfig *kc)
703 {
704   return WM_gizmo_keymap_generic_with_keyconfig(kc);
705 }
706 
WM_gizmogroup_setup_keymap_generic_drag(const wmGizmoGroupType * UNUSED (gzgt),wmKeyConfig * kc)707 wmKeyMap *WM_gizmogroup_setup_keymap_generic_drag(const wmGizmoGroupType *UNUSED(gzgt),
708                                                   wmKeyConfig *kc)
709 {
710   return WM_gizmo_keymap_generic_drag_with_keyconfig(kc);
711 }
712 
WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType * UNUSED (gzgt),wmKeyConfig * kc)713 wmKeyMap *WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType *UNUSED(gzgt),
714                                                         wmKeyConfig *kc)
715 {
716   return WM_gizmo_keymap_generic_maybe_drag_with_keyconfig(kc);
717 }
718 
719 /**
720  * Variation of #WM_gizmogroup_keymap_common but with keymap items for selection
721  *
722  * TODO(campbell): move to Python.
723  *
724  * \param name: Typically #wmGizmoGroupType.name
725  * \param params: Typically #wmGizmoGroupType.gzmap_params
726  */
WM_gizmogroup_keymap_template_select_ex(wmKeyConfig * kc,const char * name,const struct wmGizmoMapType_Params * params)727 static wmKeyMap *WM_gizmogroup_keymap_template_select_ex(
728     wmKeyConfig *kc, const char *name, const struct wmGizmoMapType_Params *params)
729 {
730   /* Use area and region id since we might have multiple gizmos
731    * with the same name in different areas/regions. */
732   wmKeyMap *km = WM_keymap_ensure(kc, name, params->spaceid, params->regionid);
733   const bool do_init = BLI_listbase_is_empty(&km->items);
734 
735   /* FIXME(campbell) */
736 #if 0
737   const int select_mouse = (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE;
738   const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R;
739   const int action_mouse = (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE;
740 #else
741   const int select_mouse = RIGHTMOUSE;
742   const int select_tweak = EVT_TWEAK_R;
743   const int action_mouse = LEFTMOUSE;
744 #endif
745 
746   if (do_init) {
747     WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, KM_PRESS, KM_ANY, 0);
748     WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, KM_ANY, 0, 0);
749   }
750 
751   if (do_init) {
752     wmKeyMapItem *kmi = WM_keymap_add_item(
753         km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, 0, 0);
754     RNA_boolean_set(kmi->ptr, "extend", false);
755     RNA_boolean_set(kmi->ptr, "deselect", false);
756     RNA_boolean_set(kmi->ptr, "toggle", false);
757     kmi = WM_keymap_add_item(
758         km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, KM_SHIFT, 0);
759     RNA_boolean_set(kmi->ptr, "extend", false);
760     RNA_boolean_set(kmi->ptr, "deselect", false);
761     RNA_boolean_set(kmi->ptr, "toggle", true);
762   }
763 
764   return km;
765 }
766 
WM_gizmogroup_setup_keymap_generic_select(const wmGizmoGroupType * UNUSED (gzgt),wmKeyConfig * kc)767 wmKeyMap *WM_gizmogroup_setup_keymap_generic_select(const wmGizmoGroupType *UNUSED(gzgt),
768                                                     wmKeyConfig *kc)
769 {
770   struct wmGizmoMapType_Params params = {
771       .spaceid = SPACE_EMPTY,
772       .regionid = RGN_TYPE_WINDOW,
773   };
774   return WM_gizmogroup_keymap_template_select_ex(kc, "Generic Gizmo Select", &params);
775 }
776 
777 /* -------------------------------------------------------------------- */
778 /** \name wmGizmo (Key-map access)
779  *
780  * Key config version so these can be called from #wmGizmoGroupFnSetupKeymap.
781  *
782  * \{ */
783 
WM_gizmo_keymap_generic_with_keyconfig(wmKeyConfig * kc)784 struct wmKeyMap *WM_gizmo_keymap_generic_with_keyconfig(wmKeyConfig *kc)
785 {
786   const char *idname = "Generic Gizmo";
787   return WM_keymap_ensure(kc, idname, SPACE_EMPTY, RGN_TYPE_WINDOW);
788 }
WM_gizmo_keymap_generic(wmWindowManager * wm)789 struct wmKeyMap *WM_gizmo_keymap_generic(wmWindowManager *wm)
790 {
791   return WM_gizmo_keymap_generic_with_keyconfig(wm->defaultconf);
792 }
793 
WM_gizmo_keymap_generic_select_with_keyconfig(wmKeyConfig * kc)794 struct wmKeyMap *WM_gizmo_keymap_generic_select_with_keyconfig(wmKeyConfig *kc)
795 {
796   const char *idname = "Generic Gizmo Select";
797   return WM_keymap_ensure(kc, idname, SPACE_EMPTY, RGN_TYPE_WINDOW);
798 }
WM_gizmo_keymap_generic_select(wmWindowManager * wm)799 struct wmKeyMap *WM_gizmo_keymap_generic_select(wmWindowManager *wm)
800 {
801   return WM_gizmo_keymap_generic_select_with_keyconfig(wm->defaultconf);
802 }
803 
WM_gizmo_keymap_generic_drag_with_keyconfig(wmKeyConfig * kc)804 struct wmKeyMap *WM_gizmo_keymap_generic_drag_with_keyconfig(wmKeyConfig *kc)
805 {
806   const char *idname = "Generic Gizmo Drag";
807   return WM_keymap_ensure(kc, idname, SPACE_EMPTY, RGN_TYPE_WINDOW);
808 }
WM_gizmo_keymap_generic_drag(wmWindowManager * wm)809 struct wmKeyMap *WM_gizmo_keymap_generic_drag(wmWindowManager *wm)
810 {
811   return WM_gizmo_keymap_generic_drag_with_keyconfig(wm->defaultconf);
812 }
813 
WM_gizmo_keymap_generic_click_drag_with_keyconfig(wmKeyConfig * kc)814 struct wmKeyMap *WM_gizmo_keymap_generic_click_drag_with_keyconfig(wmKeyConfig *kc)
815 {
816   const char *idname = "Generic Gizmo Click Drag";
817   return WM_keymap_ensure(kc, idname, SPACE_EMPTY, RGN_TYPE_WINDOW);
818 }
WM_gizmo_keymap_generic_click_drag(wmWindowManager * wm)819 struct wmKeyMap *WM_gizmo_keymap_generic_click_drag(wmWindowManager *wm)
820 {
821   return WM_gizmo_keymap_generic_click_drag_with_keyconfig(wm->defaultconf);
822 }
823 
824 /** Drag or press depending on preference. */
WM_gizmo_keymap_generic_maybe_drag_with_keyconfig(wmKeyConfig * kc)825 struct wmKeyMap *WM_gizmo_keymap_generic_maybe_drag_with_keyconfig(wmKeyConfig *kc)
826 {
827   const char *idname = "Generic Gizmo Maybe Drag";
828   return WM_keymap_ensure(kc, idname, SPACE_EMPTY, RGN_TYPE_WINDOW);
829 }
WM_gizmo_keymap_generic_maybe_drag(wmWindowManager * wm)830 struct wmKeyMap *WM_gizmo_keymap_generic_maybe_drag(wmWindowManager *wm)
831 {
832   return WM_gizmo_keymap_generic_maybe_drag_with_keyconfig(wm->defaultconf);
833 }
834 
835 /** \} */
836 
837 /* -------------------------------------------------------------------- */
838 /** \name wmGizmoGroupType
839  *
840  * \{ */
841 
WM_gizmomaptype_group_find_ptr(struct wmGizmoMapType * gzmap_type,const wmGizmoGroupType * gzgt)842 struct wmGizmoGroupTypeRef *WM_gizmomaptype_group_find_ptr(struct wmGizmoMapType *gzmap_type,
843                                                            const wmGizmoGroupType *gzgt)
844 {
845   /* could use hash lookups as operator types do, for now simple search. */
846   LISTBASE_FOREACH (wmGizmoGroupTypeRef *, gzgt_ref, &gzmap_type->grouptype_refs) {
847     if (gzgt_ref->type == gzgt) {
848       return gzgt_ref;
849     }
850   }
851   return NULL;
852 }
853 
WM_gizmomaptype_group_find(struct wmGizmoMapType * gzmap_type,const char * idname)854 struct wmGizmoGroupTypeRef *WM_gizmomaptype_group_find(struct wmGizmoMapType *gzmap_type,
855                                                        const char *idname)
856 {
857   /* could use hash lookups as operator types do, for now simple search. */
858   LISTBASE_FOREACH (wmGizmoGroupTypeRef *, gzgt_ref, &gzmap_type->grouptype_refs) {
859     if (STREQ(idname, gzgt_ref->type->idname)) {
860       return gzgt_ref;
861     }
862   }
863   return NULL;
864 }
865 
866 /**
867  * Use this for registering gizmos on startup.
868  * For runtime, use #WM_gizmomaptype_group_link_runtime.
869  */
WM_gizmomaptype_group_link(wmGizmoMapType * gzmap_type,const char * idname)870 wmGizmoGroupTypeRef *WM_gizmomaptype_group_link(wmGizmoMapType *gzmap_type, const char *idname)
871 {
872   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
873   BLI_assert(gzgt != NULL);
874   return WM_gizmomaptype_group_link_ptr(gzmap_type, gzgt);
875 }
876 
WM_gizmomaptype_group_link_ptr(wmGizmoMapType * gzmap_type,wmGizmoGroupType * gzgt)877 wmGizmoGroupTypeRef *WM_gizmomaptype_group_link_ptr(wmGizmoMapType *gzmap_type,
878                                                     wmGizmoGroupType *gzgt)
879 {
880   wmGizmoGroupTypeRef *gzgt_ref = MEM_callocN(sizeof(wmGizmoGroupTypeRef), "gizmo-group-ref");
881   gzgt_ref->type = gzgt;
882   BLI_addtail(&gzmap_type->grouptype_refs, gzgt_ref);
883   return gzgt_ref;
884 }
885 
WM_gizmomaptype_group_init_runtime_keymap(const Main * bmain,wmGizmoGroupType * gzgt)886 void WM_gizmomaptype_group_init_runtime_keymap(const Main *bmain, wmGizmoGroupType *gzgt)
887 {
888   /* init keymap - on startup there's an extra call to init keymaps for 'permanent' gizmo-groups */
889   wm_gizmogrouptype_setup_keymap(gzgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
890 }
891 
WM_gizmomaptype_group_init_runtime(const Main * bmain,wmGizmoMapType * gzmap_type,wmGizmoGroupType * gzgt)892 void WM_gizmomaptype_group_init_runtime(const Main *bmain,
893                                         wmGizmoMapType *gzmap_type,
894                                         wmGizmoGroupType *gzgt)
895 {
896   /* Tools add themselves. */
897   if (gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) {
898     return;
899   }
900 
901   /* now create a gizmo for all existing areas */
902   for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
903     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
904       LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
905         ListBase *lb = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
906         LISTBASE_FOREACH (ARegion *, region, lb) {
907           wmGizmoMap *gzmap = region->gizmo_map;
908           if (gzmap && gzmap->type == gzmap_type) {
909             WM_gizmomaptype_group_init_runtime_with_region(gzmap_type, gzgt, region);
910           }
911         }
912       }
913     }
914   }
915 }
916 
WM_gizmomaptype_group_init_runtime_with_region(wmGizmoMapType * gzmap_type,wmGizmoGroupType * gzgt,ARegion * region)917 wmGizmoGroup *WM_gizmomaptype_group_init_runtime_with_region(wmGizmoMapType *gzmap_type,
918                                                              wmGizmoGroupType *gzgt,
919                                                              ARegion *region)
920 {
921   wmGizmoMap *gzmap = region->gizmo_map;
922   BLI_assert(gzmap && gzmap->type == gzmap_type);
923   UNUSED_VARS_NDEBUG(gzmap_type);
924 
925   wmGizmoGroup *gzgroup = wm_gizmogroup_new_from_type(gzmap, gzgt);
926 
927   /* Don't allow duplicates when switching modes for e.g. see: T66229. */
928   LISTBASE_FOREACH (wmGizmoGroup *, gzgroup_iter, &gzmap->groups) {
929     if (gzgroup_iter->type == gzgt) {
930       if (gzgroup_iter != gzgroup) {
931         WM_gizmo_group_tag_remove(gzgroup_iter);
932       }
933     }
934   }
935 
936   wm_gizmomap_highlight_set(gzmap, NULL, NULL, 0);
937 
938   ED_region_tag_redraw_editor_overlays(region);
939 
940   return gzgroup;
941 }
942 
943 /**
944  * Unlike #WM_gizmomaptype_group_unlink this doesn't maintain correct state, simply free.
945  */
WM_gizmomaptype_group_free(wmGizmoGroupTypeRef * gzgt_ref)946 void WM_gizmomaptype_group_free(wmGizmoGroupTypeRef *gzgt_ref)
947 {
948   MEM_freeN(gzgt_ref);
949 }
950 
WM_gizmomaptype_group_unlink(bContext * C,Main * bmain,wmGizmoMapType * gzmap_type,const wmGizmoGroupType * gzgt)951 void WM_gizmomaptype_group_unlink(bContext *C,
952                                   Main *bmain,
953                                   wmGizmoMapType *gzmap_type,
954                                   const wmGizmoGroupType *gzgt)
955 {
956   /* Free instances. */
957   for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
958     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
959       LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
960         ListBase *lb = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
961         LISTBASE_FOREACH (ARegion *, region, lb) {
962           wmGizmoMap *gzmap = region->gizmo_map;
963           if (gzmap && gzmap->type == gzmap_type) {
964             wmGizmoGroup *gzgroup, *gzgroup_next;
965             for (gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup_next) {
966               gzgroup_next = gzgroup->next;
967               if (gzgroup->type == gzgt) {
968                 BLI_assert(gzgroup->parent_gzmap == gzmap);
969                 wm_gizmogroup_free(C, gzgroup);
970                 ED_region_tag_redraw_editor_overlays(region);
971               }
972             }
973           }
974         }
975       }
976     }
977   }
978 
979   /* Free types. */
980   wmGizmoGroupTypeRef *gzgt_ref = WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt);
981   if (gzgt_ref) {
982     BLI_remlink(&gzmap_type->grouptype_refs, gzgt_ref);
983     WM_gizmomaptype_group_free(gzgt_ref);
984   }
985 
986   /* TODO(campbell): Gizmos may share keymaps, for now don't
987    * remove however we could flag them as temporary/owned by the gizmo. */
988 #if 0
989   /* Note, we may want to keep this keymap for editing */
990   WM_keymap_remove(gzgt->keyconf, gzgt->keymap);
991 #endif
992 
993   BLI_assert(WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt) == NULL);
994 }
995 
wm_gizmogrouptype_setup_keymap(wmGizmoGroupType * gzgt,wmKeyConfig * keyconf)996 void wm_gizmogrouptype_setup_keymap(wmGizmoGroupType *gzgt, wmKeyConfig *keyconf)
997 {
998   /* Use flag since setup_keymap may return NULL,
999    * in that case we better not keep calling it. */
1000   if (gzgt->type_update_flag & WM_GIZMOMAPTYPE_KEYMAP_INIT) {
1001     gzgt->keymap = gzgt->setup_keymap(gzgt, keyconf);
1002     gzgt->keyconf = keyconf;
1003     gzgt->type_update_flag &= ~WM_GIZMOMAPTYPE_KEYMAP_INIT;
1004   }
1005 }
1006 
1007 /** \} */ /* wmGizmoGroupType */
1008 
1009 /* -------------------------------------------------------------------- */
1010 /** \name High Level Add/Remove API
1011  *
1012  * For use directly from operators & RNA registration.
1013  *
1014  * \note In context of gizmo API these names are a bit misleading,
1015  * but for general use terms its OK.
1016  * `WM_gizmo_group_type_add` would be more correctly called:
1017  * `WM_gizmomaptype_grouptype_reference_link`
1018  * but for general purpose API this is too detailed & annoying.
1019  *
1020  * \note We may want to return a value if there is nothing to remove.
1021  *
1022  * \{ */
1023 
WM_gizmo_group_type_add_ptr_ex(wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type)1024 void WM_gizmo_group_type_add_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type)
1025 {
1026   WM_gizmomaptype_group_link_ptr(gzmap_type, gzgt);
1027 
1028   WM_gizmoconfig_update_tag_group_type_init(gzmap_type, gzgt);
1029 }
WM_gizmo_group_type_add_ptr(wmGizmoGroupType * gzgt)1030 void WM_gizmo_group_type_add_ptr(wmGizmoGroupType *gzgt)
1031 {
1032   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
1033   WM_gizmo_group_type_add_ptr_ex(gzgt, gzmap_type);
1034 }
WM_gizmo_group_type_add(const char * idname)1035 void WM_gizmo_group_type_add(const char *idname)
1036 {
1037   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
1038   BLI_assert(gzgt != NULL);
1039   WM_gizmo_group_type_add_ptr(gzgt);
1040 }
1041 
WM_gizmo_group_type_ensure_ptr_ex(wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type)1042 bool WM_gizmo_group_type_ensure_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type)
1043 {
1044   wmGizmoGroupTypeRef *gzgt_ref = WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt);
1045   if (gzgt_ref == NULL) {
1046     WM_gizmo_group_type_add_ptr_ex(gzgt, gzmap_type);
1047     return true;
1048   }
1049   return false;
1050 }
WM_gizmo_group_type_ensure_ptr(wmGizmoGroupType * gzgt)1051 bool WM_gizmo_group_type_ensure_ptr(wmGizmoGroupType *gzgt)
1052 {
1053   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
1054   return WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
1055 }
WM_gizmo_group_type_ensure(const char * idname)1056 bool WM_gizmo_group_type_ensure(const char *idname)
1057 {
1058   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
1059   BLI_assert(gzgt != NULL);
1060   return WM_gizmo_group_type_ensure_ptr(gzgt);
1061 }
1062 
WM_gizmo_group_type_remove_ptr_ex(struct Main * bmain,wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type)1063 void WM_gizmo_group_type_remove_ptr_ex(struct Main *bmain,
1064                                        wmGizmoGroupType *gzgt,
1065                                        wmGizmoMapType *gzmap_type)
1066 {
1067   WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
1068   WM_gizmogrouptype_free_ptr(gzgt);
1069 }
WM_gizmo_group_type_remove_ptr(struct Main * bmain,wmGizmoGroupType * gzgt)1070 void WM_gizmo_group_type_remove_ptr(struct Main *bmain, wmGizmoGroupType *gzgt)
1071 {
1072   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
1073   WM_gizmo_group_type_remove_ptr_ex(bmain, gzgt, gzmap_type);
1074 }
WM_gizmo_group_type_remove(struct Main * bmain,const char * idname)1075 void WM_gizmo_group_type_remove(struct Main *bmain, const char *idname)
1076 {
1077   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
1078   BLI_assert(gzgt != NULL);
1079   WM_gizmo_group_type_remove_ptr(bmain, gzgt);
1080 }
1081 
WM_gizmo_group_type_reinit_ptr_ex(struct Main * bmain,wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type)1082 void WM_gizmo_group_type_reinit_ptr_ex(struct Main *bmain,
1083                                        wmGizmoGroupType *gzgt,
1084                                        wmGizmoMapType *gzmap_type)
1085 {
1086   wmGizmoGroupTypeRef *gzgt_ref = WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt);
1087   BLI_assert(gzgt_ref != NULL);
1088   UNUSED_VARS_NDEBUG(gzgt_ref);
1089   WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
1090   WM_gizmo_group_type_add_ptr_ex(gzgt, gzmap_type);
1091 }
WM_gizmo_group_type_reinit_ptr(struct Main * bmain,wmGizmoGroupType * gzgt)1092 void WM_gizmo_group_type_reinit_ptr(struct Main *bmain, wmGizmoGroupType *gzgt)
1093 {
1094   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
1095   WM_gizmo_group_type_reinit_ptr_ex(bmain, gzgt, gzmap_type);
1096 }
WM_gizmo_group_type_reinit(struct Main * bmain,const char * idname)1097 void WM_gizmo_group_type_reinit(struct Main *bmain, const char *idname)
1098 {
1099   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
1100   BLI_assert(gzgt != NULL);
1101   WM_gizmo_group_type_reinit_ptr(bmain, gzgt);
1102 }
1103 
1104 /* delayed versions */
1105 
WM_gizmo_group_type_unlink_delayed_ptr_ex(wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type)1106 void WM_gizmo_group_type_unlink_delayed_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type)
1107 {
1108   WM_gizmoconfig_update_tag_group_type_remove(gzmap_type, gzgt);
1109 }
1110 
WM_gizmo_group_type_unlink_delayed_ptr(wmGizmoGroupType * gzgt)1111 void WM_gizmo_group_type_unlink_delayed_ptr(wmGizmoGroupType *gzgt)
1112 {
1113   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
1114   WM_gizmo_group_type_unlink_delayed_ptr_ex(gzgt, gzmap_type);
1115 }
1116 
WM_gizmo_group_type_unlink_delayed(const char * idname)1117 void WM_gizmo_group_type_unlink_delayed(const char *idname)
1118 {
1119   wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
1120   BLI_assert(gzgt != NULL);
1121   WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
1122 }
1123 
WM_gizmo_group_unlink_delayed_ptr_from_space(wmGizmoGroupType * gzgt,wmGizmoMapType * gzmap_type,ScrArea * area)1124 void WM_gizmo_group_unlink_delayed_ptr_from_space(wmGizmoGroupType *gzgt,
1125                                                   wmGizmoMapType *gzmap_type,
1126                                                   ScrArea *area)
1127 {
1128   LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1129     wmGizmoMap *gzmap = region->gizmo_map;
1130     if (gzmap && gzmap->type == gzmap_type) {
1131       LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
1132         if (gzgroup->type == gzgt) {
1133           WM_gizmo_group_tag_remove(gzgroup);
1134         }
1135       }
1136     }
1137   }
1138 }
1139 
1140 /** \} */
1141 
1142 /* -------------------------------------------------------------------- */
1143 /** \name Gizmo Group Type Callback Wrappers
1144  *
1145  * \{ */
1146 
WM_gizmo_group_type_poll(const bContext * C,const wmGizmoGroupType * gzgt)1147 bool WM_gizmo_group_type_poll(const bContext *C, const wmGizmoGroupType *gzgt)
1148 {
1149   /* If we're tagged, only use compatible. */
1150   if (gzgt->owner_id[0] != '\0') {
1151     const WorkSpace *workspace = CTX_wm_workspace(C);
1152     if (BKE_workspace_owner_id_check(workspace, gzgt->owner_id) == false) {
1153       return false;
1154     }
1155   }
1156   /* Check for poll function, if gizmo-group belongs to an operator,
1157    * also check if the operator is running. */
1158   return (!gzgt->poll || gzgt->poll(C, (wmGizmoGroupType *)gzgt));
1159 }
1160 
WM_gizmo_group_refresh(const bContext * C,wmGizmoGroup * gzgroup)1161 void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1162 {
1163   const wmGizmoGroupType *gzgt = gzgroup->type;
1164   if (gzgt->flag & WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK) {
1165     wmGizmoMap *gzmap = gzgroup->parent_gzmap;
1166     wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
1167     if (!gz || gz->parent_gzgroup != gzgroup) {
1168       wmWindow *win = CTX_wm_window(C);
1169       ARegion *region = CTX_wm_region(C);
1170       BLI_assert(region->gizmo_map == gzmap);
1171       /* Check if the tweak event originated from this region. */
1172       if ((win->tweak != NULL) && BLI_rcti_compare(&region->winrct, &win->tweak->winrct)) {
1173         /* We need to run refresh again. */
1174         gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
1175         WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup));
1176         gzgroup->hide.delay_refresh_for_tweak = true;
1177         return;
1178       }
1179     }
1180     gzgroup->hide.delay_refresh_for_tweak = false;
1181   }
1182 
1183   if (gzgroup->hide.any) {
1184     return;
1185   }
1186 
1187   if (gzgt->refresh) {
1188     gzgt->refresh(C, gzgroup);
1189   }
1190 }
1191 
1192 /** \} */
1193