ba3b405e612f819e75a8d265c8be53a27880f1bf
[blender.git] / source / blender / windowmanager / manipulators / intern / wm_manipulator_map.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_map.c
27  *  \ingroup wm
28  */
29
30 #include <string.h>
31
32 #include "BLI_listbase.h"
33 #include "BLI_math.h"
34 #include "BLI_rect.h"
35 #include "BLI_string.h"
36 #include "BLI_ghash.h"
37
38 #include "BKE_context.h"
39 #include "BKE_global.h"
40
41 #include "ED_screen.h"
42 #include "ED_view3d.h"
43
44 #include "GPU_glew.h"
45 #include "GPU_matrix.h"
46 #include "GPU_select.h"
47
48 #include "MEM_guardedalloc.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52 #include "wm_event_system.h"
53
54 #include "DEG_depsgraph.h"
55
56 /* own includes */
57 #include "wm_manipulator_wmapi.h"
58 #include "wm_manipulator_intern.h"
59
60 /**
61  * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain
62  * area type can query the manipulator-map to do so.
63  */
64 static ListBase manipulatormaptypes = {NULL, NULL};
65
66 /**
67  * Update when manipulator-map types change.
68  */
69 /* so operator removal can trigger update */
70 typedef enum eWM_ManipulatorGroupTypeGlobalFlag {
71         WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
72         WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
73 } eWM_ManipulatorGroupTypeGlobalFlag;
74
75 static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0;
76
77 /**
78  * Manipulator-map update tagging.
79  */
80 enum {
81         /** #manipulatormap_prepare_drawing has run */
82         MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0),
83         MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1),
84 };
85
86
87 /* -------------------------------------------------------------------- */
88 /** \name wmManipulatorMap Selection Array API
89  *
90  * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
91  *
92  * \{ */
93
94 static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
95 {
96         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
97         if (len <= msel->len_alloc) {
98                 return;
99         }
100         msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
101         msel->len_alloc = len;
102 }
103
104 void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
105 {
106         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
107         MEM_SAFE_FREE(msel->items);
108         msel->len = 0;
109         msel->len_alloc = 0;
110 }
111
112 void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
113 {
114         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
115         msel->len -= len_subtract;
116         if (msel->len <= 0) {
117                 wm_manipulatormap_select_array_clear(mmap);
118         }
119         else {
120                 if (msel->len < msel->len_alloc / 2) {
121                         msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
122                         msel->len_alloc = msel->len;
123                 }
124         }
125 }
126
127 void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr)
128 {
129         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
130         BLI_assert(msel->len <= msel->len_alloc);
131         if (msel->len == msel->len_alloc) {
132                 msel->len_alloc = (msel->len + 1) * 2;
133                 msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
134         }
135         msel->items[msel->len++] = mpr;
136 }
137
138 void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr)
139 {
140         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
141         /* remove manipulator from selected_manipulators array */
142         for (int i = 0; i < msel->len; i++) {
143                 if (msel->items[i] == mpr) {
144                         for (int j = i; j < (msel->len - 1); j++) {
145                                 msel->items[j] = msel->items[j + 1];
146                         }
147                         wm_manipulatormap_select_array_shrink(mmap, 1);
148                         break;
149                 }
150         }
151
152 }
153
154 /** \} */
155
156
157 /* -------------------------------------------------------------------- */
158 /** \name wmManipulatorMap
159  *
160  * \{ */
161
162 /**
163  * Creates a manipulator-map with all registered manipulators for that type
164  */
165 wmManipulatorMap *WM_manipulatormap_new_from_type(
166         const struct wmManipulatorMapType_Params *mmap_params)
167 {
168         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params);
169         wmManipulatorMap *mmap;
170
171         mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap");
172         mmap->type = mmap_type;
173         WM_manipulatormap_tag_refresh(mmap);
174
175         /* create all manipulator-groups for this manipulator-map. We may create an empty one
176          * too in anticipation of manipulators from operators etc */
177         for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
178                 wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type);
179         }
180
181         return mmap;
182 }
183
184 void wm_manipulatormap_remove(wmManipulatorMap *mmap)
185 {
186         /* Clear first so further calls don't waste time trying to maintain correct array state. */
187         wm_manipulatormap_select_array_clear(mmap);
188
189         for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) {
190                 mgroup_next = mgroup->next;
191                 BLI_assert(mgroup->parent_mmap == mmap);
192                 wm_manipulatorgroup_free(NULL, mgroup);
193         }
194         BLI_assert(BLI_listbase_is_empty(&mmap->groups));
195
196         MEM_freeN(mmap);
197 }
198
199
200 wmManipulatorGroup *WM_manipulatormap_group_find(
201         struct wmManipulatorMap *mmap,
202         const char *idname)
203 {
204         wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
205         if (wgt) {
206                 return WM_manipulatormap_group_find_ptr(mmap, wgt);
207         }
208         return NULL;
209 }
210
211 wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
212         struct wmManipulatorMap *mmap,
213         const struct wmManipulatorGroupType *wgt)
214 {
215         for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
216                 if (mgroup->type == wgt) {
217                         return mgroup;
218                 }
219         }
220         return NULL;
221 }
222
223 const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap)
224 {
225         return &mmap->groups;
226 }
227
228 bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap)
229 {
230         return mmap->mmap_context.select.len != 0;
231 }
232
233 /**
234  * \note We could use a callback to define bounds, for now just use matrix location.
235  */
236 bool WM_manipulatormap_minmax(
237         const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select,
238         float r_min[3], float r_max[3])
239 {
240         if (use_select) {
241                 int i;
242                 for (i = 0; i < mmap->mmap_context.select.len; i++) {
243                         minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]);
244                 }
245                 return i != 0;
246         }
247         else {
248                 bool ok = false;
249                 BLI_assert(!"TODO");
250                 return ok;
251         }
252 }
253
254 /**
255  * Creates and returns idname hash table for (visible) manipulators in \a mmap
256  *
257  * \param poll  Polling function for excluding manipulators.
258  * \param data  Custom data passed to \a poll
259  *
260  * TODO(campbell): this uses unreliable order,
261  * best we use an iterator function instead of a hash.
262  */
263 static GHash *WM_manipulatormap_manipulator_hash_new(
264         const bContext *C, wmManipulatorMap *mmap,
265         bool (*poll)(const wmManipulator *, void *),
266         void *data, const bool include_hidden)
267 {
268         GHash *hash = BLI_ghash_ptr_new(__func__);
269
270         /* collect manipulators */
271         for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
272                 if (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)) {
273                         for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
274                                 if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) &&
275                                     (!poll || poll(mpr, data)))
276                                 {
277                                         BLI_ghash_insert(hash, mpr, mpr);
278                                 }
279                         }
280                 }
281         }
282
283         return hash;
284 }
285
286 void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap)
287 {
288         if (mmap) {
289                 /* We might want only to refresh some, for tag all steps. */
290                 for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) {
291                         mmap->update_flag[i] |= (
292                                 MANIPULATORMAP_IS_PREPARE_DRAW |
293                                 MANIPULATORMAP_IS_REFRESH_CALLBACK);
294                 }
295         }
296 }
297
298 static bool manipulator_prepare_drawing(
299         wmManipulatorMap *mmap, wmManipulator *mpr,
300         const bContext *C, ListBase *draw_manipulators,
301         const eWM_ManipulatorMapDrawStep drawstep)
302 {
303         int do_draw = wm_manipulator_is_visible(mpr);
304         if (do_draw == 0) {
305                 /* skip */
306         }
307         else {
308                 /* Ensure we get RNA updates */
309                 if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) {
310                         /* hover manipulators need updating, even if we don't draw them */
311                         wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0);
312                 }
313                 if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) {
314                         BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr));
315                 }
316                 return true;
317         }
318
319         return false;
320 }
321
322 /**
323  * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that
324  * should be drawn to list \a draw_manipulators, note that added items need freeing.
325  */
326 static void manipulatormap_prepare_drawing(
327         wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators,
328         const eWM_ManipulatorMapDrawStep drawstep)
329 {
330         if (!mmap || BLI_listbase_is_empty(&mmap->groups))
331                 return;
332         wmManipulator *mpr_modal = mmap->mmap_context.modal;
333
334         /* only active manipulator needs updating */
335         if (mpr_modal) {
336                 if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) {
337                         if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) {
338                                 if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) {
339                                         mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW;
340                                 }
341                         }
342                         /* don't draw any other manipulators */
343                         return;
344                 }
345         }
346
347         for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
348                 /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
349                 if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) ||
350                     !wm_manipulatorgroup_is_visible(mgroup, C))
351                 {
352                         continue;
353                 }
354
355                 /* needs to be initialized on first draw */
356                 /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
357                 if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) {
358                         /* force refresh again. */
359                         mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH;
360                 }
361                 /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
362                 wm_manipulatorgroup_ensure_initialized(mgroup, C);
363
364                 /* prepare drawing */
365                 if (mgroup->type->draw_prepare) {
366                         mgroup->type->draw_prepare(C, mgroup);
367                 }
368
369                 for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
370                         manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep);
371                 }
372         }
373
374         mmap->update_flag[drawstep] &=
375                 ~(MANIPULATORMAP_IS_REFRESH_CALLBACK |
376                   MANIPULATORMAP_IS_PREPARE_DRAW);
377 }
378
379 /**
380  * Draw all visible manipulators in \a mmap.
381  * Uses global draw_manipulators listbase.
382  */
383 static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators)
384 {
385         /* Can be empty if we're dynamically added and removed. */
386         if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) {
387                 return;
388         }
389
390         const bool draw_multisample = (U.ogl_multisamples != USER_MULTISAMPLE_NONE);
391
392         /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
393 /*      const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */
394
395         /* enable multisampling */
396         if (draw_multisample) {
397                 glEnable(GL_MULTISAMPLE);
398         }
399
400         bool is_depth_prev = false;
401
402         /* draw_manipulators contains all visible manipulators - draw them */
403         for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) {
404                 wmManipulator *mpr = link->data;
405                 link_next = link->next;
406
407                 bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
408
409                 /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
410                 if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) {
411                         is_depth = false;
412                 }
413
414                 if (is_depth == is_depth_prev) {
415                         /* pass */
416                 }
417                 else {
418                         if (is_depth) {
419                                 glEnable(GL_DEPTH_TEST);
420                         }
421                         else {
422                                 glDisable(GL_DEPTH_TEST);
423                         }
424                         is_depth_prev = is_depth;
425                 }
426
427                 mpr->type->draw(C, mpr);
428                 /* free/remove manipulator link after drawing */
429                 BLI_freelinkN(draw_manipulators, link);
430         }
431
432         if (is_depth_prev) {
433                 glDisable(GL_DEPTH_TEST);
434         }
435
436         if (draw_multisample) {
437                 glDisable(GL_MULTISAMPLE);
438         }
439 }
440
441 void WM_manipulatormap_draw(
442         wmManipulatorMap *mmap, const bContext *C,
443         const eWM_ManipulatorMapDrawStep drawstep)
444 {
445         ListBase draw_manipulators = {NULL};
446
447         manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep);
448         manipulators_draw_list(mmap, C, &draw_manipulators);
449         BLI_assert(BLI_listbase_is_empty(&draw_manipulators));
450 }
451
452 static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators)
453 {
454         int select_id = 0;
455         wmManipulator *mpr;
456
457         /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
458         bool is_depth_prev = false;
459
460         for (LinkData *link = visible_manipulators->first; link; link = link->next) {
461                 mpr = link->data;
462
463                 bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
464                 if (is_depth == is_depth_prev) {
465                         /* pass */
466                 }
467                 else {
468                         if (is_depth) {
469                                 glEnable(GL_DEPTH_TEST);
470                         }
471                         else {
472                                 glDisable(GL_DEPTH_TEST);
473                         }
474                         is_depth_prev = is_depth;
475                 }
476
477                 /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */
478
479                 mpr->type->draw_select(C, mpr, select_id << 8);
480
481
482                 select_id++;
483         }
484
485         if (is_depth_prev) {
486                 glDisable(GL_DEPTH_TEST);
487         }
488 }
489
490 static int manipulator_find_intersected_3d_intern(
491         ListBase *visible_manipulators, const bContext *C, const int co[2],
492         const int hotspot)
493 {
494         EvaluationContext eval_ctx;
495         ScrArea *sa = CTX_wm_area(C);
496         ARegion *ar = CTX_wm_region(C);
497         View3D *v3d = sa->spacedata.first;
498         rcti rect;
499         /* Almost certainly overkill, but allow for many custom manipulators. */
500         GLuint buffer[MAXPICKBUF];
501         short hits;
502         const bool do_passes = GPU_select_query_check_active();
503
504         BLI_rcti_init_pt_radius(&rect, co, hotspot);
505
506         CTX_data_eval_ctx(C, &eval_ctx);
507
508         ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
509
510         if (do_passes)
511                 GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
512         else
513                 GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0);
514         /* do the drawing */
515         manipulator_draw_select_3D_loop(C, visible_manipulators);
516
517         hits = GPU_select_end();
518
519         if (do_passes && (hits > 0)) {
520                 GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
521                 manipulator_draw_select_3D_loop(C, visible_manipulators);
522                 GPU_select_end();
523         }
524
525         ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
526
527         const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
528
529         return hit_near ? hit_near[3] : -1;
530 }
531
532 /**
533  * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking.
534  */
535 static wmManipulator *manipulator_find_intersected_3d(
536         bContext *C, const int co[2], ListBase *visible_manipulators,
537         int *r_part)
538 {
539         wmManipulator *result = NULL;
540         int hit = -1;
541
542         int hotspot_radii[] = {
543                 3 * U.pixelsize,
544 #if 0 /* We may want to enable when selection doesn't run on mousemove! */
545                 7 * U.pixelsize,
546 #endif
547         };
548
549         *r_part = 0;
550         /* set up view matrices */
551         view3d_operator_needs_opengl(C);
552
553         hit = -1;
554
555         for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
556                 hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]);
557                 if (hit != -1) {
558                         break;
559                 }
560         }
561
562         if (hit != -1) {
563                 LinkData *link = BLI_findlink(visible_manipulators, hit >> 8);
564                 if (link != NULL) {
565                         *r_part = hit & 255;
566                         result = link->data;
567                 }
568                 else {
569                         /* All manipulators should use selection ID they're given as part of the callback,
570                          * if they don't it will attempt tp lookup non-existing index. */
571                         BLI_assert(0);
572                 }
573         }
574
575         return result;
576 }
577
578 /**
579  * Try to find a manipulator under the mouse position. 2D intersections have priority over
580  * 3D ones (could check for smallest screen-space distance but not needed right now).
581  */
582 wmManipulator *wm_manipulatormap_highlight_find(
583         wmManipulatorMap *mmap, bContext *C, const wmEvent *event,
584         int *r_part)
585 {
586         wmManipulator *mpr = NULL;
587         ListBase visible_3d_manipulators = {NULL};
588
589         for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
590
591                 /* If it were important we could initialize here,
592                  * but this only happens when events are handled before drawing,
593                  * just skip to keep code-path for initializing manipulators simple. */
594                 if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) {
595                         continue;
596                 }
597
598                 if (wm_manipulatorgroup_is_visible(mgroup, C)) {
599                         if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) {
600                                 if ((mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] & MANIPULATORMAP_IS_REFRESH_CALLBACK) &&
601                                     mgroup->type->refresh)
602                                 {
603                                         mgroup->type->refresh(C, mgroup);
604                                         /* cleared below */
605                                 }
606                                 wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators);
607                         }
608                         else {
609                                 if ((mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] & MANIPULATORMAP_IS_REFRESH_CALLBACK) &&
610                                     mgroup->type->refresh)
611                                 {
612                                         mgroup->type->refresh(C, mgroup);
613                                         /* cleared below */
614                                 }
615
616                                 if ((mpr = wm_manipulatorgroup_find_intersected_mainpulator(mgroup, C, event, r_part))) {
617                                         break;
618                                 }
619                         }
620                 }
621         }
622
623         if (!BLI_listbase_is_empty(&visible_3d_manipulators)) {
624                 /* 2D manipulators get priority. */
625                 if (mpr == NULL) {
626                         mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part);
627                 }
628                 BLI_freelistN(&visible_3d_manipulators);
629         }
630
631         mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
632         mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
633
634         return mpr;
635 }
636
637 void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap)
638 {
639         wmEventHandler *handler;
640
641         for (handler = ar->handlers.first; handler; handler = handler->next) {
642                 if (handler->manipulator_map == mmap) {
643                         return;
644                 }
645         }
646
647         handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler");
648
649         BLI_assert(mmap == ar->manipulator_map);
650         handler->manipulator_map = mmap;
651         BLI_addtail(&ar->handlers, handler);
652 }
653
654 void wm_manipulatormaps_handled_modal_update(
655         bContext *C, wmEvent *event, wmEventHandler *handler)
656 {
657         const bool modal_running = (handler->op != NULL);
658
659         /* happens on render or when joining areas */
660         if (!handler->op_region || !handler->op_region->manipulator_map) {
661                 return;
662         }
663
664         wmManipulatorMap *mmap = handler->op_region->manipulator_map;
665         wmManipulator *mpr = wm_manipulatormap_modal_get(mmap);
666         ScrArea *area = CTX_wm_area(C);
667         ARegion *region = CTX_wm_region(C);
668
669         wm_manipulatormap_handler_context(C, handler);
670
671         /* regular update for running operator */
672         if (modal_running) {
673                 wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL;
674                 if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) {
675                         wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
676                         if (modal_fn != NULL) {
677                                 int retval = modal_fn(C, mpr, event, 0);
678                                 /* The manipulator is tried to the operator, we can't choose when to exit. */
679                                 BLI_assert(retval & OPERATOR_RUNNING_MODAL);
680                                 UNUSED_VARS_NDEBUG(retval);
681                         }
682                 }
683         }
684         /* operator not running anymore */
685         else {
686                 wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
687                 if (mpr) {
688                         /* This isn't defined if it ends because of success of cancel, we may want to change. */
689                         bool cancel = true;
690                         if (mpr->type->exit) {
691                                 mpr->type->exit(C, mpr, cancel);
692                         }
693                         wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
694                 }
695         }
696
697         /* restore the area */
698         CTX_wm_area_set(C, area);
699         CTX_wm_region_set(C, region);
700 }
701
702 /**
703  * Deselect all selected manipulators in \a mmap.
704  * \return if selection has changed.
705  */
706 bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap)
707 {
708         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
709
710         if (msel->items == NULL || msel->len == 0) {
711                 return false;
712         }
713
714         for (int i = 0; i < msel->len; i++) {
715                 wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true);
716         }
717
718         wm_manipulatormap_select_array_clear(mmap);
719
720         /* always return true, we already checked
721          * if there's anything to deselect */
722         return true;
723 }
724
725 BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data))
726 {
727         return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT);
728 }
729
730 /**
731  * Select all selectable manipulators in \a mmap.
732  * \return if selection has changed.
733  */
734 static bool wm_manipulatormap_select_all_intern(
735         bContext *C, wmManipulatorMap *mmap)
736 {
737         wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
738         /* GHash is used here to avoid having to loop over all manipulators twice (once to
739          * get tot_sel for allocating, once for actually selecting). Instead we collect
740          * selectable manipulators in hash table and use this to get tot_sel and do selection */
741
742         GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true);
743         GHashIterator gh_iter;
744         int i;
745         bool changed = false;
746
747         wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_size(hash));
748
749         GHASH_ITER_INDEX (gh_iter, hash, i) {
750                 wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter);
751                 WM_manipulator_select_set(mmap, mpr_iter, true);
752         }
753         /* highlight first manipulator */
754         wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part);
755
756         BLI_assert(BLI_ghash_size(hash) == msel->len);
757
758         BLI_ghash_free(hash, NULL, NULL);
759         return changed;
760 }
761
762 /**
763  * Select/Deselect all selectable manipulators in \a mmap.
764  * \return if selection has changed.
765  *
766  * TODO select all by type
767  */
768 bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action)
769 {
770         bool changed = false;
771
772         switch (action) {
773                 case SEL_SELECT:
774                         changed = wm_manipulatormap_select_all_intern(C, mmap);
775                         break;
776                 case SEL_DESELECT:
777                         changed = wm_manipulatormap_deselect_all(mmap);
778                         break;
779                 default:
780                         BLI_assert(0);
781                         break;
782         }
783
784         if (changed)
785                 WM_event_add_mousemove(C);
786
787         return changed;
788 }
789
790 /**
791  * Prepare context for manipulator handling (but only if area/region is
792  * part of screen). Version of #wm_handler_op_context for manipulators.
793  */
794 void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler)
795 {
796         bScreen *screen = CTX_wm_screen(C);
797
798         if (screen) {
799                 if (handler->op_area == NULL) {
800                         /* do nothing in this context */
801                 }
802                 else {
803                         ScrArea *sa;
804
805                         for (sa = screen->areabase.first; sa; sa = sa->next)
806                                 if (sa == handler->op_area)
807                                         break;
808                         if (sa == NULL) {
809                                 /* when changing screen layouts with running modal handlers (like render display), this
810                                  * is not an error to print */
811                                 if (handler->manipulator_map == NULL)
812                                         printf("internal error: modal manipulator-map handler has invalid area\n");
813                         }
814                         else {
815                                 ARegion *ar;
816                                 CTX_wm_area_set(C, sa);
817                                 for (ar = sa->regionbase.first; ar; ar = ar->next)
818                                         if (ar == handler->op_region)
819                                                 break;
820                                 /* XXX no warning print here, after full-area and back regions are remade */
821                                 if (ar)
822                                         CTX_wm_region_set(C, ar);
823                         }
824                 }
825         }
826 }
827
828 bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win)
829 {
830         wmManipulator *mpr = mmap->mmap_context.highlight;
831         if (mpr && mpr->type->cursor_get) {
832                 WM_cursor_set(win, mpr->type->cursor_get(mpr));
833                 return true;
834         }
835
836         return false;
837 }
838
839 void wm_manipulatormap_highlight_set(
840         wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part)
841 {
842         if ((mpr != mmap->mmap_context.highlight) ||
843             (mpr && part != mpr->highlight_part))
844         {
845                 if (mmap->mmap_context.highlight) {
846                         mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT;
847                         mmap->mmap_context.highlight->highlight_part = -1;
848                 }
849
850                 mmap->mmap_context.highlight = mpr;
851
852                 if (mpr) {
853                         mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT;
854                         mpr->highlight_part = part;
855
856                         if (C && mpr->type->cursor_get) {
857                                 wmWindow *win = CTX_wm_window(C);
858                                 WM_cursor_set(win, mpr->type->cursor_get(mpr));
859                         }
860                 }
861                 else {
862                         if (C) {
863                                 wmWindow *win = CTX_wm_window(C);
864                                 WM_cursor_set(win, CURSOR_STD);
865                         }
866                 }
867
868                 /* tag the region for redraw */
869                 if (C) {
870                         ARegion *ar = CTX_wm_region(C);
871                         ED_region_tag_redraw(ar);
872                 }
873         }
874 }
875
876 wmManipulator *wm_manipulatormap_highlight_get(wmManipulatorMap *mmap)
877 {
878         return mmap->mmap_context.highlight;
879 }
880
881 /**
882  * Caller should call exit when (enable == False).
883  */
884 void wm_manipulatormap_modal_set(
885         wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable)
886 {
887         if (enable) {
888                 BLI_assert(mmap->mmap_context.modal == NULL);
889                 wmWindow *win = CTX_wm_window(C);
890
891                 /* For now only grab cursor for 3D manipulators. */
892                 int retval = OPERATOR_RUNNING_MODAL;
893
894                 if (mpr->type->invoke &&
895                     (mpr->type->modal || mpr->custom_modal))
896                 {
897                         retval = mpr->type->invoke(C, mpr, event);
898                 }
899
900                 if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
901                         return;
902                 }
903
904                 mpr->state |= WM_MANIPULATOR_STATE_MODAL;
905                 mmap->mmap_context.modal = mpr;
906
907                 if ((mpr->flag & WM_MANIPULATOR_GRAB_CURSOR) &&
908                     (WM_event_is_absolute(event) == false))
909                 {
910                         WM_cursor_grab_enable(win, true, true, NULL);
911                         copy_v2_v2_int(mmap->mmap_context.event_xy, &event->x);
912                         mmap->mmap_context.event_grabcursor = win->grabcursor;
913                 }
914                 else {
915                         mmap->mmap_context.event_xy[0] = INT_MAX;
916                 }
917
918                 struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
919                 if (mpop && mpop->type) {
920                         WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
921
922                         /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */
923                         if (!mmap->mmap_context.modal) {
924                                 mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
925                                 MEM_SAFE_FREE(mpr->interaction_data);
926                         }
927                         return;
928                 }
929         }
930         else {
931                 BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr));
932
933                 /* deactivate, manipulator but first take care of some stuff */
934                 if (mpr) {
935                         mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
936                         MEM_SAFE_FREE(mpr->interaction_data);
937                 }
938                 mmap->mmap_context.modal = NULL;
939
940                 if (C) {
941                         wmWindow *win = CTX_wm_window(C);
942                         if (mmap->mmap_context.event_xy[0] != INT_MAX) {
943                                 /* Check if some other part of Blender (typically operators)
944                                  * have adjusted the grab mode since it was set.
945                                  * If so: warp, so we have a predictable outcome. */
946                                 if (mmap->mmap_context.event_grabcursor == win->grabcursor) {
947                                         WM_cursor_grab_disable(win, mmap->mmap_context.event_xy);
948                                 }
949                                 else {
950                                         WM_cursor_warp(win, UNPACK2(mmap->mmap_context.event_xy));
951                                 }
952                         }
953                         ED_region_tag_redraw(CTX_wm_region(C));
954                         WM_event_add_mousemove(C);
955                 }
956         }
957 }
958
959 wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap)
960 {
961         return mmap->mmap_context.modal;
962 }
963
964 wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len)
965 {
966         *r_selected_len = mmap->mmap_context.select.len;
967         return mmap->mmap_context.select.items;
968 }
969
970 ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap)
971 {
972         return &mmap->groups;
973 }
974
975 void WM_manipulatormap_message_subscribe(
976         bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus)
977 {
978         for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
979                 if (!wm_manipulatorgroup_is_visible(mgroup, C)) {
980                         continue;
981                 }
982                 for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
983                         if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
984                                 continue;
985                         }
986                         WM_manipulator_target_property_subscribe_all(mpr, mbus, ar);
987                 }
988                 if (mgroup->type->message_subscribe != NULL) {
989                         mgroup->type->message_subscribe(C, mgroup, mbus);
990                 }
991         }
992 }
993
994 /** \} */ /* wmManipulatorMap */
995
996
997 /* -------------------------------------------------------------------- */
998 /** \name wmManipulatorMapType
999  *
1000  * \{ */
1001
1002 wmManipulatorMapType *WM_manipulatormaptype_find(
1003         const struct wmManipulatorMapType_Params *mmap_params)
1004 {
1005         for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
1006                 if (mmap_type->spaceid == mmap_params->spaceid &&
1007                     mmap_type->regionid == mmap_params->regionid)
1008                 {
1009                         return mmap_type;
1010                 }
1011         }
1012
1013         return NULL;
1014 }
1015
1016 wmManipulatorMapType *WM_manipulatormaptype_ensure(
1017         const struct wmManipulatorMapType_Params *mmap_params)
1018 {
1019         wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params);
1020
1021         if (mmap_type) {
1022                 return mmap_type;
1023         }
1024
1025         mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list");
1026         mmap_type->spaceid = mmap_params->spaceid;
1027         mmap_type->regionid = mmap_params->regionid;
1028         BLI_addhead(&manipulatormaptypes, mmap_type);
1029
1030         return mmap_type;
1031 }
1032
1033 void wm_manipulatormaptypes_free(void)
1034 {
1035         for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next;
1036              mmap_type;
1037              mmap_type = mmap_type_next)
1038         {
1039                 mmap_type_next = mmap_type->next;
1040                 for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next;
1041                      wgt_ref;
1042                      wgt_ref = wgt_next)
1043                 {
1044                         wgt_next = wgt_ref->next;
1045                         WM_manipulatormaptype_group_free(wgt_ref);
1046                 }
1047                 MEM_freeN(mmap_type);
1048         }
1049 }
1050
1051 /**
1052  * Initialize keymaps for all existing manipulator-groups
1053  */
1054 void wm_manipulators_keymap(wmKeyConfig *keyconf)
1055 {
1056         /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */
1057         WM_keymap_find(keyconf, "Manipulators", 0, 0);
1058
1059         for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
1060                 for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
1061                         wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf);
1062                 }
1063         }
1064 }
1065
1066 /** \} */ /* wmManipulatorMapType */
1067
1068 /* -------------------------------------------------------------------- */
1069 /** \name Updates for Dynamic Type Registraion
1070  *
1071  * \{ */
1072
1073
1074 void WM_manipulatorconfig_update_tag_init(
1075         wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
1076 {
1077         /* tag for update on next use */
1078         mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
1079         wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
1080
1081         wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
1082 }
1083
1084 void WM_manipulatorconfig_update_tag_remove(
1085         wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
1086 {
1087         /* tag for update on next use */
1088         mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
1089         wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
1090
1091         wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
1092 }
1093
1094 /**
1095  * Run incase new types have been added (runs often, early exit where possible).
1096  * Follows #WM_keyconfig_update concentions.
1097  */
1098 void WM_manipulatorconfig_update(struct Main *bmain)
1099 {
1100         if (G.background)
1101                 return;
1102
1103         if (wm_mmap_type_update_flag == 0)
1104                 return;
1105
1106         if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1107                 for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
1108                      mmap_type;
1109                      mmap_type = mmap_type->next)
1110                 {
1111                         if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1112                                 mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
1113                                 for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next;
1114                                      wgt_ref;
1115                                      wgt_ref = wgt_ref_next)
1116                                 {
1117                                         wgt_ref_next = wgt_ref->next;
1118                                         if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) {
1119                                                 WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type);
1120                                         }
1121                                 }
1122                         }
1123                 }
1124
1125                 wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
1126         }
1127
1128         if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) {
1129                 for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
1130                      mmap_type;
1131                      mmap_type = mmap_type->next)
1132                 {
1133                         const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
1134                         if (mmap_type->type_update_flag & type_update_all) {
1135                                 mmap_type->type_update_flag &= ~type_update_all;
1136                                 for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
1137                                      wgt_ref;
1138                                      wgt_ref = wgt_ref->next)
1139                                 {
1140                                         if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
1141                                                 WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type);
1142                                                 wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
1143                                         }
1144
1145                                         if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) {
1146                                                 WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type);
1147                                                 wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT;
1148                                         }
1149                                 }
1150                         }
1151                 }
1152
1153                 wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
1154         }
1155 }
1156
1157 /** \} */