edf10afad8c832f5451c486e6c06aa327ab5bc43
[blender.git] / source / blender / windowmanager / manipulators / intern / wm_manipulator_group.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  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/windowmanager/manipulators/intern/wm_manipulator_group.c
27  *  \ingroup wm
28  *
29  * \name Manipulator-Group
30  *
31  * Manipulator-groups store and manage groups of manipulators. They can be
32  * attached to modal handlers and have own keymaps.
33  */
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_listbase.h"
41 #include "BLI_string.h"
42
43 #include "BKE_context.h"
44 #include "BKE_main.h"
45 #include "BKE_report.h"
46
47 #include "RNA_access.h"
48 #include "RNA_define.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52 #include "wm_event_system.h"
53
54 #include "ED_screen.h"
55
56 /* own includes */
57 #include "wm_manipulator_wmapi.h"
58 #include "wm_manipulator_intern.h"
59
60 #ifdef WITH_PYTHON
61 #  include "BPY_extern.h"
62 #endif
63
64 /* -------------------------------------------------------------------- */
65 /** \name wmManipulatorGroup
66  *
67  * \{ */
68
69 /**
70  * Create a new manipulator-group from \a wgt.
71  */
72 wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
73         wmManipulatorMap *mmap, wmManipulatorGroupType *wgt)
74 {
75         wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group");
76         mgroup->type = wgt;
77
78         /* keep back-link */
79         mgroup->parent_mmap = mmap;
80
81         BLI_addtail(&mmap->groups, mgroup);
82
83         return mgroup;
84 }
85
86 void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup)
87 {
88         wmManipulatorMap *mmap = mgroup->parent_mmap;
89
90         /* Similar to WM_manipulator_unlink, but only to keep mmap state correct,
91          * we don't want to run callbacks. */
92         if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) {
93                 wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
94         }
95         if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) {
96                 wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false);
97         }
98
99         for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
100                 mpr_next = mpr->next;
101                 if (mmap->mmap_context.select.len) {
102                         WM_manipulator_select_unlink(mmap, mpr);
103                 }
104                 WM_manipulator_free(mpr);
105         }
106         BLI_listbase_clear(&mgroup->manipulators);
107
108 #ifdef WITH_PYTHON
109         if (mgroup->py_instance) {
110                 /* do this first in case there are any __del__ functions or
111                  * similar that use properties */
112                 BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance);
113         }
114 #endif
115
116         if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) {
117                 BKE_reports_clear(mgroup->reports);
118                 MEM_freeN(mgroup->reports);
119         }
120
121         if (mgroup->customdata_free) {
122                 mgroup->customdata_free(mgroup->customdata);
123         }
124         else {
125                 MEM_SAFE_FREE(mgroup->customdata);
126         }
127
128         BLI_remlink(&mmap->groups, mgroup);
129
130         MEM_freeN(mgroup);
131 }
132
133 /**
134  * Add \a manipulator to \a mgroup and make sure its name is unique within the group.
135  */
136 void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
137 {
138         BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1);
139         BLI_addtail(&mgroup->manipulators, mpr);
140         mpr->parent_mgroup = mgroup;
141 }
142
143 wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator(
144         const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event,
145         int *r_part)
146 {
147         for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
148                 if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
149                         if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) {
150                                 return mpr;
151                         }
152                 }
153         }
154
155         return NULL;
156 }
157
158 /**
159  * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing!
160  */
161 void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase)
162 {
163         for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
164                 if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
165                         if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) ||
166                             ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select))
167                         {
168                                 BLI_addhead(listbase, BLI_genericNodeN(mpr));
169                         }
170                 }
171         }
172 }
173
174 void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C)
175 {
176         /* prepare for first draw */
177         if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) {
178                 mgroup->type->setup(C, mgroup);
179
180                 /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */
181                 wmManipulatorGroupType *wgt = mgroup->type;
182                 if (wgt->keymap == NULL) {
183                         wmWindowManager *wm = CTX_wm_manager(C);
184                         wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf);
185                         BLI_assert(wgt->keymap != NULL);
186                 }
187                 mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP;
188         }
189
190         /* refresh may be called multiple times, this just ensures its called at least once before we draw. */
191         if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) {
192                 if (mgroup->type->refresh) {
193                         mgroup->type->refresh(C, mgroup);
194                 }
195                 mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH;
196         }
197 }
198
199 bool wm_manipulatorgroup_is_visible(const wmManipulatorGroup *mgroup, const bContext *C)
200 {
201         /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */
202         return (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type));
203 }
204
205 bool wm_manipulatorgroup_is_visible_in_drawstep(
206         const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep)
207 {
208         switch (drawstep) {
209                 case WM_MANIPULATORMAP_DRAWSTEP_2D:
210                         return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0;
211                 case WM_MANIPULATORMAP_DRAWSTEP_3D:
212                         return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D);
213                 default:
214                         BLI_assert(0);
215                         return false;
216         }
217 }
218
219 bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup)
220 {
221         if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
222                 for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
223                         if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
224                                 return true;
225                         }
226                 }
227         }
228         return false;
229 }
230
231 /** \} */
232
233 /** \name Manipulator operators
234  *
235  * Basic operators for manipulator interaction with user configurable keymaps.
236  *
237  * \{ */
238
239 static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
240 {
241         ARegion *ar = CTX_wm_region(C);
242         wmManipulatorMap *mmap = ar->manipulator_map;
243         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
244         wmManipulator *highlight = mmap->mmap_context.highlight;
245
246         bool extend = RNA_boolean_get(op->ptr, "extend");
247         bool deselect = RNA_boolean_get(op->ptr, "deselect");
248         bool toggle = RNA_boolean_get(op->ptr, "toggle");
249
250         /* deselect all first */
251         if (extend == false && deselect == false && toggle == false) {
252                 wm_manipulatormap_deselect_all(mmap);
253                 BLI_assert(msel->items == NULL && msel->len == 0);
254                 UNUSED_VARS_NDEBUG(msel);
255         }
256
257         if (highlight) {
258                 const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT);
259                 bool redraw = false;
260
261                 if (toggle) {
262                         /* toggle: deselect if already selected, else select */
263                         deselect = is_selected;
264                 }
265
266                 if (deselect) {
267                         if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) {
268                                 redraw = true;
269                         }
270                 }
271                 else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) {
272                         redraw = true;
273                 }
274
275                 if (redraw) {
276                         ED_region_tag_redraw(ar);
277                 }
278
279                 return OPERATOR_FINISHED;
280         }
281         else {
282                 BLI_assert(0);
283                 return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
284         }
285
286         return OPERATOR_PASS_THROUGH;
287 }
288
289 void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot)
290 {
291         /* identifiers */
292         ot->name = "Manipulator Select";
293         ot->description = "Select the currently highlighted manipulator";
294         ot->idname = "MANIPULATORGROUP_OT_manipulator_select";
295
296         /* api callbacks */
297         ot->invoke = manipulator_select_invoke;
298
299         ot->flag = OPTYPE_UNDO;
300
301         WM_operator_properties_mouse_select(ot);
302 }
303
304 typedef struct ManipulatorTweakData {
305         wmManipulatorMap *mmap;
306         wmManipulator *mpr_modal;
307
308         int init_event; /* initial event type */
309         int flag;       /* tweak flags */
310 } ManipulatorTweakData;
311
312 static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel)
313 {
314         ManipulatorTweakData *mtweak = op->customdata;
315         if (mtweak->mpr_modal->type->exit) {
316                 mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel);
317         }
318         wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
319         MEM_freeN(mtweak);
320 }
321
322 static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
323 {
324         ManipulatorTweakData *mtweak = op->customdata;
325         wmManipulator *mpr = mtweak->mpr_modal;
326         int retval = OPERATOR_PASS_THROUGH;
327
328         if (mpr == NULL) {
329                 BLI_assert(0);
330                 return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
331         }
332
333         if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
334                 retval = OPERATOR_FINISHED;
335         }
336         else if (event->type == EVT_MODAL_MAP) {
337                 switch (event->val) {
338                         case TWEAK_MODAL_CANCEL:
339                                 retval = OPERATOR_CANCELLED;
340                                 break;
341                         case TWEAK_MODAL_CONFIRM:
342                                 retval = OPERATOR_FINISHED;
343                                 break;
344                         case TWEAK_MODAL_PRECISION_ON:
345                                 mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE;
346                                 break;
347                         case TWEAK_MODAL_PRECISION_OFF:
348                                 mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE;
349                                 break;
350
351                         case TWEAK_MODAL_SNAP_ON:
352                                 mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP;
353                                 break;
354                         case TWEAK_MODAL_SNAP_OFF:
355                                 mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP;
356                                 break;
357                 }
358         }
359
360         if (retval != OPERATOR_PASS_THROUGH) {
361                 manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED);
362                 return retval;
363         }
364
365         /* handle manipulator */
366         wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
367         if (modal_fn) {
368                 int modal_retval = modal_fn(C, mpr, event, mtweak->flag);
369
370                 if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
371                         manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0);
372                         return OPERATOR_FINISHED;
373                 }
374
375                 /* Ugly hack to send manipulator events */
376                 ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE;
377         }
378
379         /* always return PASS_THROUGH so modal handlers
380          * with manipulators attached can update */
381         BLI_assert(retval == OPERATOR_PASS_THROUGH);
382         return OPERATOR_PASS_THROUGH;
383 }
384
385 static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
386 {
387         ARegion *ar = CTX_wm_region(C);
388         wmManipulatorMap *mmap = ar->manipulator_map;
389         wmManipulator *mpr = mmap->mmap_context.highlight;
390
391         if (!mpr) {
392                 /* wm_handlers_do_intern shouldn't let this happen */
393                 BLI_assert(0);
394                 return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
395         }
396
397         wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
398
399         /* Allow for 'button' manipulators, single click to run an action. */
400         if (mpop && mpop->type) {
401                 if (mpop->type->modal == NULL) {
402                         WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
403                         return OPERATOR_FINISHED;
404                 }
405         }
406
407         /* activate highlighted manipulator */
408         wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
409
410         /* XXX temporary workaround for modal manipulator operator
411          * conflicting with modal operator attached to manipulator */
412         if (mpop && mpop->type) {
413                 if (mpop->type->modal) {
414                         return OPERATOR_FINISHED;
415                 }
416         }
417
418         /* Couldn't start the manipulator. */
419         if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) == 0) {
420                 return OPERATOR_PASS_THROUGH;
421         }
422
423         ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
424
425         mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
426         mtweak->mpr_modal = mmap->mmap_context.highlight;
427         mtweak->mmap = mmap;
428         mtweak->flag = 0;
429
430         op->customdata = mtweak;
431
432         WM_event_add_modal_handler(C, op);
433
434         return OPERATOR_RUNNING_MODAL;
435 }
436
437 void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot)
438 {
439         /* identifiers */
440         ot->name = "Manipulator Tweak";
441         ot->description = "Tweak the active manipulator";
442         ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak";
443
444         /* api callbacks */
445         ot->invoke = manipulator_tweak_invoke;
446         ot->modal = manipulator_tweak_modal;
447
448         /* TODO(campbell) This causes problems tweaking settings for operators,
449          * need to find a way to support this. */
450 #if 0
451         ot->flag = OPTYPE_UNDO;
452 #endif
453 }
454
455 /** \} */ // Manipulator operators
456
457
458 static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname)
459 {
460         wmKeyMap *keymap;
461         char name[KMAP_MAX_NAME];
462
463         static EnumPropertyItem modal_items[] = {
464                 {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
465                 {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
466                 {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""},
467                 {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""},
468                 {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""},
469                 {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""},
470                 {0, NULL, 0, NULL, NULL}
471         };
472
473
474         BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname);
475         keymap = WM_modalkeymap_get(keyconf, name);
476
477         /* this function is called for each spacetype, only needs to add map once */
478         if (keymap && keymap->modal_items)
479                 return NULL;
480
481         keymap = WM_modalkeymap_add(keyconf, name, modal_items);
482
483
484         /* items for modal map */
485         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
486         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
487
488         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
489         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
490
491         WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
492         WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
493         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
494         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
495
496         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
497         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
498         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
499         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
500
501         WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak");
502
503         return keymap;
504 }
505
506 /**
507  * Common default keymap for manipulator groups
508  */
509 wmKeyMap *WM_manipulatorgroup_keymap_common(
510         const wmManipulatorGroupType *wgt, wmKeyConfig *config)
511 {
512         /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
513         wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
514
515         WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
516         manipulatorgroup_tweak_modal_keymap(config, wgt->name);
517
518         return km;
519 }
520
521 /**
522  * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection
523  */
524 wmKeyMap *WM_manipulatorgroup_keymap_common_select(
525         const wmManipulatorGroupType *wgt, wmKeyConfig *config)
526 {
527         /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
528         wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
529
530         WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
531         WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0);
532         manipulatorgroup_tweak_modal_keymap(config, wgt->name);
533
534         wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0);
535         RNA_boolean_set(kmi->ptr, "extend", false);
536         RNA_boolean_set(kmi->ptr, "deselect", false);
537         RNA_boolean_set(kmi->ptr, "toggle", false);
538         kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
539         RNA_boolean_set(kmi->ptr, "extend", false);
540         RNA_boolean_set(kmi->ptr, "deselect", false);
541         RNA_boolean_set(kmi->ptr, "toggle", true);
542
543         return km;
544 }
545
546 /** \} */ /* wmManipulatorGroup */
547
548 /* -------------------------------------------------------------------- */
549 /** \name wmManipulatorGroupType
550  *
551  * \{ */
552
553 struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
554         struct wmManipulatorMapType *mmap_type,
555         const wmManipulatorGroupType *wgt)
556 {
557         /* could use hash lookups as operator types do, for now simple search. */
558         for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
559              wgt_ref;
560              wgt_ref = wgt_ref->next)
561         {
562                 if (wgt_ref->type == wgt) {
563                         return wgt_ref;
564                 }
565         }
566         return NULL;
567 }
568
569 struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
570         struct wmManipulatorMapType *mmap_type,
571         const char *idname)
572 {
573         /* could use hash lookups as operator types do, for now simple search. */
574         for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
575              wgt_ref;
576              wgt_ref = wgt_ref->next)
577         {
578                 if (STREQ(idname, wgt_ref->type->idname)) {
579                         return wgt_ref;
580                 }
581         }
582         return NULL;
583 }
584
585 /**
586  * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime.
587  */
588 wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
589         wmManipulatorMapType *mmap_type, const char *idname)
590 {
591         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
592         BLI_assert(wgt != NULL);
593         return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
594 }
595
596 wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
597         wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
598 {
599         wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref");
600         wgt_ref->type = wgt;
601         BLI_addtail(&mmap_type->grouptype_refs, wgt_ref);
602         return wgt_ref;
603 }
604
605 void WM_manipulatormaptype_group_init_runtime_keymap(
606         const Main *bmain,
607         wmManipulatorGroupType *wgt)
608 {
609         /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */
610         wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
611 }
612
613 void WM_manipulatormaptype_group_init_runtime(
614         const Main *bmain, wmManipulatorMapType *mmap_type,
615         wmManipulatorGroupType *wgt)
616 {
617         /* now create a manipulator for all existing areas */
618         for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
619                 for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
620                         for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
621                                 ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
622                                 for (ARegion *ar = lb->first; ar; ar = ar->next) {
623                                         wmManipulatorMap *mmap = ar->manipulator_map;
624                                         if (mmap && mmap->type == mmap_type) {
625                                                 wm_manipulatorgroup_new_from_type(mmap, wgt);
626
627                                                 /* just add here, drawing will occur on next update */
628                                                 wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0);
629                                                 ED_region_tag_redraw(ar);
630                                         }
631                                 }
632                         }
633                 }
634         }
635 }
636
637
638 /**
639  * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free.
640  */
641 void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref)
642 {
643         MEM_freeN(wgt_ref);
644 }
645
646 void WM_manipulatormaptype_group_unlink(
647         bContext *C, Main *bmain, wmManipulatorMapType *mmap_type,
648         const wmManipulatorGroupType *wgt)
649 {
650         /* Free instances. */
651         for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
652                 for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
653                         for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
654                                 ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
655                                 for (ARegion *ar = lb->first; ar; ar = ar->next) {
656                                         wmManipulatorMap *mmap = ar->manipulator_map;
657                                         if (mmap && mmap->type == mmap_type) {
658                                                 wmManipulatorGroup *mgroup, *mgroup_next;
659                                                 for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) {
660                                                         mgroup_next = mgroup->next;
661                                                         if (mgroup->type == wgt) {
662                                                                 BLI_assert(mgroup->parent_mmap == mmap);
663                                                                 wm_manipulatorgroup_free(C, mgroup);
664                                                                 ED_region_tag_redraw(ar);
665                                                         }
666                                                 }
667                                         }
668                                 }
669                         }
670                 }
671         }
672
673         /* Free types. */
674         wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
675         if (wgt_ref) {
676                 BLI_remlink(&mmap_type->grouptype_refs, wgt_ref);
677                 WM_manipulatormaptype_group_free(wgt_ref);
678         }
679
680         /* Note, we may want to keep this keymap for editing */
681         WM_keymap_remove(wgt->keyconf, wgt->keymap);
682
683         BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL);
684 }
685
686 void wm_manipulatorgrouptype_setup_keymap(
687         wmManipulatorGroupType *wgt, wmKeyConfig *keyconf)
688 {
689         /* Use flag since setup_keymap may return NULL,
690          * in that case we better not keep calling it. */
691         if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
692                 wgt->keymap = wgt->setup_keymap(wgt, keyconf);
693                 wgt->keyconf = keyconf;
694                 wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
695         }
696 }
697
698 /** \} */ /* wmManipulatorGroupType */
699
700 /* -------------------------------------------------------------------- */
701 /** \name High Level Add/Remove API
702  *
703  * For use directly from operators & RNA registration.
704  *
705  * \note In context of manipulator API these names are a bit misleading,
706  * but for general use terms its OK.
707  * `WM_manipulator_group_type_add` would be more correctly called:
708  * `WM_manipulatormaptype_grouptype_reference_link`
709  * but for general purpose API this is too detailed & annoying.
710  *
711  * \note We may want to return a value if there is nothing to remove.
712  *
713  * \{ */
714
715 void WM_manipulator_group_type_add_ptr_ex(
716         wmManipulatorGroupType *wgt,
717         wmManipulatorMapType *mmap_type)
718 {
719         WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
720
721         WM_manipulatorconfig_update_tag_init(mmap_type, wgt);
722 }
723 void WM_manipulator_group_type_add_ptr(
724         wmManipulatorGroupType *wgt)
725 {
726         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
727         WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
728 }
729 void WM_manipulator_group_type_add(const char *idname)
730 {
731         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
732         BLI_assert(wgt != NULL);
733         WM_manipulator_group_type_add_ptr(wgt);
734 }
735
736 void WM_manipulator_group_type_ensure_ptr_ex(
737         wmManipulatorGroupType *wgt,
738         wmManipulatorMapType *mmap_type)
739 {
740         wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
741         if (wgt_ref == NULL) {
742                 WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
743         }
744 }
745 void WM_manipulator_group_type_ensure_ptr(
746         wmManipulatorGroupType *wgt)
747 {
748         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
749         WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type);
750 }
751 void WM_manipulator_group_type_ensure(const char *idname)
752 {
753         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
754         BLI_assert(wgt != NULL);
755         WM_manipulator_group_type_ensure_ptr(wgt);
756 }
757
758 void WM_manipulator_group_type_remove_ptr_ex(
759         struct Main *bmain, wmManipulatorGroupType *wgt,
760         wmManipulatorMapType *mmap_type)
761 {
762         WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt);
763         WM_manipulatorgrouptype_free_ptr(wgt);
764 }
765 void WM_manipulator_group_type_remove_ptr(
766         struct Main *bmain, wmManipulatorGroupType *wgt)
767 {
768         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
769         WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type);
770 }
771 void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname)
772 {
773         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
774         BLI_assert(wgt != NULL);
775         WM_manipulator_group_type_remove_ptr(bmain, wgt);
776 }
777
778 /* delayed versions */
779
780 void WM_manipulator_group_type_unlink_delayed_ptr_ex(
781         wmManipulatorGroupType *wgt,
782         wmManipulatorMapType *mmap_type)
783 {
784         WM_manipulatorconfig_update_tag_remove(mmap_type, wgt);
785 }
786
787 void WM_manipulator_group_type_unlink_delayed_ptr(
788         wmManipulatorGroupType *wgt)
789 {
790         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
791         WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type);
792 }
793
794 void WM_manipulator_group_type_unlink_delayed(const char *idname)
795 {
796         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
797         BLI_assert(wgt != NULL);
798         WM_manipulator_group_type_unlink_delayed_ptr(wgt);
799 }
800
801 /** \} */