2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation, Joshua Leung
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/interface/view2d_ops.c
27 * \ingroup edinterface
33 #include "MEM_guardedalloc.h"
35 #include "DNA_userdef_types.h"
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_math_base.h"
41 #include "BKE_context.h"
43 #include "RNA_access.h"
44 #include "RNA_define.h"
50 #include "ED_screen.h"
52 #include "UI_view2d.h"
53 #include "UI_interface.h"
55 #include "PIL_time.h" /* USER_ZOOM_CONT */
57 static int view2d_poll(bContext *C)
59 ARegion *ar = CTX_wm_region(C);
61 return (ar != NULL) && (ar->v2d.flag & V2D_IS_INITIALISED);
64 /* ********************************************************* */
65 /* VIEW PANNING OPERATOR */
67 /* This group of operators come in several forms:
68 * 1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
69 * 2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
71 * In order to make sure this works, each operator must define the following RNA-Operator Props:
72 * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
75 /* ------------------ Shared 'core' stuff ---------------------- */
77 /* temp customdata for operator */
78 typedef struct v2dViewPanData {
79 bScreen *sc; /* screen where view pan was initiated */
80 ScrArea *sa; /* area where view pan was initiated */
81 ARegion *ar; /* region where view pan was initiated */
82 View2D *v2d; /* view2d we're operating in */
84 float facx, facy; /* amount to move view relative to zoom */
86 /* options for version 1 */
87 int startx, starty; /* mouse x/y values in window when operator was initiated */
88 int lastx, lasty; /* previous x/y values of mouse in window */
89 int invoke_event; /* event starting pan, for modal exit */
91 short in_scroller; /* for MMB in scrollers (old feature in past, but now not that useful) */
94 /* initialize panning customdata */
95 static int view_pan_init(bContext *C, wmOperator *op)
97 ARegion *ar = CTX_wm_region(C);
102 /* regions now have v2d-data by default, so check for region */
106 /* check if panning is allowed at all */
108 if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y))
111 /* set custom-data for operator */
112 vpd = MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
113 op->customdata = vpd;
115 /* set pointers to owners */
116 vpd->sc = CTX_wm_screen(C);
117 vpd->sa = CTX_wm_area(C);
121 /* calculate translation factor - based on size of view */
122 winx = (float)(BLI_RCT_SIZE_X(&ar->winrct) + 1);
123 winy = (float)(BLI_RCT_SIZE_Y(&ar->winrct) + 1);
124 vpd->facx = (BLI_RCT_SIZE_X(&v2d->cur)) / winx;
125 vpd->facy = (BLI_RCT_SIZE_Y(&v2d->cur)) / winy;
130 /* apply transform to view (i.e. adjust 'cur' rect) */
131 static void view_pan_apply(wmOperator *op)
133 v2dViewPanData *vpd = op->customdata;
134 View2D *v2d = vpd->v2d;
137 /* calculate amount to move view by */
138 dx = vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
139 dy = vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
141 /* only move view on an axis if change is allowed */
142 if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
146 if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
151 /* validate that view is in valid configuration after this operation */
152 UI_view2d_curRect_validate(v2d);
154 /* request updates to be done... */
155 ED_region_tag_redraw(vpd->ar);
157 UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
160 if (vpd->sa->spacetype == SPACE_OUTLINER) {
161 /* don't rebuild full tree, since we're just changing our view */
162 SpaceOops *soops = vpd->sa->spacedata.first;
163 soops->storeflag |= SO_TREESTORE_REDRAW;
167 /* cleanup temp customdata */
168 static void view_pan_exit(wmOperator *op)
170 if (op->customdata) {
171 MEM_freeN(op->customdata);
172 op->customdata = NULL;
176 /* ------------------ Modal Drag Version (1) ---------------------- */
178 /* for 'redo' only, with no user input */
179 static int view_pan_exec(bContext *C, wmOperator *op)
181 if (!view_pan_init(C, op))
182 return OPERATOR_CANCELLED;
186 return OPERATOR_FINISHED;
189 /* set up modal operator and relevant settings */
190 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
192 wmWindow *window = CTX_wm_window(C);
196 /* set up customdata */
197 if (!view_pan_init(C, op))
198 return OPERATOR_PASS_THROUGH;
200 vpd = op->customdata;
203 /* set initial settings */
204 vpd->startx = vpd->lastx = event->x;
205 vpd->starty = vpd->lasty = event->y;
206 vpd->invoke_event = event->type;
208 if (event->type == MOUSEPAN) {
209 RNA_int_set(op->ptr, "deltax", event->prevx - event->x);
210 RNA_int_set(op->ptr, "deltay", event->prevy - event->y);
214 return OPERATOR_FINISHED;
217 RNA_int_set(op->ptr, "deltax", 0);
218 RNA_int_set(op->ptr, "deltay", 0);
220 if (v2d->keepofs & V2D_LOCKOFS_X)
221 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
222 else if (v2d->keepofs & V2D_LOCKOFS_Y)
223 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
225 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
227 /* add temp handler */
228 WM_event_add_modal_handler(C, op);
230 return OPERATOR_RUNNING_MODAL;
233 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
234 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
236 v2dViewPanData *vpd = op->customdata;
238 /* execute the events */
239 switch (event->type) {
242 /* calculate new delta transform, then store mouse-coordinates for next-time */
243 RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
244 RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
246 vpd->lastx = event->x;
247 vpd->lasty = event->y;
252 /* XXX - Mode switching isn't implemented. See comments in 36818.
256 if (event->val == KM_PRESS) {
257 /* calculate overall delta mouse-movement for redo */
258 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
259 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
262 WM_cursor_restore(CTX_wm_window(C));
263 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
264 return OPERATOR_FINISHED;
268 if (event->type == vpd->invoke_event || event->type == ESCKEY) {
269 if (event->val == KM_RELEASE) {
270 /* calculate overall delta mouse-movement for redo */
271 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
272 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
275 WM_cursor_restore(CTX_wm_window(C));
277 return OPERATOR_FINISHED;
283 return OPERATOR_RUNNING_MODAL;
286 static int view_pan_cancel(bContext *UNUSED(C), wmOperator *op)
289 return OPERATOR_CANCELLED;
292 static void VIEW2D_OT_pan(wmOperatorType *ot)
295 ot->name = "Pan View";
296 ot->description = "Pan the view";
297 ot->idname = "VIEW2D_OT_pan";
300 ot->exec = view_pan_exec;
301 ot->invoke = view_pan_invoke;
302 ot->modal = view_pan_modal;
303 ot->cancel = view_pan_cancel;
305 /* operator is modal */
306 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
308 /* rna - must keep these in sync with the other operators */
309 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
310 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
313 /* ------------------ Scrollwheel Versions (2) ---------------------- */
315 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
316 static int view_scrollright_exec(bContext *C, wmOperator *op)
320 /* initialize default settings (and validate if ok to run) */
321 if (!view_pan_init(C, op))
322 return OPERATOR_PASS_THROUGH;
324 /* also, check if can pan in horizontal axis */
325 vpd = op->customdata;
326 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
328 return OPERATOR_PASS_THROUGH;
331 /* set RNA-Props - only movement in positive x-direction */
332 RNA_int_set(op->ptr, "deltax", 20);
333 RNA_int_set(op->ptr, "deltay", 0);
335 /* apply movement, then we're done */
339 return OPERATOR_FINISHED;
342 static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
345 ot->name = "Scroll Right";
346 ot->description = "Scroll the view right";
347 ot->idname = "VIEW2D_OT_scroll_right";
350 ot->exec = view_scrollright_exec;
352 /* rna - must keep these in sync with the other operators */
353 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
354 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
359 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
360 static int view_scrollleft_exec(bContext *C, wmOperator *op)
364 /* initialize default settings (and validate if ok to run) */
365 if (!view_pan_init(C, op))
366 return OPERATOR_PASS_THROUGH;
368 /* also, check if can pan in horizontal axis */
369 vpd = op->customdata;
370 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
372 return OPERATOR_PASS_THROUGH;
375 /* set RNA-Props - only movement in negative x-direction */
376 RNA_int_set(op->ptr, "deltax", -20);
377 RNA_int_set(op->ptr, "deltay", 0);
379 /* apply movement, then we're done */
383 return OPERATOR_FINISHED;
386 static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
389 ot->name = "Scroll Left";
390 ot->description = "Scroll the view left";
391 ot->idname = "VIEW2D_OT_scroll_left";
394 ot->exec = view_scrollleft_exec;
396 /* rna - must keep these in sync with the other operators */
397 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
398 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
402 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
403 static int view_scrolldown_exec(bContext *C, wmOperator *op)
407 /* initialize default settings (and validate if ok to run) */
408 if (!view_pan_init(C, op))
409 return OPERATOR_PASS_THROUGH;
411 /* also, check if can pan in vertical axis */
412 vpd = op->customdata;
413 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
415 return OPERATOR_PASS_THROUGH;
419 RNA_int_set(op->ptr, "deltax", 0);
420 RNA_int_set(op->ptr, "deltay", -40);
422 if (RNA_boolean_get(op->ptr, "page")) {
423 ARegion *ar = CTX_wm_region(C);
424 RNA_int_set(op->ptr, "deltay", ar->v2d.mask.ymin - ar->v2d.mask.ymax);
427 /* apply movement, then we're done */
431 return OPERATOR_FINISHED;
434 static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
437 ot->name = "Scroll Down";
438 ot->description = "Scroll the view down";
439 ot->idname = "VIEW2D_OT_scroll_down";
442 ot->exec = view_scrolldown_exec;
444 /* rna - must keep these in sync with the other operators */
445 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
446 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
447 RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll down one page");
452 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
453 static int view_scrollup_exec(bContext *C, wmOperator *op)
457 /* initialize default settings (and validate if ok to run) */
458 if (!view_pan_init(C, op))
459 return OPERATOR_PASS_THROUGH;
461 /* also, check if can pan in vertical axis */
462 vpd = op->customdata;
463 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
465 return OPERATOR_PASS_THROUGH;
469 RNA_int_set(op->ptr, "deltax", 0);
470 RNA_int_set(op->ptr, "deltay", 40);
472 if (RNA_boolean_get(op->ptr, "page")) {
473 ARegion *ar = CTX_wm_region(C);
474 RNA_int_set(op->ptr, "deltay", BLI_RCT_SIZE_Y(&ar->v2d.mask));
477 /* apply movement, then we're done */
481 return OPERATOR_FINISHED;
484 static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
487 ot->name = "Scroll Up";
488 ot->description = "Scroll the view up";
489 ot->idname = "VIEW2D_OT_scroll_up";
492 ot->exec = view_scrollup_exec;
494 /* rna - must keep these in sync with the other operators */
495 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
496 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
497 RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll up one page");
500 /* ********************************************************* */
501 /* SINGLE-STEP VIEW ZOOMING OPERATOR */
503 /* This group of operators come in several forms:
504 * 1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
505 * 2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y) // XXX this could be implemented...
506 * 3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
508 * In order to make sure this works, each operator must define the following RNA-Operator Props:
509 * zoomfacx, zoomfacy - These two zoom factors allow for non-uniform scaling.
510 * It is safe to scale by 0, as these factors are used to determine
511 * amount to enlarge 'cur' by
514 /* ------------------ 'Shared' stuff ------------------------ */
516 /* temp customdata for operator */
517 typedef struct v2dViewZoomData {
518 View2D *v2d; /* view2d we're operating in */
521 /* needed for continuous zoom */
523 double timer_lastdraw;
525 int lastx, lasty; /* previous x/y values of mouse in window */
526 int invoke_event; /* event type that invoked, for modal exits */
527 float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
528 float mx_2d, my_2d; /* initial mouse location in v2d coords */
532 /* initialize panning customdata */
533 static int view_zoomdrag_init(bContext *C, wmOperator *op)
535 ARegion *ar = CTX_wm_region(C);
536 v2dViewZoomData *vzd;
539 /* regions now have v2d-data by default, so check for region */
544 /* check that 2d-view is zoomable */
545 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
548 /* set custom-data for operator */
549 vzd = MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
550 op->customdata = vzd;
552 /* set pointers to owners */
559 /* check if step-zoom can be applied */
560 static int view_zoom_poll(bContext *C)
562 ARegion *ar = CTX_wm_region(C);
565 /* check if there's a region in context to work with */
570 /* check that 2d-view is zoomable */
571 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
574 /* view is zoomable */
578 /* apply transform to view (i.e. adjust 'cur' rect) */
579 static void view_zoomstep_apply(bContext *C, wmOperator *op)
581 v2dViewZoomData *vzd = op->customdata;
582 ARegion *ar = CTX_wm_region(C);
583 View2D *v2d = &ar->v2d;
584 float dx, dy, facx, facy;
586 /* calculate amount to move view by, ensuring symmetry so the
587 * old zoom level is restored after zooming back the same amount
589 facx = RNA_float_get(op->ptr, "zoomfacx");
590 facy = RNA_float_get(op->ptr, "zoomfacy");
593 dx = BLI_RCT_SIZE_X(&v2d->cur) * facx;
594 dy = BLI_RCT_SIZE_Y(&v2d->cur) * facy;
597 dx = (BLI_RCT_SIZE_X(&v2d->cur) / (1.0f + 2.0f * facx)) * facx;
598 dy = (BLI_RCT_SIZE_Y(&v2d->cur) / (1.0f + 2.0f * facy)) * facy;
601 /* only resize view on an axis if change is allowed */
602 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
603 if (v2d->keepofs & V2D_LOCKOFS_X) {
604 v2d->cur.xmax -= 2 * dx;
606 else if (v2d->keepofs & V2D_KEEPOFS_X) {
607 if (v2d->align & V2D_ALIGN_NO_POS_X)
608 v2d->cur.xmin += 2 * dx;
610 v2d->cur.xmax -= 2 * dx;
613 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
614 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_RCT_SIZE_X(&v2d->cur);
615 float mval_faci = 1.0f - mval_fac;
616 float ofs = (mval_fac * dx) - (mval_faci * dx);
618 v2d->cur.xmin += ofs + dx;
619 v2d->cur.xmax += ofs - dx;
627 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
628 if (v2d->keepofs & V2D_LOCKOFS_Y) {
629 v2d->cur.ymax -= 2 * dy;
631 else if (v2d->keepofs & V2D_KEEPOFS_Y) {
632 if (v2d->align & V2D_ALIGN_NO_POS_Y)
633 v2d->cur.ymin += 2 * dy;
635 v2d->cur.ymax -= 2 * dy;
638 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
639 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_RCT_SIZE_Y(&v2d->cur);
640 float mval_faci = 1.0f - mval_fac;
641 float ofs = (mval_fac * dy) - (mval_faci * dy);
643 v2d->cur.ymin += ofs + dy;
644 v2d->cur.ymax += ofs - dy;
653 /* validate that view is in valid configuration after this operation */
654 UI_view2d_curRect_validate(v2d);
656 /* request updates to be done... */
657 ED_region_tag_redraw(vzd->ar);
658 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
661 /* --------------- Individual Operators ------------------- */
663 /* cleanup temp customdata */
664 static void view_zoomstep_exit(wmOperator *op)
666 if (op->customdata) {
667 MEM_freeN(op->customdata);
668 op->customdata = NULL;
672 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
673 static int view_zoomin_exec(bContext *C, wmOperator *op)
675 ScrArea *sa = CTX_wm_area(C);
676 short do_zoom_x = TRUE;
677 short do_zoom_y = TRUE;
679 /* check that there's an active region, as View2D data resides there */
680 if (!view_zoom_poll(C))
681 return OPERATOR_PASS_THROUGH;
683 /* default not to zoom the sequencer vertically */
684 if (sa && sa->spacetype == SPACE_SEQ) {
685 ARegion *ar = CTX_wm_region(C);
687 if (ar && ar->regiontype != RGN_TYPE_PREVIEW)
691 /* set RNA-Props - zooming in by uniform factor */
692 RNA_float_set(op->ptr, "zoomfacx", do_zoom_x ? 0.0375f : 0.0f);
693 RNA_float_set(op->ptr, "zoomfacy", do_zoom_y ? 0.0375f : 0.0f);
695 /* apply movement, then we're done */
696 view_zoomstep_apply(C, op);
698 view_zoomstep_exit(op);
700 return OPERATOR_FINISHED;
703 static int view_zoomin_invoke(bContext *C, wmOperator *op, wmEvent *event)
705 v2dViewZoomData *vzd;
707 if (!view_zoomdrag_init(C, op))
708 return OPERATOR_PASS_THROUGH;
710 vzd = op->customdata;
712 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
713 ARegion *ar = CTX_wm_region(C);
715 /* store initial mouse position (in view space) */
716 UI_view2d_region_to_view(&ar->v2d,
717 event->mval[0], event->mval[1],
718 &vzd->mx_2d, &vzd->my_2d);
721 return view_zoomin_exec(C, op);
724 static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
727 ot->name = "Zoom In";
728 ot->description = "Zoom in the view";
729 ot->idname = "VIEW2D_OT_zoom_in";
732 ot->invoke = view_zoomin_invoke;
733 ot->exec = view_zoomin_exec; // XXX, needs view_zoomdrag_init called first.
734 ot->poll = view_zoom_poll;
736 /* rna - must keep these in sync with the other operators */
737 RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
738 RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
741 /* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
742 static int view_zoomout_exec(bContext *C, wmOperator *op)
744 ScrArea *sa = CTX_wm_area(C);
745 short do_zoom_x = TRUE;
746 short do_zoom_y = TRUE;
748 /* check that there's an active region, as View2D data resides there */
749 if (!view_zoom_poll(C))
750 return OPERATOR_PASS_THROUGH;
752 /* default not to zoom the sequencer vertically */
753 if (sa && sa->spacetype == SPACE_SEQ) {
754 ARegion *ar = CTX_wm_region(C);
756 if (ar && ar->regiontype != RGN_TYPE_PREVIEW)
760 /* set RNA-Props - zooming in by uniform factor */
761 RNA_float_set(op->ptr, "zoomfacx", do_zoom_x ? -0.0375f : 0.0f);
762 RNA_float_set(op->ptr, "zoomfacy", do_zoom_y ? -0.0375f : 0.0f);
764 /* apply movement, then we're done */
765 view_zoomstep_apply(C, op);
767 view_zoomstep_exit(op);
769 return OPERATOR_FINISHED;
772 static int view_zoomout_invoke(bContext *C, wmOperator *op, wmEvent *event)
774 v2dViewZoomData *vzd;
776 if (!view_zoomdrag_init(C, op))
777 return OPERATOR_PASS_THROUGH;
779 vzd = op->customdata;
781 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
782 ARegion *ar = CTX_wm_region(C);
784 /* store initial mouse position (in view space) */
785 UI_view2d_region_to_view(&ar->v2d,
786 event->mval[0], event->mval[1],
787 &vzd->mx_2d, &vzd->my_2d);
790 return view_zoomout_exec(C, op);
793 static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
796 ot->name = "Zoom Out";
797 ot->description = "Zoom out the view";
798 ot->idname = "VIEW2D_OT_zoom_out";
801 ot->invoke = view_zoomout_invoke;
802 // ot->exec = view_zoomout_exec; // XXX, needs view_zoomdrag_init called first.
803 ot->poll = view_zoom_poll;
805 /* rna - must keep these in sync with the other operators */
806 RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
807 RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
810 /* ********************************************************* */
811 /* DRAG-ZOOM OPERATOR */
813 /* MMB Drag - allows non-uniform scaling by dragging mouse
815 * In order to make sure this works, each operator must define the following RNA-Operator Props:
816 * deltax, deltay - amounts to add to each side of the 'cur' rect
819 /* apply transform to view (i.e. adjust 'cur' rect) */
820 static void view_zoomdrag_apply(bContext *C, wmOperator *op)
822 v2dViewZoomData *vzd = op->customdata;
823 View2D *v2d = vzd->v2d;
826 /* get amount to move view by */
827 dx = RNA_float_get(op->ptr, "deltax");
828 dy = RNA_float_get(op->ptr, "deltay");
830 /* continuous zoom shouldn't move that fast... */
831 if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
832 double time = PIL_check_seconds_timer();
833 float time_step = (float)(time - vzd->timer_lastdraw);
835 dx *= time_step * 0.5f;
836 dy *= time_step * 0.5f;
838 vzd->timer_lastdraw = time;
841 /* only move view on an axis if change is allowed */
842 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
843 if (v2d->keepofs & V2D_LOCKOFS_X) {
844 v2d->cur.xmax -= 2 * dx;
847 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
848 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_RCT_SIZE_X(&v2d->cur);
849 float mval_faci = 1.0f - mval_fac;
850 float ofs = (mval_fac * dx) - (mval_faci * dx);
852 v2d->cur.xmin += ofs + dx;
853 v2d->cur.xmax += ofs - dx;
861 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
862 if (v2d->keepofs & V2D_LOCKOFS_Y) {
863 v2d->cur.ymax -= 2 * dy;
866 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
867 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_RCT_SIZE_Y(&v2d->cur);
868 float mval_faci = 1.0f - mval_fac;
869 float ofs = (mval_fac * dy) - (mval_faci * dy);
871 v2d->cur.ymin += ofs + dy;
872 v2d->cur.ymax += ofs - dy;
881 /* validate that view is in valid configuration after this operation */
882 UI_view2d_curRect_validate(v2d);
884 /* request updates to be done... */
885 ED_region_tag_redraw(vzd->ar);
886 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
889 /* cleanup temp customdata */
890 static void view_zoomdrag_exit(bContext *C, wmOperator *op)
892 if (op->customdata) {
893 v2dViewZoomData *vzd = op->customdata;
896 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer);
898 MEM_freeN(op->customdata);
899 op->customdata = NULL;
903 static int view_zoomdrag_cancel(bContext *C, wmOperator *op)
905 view_zoomdrag_exit(C, op);
907 return OPERATOR_CANCELLED;
910 /* for 'redo' only, with no user input */
911 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
913 if (!view_zoomdrag_init(C, op))
914 return OPERATOR_PASS_THROUGH;
916 view_zoomdrag_apply(C, op);
917 view_zoomdrag_exit(C, op);
918 return OPERATOR_FINISHED;
921 /* set up modal operator and relevant settings */
922 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
924 wmWindow *window = CTX_wm_window(C);
925 v2dViewZoomData *vzd;
928 /* set up customdata */
929 if (!view_zoomdrag_init(C, op))
930 return OPERATOR_PASS_THROUGH;
932 vzd = op->customdata;
935 if (event->type == MOUSEZOOM) {
938 vzd->lastx = event->prevx;
939 vzd->lasty = event->prevy;
941 /* As we have only 1D information (magnify value), feed both axes
942 * with magnify information that is stored in x axis
944 fac = 0.01f * (event->x - event->prevx);
945 dx = fac * BLI_RCT_SIZE_X(&v2d->cur) / 10.0f;
946 dy = fac * BLI_RCT_SIZE_Y(&v2d->cur) / 10.0f;
948 RNA_float_set(op->ptr, "deltax", dx);
949 RNA_float_set(op->ptr, "deltay", dy);
951 view_zoomdrag_apply(C, op);
952 view_zoomdrag_exit(C, op);
953 return OPERATOR_FINISHED;
956 /* set initial settings */
957 vzd->lastx = event->x;
958 vzd->lasty = event->y;
959 RNA_float_set(op->ptr, "deltax", 0);
960 RNA_float_set(op->ptr, "deltay", 0);
962 /* for modal exit test */
963 vzd->invoke_event = event->type;
965 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
966 ARegion *ar = CTX_wm_region(C);
968 /* store initial mouse position (in view space) */
969 UI_view2d_region_to_view(&ar->v2d,
970 event->mval[0], event->mval[1],
971 &vzd->mx_2d, &vzd->my_2d);
974 if (v2d->keepofs & V2D_LOCKOFS_X)
975 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
976 else if (v2d->keepofs & V2D_LOCKOFS_Y)
977 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
979 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
981 /* add temp handler */
982 WM_event_add_modal_handler(C, op);
984 if (U.viewzoom == USER_ZOOM_CONT) {
985 /* needs a timer to continue redrawing */
986 vzd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
987 vzd->timer_lastdraw = PIL_check_seconds_timer();
990 return OPERATOR_RUNNING_MODAL;
993 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
994 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
996 v2dViewZoomData *vzd = op->customdata;
997 View2D *v2d = vzd->v2d;
999 /* execute the events */
1000 if (event->type == TIMER && event->customdata == vzd->timer) {
1001 view_zoomdrag_apply(C, op);
1003 else if (event->type == MOUSEMOVE) {
1006 /* calculate new delta transform, based on zooming mode */
1007 if (U.viewzoom == USER_ZOOM_SCALE) {
1008 /* 'scale' zooming */
1011 /* x-axis transform */
1012 dist = BLI_RCT_SIZE_X(&v2d->mask) / 2.0f;
1013 dx = 1.0f - (fabsf(vzd->lastx - dist) + 2.0f) / (fabsf(event->x - dist) + 2.0f);
1014 dx *= 0.5f * BLI_RCT_SIZE_X(&v2d->cur);
1016 /* y-axis transform */
1017 dist = BLI_RCT_SIZE_Y(&v2d->mask) / 2.0f;
1018 dy = 1.0f - (fabsf(vzd->lasty - dist) + 2.0f) / (fabsf(event->y - dist) + 2.0f);
1019 dy *= 0.5f * BLI_RCT_SIZE_Y(&v2d->cur);
1022 /* 'continuous' or 'dolly' */
1025 /* x-axis transform */
1026 fac = 0.01f * (event->x - vzd->lastx);
1027 dx = fac * BLI_RCT_SIZE_X(&v2d->cur);
1029 /* y-axis transform */
1030 fac = 0.01f * (event->y - vzd->lasty);
1031 dy = fac * BLI_RCT_SIZE_Y(&v2d->cur);
1033 /* continuous zoom shouldn't move that fast... */
1034 if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1035 double time = PIL_check_seconds_timer();
1036 float time_step = (float)(time - vzd->timer_lastdraw);
1038 dx /= (0.1f / time_step);
1039 dy /= (0.1f / time_step);
1041 vzd->timer_lastdraw = time;
1046 /* set transform amount, and add current deltas to stored total delta (for redo) */
1047 RNA_float_set(op->ptr, "deltax", dx);
1048 RNA_float_set(op->ptr, "deltay", dy);
1052 /* store mouse coordinates for next time, if not doing continuous zoom
1053 * - continuous zoom only depends on distance of mouse to starting point to determine rate of change
1055 if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1056 vzd->lastx = event->x;
1057 vzd->lasty = event->y;
1061 view_zoomdrag_apply(C, op);
1063 else if (event->type == vzd->invoke_event || event->type == ESCKEY) {
1064 if (event->val == KM_RELEASE) {
1066 /* for redo, store the overall deltas - need to respect zoom-locks here... */
1067 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0)
1068 RNA_float_set(op->ptr, "deltax", vzd->dx);
1070 RNA_float_set(op->ptr, "deltax", 0);
1072 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0)
1073 RNA_float_set(op->ptr, "deltay", vzd->dy);
1075 RNA_float_set(op->ptr, "deltay", 0);
1077 /* free customdata */
1078 view_zoomdrag_exit(C, op);
1079 WM_cursor_restore(CTX_wm_window(C));
1081 return OPERATOR_FINISHED;
1085 return OPERATOR_RUNNING_MODAL;
1088 static void VIEW2D_OT_zoom(wmOperatorType *ot)
1091 ot->name = "Zoom 2D View";
1092 ot->description = "Zoom in/out the view";
1093 ot->idname = "VIEW2D_OT_zoom";
1096 ot->exec = view_zoomdrag_exec;
1097 ot->invoke = view_zoomdrag_invoke;
1098 ot->modal = view_zoomdrag_modal;
1099 ot->cancel = view_zoomdrag_cancel;
1101 ot->poll = view_zoom_poll;
1103 /* operator is repeatable */
1104 // ot->flag = OPTYPE_BLOCKING;
1106 /* rna - must keep these in sync with the other operators */
1107 RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1108 RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1111 /* ********************************************************* */
1114 /* The user defines a rect using standard borderselect tools, and we use this rect to
1115 * define the new zoom-level of the view in the following ways:
1116 * 1) LEFTMOUSE - zoom in to view
1117 * 2) RIGHTMOUSE - zoom out of view
1119 * Currently, these key mappings are hardcoded, but it shouldn't be too important to
1120 * have custom keymappings for this...
1123 static int view_borderzoom_exec(bContext *C, wmOperator *op)
1125 ARegion *ar = CTX_wm_region(C);
1126 View2D *v2d = &ar->v2d;
1128 rctf cur_new = v2d->cur;
1131 /* convert coordinates of rect to 'tot' rect coordinates */
1132 UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
1133 UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
1135 /* check if zooming in/out view */
1136 gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
1138 if (gesture_mode == GESTURE_MODAL_IN) {
1140 * - 'cur' rect will be defined by the coordinates of the border region
1141 * - just set the 'cur' rect to have the same coordinates as the border region
1142 * if zoom is allowed to be changed
1144 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1145 cur_new.xmin = rect.xmin;
1146 cur_new.xmax = rect.xmax;
1148 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1149 cur_new.ymin = rect.ymin;
1150 cur_new.ymax = rect.ymax;
1153 else { /* if (gesture_mode == GESTURE_MODAL_OUT) */
1156 * - the current 'cur' rect coordinates are going to end up where the 'rect' ones are,
1157 * but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1158 * - calculate zoom factor, and adjust using center-point
1160 float zoom, center, size;
1162 /* TODO: is this zoom factor calculation valid? It seems to produce same results everytime... */
1163 if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0) {
1164 size = BLI_RCT_SIZE_X(&cur_new);
1165 zoom = size / BLI_RCT_SIZE_X(&rect);
1166 center = BLI_RCT_CENTER_X(&cur_new);
1168 cur_new.xmin = center - (size * zoom);
1169 cur_new.xmax = center + (size * zoom);
1171 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) == 0) {
1172 size = BLI_RCT_SIZE_Y(&cur_new);
1173 zoom = size / BLI_RCT_SIZE_Y(&rect);
1174 center = BLI_RCT_CENTER_Y(&cur_new);
1176 cur_new.ymin = center - (size * zoom);
1177 cur_new.ymax = center + (size * zoom);
1181 UI_view2d_smooth_view(C, ar, &cur_new);
1183 return OPERATOR_FINISHED;
1186 static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
1189 ot->name = "Zoom to Border";
1190 ot->description = "Zoom in the view to the nearest item contained in the border";
1191 ot->idname = "VIEW2D_OT_zoom_border";
1194 ot->invoke = WM_border_select_invoke;
1195 ot->exec = view_borderzoom_exec;
1196 ot->modal = WM_border_select_modal;
1197 ot->cancel = WM_border_select_cancel;
1199 ot->poll = view_zoom_poll;
1202 WM_operator_properties_gesture_border(ot, FALSE);
1205 /* ********************************************************* */
1208 struct SmoothView2DStore {
1209 rctf orig_cur, new_cur;
1211 double time_allowed;
1215 * function to get a factor out of a rectangle
1217 * note: this doesn't always work as well as it might because the target size
1218 * may not be reached because of clamping the desired rect, we _could_
1219 * attempt to clamp the rect before working out the zoom factor but its
1220 * not really worthwhile for the few cases this happens.
1222 static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b)
1224 float size_a[2] = {BLI_RCT_SIZE_X(rect_a),
1225 BLI_RCT_SIZE_Y(rect_a)};
1226 float size_b[2] = {BLI_RCT_SIZE_X(rect_b),
1227 BLI_RCT_SIZE_Y(rect_b)};
1228 float cent_a[2] = {BLI_RCT_CENTER_X(rect_a),
1229 BLI_RCT_CENTER_Y(rect_a)};
1230 float cent_b[2] = {BLI_RCT_CENTER_X(rect_b),
1231 BLI_RCT_CENTER_Y(rect_b)};
1233 float fac_max = 0.0f;
1238 for (i = 0; i < 2; i++) {
1239 /* axis translation normalized to scale */
1240 tfac = fabsf(cent_a[i] - cent_b[i]) / minf(size_a[i], size_b[i]);
1241 fac_max = maxf(fac_max, tfac);
1242 if (fac_max >= 1.0f) break;
1244 /* axis scale difference, x2 so doubling or half gives 1.0f */
1245 tfac = (1.0f - (minf(size_a[i], size_b[i]) / maxf(size_a[i], size_b[i]))) * 2.0f;
1246 fac_max = maxf(fac_max, tfac);
1247 if (fac_max >= 1.0f) break;
1249 return minf(fac_max, 1.0f);
1252 /* will start timer if appropriate */
1253 /* the arguments are the desired situation */
1254 void UI_view2d_smooth_view(bContext *C, ARegion *ar,
1257 wmWindowManager *wm = CTX_wm_manager(C);
1258 wmWindow *win = CTX_wm_window(C);
1260 View2D *v2d = &ar->v2d;
1261 struct SmoothView2DStore sms = {{0}};
1265 /* initialize sms */
1266 sms.new_cur = v2d->cur;
1268 /* store the options we want to end with */
1269 if (cur) sms.new_cur = *cur;
1272 fac = smooth_view_rect_to_fac(&v2d->cur, cur);
1275 if (C && U.smooth_viewtx && fac > FLT_EPSILON) {
1276 int changed = 0; /* zero means no difference */
1278 if (BLI_rctf_compare(&sms.new_cur, &v2d->cur, FLT_EPSILON) == FALSE)
1282 /* The new view is different from the old one
1283 * so animate the view */
1285 sms.orig_cur = v2d->cur;
1287 sms.time_allowed = (double)U.smooth_viewtx / 1000.0;
1289 /* scale the time allowed the change in view */
1290 sms.time_allowed *= (double)fac;
1292 /* keep track of running timer! */
1293 if (v2d->sms == NULL)
1294 v2d->sms = MEM_mallocN(sizeof(struct SmoothView2DStore), "smoothview v2d");
1296 if (v2d->smooth_timer)
1297 WM_event_remove_timer(wm, win, v2d->smooth_timer);
1298 /* TIMER1 is hardcoded in keymap */
1299 v2d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); /* max 30 frs/sec */
1305 /* if we get here nothing happens */
1307 v2d->cur = sms.new_cur;
1309 UI_view2d_curRect_validate(v2d);
1310 ED_region_tag_redraw(ar);
1311 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1315 /* only meant for timer usage */
1316 static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
1318 ARegion *ar = CTX_wm_region(C);
1319 View2D *v2d = &ar->v2d;
1320 struct SmoothView2DStore *sms = v2d->sms;
1323 /* escape if not our timer */
1324 if (v2d->smooth_timer == NULL || v2d->smooth_timer != event->customdata)
1325 return OPERATOR_PASS_THROUGH;
1327 if (sms->time_allowed != 0.0)
1328 step = (float)((v2d->smooth_timer->duration) / sms->time_allowed);
1334 v2d->cur = sms->new_cur;
1336 MEM_freeN(v2d->sms);
1339 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), v2d->smooth_timer);
1340 v2d->smooth_timer = NULL;
1344 step = (3.0f * step * step - 2.0f * step * step * step);
1346 BLI_rctf_interp(&v2d->cur, &sms->orig_cur, &sms->new_cur, step);
1349 UI_view2d_curRect_validate(v2d);
1350 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1351 ED_region_tag_redraw(ar);
1353 return OPERATOR_FINISHED;
1356 static void VIEW2D_OT_smoothview(wmOperatorType *ot)
1359 ot->name = "Smooth View 2D";
1360 ot->description = "Zoom in the view to the nearest item contained in the border";
1361 ot->idname = "VIEW2D_OT_smoothview";
1364 ot->invoke = view2d_smoothview_invoke;
1366 ot->poll = view2d_poll;
1369 WM_operator_properties_gesture_border(ot, FALSE);
1372 /* ********************************************************* */
1375 /* Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
1376 * 1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable,
1377 * enlarge 'cur' rect on the relevant side
1378 * 2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
1380 * In order to make sure this works, each operator must define the following RNA-Operator Props:
1381 * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
1384 /* customdata for scroller-invoke data */
1385 typedef struct v2dScrollerMove {
1386 View2D *v2d; /* View2D data that this operation affects */
1387 ARegion *ar; /* region that the scroller is in */
1389 short scroller; /* scroller that mouse is in ('h' or 'v') */
1390 short zone; /* -1 is min zoomer, 0 is bar, 1 is max zoomer */ // XXX find some way to provide visual feedback of this (active color?)
1392 float fac; /* view adjustment factor, based on size of region */
1393 float delta; /* amount moved by mouse on axis of interest */
1395 float scrollbarwidth; /* width of the scrollbar itself, used for page up/down clicks */
1396 int scrollbar_orig; /* initial location of scrollbar x/y, mouse relative */
1398 int lastx, lasty; /* previous mouse coordinates (in screen coordinates) for determining movement */
1402 /* View2DScrollers is typedef'd in UI_view2d.h
1403 * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
1404 * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
1405 * For now, we don't need to have a separate (internal) header for structs like this...
1407 struct View2DScrollers {
1409 int vert_min, vert_max; /* vertical scrollbar */
1410 int hor_min, hor_max; /* horizontal scrollbar */
1413 /* quick enum for vsm->zone (scroller handles) */
1415 SCROLLHANDLE_MIN = -1,
1418 SCROLLHANDLE_MIN_OUTSIDE,
1419 SCROLLHANDLE_MAX_OUTSIDE
1420 } /*eV2DScrollerHandle_Zone*/;
1422 /* ------------------------ */
1424 /* check if mouse is within scroller handle
1425 * - mouse = relevant mouse coordinate in region space
1426 * - sc_min, sc_max = extents of scroller 'groove' (potential available space for scroller)
1427 * - sh_min, sh_max = positions of scrollbar handles
1429 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1431 short in_min, in_max, in_bar, out_min, out_max, in_view = 1;
1433 /* firstly, check if
1434 * - 'bubble' fills entire scroller
1435 * - 'bubble' completely out of view on either side
1437 if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view = 0;
1438 if (sh_min == sh_max) {
1439 if (sh_min <= sc_min) in_view = 0;
1440 if (sh_max >= sc_max) in_view = 0;
1443 if (sh_max <= sc_min) in_view = 0;
1444 if (sh_min >= sc_max) in_view = 0;
1449 return SCROLLHANDLE_BAR;
1452 /* check if mouse is in or past either handle */
1453 /* TODO: check if these extents are still valid or not */
1454 in_max = ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
1455 in_min = ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
1456 in_bar = ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) );
1457 out_min = mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE);
1458 out_max = mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE);
1461 return SCROLLHANDLE_BAR;
1463 return SCROLLHANDLE_MAX;
1465 return SCROLLHANDLE_MIN;
1467 return SCROLLHANDLE_MIN_OUTSIDE;
1469 return SCROLLHANDLE_MAX_OUTSIDE;
1471 /* unlikely to happen, though we just cover it in case */
1472 return SCROLLHANDLE_BAR;
1475 /* initialize customdata for scroller manipulation operator */
1476 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
1478 v2dScrollerMove *vsm;
1479 View2DScrollers *scrollers;
1480 ARegion *ar = CTX_wm_region(C);
1481 View2D *v2d = &ar->v2d;
1484 /* set custom-data for operator */
1485 vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
1486 op->customdata = vsm;
1488 /* set general data */
1491 vsm->scroller = in_scroller;
1493 /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1494 vsm->lastx = event->x;
1495 vsm->lasty = event->y;
1496 /* 'zone' depends on where mouse is relative to bubble
1497 * - zooming must be allowed on this axis, otherwise, default to pan
1499 scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1502 if (in_scroller == 'h') {
1503 /* horizontal scroller - calculate adjustment factor first */
1504 mask_size = (float)BLI_RCT_SIZE_X(&v2d->hor);
1505 vsm->fac = BLI_RCT_SIZE_X(&v2d->tot) / mask_size;
1507 /* get 'zone' (i.e. which part of scroller is activated) */
1508 vsm->zone = mouse_in_scroller_handle(event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
1510 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1511 /* default to scroll, as handles not usable */
1512 vsm->zone = SCROLLHANDLE_BAR;
1515 vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
1516 vsm->scrollbar_orig = ((scrollers->hor_max + scrollers->hor_min) / 2) + ar->winrct.xmin;
1519 /* vertical scroller - calculate adjustment factor first */
1520 mask_size = (float)BLI_RCT_SIZE_Y(&v2d->vert);
1521 vsm->fac = BLI_RCT_SIZE_Y(&v2d->tot) / mask_size;
1523 /* get 'zone' (i.e. which part of scroller is activated) */
1524 vsm->zone = mouse_in_scroller_handle(event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
1526 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1527 /* default to scroll, as handles not usable */
1528 vsm->zone = SCROLLHANDLE_BAR;
1531 vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
1532 vsm->scrollbar_orig = ((scrollers->vert_max + scrollers->vert_min) / 2) + +ar->winrct.ymin;
1535 UI_view2d_scrollers_free(scrollers);
1536 ED_region_tag_redraw(ar);
1539 /* cleanup temp customdata */
1540 static void scroller_activate_exit(bContext *C, wmOperator *op)
1542 if (op->customdata) {
1543 v2dScrollerMove *vsm = op->customdata;
1545 vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE | V2D_SCROLL_V_ACTIVE);
1547 MEM_freeN(op->customdata);
1548 op->customdata = NULL;
1550 ED_region_tag_redraw(CTX_wm_region(C));
1554 static int scroller_activate_cancel(bContext *C, wmOperator *op)
1556 scroller_activate_exit(C, op);
1558 return OPERATOR_CANCELLED;
1561 /* apply transform to view (i.e. adjust 'cur' rect) */
1562 static void scroller_activate_apply(bContext *C, wmOperator *op)
1564 v2dScrollerMove *vsm = op->customdata;
1565 View2D *v2d = vsm->v2d;
1568 /* calculate amount to move view by */
1569 temp = vsm->fac * vsm->delta;
1571 /* type of movement */
1572 switch (vsm->zone) {
1573 case SCROLLHANDLE_MIN:
1574 /* only expand view on axis if zoom is allowed */
1575 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1576 v2d->cur.xmin -= temp;
1577 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1578 v2d->cur.ymin -= temp;
1581 case SCROLLHANDLE_MAX:
1583 /* only expand view on axis if zoom is allowed */
1584 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1585 v2d->cur.xmax += temp;
1586 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1587 v2d->cur.ymax += temp;
1590 case SCROLLHANDLE_MIN_OUTSIDE:
1591 case SCROLLHANDLE_MAX_OUTSIDE:
1592 case SCROLLHANDLE_BAR:
1594 /* only move view on an axis if panning is allowed */
1595 if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1596 v2d->cur.xmin += temp;
1597 v2d->cur.xmax += temp;
1599 if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1600 v2d->cur.ymin += temp;
1601 v2d->cur.ymax += temp;
1607 /* validate that view is in valid configuration after this operation */
1608 UI_view2d_curRect_validate(v2d);
1610 /* request updates to be done... */
1611 ED_region_tag_redraw(vsm->ar);
1612 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1615 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
1616 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
1618 v2dScrollerMove *vsm = op->customdata;
1620 /* execute the events */
1621 switch (event->type) {
1624 /* calculate new delta transform, then store mouse-coordinates for next-time */
1625 if (ELEM(vsm->zone, SCROLLHANDLE_BAR, SCROLLHANDLE_MAX)) {
1626 /* if using bar (i.e. 'panning') or 'max' zoom widget */
1627 switch (vsm->scroller) {
1628 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1629 vsm->delta = (float)(event->x - vsm->lastx);
1631 case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1632 vsm->delta = (float)(event->y - vsm->lasty);
1636 else if (vsm->zone == SCROLLHANDLE_MIN) {
1637 /* using 'min' zoom widget */
1638 switch (vsm->scroller) {
1639 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
1640 vsm->delta = (float)(vsm->lastx - event->x);
1642 case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
1643 vsm->delta = (float)(vsm->lasty - event->y);
1648 /* store previous coordinates */
1649 vsm->lastx = event->x;
1650 vsm->lasty = event->y;
1652 scroller_activate_apply(C, op);
1658 if (event->val == KM_RELEASE) {
1659 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
1660 if (ELEM(vsm->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
1661 if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE)
1662 vsm->delta = -vsm->scrollbarwidth * 0.8f;
1663 else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE)
1664 vsm->delta = vsm->scrollbarwidth * 0.8f;
1666 scroller_activate_apply(C, op);
1667 scroller_activate_exit(C, op);
1668 return OPERATOR_FINISHED;
1671 /* otherwise, end the drag action */
1672 if (vsm->lastx || vsm->lasty) {
1673 scroller_activate_exit(C, op);
1674 return OPERATOR_FINISHED;
1681 return OPERATOR_RUNNING_MODAL;
1685 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
1686 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1688 ARegion *ar = CTX_wm_region(C);
1689 View2D *v2d = &ar->v2d;
1690 short in_scroller = 0;
1692 /* check if mouse in scrollbars, if they're enabled */
1693 in_scroller = UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
1695 /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
1697 v2dScrollerMove *vsm;
1699 /* initialize customdata */
1700 scroller_activate_init(C, op, event, in_scroller);
1701 vsm = (v2dScrollerMove *)op->customdata;
1703 /* support for quick jump to location - gtk and qt do this on linux */
1704 if (event->type == MIDDLEMOUSE) {
1705 switch (vsm->scroller) {
1706 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1707 vsm->delta = (float)(event->x - vsm->scrollbar_orig);
1709 case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1710 vsm->delta = (float)(event->y - vsm->scrollbar_orig);
1713 scroller_activate_apply(C, op);
1715 vsm->zone = SCROLLHANDLE_BAR;
1718 /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
1719 * NOTE: see view2d.c for latest conditions, and keep this in sync with that
1721 if (ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1722 if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) == 0) ||
1723 ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) == 0))
1725 /* switch to bar (i.e. no scaling gets handled) */
1726 vsm->zone = SCROLLHANDLE_BAR;
1730 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
1731 if (vsm->zone == SCROLLHANDLE_BAR) {
1732 if (((vsm->scroller == 'h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
1733 ((vsm->scroller == 'v') && (v2d->keepofs & V2D_LOCKOFS_Y)))
1735 /* free customdata initialized */
1736 scroller_activate_exit(C, op);
1738 /* can't catch this event for ourselves, so let it go to someone else? */
1739 return OPERATOR_PASS_THROUGH;
1743 /* zone is also inappropriate if scroller is not visible... */
1744 if (((vsm->scroller == 'h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_HORIZONTAL_FULLR))) ||
1745 ((vsm->scroller == 'v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE | V2D_SCROLL_VERTICAL_FULLR))) )
1747 /* free customdata initialized */
1748 scroller_activate_exit(C, op);
1750 /* can't catch this event for ourselves, so let it go to someone else? */
1751 /* XXX note: if handlers use mask rect to clip input, input will fail for this case */
1752 return OPERATOR_PASS_THROUGH;
1755 /* activate the scroller */
1756 if (vsm->scroller == 'h')
1757 v2d->scroll_ui |= V2D_SCROLL_H_ACTIVE;
1759 v2d->scroll_ui |= V2D_SCROLL_V_ACTIVE;
1761 /* still ok, so can add */
1762 WM_event_add_modal_handler(C, op);
1763 return OPERATOR_RUNNING_MODAL;
1766 /* not in scroller, so nothing happened... (pass through let's something else catch event) */
1767 return OPERATOR_PASS_THROUGH;
1771 /* LMB-Drag in Scrollers - not repeatable operator! */
1772 static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
1775 ot->name = "Scroller Activate";
1776 ot->description = "Scroll view by mouse click and drag";
1777 ot->idname = "VIEW2D_OT_scroller_activate";
1780 ot->flag = OPTYPE_BLOCKING;
1783 ot->invoke = scroller_activate_invoke;
1784 ot->modal = scroller_activate_modal;
1785 ot->cancel = scroller_activate_cancel;
1787 ot->poll = view2d_poll;
1790 /* ********************************************************* */
1793 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
1795 uiStyle *style = UI_GetStyle();
1796 ARegion *ar = CTX_wm_region(C);
1797 View2D *v2d = &ar->v2d;
1801 winx = (float)(BLI_RCT_SIZE_X(&v2d->mask) + 1);
1802 winy = (float)(BLI_RCT_SIZE_Y(&v2d->mask) + 1);
1804 v2d->cur.xmax = v2d->cur.xmin + winx;
1805 v2d->cur.ymax = v2d->cur.ymin + winy;
1809 /* posx and negx flags are mutually exclusive, so watch out */
1810 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1811 v2d->cur.xmax = 0.0f;
1812 v2d->cur.xmin = -winx * style->panelzoom;
1814 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
1815 v2d->cur.xmax = winx * style->panelzoom;
1816 v2d->cur.xmin = 0.0f;
1819 /* - posx and negx flags are mutually exclusive, so watch out */
1820 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1821 v2d->cur.ymax = 0.0f;
1822 v2d->cur.ymin = -winy * style->panelzoom;
1824 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
1825 v2d->cur.ymax = winy * style->panelzoom;
1826 v2d->cur.ymin = 0.0f;
1830 /* validate that view is in valid configuration after this operation */
1831 UI_view2d_curRect_validate(v2d);
1833 /* request updates to be done... */
1834 ED_region_tag_redraw(ar);
1835 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1837 return OPERATOR_FINISHED;
1840 static void VIEW2D_OT_reset(wmOperatorType *ot)
1843 ot->name = "Reset View";
1844 ot->description = "Reset the view";
1845 ot->idname = "VIEW2D_OT_reset";
1848 ot->exec = reset_exec;
1849 ot->poll = view2d_poll;
1852 /* ********************************************************* */
1855 void UI_view2d_operatortypes(void)
1857 WM_operatortype_append(VIEW2D_OT_pan);
1859 WM_operatortype_append(VIEW2D_OT_scroll_left);
1860 WM_operatortype_append(VIEW2D_OT_scroll_right);
1861 WM_operatortype_append(VIEW2D_OT_scroll_up);
1862 WM_operatortype_append(VIEW2D_OT_scroll_down);
1864 WM_operatortype_append(VIEW2D_OT_zoom_in);
1865 WM_operatortype_append(VIEW2D_OT_zoom_out);
1867 WM_operatortype_append(VIEW2D_OT_zoom);
1868 WM_operatortype_append(VIEW2D_OT_zoom_border);
1870 WM_operatortype_append(VIEW2D_OT_smoothview);
1872 WM_operatortype_append(VIEW2D_OT_scroller_activate);
1874 WM_operatortype_append(VIEW2D_OT_reset);
1877 void UI_view2d_keymap(wmKeyConfig *keyconf)
1879 wmKeyMap *keymap = WM_keymap_find(keyconf, "View2D", 0, 0);
1883 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1884 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", MIDDLEMOUSE, KM_PRESS, 0, 0);
1887 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1888 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
1890 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1892 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
1893 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
1895 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
1896 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
1898 /* zoom - single step */
1899 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
1900 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
1901 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1902 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1904 WM_keymap_verify_item(keymap, "VIEW2D_OT_smoothview", TIMER1, KM_ANY, KM_ANY, 0);
1906 /* scroll up/down - no modifiers, only when zoom fails */
1907 /* these may fail if zoom is disallowed, in which case they should pass on event */
1908 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1909 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1910 /* these may be necessary if vertical scroll is disallowed */
1911 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1912 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
1914 /* alternatives for page up/down to scroll */
1915 #if 0 // XXX disabled, since this causes conflicts with hotkeys in animation editors
1916 /* scroll up/down may fall through to left/right */
1917 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1918 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1919 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, 0, 0);
1920 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, 0, 0);
1921 /* shift for moving view left/right with page up/down */
1922 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
1923 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
1927 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1928 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1930 /* borderzoom - drag */
1931 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
1933 /* Alternative keymap for buttons listview */
1934 keymap = WM_keymap_find(keyconf, "View2D Buttons List", 0, 0);
1936 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1937 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", MIDDLEMOUSE, KM_PRESS, 0, 0);
1939 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1940 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1941 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1942 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1944 kmi = WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1945 RNA_boolean_set(kmi->ptr, "page", TRUE);
1946 kmi = WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1947 RNA_boolean_set(kmi->ptr, "page", TRUE);
1949 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1950 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1951 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1952 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1953 WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);