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