4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
23 * Contributor(s): Blender Foundation, Joshua Leung
25 * ***** END GPL LICENSE BLOCK *****
30 #include "MEM_guardedalloc.h"
32 #include "DNA_userdef_types.h"
34 #include "BLI_blenlib.h"
36 #include "BKE_context.h"
37 #include "BKE_utildefines.h"
39 #include "RNA_access.h"
40 #include "RNA_define.h"
47 #include "ED_screen.h"
49 #include "UI_view2d.h"
51 static int view2d_poll(bContext *C)
53 ARegion *ar= CTX_wm_region(C);
55 return (ar != NULL) && (ar->v2d.flag & V2D_IS_INITIALISED);
58 /* ********************************************************* */
59 /* VIEW PANNING OPERATOR */
61 /* This group of operators come in several forms:
62 * 1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
63 * 2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
65 * In order to make sure this works, each operator must define the following RNA-Operator Props:
66 * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
69 /* ------------------ Shared 'core' stuff ---------------------- */
71 /* temp customdata for operator */
72 typedef struct v2dViewPanData {
73 bScreen *sc; /* screen where view pan was initiated */
74 ScrArea *sa; /* area where view pan was initiated */
75 ARegion *ar; /* region where view pan was initiated */
76 View2D *v2d; /* view2d we're operating in */
78 float facx, facy; /* amount to move view relative to zoom */
80 /* options for version 1 */
81 int startx, starty; /* mouse x/y values in window when operator was initiated */
82 int lastx, lasty; /* previous x/y values of mouse in window */
84 short in_scroller; /* for MMB in scrollers (old feature in past, but now not that useful) */
87 /* initialise panning customdata */
88 static int view_pan_init(bContext *C, wmOperator *op)
90 ARegion *ar= CTX_wm_region(C);
95 /* regions now have v2d-data by default, so check for region */
99 /* check if panning is allowed at all */
101 if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y))
104 /* set custom-data for operator */
105 vpd= MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
108 /* set pointers to owners */
109 vpd->sc= CTX_wm_screen(C);
110 vpd->sa= CTX_wm_area(C);
114 /* calculate translation factor - based on size of view */
115 winx= (float)(ar->winrct.xmax - ar->winrct.xmin + 1);
116 winy= (float)(ar->winrct.ymax - ar->winrct.ymin + 1);
117 vpd->facx= (v2d->cur.xmax - v2d->cur.xmin) / winx;
118 vpd->facy= (v2d->cur.ymax - v2d->cur.ymin) / winy;
123 /* apply transform to view (i.e. adjust 'cur' rect) */
124 static void view_pan_apply(bContext *C, wmOperator *op)
126 v2dViewPanData *vpd= op->customdata;
127 View2D *v2d= vpd->v2d;
130 /* calculate amount to move view by */
131 dx= vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
132 dy= vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
134 /* only move view on an axis if change is allowed */
135 if ((v2d->keepofs & V2D_LOCKOFS_X)==0) {
139 if ((v2d->keepofs & V2D_LOCKOFS_Y)==0) {
144 /* validate that view is in valid configuration after this operation */
145 UI_view2d_curRect_validate(v2d);
147 /* request updates to be done... */
148 ED_region_tag_redraw(vpd->ar);
150 UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
151 WM_event_add_mousemove(C);
154 if(vpd->sa->spacetype==SPACE_OUTLINER) {
155 SpaceOops *soops= vpd->sa->spacedata.first;
156 soops->storeflag |= SO_TREESTORE_REDRAW;
160 /* cleanup temp customdata */
161 static void view_pan_exit(bContext *C, wmOperator *op)
163 if (op->customdata) {
164 MEM_freeN(op->customdata);
165 op->customdata= NULL;
169 /* ------------------ Modal Drag Version (1) ---------------------- */
171 /* for 'redo' only, with no user input */
172 static int view_pan_exec(bContext *C, wmOperator *op)
174 if (!view_pan_init(C, op))
175 return OPERATOR_CANCELLED;
177 view_pan_apply(C, op);
178 view_pan_exit(C, op);
179 return OPERATOR_FINISHED;
182 /* set up modal operator and relevant settings */
183 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
185 wmWindow *window= CTX_wm_window(C);
189 /* set up customdata */
190 if (!view_pan_init(C, op))
191 return OPERATOR_PASS_THROUGH;
196 /* set initial settings */
197 vpd->startx= vpd->lastx= event->x;
198 vpd->starty= vpd->lasty= event->y;
200 if (event->type == MOUSEPAN) {
201 RNA_int_set(op->ptr, "deltax", event->prevx - event->x);
202 RNA_int_set(op->ptr, "deltay", event->prevy - event->y);
204 view_pan_apply(C, op);
205 view_pan_exit(C, op);
206 return OPERATOR_FINISHED;
209 RNA_int_set(op->ptr, "deltax", 0);
210 RNA_int_set(op->ptr, "deltay", 0);
212 if (v2d->keepofs & V2D_LOCKOFS_X)
213 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
214 else if (v2d->keepofs & V2D_LOCKOFS_Y)
215 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
217 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
219 /* add temp handler */
220 WM_event_add_modal_handler(C, op);
222 return OPERATOR_RUNNING_MODAL;
225 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
226 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
228 v2dViewPanData *vpd= op->customdata;
230 /* execute the events */
231 switch (event->type) {
234 /* calculate new delta transform, then store mouse-coordinates for next-time */
235 RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
236 RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
238 vpd->lastx= event->x;
239 vpd->lasty= event->y;
241 view_pan_apply(C, op);
247 if (event->val==KM_PRESS) {
248 /* calculate overall delta mouse-movement for redo */
249 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
250 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
252 view_pan_exit(C, op);
253 WM_cursor_restore(CTX_wm_window(C));
255 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
256 return OPERATOR_FINISHED;
260 if (event->val==KM_RELEASE) {
261 /* calculate overall delta mouse-movement for redo */
262 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
263 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
265 view_pan_exit(C, op);
266 WM_cursor_restore(CTX_wm_window(C));
268 return OPERATOR_FINISHED;
273 return OPERATOR_RUNNING_MODAL;
276 static int view_pan_cancel(bContext *C, wmOperator *op)
278 view_pan_exit(C, op);
279 return OPERATOR_CANCELLED;
282 void VIEW2D_OT_pan(wmOperatorType *ot)
285 ot->name= "Pan View";
286 ot->description= "Pan the view";
287 ot->idname= "VIEW2D_OT_pan";
290 ot->exec= view_pan_exec;
291 ot->invoke= view_pan_invoke;
292 ot->modal= view_pan_modal;
293 ot->cancel= view_pan_cancel;
295 /* operator is repeatable */
296 ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
298 /* rna - must keep these in sync with the other operators */
299 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
300 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
303 /* ------------------ Scrollwheel Versions (2) ---------------------- */
305 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
306 static int view_scrollright_exec(bContext *C, wmOperator *op)
310 /* initialise default settings (and validate if ok to run) */
311 if (!view_pan_init(C, op))
312 return OPERATOR_PASS_THROUGH;
314 /* also, check if can pan in horizontal axis */
316 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
317 view_pan_exit(C, op);
318 return OPERATOR_PASS_THROUGH;
321 /* set RNA-Props - only movement in positive x-direction */
322 RNA_int_set(op->ptr, "deltax", 20);
323 RNA_int_set(op->ptr, "deltay", 0);
325 /* apply movement, then we're done */
326 view_pan_apply(C, op);
327 view_pan_exit(C, op);
329 return OPERATOR_FINISHED;
332 void VIEW2D_OT_scroll_right(wmOperatorType *ot)
335 ot->name= "Scroll Right";
336 ot->description= "Scroll the view right";
337 ot->idname= "VIEW2D_OT_scroll_right";
340 ot->exec= view_scrollright_exec;
342 /* operator is repeatable */
343 // ot->flag= OPTYPE_REGISTER;
345 /* rna - must keep these in sync with the other operators */
346 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
347 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
352 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
353 static int view_scrollleft_exec(bContext *C, wmOperator *op)
357 /* initialise default settings (and validate if ok to run) */
358 if (!view_pan_init(C, op))
359 return OPERATOR_PASS_THROUGH;
361 /* also, check if can pan in horizontal axis */
363 if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
364 view_pan_exit(C, op);
365 return OPERATOR_PASS_THROUGH;
368 /* set RNA-Props - only movement in negative x-direction */
369 RNA_int_set(op->ptr, "deltax", -20);
370 RNA_int_set(op->ptr, "deltay", 0);
372 /* apply movement, then we're done */
373 view_pan_apply(C, op);
374 view_pan_exit(C, op);
376 return OPERATOR_FINISHED;
379 void VIEW2D_OT_scroll_left(wmOperatorType *ot)
382 ot->name= "Scroll Left";
383 ot->description= "Scroll the view left";
384 ot->idname= "VIEW2D_OT_scroll_left";
387 ot->exec= view_scrollleft_exec;
389 /* operator is repeatable */
390 // ot->flag= OPTYPE_REGISTER;
392 /* rna - must keep these in sync with the other operators */
393 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
394 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
398 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
399 static int view_scrolldown_exec(bContext *C, wmOperator *op)
403 /* initialise default settings (and validate if ok to run) */
404 if (!view_pan_init(C, op))
405 return OPERATOR_PASS_THROUGH;
407 /* also, check if can pan in vertical axis */
409 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
410 view_pan_exit(C, op);
411 return OPERATOR_PASS_THROUGH;
415 RNA_int_set(op->ptr, "deltax", 0);
416 RNA_int_set(op->ptr, "deltay", -40);
418 /* apply movement, then we're done */
419 view_pan_apply(C, op);
420 view_pan_exit(C, op);
422 return OPERATOR_FINISHED;
425 void VIEW2D_OT_scroll_down(wmOperatorType *ot)
428 ot->name= "Scroll Down";
429 ot->description= "Scroll the view down";
430 ot->idname= "VIEW2D_OT_scroll_down";
433 ot->exec= view_scrolldown_exec;
435 /* operator is repeatable */
436 // ot->flag= OPTYPE_REGISTER;
438 /* rna - must keep these in sync with the other operators */
439 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
440 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
445 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
446 static int view_scrollup_exec(bContext *C, wmOperator *op)
450 /* initialise default settings (and validate if ok to run) */
451 if (!view_pan_init(C, op))
452 return OPERATOR_PASS_THROUGH;
454 /* also, check if can pan in vertical axis */
456 if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
457 view_pan_exit(C, op);
458 return OPERATOR_PASS_THROUGH;
462 RNA_int_set(op->ptr, "deltax", 0);
463 RNA_int_set(op->ptr, "deltay", 40);
465 /* apply movement, then we're done */
466 view_pan_apply(C, op);
467 view_pan_exit(C, op);
469 return OPERATOR_FINISHED;
472 void VIEW2D_OT_scroll_up(wmOperatorType *ot)
475 ot->name= "Scroll Up";
476 ot->description= "Scroll the view up";
477 ot->idname= "VIEW2D_OT_scroll_up";
480 ot->exec= view_scrollup_exec;
482 /* operator is repeatable */
483 // ot->flag= OPTYPE_REGISTER;
485 /* rna - must keep these in sync with the other operators */
486 RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
487 RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
490 /* ********************************************************* */
491 /* SINGLE-STEP VIEW ZOOMING OPERATOR */
493 /* This group of operators come in several forms:
494 * 1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
495 * 2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y) // XXX this could be implemented...
496 * 3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
498 * In order to make sure this works, each operator must define the following RNA-Operator Props:
499 * zoomfacx, zoomfacy - These two zoom factors allow for non-uniform scaling.
500 * It is safe to scale by 0, as these factors are used to determine
501 * amount to enlarge 'cur' by
504 /* ------------------ 'Shared' stuff ------------------------ */
506 /* temp customdata for operator */
507 typedef struct v2dViewZoomData {
508 View2D *v2d; /* view2d we're operating in */
511 int lastx, lasty; /* previous x/y values of mouse in window */
512 float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
513 float mx_2d, my_2d; /* initial mouse location in v2d coords */
517 /* initialise panning customdata */
518 static int view_zoomdrag_init(bContext *C, wmOperator *op)
520 ARegion *ar= CTX_wm_region(C);
521 v2dViewZoomData *vzd;
524 /* regions now have v2d-data by default, so check for region */
529 /* check that 2d-view is zoomable */
530 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
533 /* set custom-data for operator */
534 vzd= MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
537 /* set pointers to owners */
544 /* check if step-zoom can be applied */
545 static int view_zoom_poll(bContext *C)
547 ARegion *ar= CTX_wm_region(C);
550 /* check if there's a region in context to work with */
555 /* check that 2d-view is zoomable */
556 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
559 /* view is zoomable */
563 /* apply transform to view (i.e. adjust 'cur' rect) */
564 static void view_zoomstep_apply(bContext *C, wmOperator *op)
566 v2dViewZoomData *vzd= op->customdata;
567 ARegion *ar= CTX_wm_region(C);
568 View2D *v2d= &ar->v2d;
569 float dx, dy, facx, facy;
571 /* calculate amount to move view by, ensuring symmetry so the
572 * old zoom level is restored after zooming back the same amount */
573 facx= RNA_float_get(op->ptr, "zoomfacx");
574 facy= RNA_float_get(op->ptr, "zoomfacy");
577 dx= (v2d->cur.xmax - v2d->cur.xmin) * facx;
578 dy= (v2d->cur.ymax - v2d->cur.ymin) * facy;
581 dx= ((v2d->cur.xmax - v2d->cur.xmin)/(1.0f + 2.0f*facx)) * facx;
582 dy= ((v2d->cur.ymax - v2d->cur.ymin)/(1.0f + 2.0f*facy)) * facy;
585 /* only resize view on an axis if change is allowed */
586 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
587 if (v2d->keepofs & V2D_LOCKOFS_X) {
588 v2d->cur.xmax -= 2*dx;
590 else if (v2d->keepofs & V2D_KEEPOFS_X) {
591 if(v2d->align & V2D_ALIGN_NO_POS_X)
592 v2d->cur.xmin += 2*dx;
594 v2d->cur.xmax -= 2*dx;
597 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
598 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
599 float mval_faci = 1.0 - mval_fac;
600 float ofs= (mval_fac * dx) - (mval_faci * dx);
601 v2d->cur.xmin += ofs + dx;
602 v2d->cur.xmax += ofs - dx;
610 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
611 if (v2d->keepofs & V2D_LOCKOFS_Y) {
612 v2d->cur.ymax -= 2*dy;
614 else if (v2d->keepofs & V2D_KEEPOFS_Y) {
615 if(v2d->align & V2D_ALIGN_NO_POS_Y)
616 v2d->cur.ymin += 2*dy;
618 v2d->cur.ymax -= 2*dy;
621 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
622 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
623 float mval_faci = 1.0 - mval_fac;
624 float ofs= (mval_fac * dy) - (mval_faci * dy);
625 v2d->cur.ymin += ofs + dy;
626 v2d->cur.ymax += ofs - dy;
634 /* validate that view is in valid configuration after this operation */
635 UI_view2d_curRect_validate(v2d);
637 /* request updates to be done... */
638 ED_region_tag_redraw(vzd->ar);
639 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
640 WM_event_add_mousemove(C);
643 /* --------------- Individual Operators ------------------- */
645 /* cleanup temp customdata */
646 static void view_zoomstep_exit(bContext *C, wmOperator *op)
648 if (op->customdata) {
649 MEM_freeN(op->customdata);
650 op->customdata= NULL;
654 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
655 static int view_zoomin_exec(bContext *C, wmOperator *op)
657 /* check that there's an active region, as View2D data resides there */
658 if (!view_zoom_poll(C))
659 return OPERATOR_PASS_THROUGH;
661 /* set RNA-Props - zooming in by uniform factor */
662 RNA_float_set(op->ptr, "zoomfacx", 0.0375f);
663 RNA_float_set(op->ptr, "zoomfacy", 0.0375f);
665 /* apply movement, then we're done */
666 view_zoomstep_apply(C, op);
668 view_zoomstep_exit(C, op);
670 return OPERATOR_FINISHED;
673 static int view_zoomin_invoke(bContext *C, wmOperator *op, wmEvent *event)
675 v2dViewZoomData *vzd;
677 if (!view_zoomdrag_init(C, op))
678 return OPERATOR_PASS_THROUGH;
682 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
683 ARegion *ar= CTX_wm_region(C);
684 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, &vzd->mx_2d, &vzd->my_2d);
687 return view_zoomin_exec(C, op);
690 void VIEW2D_OT_zoom_in(wmOperatorType *ot)
694 ot->description= "Zoom in the view";
695 ot->idname= "VIEW2D_OT_zoom_in";
698 ot->invoke= view_zoomin_invoke;
699 ot->exec= view_zoomin_exec;
700 ot->poll= view_zoom_poll;
702 /* operator is repeatable */
703 // ot->flag= OPTYPE_REGISTER;
705 /* rna - must keep these in sync with the other operators */
706 RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
707 RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
710 /* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
711 static int view_zoomout_exec(bContext *C, wmOperator *op)
713 /* check that there's an active region, as View2D data resides there */
714 if (!view_zoom_poll(C))
715 return OPERATOR_PASS_THROUGH;
717 /* set RNA-Props - zooming in by uniform factor */
718 RNA_float_set(op->ptr, "zoomfacx", -0.0375f);
719 RNA_float_set(op->ptr, "zoomfacy", -0.0375f);
721 /* apply movement, then we're done */
722 view_zoomstep_apply(C, op);
724 view_zoomstep_exit(C, op);
726 return OPERATOR_FINISHED;
729 static int view_zoomout_invoke(bContext *C, wmOperator *op, wmEvent *event)
731 v2dViewZoomData *vzd;
733 if (!view_zoomdrag_init(C, op))
734 return OPERATOR_PASS_THROUGH;
738 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
739 ARegion *ar= CTX_wm_region(C);
740 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, &vzd->mx_2d, &vzd->my_2d);
743 return view_zoomout_exec(C, op);
746 void VIEW2D_OT_zoom_out(wmOperatorType *ot)
749 ot->name= "Zoom Out";
750 ot->description= "Zoom out the view";
751 ot->idname= "VIEW2D_OT_zoom_out";
754 ot->invoke= view_zoomout_invoke;
755 ot->exec= view_zoomout_exec;
756 ot->poll= view_zoom_poll;
758 /* operator is repeatable */
759 // ot->flag= OPTYPE_REGISTER;
761 /* rna - must keep these in sync with the other operators */
762 RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
763 RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
766 /* ********************************************************* */
767 /* DRAG-ZOOM OPERATOR */
769 /* MMB Drag - allows non-uniform scaling by dragging mouse
771 * In order to make sure this works, each operator must define the following RNA-Operator Props:
772 * deltax, deltay - amounts to add to each side of the 'cur' rect
775 /* apply transform to view (i.e. adjust 'cur' rect) */
776 static void view_zoomdrag_apply(bContext *C, wmOperator *op)
778 v2dViewZoomData *vzd= op->customdata;
779 View2D *v2d= vzd->v2d;
782 /* get amount to move view by */
783 dx= RNA_float_get(op->ptr, "deltax");
784 dy= RNA_float_get(op->ptr, "deltay");
786 /* only move view on an axis if change is allowed */
787 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
788 if (v2d->keepofs & V2D_LOCKOFS_X) {
789 v2d->cur.xmax -= 2*dx;
792 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
793 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
794 float mval_faci = 1.0 - mval_fac;
795 float ofs= (mval_fac * dx) - (mval_faci * dx);
796 v2d->cur.xmin += ofs + dx;
797 v2d->cur.xmax += ofs - dx;
805 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
806 if (v2d->keepofs & V2D_LOCKOFS_Y) {
807 v2d->cur.ymax -= 2*dy;
810 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
811 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
812 float mval_faci = 1.0 - mval_fac;
813 float ofs= (mval_fac * dy) - (mval_faci * dy);
814 v2d->cur.ymin += ofs + dy;
815 v2d->cur.ymax += ofs - dy;
824 /* validate that view is in valid configuration after this operation */
825 UI_view2d_curRect_validate(v2d);
827 /* request updates to be done... */
828 ED_region_tag_redraw(vzd->ar);
829 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
830 WM_event_add_mousemove(C);
833 /* cleanup temp customdata */
834 static void view_zoomdrag_exit(bContext *C, wmOperator *op)
836 if (op->customdata) {
837 MEM_freeN(op->customdata);
838 op->customdata= NULL;
842 /* for 'redo' only, with no user input */
843 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
845 if (!view_zoomdrag_init(C, op))
846 return OPERATOR_PASS_THROUGH;
848 view_zoomdrag_apply(C, op);
849 view_zoomdrag_exit(C, op);
850 return OPERATOR_FINISHED;
853 /* set up modal operator and relevant settings */
854 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
856 wmWindow *window= CTX_wm_window(C);
857 v2dViewZoomData *vzd;
860 /* set up customdata */
861 if (!view_zoomdrag_init(C, op))
862 return OPERATOR_PASS_THROUGH;
867 if (event->type == MOUSEZOOM) {
870 vzd->lastx= event->prevx;
871 vzd->lasty= event->prevy;
873 /* As we have only 1D information (magnify value), feed both axes
874 with magnify information that is stored in x axis */
875 fac= 0.01f * (event->x - event->prevx);
876 dx= fac * (v2d->cur.xmax - v2d->cur.xmin) / 10.0f;
877 dy= fac * (v2d->cur.ymax - v2d->cur.ymin) / 10.0f;
879 RNA_float_set(op->ptr, "deltax", dx);
880 RNA_float_set(op->ptr, "deltay", dy);
882 view_zoomdrag_apply(C, op);
883 view_zoomdrag_exit(C, op);
884 return OPERATOR_FINISHED;
887 /* set initial settings */
888 vzd->lastx= event->x;
889 vzd->lasty= event->y;
890 RNA_float_set(op->ptr, "deltax", 0);
891 RNA_float_set(op->ptr, "deltay", 0);
893 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
894 ARegion *ar= CTX_wm_region(C);
895 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, &vzd->mx_2d, &vzd->my_2d);
898 if (v2d->keepofs & V2D_LOCKOFS_X)
899 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
900 else if (v2d->keepofs & V2D_LOCKOFS_Y)
901 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
903 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
905 /* add temp handler */
906 WM_event_add_modal_handler(C, op);
908 return OPERATOR_RUNNING_MODAL;
911 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
912 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
914 v2dViewZoomData *vzd= op->customdata;
915 View2D *v2d= vzd->v2d;
917 /* execute the events */
918 switch (event->type) {
923 /* calculate new delta transform, based on zooming mode */
924 if (U.viewzoom == USER_ZOOM_SCALE) {
925 /* 'scale' zooming */
928 /* x-axis transform */
929 dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
930 dx= 1.0f - ((float)fabs(vzd->lastx - dist) + 2.0f) / ((float)fabs(event->x - dist) + 2.0f);
931 dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
933 /* y-axis transform */
934 dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
935 dy= 1.0f - ((float)fabs(vzd->lasty - dist) + 2.0f) / ((float)fabs(event->y - dist) + 2.0f);
936 dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
939 /* 'continuous' or 'dolly' */
942 /* x-axis transform */
943 fac= 0.01f * (event->x - vzd->lastx);
944 dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
946 /* y-axis transform */
947 fac= 0.01f * (event->y - vzd->lasty);
948 dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
950 /* continous zoom shouldn't move that fast... */
951 if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
957 /* set transform amount, and add current deltas to stored total delta (for redo) */
958 RNA_float_set(op->ptr, "deltax", dx);
959 RNA_float_set(op->ptr, "deltay", dy);
963 /* store mouse coordinates for next time, if not doing continuous zoom
964 * - continuous zoom only depends on distance of mouse to starting point to determine rate of change
966 if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
967 vzd->lastx= event->x;
968 vzd->lasty= event->y;
972 view_zoomdrag_apply(C, op);
978 if (event->val==KM_RELEASE) {
979 /* for redo, store the overall deltas - need to respect zoom-locks here... */
980 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
981 RNA_float_set(op->ptr, "deltax", vzd->dx);
983 RNA_float_set(op->ptr, "deltax", 0);
985 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
986 RNA_float_set(op->ptr, "deltay", vzd->dy);
988 RNA_float_set(op->ptr, "deltay", 0);
990 /* free customdata */
991 view_zoomdrag_exit(C, op);
992 WM_cursor_restore(CTX_wm_window(C));
994 return OPERATOR_FINISHED;
999 return OPERATOR_RUNNING_MODAL;
1002 void VIEW2D_OT_zoom(wmOperatorType *ot)
1005 ot->name= "Zoom View";
1006 ot->description= "Zoom in/out the view";
1007 ot->idname= "VIEW2D_OT_zoom";
1010 ot->exec= view_zoomdrag_exec;
1011 ot->invoke= view_zoomdrag_invoke;
1012 ot->modal= view_zoomdrag_modal;
1014 ot->poll= view_zoom_poll;
1016 /* operator is repeatable */
1017 // ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING;
1019 /* rna - must keep these in sync with the other operators */
1020 RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1021 RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1024 /* ********************************************************* */
1027 /* The user defines a rect using standard borderselect tools, and we use this rect to
1028 * define the new zoom-level of the view in the following ways:
1029 * 1) LEFTMOUSE - zoom in to view
1030 * 2) RIGHTMOUSE - zoom out of view
1032 * Currently, these key mappings are hardcoded, but it shouldn't be too important to
1033 * have custom keymappings for this...
1036 static int view_borderzoom_exec(bContext *C, wmOperator *op)
1038 ARegion *ar= CTX_wm_region(C);
1039 View2D *v2d= &ar->v2d;
1043 /* convert coordinates of rect to 'tot' rect coordinates */
1044 UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
1045 UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
1047 /* check if zooming in/out view */
1048 gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
1050 if (gesture_mode == GESTURE_MODAL_IN) {
1052 * - 'cur' rect will be defined by the coordinates of the border region
1053 * - just set the 'cur' rect to have the same coordinates as the border region
1054 * if zoom is allowed to be changed
1056 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1057 v2d->cur.xmin= rect.xmin;
1058 v2d->cur.xmax= rect.xmax;
1060 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1061 v2d->cur.ymin= rect.ymin;
1062 v2d->cur.ymax= rect.ymax;
1065 else /* if (gesture_mode == GESTURE_MODAL_OUT) */ {
1067 * - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are,
1068 * but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1069 * - calculate zoom factor, and adjust using center-point
1071 float zoom, center, size;
1073 // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
1074 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1075 size= (v2d->cur.xmax - v2d->cur.xmin);
1076 zoom= size / (rect.xmax - rect.xmin);
1077 center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
1079 v2d->cur.xmin= center - (size * zoom);
1080 v2d->cur.xmax= center + (size * zoom);
1082 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1083 size= (v2d->cur.ymax - v2d->cur.ymin);
1084 zoom= size / (rect.ymax - rect.ymin);
1085 center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
1087 v2d->cur.ymin= center - (size * zoom);
1088 v2d->cur.ymax= center + (size * zoom);
1092 /* validate that view is in valid configuration after this operation */
1093 UI_view2d_curRect_validate(v2d);
1095 /* request updates to be done... */
1096 ED_region_tag_redraw(ar);
1097 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1098 WM_event_add_mousemove(C);
1100 return OPERATOR_FINISHED;
1103 void VIEW2D_OT_zoom_border(wmOperatorType *ot)
1106 ot->name= "Zoom to Border";
1107 ot->description= "Zoom in the view to the nearest item contained in the border";
1108 ot->idname= "VIEW2D_OT_zoom_border";
1111 ot->invoke= WM_border_select_invoke;
1112 ot->exec= view_borderzoom_exec;
1113 ot->modal= WM_border_select_modal;
1115 ot->poll= view_zoom_poll;
1118 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
1119 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1120 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1121 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1122 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1125 /* ********************************************************* */
1128 /* Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
1129 * 1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable,
1130 * enlarge 'cur' rect on the relevant side
1131 * 2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
1133 * In order to make sure this works, each operator must define the following RNA-Operator Props:
1134 * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
1137 /* customdata for scroller-invoke data */
1138 typedef struct v2dScrollerMove {
1139 View2D *v2d; /* View2D data that this operation affects */
1140 ARegion *ar; /* region that the scroller is in */
1142 short scroller; /* scroller that mouse is in ('h' or 'v') */
1143 short zone; /* -1 is min zoomer, 0 is bar, 1 is max zoomer */ // XXX find some way to provide visual feedback of this (active colour?)
1145 float fac; /* view adjustment factor, based on size of region */
1146 float delta; /* amount moved by mouse on axis of interest */
1148 float scrollbarwidth; /* width of the scrollbar itself, used for page up/down clicks */
1150 int lastx, lasty; /* previous mouse coordinates (in screen coordinates) for determining movement */
1154 /* View2DScrollers is typedef'd in UI_view2d.h
1155 * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
1156 * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
1157 * For now, we don't need to have a separate (internal) header for structs like this...
1159 struct View2DScrollers {
1161 int vert_min, vert_max; /* vertical scrollbar */
1162 int hor_min, hor_max; /* horizontal scrollbar */
1165 /* quick enum for vsm->zone (scroller handles) */
1167 SCROLLHANDLE_MIN= -1,
1170 SCROLLHANDLE_MIN_OUTSIDE,
1171 SCROLLHANDLE_MAX_OUTSIDE
1172 } eV2DScrollerHandle_Zone;
1174 /* ------------------------ */
1176 /* check if mouse is within scroller handle
1177 * - mouse = relevant mouse coordinate in region space
1178 * - sc_min, sc_max = extents of scroller 'groove' (potential available space for scroller)
1179 * - sh_min, sh_max = positions of scrollbar handles
1181 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1183 short in_min, in_max, in_bar, out_min, out_max, in_view=1;
1185 /* firstly, check if
1186 * - 'bubble' fills entire scroller
1187 * - 'bubble' completely out of view on either side
1189 if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
1190 if (sh_min == sh_max) {
1191 if (sh_min <= sc_min) in_view= 0;
1192 if (sh_max >= sc_max) in_view= 0;
1195 if (sh_max <= sc_min) in_view= 0;
1196 if (sh_min >= sc_max) in_view= 0;
1201 return SCROLLHANDLE_BAR;
1204 /* check if mouse is in or past either handle */
1205 in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
1206 in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
1207 in_bar= ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) );
1208 out_min= mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE);
1209 out_max= mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE);
1212 return SCROLLHANDLE_BAR;
1214 return SCROLLHANDLE_MAX;
1216 return SCROLLHANDLE_MIN;
1218 return SCROLLHANDLE_MIN_OUTSIDE;
1220 return SCROLLHANDLE_MAX_OUTSIDE;
1222 /* unlikely to happen, though we just cover it in case */
1223 return SCROLLHANDLE_BAR;
1226 /* initialise customdata for scroller manipulation operator */
1227 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
1229 v2dScrollerMove *vsm;
1230 View2DScrollers *scrollers;
1231 ARegion *ar= CTX_wm_region(C);
1232 View2D *v2d= &ar->v2d;
1236 /* set custom-data for operator */
1237 vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
1238 op->customdata= vsm;
1240 /* set general data */
1243 vsm->scroller= in_scroller;
1245 /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1246 vsm->lastx = event->x;
1247 vsm->lasty = event->y;
1248 x= event->x - ar->winrct.xmin;
1249 y= event->y - ar->winrct.ymin;
1251 /* 'zone' depends on where mouse is relative to bubble
1252 * - zooming must be allowed on this axis, otherwise, default to pan
1254 scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1255 if (in_scroller == 'h') {
1256 /* horizontal scroller - calculate adjustment factor first */
1257 mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
1258 vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
1260 /* get 'zone' (i.e. which part of scroller is activated) */
1261 vsm->zone= mouse_in_scroller_handle(x, v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
1263 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1264 /* default to scroll, as handles not usable */
1265 vsm->zone= SCROLLHANDLE_BAR;
1268 vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
1271 /* vertical scroller - calculate adjustment factor first */
1272 mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
1273 vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
1275 /* get 'zone' (i.e. which part of scroller is activated) */
1276 vsm->zone= mouse_in_scroller_handle(y, v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
1278 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1279 /* default to scroll, as handles not usable */
1280 vsm->zone= SCROLLHANDLE_BAR;
1283 vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
1286 UI_view2d_scrollers_free(scrollers);
1287 ED_region_tag_redraw(ar);
1290 /* cleanup temp customdata */
1291 static void scroller_activate_exit(bContext *C, wmOperator *op)
1293 if (op->customdata) {
1294 v2dScrollerMove *vsm= op->customdata;
1296 vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE|V2D_SCROLL_V_ACTIVE);
1298 MEM_freeN(op->customdata);
1299 op->customdata= NULL;
1301 ED_region_tag_redraw(CTX_wm_region(C));
1305 /* apply transform to view (i.e. adjust 'cur' rect) */
1306 static void scroller_activate_apply(bContext *C, wmOperator *op)
1308 v2dScrollerMove *vsm= op->customdata;
1309 View2D *v2d= vsm->v2d;
1312 /* calculate amount to move view by */
1313 temp= vsm->fac * vsm->delta;
1315 /* type of movement */
1316 switch (vsm->zone) {
1317 case SCROLLHANDLE_MIN:
1318 /* only expand view on axis if zoom is allowed */
1319 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1320 v2d->cur.xmin -= temp;
1321 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1322 v2d->cur.ymin -= temp;
1325 case SCROLLHANDLE_MAX:
1327 /* only expand view on axis if zoom is allowed */
1328 if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1329 v2d->cur.xmax += temp;
1330 if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1331 v2d->cur.ymax += temp;
1334 case SCROLLHANDLE_MIN_OUTSIDE:
1335 case SCROLLHANDLE_MAX_OUTSIDE:
1336 case SCROLLHANDLE_BAR:
1338 /* only move view on an axis if panning is allowed */
1339 if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1340 v2d->cur.xmin += temp;
1341 v2d->cur.xmax += temp;
1343 if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1344 v2d->cur.ymin += temp;
1345 v2d->cur.ymax += temp;
1351 /* validate that view is in valid configuration after this operation */
1352 UI_view2d_curRect_validate(v2d);
1354 /* request updates to be done... */
1355 ED_region_tag_redraw(vsm->ar);
1356 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1357 WM_event_add_mousemove(C);
1360 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
1361 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
1363 v2dScrollerMove *vsm= op->customdata;
1365 /* execute the events */
1366 switch (event->type) {
1369 /* calculate new delta transform, then store mouse-coordinates for next-time */
1370 if (ELEM(vsm->zone, SCROLLHANDLE_BAR, SCROLLHANDLE_MAX)) {
1371 /* if using bar (i.e. 'panning') or 'max' zoom widget */
1372 switch (vsm->scroller) {
1373 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1374 vsm->delta= (float)(event->x - vsm->lastx);
1376 case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1377 vsm->delta= (float)(event->y - vsm->lasty);
1381 else if (vsm->zone == SCROLLHANDLE_MIN) {
1382 /* using 'min' zoom widget */
1383 switch (vsm->scroller) {
1384 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
1385 vsm->delta= (float)(vsm->lastx - event->x);
1387 case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
1388 vsm->delta= (float)(vsm->lasty - event->y);
1393 /* store previous coordinates */
1394 vsm->lastx= event->x;
1395 vsm->lasty= event->y;
1397 scroller_activate_apply(C, op);
1402 if (event->val==KM_RELEASE) {
1404 /* click was in empty space outside scroll bar */
1405 if (ELEM(vsm->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
1406 if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE)
1407 vsm->delta = -vsm->scrollbarwidth * 0.8;
1408 else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE)
1409 vsm->delta = vsm->scrollbarwidth * 0.8;
1411 scroller_activate_apply(C, op);
1412 scroller_activate_exit(C, op);
1413 return OPERATOR_FINISHED;
1416 /* otherwise, end the drag action */
1417 if (vsm->lastx || vsm->lasty) {
1418 scroller_activate_exit(C, op);
1419 return OPERATOR_FINISHED;
1425 return OPERATOR_RUNNING_MODAL;
1429 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
1430 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1432 ARegion *ar= CTX_wm_region(C);
1433 View2D *v2d= &ar->v2d;
1434 short in_scroller= 0;
1436 /* check if mouse in scrollbars, if they're enabled */
1437 in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
1439 /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
1441 v2dScrollerMove *vsm;
1443 /* initialise customdata */
1444 scroller_activate_init(C, op, event, in_scroller);
1445 vsm= (v2dScrollerMove *)op->customdata;
1447 /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
1448 * NOTE: see view2d.c for latest conditions, and keep this in sync with that
1450 if (ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1451 if ( ((vsm->scroller=='h') && (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL)==0) ||
1452 ((vsm->scroller=='v') && (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL)==0) )
1454 /* switch to bar (i.e. no scaling gets handled) */
1455 vsm->zone= SCROLLHANDLE_BAR;
1459 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
1460 if (vsm->zone == SCROLLHANDLE_BAR) {
1461 if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
1462 ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
1464 /* free customdata initialised */
1465 scroller_activate_exit(C, op);
1467 /* can't catch this event for ourselves, so let it go to someone else? */
1468 return OPERATOR_PASS_THROUGH;
1472 /* zone is also inappropriate if scroller is not visible... */
1473 if ( ((vsm->scroller=='h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_HORIZONTAL_FULLR))) ||
1474 ((vsm->scroller=='v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE|V2D_SCROLL_VERTICAL_FULLR))) )
1476 /* free customdata initialised */
1477 scroller_activate_exit(C, op);
1479 /* can't catch this event for ourselves, so let it go to someone else? */
1480 // FIXME: still this doesn't fall through to the item_activate callback for the outliner...
1481 return OPERATOR_PASS_THROUGH;
1484 /* activate the scroller */
1485 if (vsm->scroller=='h')
1486 v2d->scroll_ui |= V2D_SCROLL_H_ACTIVE;
1488 v2d->scroll_ui |= V2D_SCROLL_V_ACTIVE;
1490 /* still ok, so can add */
1491 WM_event_add_modal_handler(C, op);
1492 return OPERATOR_RUNNING_MODAL;
1495 /* not in scroller, so nothing happened... (pass through let's something else catch event) */
1496 return OPERATOR_PASS_THROUGH;
1500 /* LMB-Drag in Scrollers - not repeatable operator! */
1501 void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
1504 ot->name= "Scroller Activate";
1505 ot->description= "Scroll view by mouse click and drag";
1506 ot->idname= "VIEW2D_OT_scroller_activate";
1509 ot->flag= OPTYPE_BLOCKING;
1512 ot->invoke= scroller_activate_invoke;
1513 ot->modal= scroller_activate_modal;
1514 ot->poll= view2d_poll;
1517 /* ********************************************************* */
1520 static int reset_exec(bContext *C, wmOperator *op)
1522 uiStyle *style= U.uistyles.first;
1523 ARegion *ar= CTX_wm_region(C);
1524 View2D *v2d= &ar->v2d;
1528 winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
1529 winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
1531 v2d->cur.xmax= v2d->cur.xmin + winx;
1532 v2d->cur.ymax= v2d->cur.ymin + winy;
1536 /* posx and negx flags are mutually exclusive, so watch out */
1537 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1538 v2d->cur.xmax= 0.0f;
1539 v2d->cur.xmin= -winx*style->panelzoom;
1541 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
1542 v2d->cur.xmax= winx*style->panelzoom;
1543 v2d->cur.xmin= 0.0f;
1546 /* - posx and negx flags are mutually exclusive, so watch out */
1547 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1548 v2d->cur.ymax= 0.0f;
1549 v2d->cur.ymin= -winy*style->panelzoom;
1551 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
1552 v2d->cur.ymax= winy*style->panelzoom;
1553 v2d->cur.ymin= 0.0f;
1557 /* validate that view is in valid configuration after this operation */
1558 UI_view2d_curRect_validate(v2d);
1560 /* request updates to be done... */
1561 ED_region_tag_redraw(ar);
1562 UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1563 WM_event_add_mousemove(C);
1565 return OPERATOR_FINISHED;
1568 void VIEW2D_OT_reset(wmOperatorType *ot)
1571 ot->name= "Reset View";
1572 ot->description= "Reset the view";
1573 ot->idname= "VIEW2D_OT_reset";
1576 ot->exec= reset_exec;
1577 ot->poll= view2d_poll;
1580 // ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1583 /* ********************************************************* */
1586 void UI_view2d_operatortypes(void)
1588 WM_operatortype_append(VIEW2D_OT_pan);
1590 WM_operatortype_append(VIEW2D_OT_scroll_left);
1591 WM_operatortype_append(VIEW2D_OT_scroll_right);
1592 WM_operatortype_append(VIEW2D_OT_scroll_up);
1593 WM_operatortype_append(VIEW2D_OT_scroll_down);
1595 WM_operatortype_append(VIEW2D_OT_zoom_in);
1596 WM_operatortype_append(VIEW2D_OT_zoom_out);
1598 WM_operatortype_append(VIEW2D_OT_zoom);
1599 WM_operatortype_append(VIEW2D_OT_zoom_border);
1601 WM_operatortype_append(VIEW2D_OT_scroller_activate);
1603 WM_operatortype_append(VIEW2D_OT_reset);
1606 void UI_view2d_keymap(wmKeyConfig *keyconf)
1608 wmKeyMap *keymap= WM_keymap_find(keyconf, "View2D", 0, 0);
1611 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1612 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
1614 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1616 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
1617 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
1619 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
1620 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
1622 /* zoom - single step */
1623 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
1624 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
1625 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1626 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1628 /* scroll up/down - no modifiers, only when zoom fails */
1629 /* these may fail if zoom is disallowed, in which case they should pass on event */
1630 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1631 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1632 /* these may be necessary if vertical scroll is disallowed */
1633 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1634 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
1636 /* alternatives for page up/down to scroll */
1637 #if 0 // XXX disabled, since this causes conflicts with hotkeys in animation editors
1638 /* scroll up/down may fall through to left/right */
1639 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1640 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1641 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, 0, 0);
1642 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, 0, 0);
1643 /* shift for moving view left/right with page up/down */
1644 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
1645 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
1649 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1650 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1652 /* borderzoom - drag */
1653 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
1656 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1658 /* Alternative keymap for buttons listview */
1659 keymap= WM_keymap_find(keyconf, "View2D Buttons List", 0, 0);
1660 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1661 WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1662 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1663 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1664 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1665 WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1666 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1667 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1668 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1669 WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1670 WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);
1671 WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);