Blender V2.61 - r43446

view2d_ops.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  * 
00021  * Contributor(s): Blender Foundation, Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <math.h>
00032 
00033 #include "MEM_guardedalloc.h"
00034 
00035 #include "DNA_userdef_types.h"
00036 
00037 #include "BLI_blenlib.h"
00038 #include "BLI_utildefines.h"
00039 
00040 #include "BKE_context.h"
00041 
00042 #include "RNA_access.h"
00043 #include "RNA_define.h"
00044 
00045 #include "WM_api.h"
00046 #include "WM_types.h"
00047 
00048 
00049 #include "ED_screen.h"
00050 
00051 #include "UI_view2d.h"
00052 #include "UI_interface.h"
00053 
00054 #include "PIL_time.h" /* USER_ZOOM_CONT */
00055 
00056 static int view2d_poll(bContext *C)
00057 {
00058     ARegion *ar= CTX_wm_region(C);
00059 
00060     return (ar != NULL) && (ar->v2d.flag & V2D_IS_INITIALISED);
00061 }
00062 
00063 /* ********************************************************* */
00064 /* VIEW PANNING OPERATOR                                 */
00065 
00066 /*  This group of operators come in several forms:
00067  *      1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
00068  *      2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
00069  *
00070  *  In order to make sure this works, each operator must define the following RNA-Operator Props:
00071  *      deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
00072  */
00073 
00074 /* ------------------ Shared 'core' stuff ---------------------- */
00075  
00076 /* temp customdata for operator */
00077 typedef struct v2dViewPanData {
00078     bScreen *sc;            /* screen where view pan was initiated */
00079     ScrArea *sa;            /* area where view pan was initiated */
00080     ARegion *ar;            /* region where view pan was initiated */
00081     View2D *v2d;            /* view2d we're operating in */
00082     
00083     float facx, facy;       /* amount to move view relative to zoom */
00084     
00085         /* options for version 1 */
00086     int startx, starty;     /* mouse x/y values in window when operator was initiated */
00087     int lastx, lasty;       /* previous x/y values of mouse in window */
00088     int invoke_event;       /* event starting pan, for modal exit */
00089     
00090     short in_scroller;      /* for MMB in scrollers (old feature in past, but now not that useful) */
00091 } v2dViewPanData;
00092  
00093 /* initialise panning customdata */
00094 static int view_pan_init(bContext *C, wmOperator *op)
00095 {
00096     ARegion *ar= CTX_wm_region(C);
00097     v2dViewPanData *vpd;
00098     View2D *v2d;
00099     float winx, winy;
00100     
00101     /* regions now have v2d-data by default, so check for region */
00102     if (ar == NULL)
00103         return 0;
00104         
00105     /* check if panning is allowed at all */
00106     v2d= &ar->v2d;
00107     if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y))
00108         return 0;
00109     
00110     /* set custom-data for operator */
00111     vpd= MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
00112     op->customdata= vpd;
00113     
00114     /* set pointers to owners */
00115     vpd->sc= CTX_wm_screen(C);
00116     vpd->sa= CTX_wm_area(C);
00117     vpd->v2d= v2d;
00118     vpd->ar = ar;
00119     
00120     /* calculate translation factor - based on size of view */
00121     winx= (float)(ar->winrct.xmax - ar->winrct.xmin + 1);
00122     winy= (float)(ar->winrct.ymax - ar->winrct.ymin + 1);
00123     vpd->facx= (v2d->cur.xmax - v2d->cur.xmin) / winx;
00124     vpd->facy= (v2d->cur.ymax - v2d->cur.ymin) / winy;
00125     
00126     return 1;
00127 }
00128 
00129 /* apply transform to view (i.e. adjust 'cur' rect) */
00130 static void view_pan_apply(wmOperator *op)
00131 {
00132     v2dViewPanData *vpd= op->customdata;
00133     View2D *v2d= vpd->v2d;
00134     float dx, dy;
00135     
00136     /* calculate amount to move view by */
00137     dx= vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
00138     dy= vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
00139     
00140     /* only move view on an axis if change is allowed */
00141     if ((v2d->keepofs & V2D_LOCKOFS_X)==0) {
00142         v2d->cur.xmin += dx;
00143         v2d->cur.xmax += dx;
00144     }
00145     if ((v2d->keepofs & V2D_LOCKOFS_Y)==0) {
00146         v2d->cur.ymin += dy;
00147         v2d->cur.ymax += dy;
00148     }
00149     
00150     /* validate that view is in valid configuration after this operation */
00151     UI_view2d_curRect_validate(v2d);
00152     
00153     /* request updates to be done... */
00154     ED_region_tag_redraw(vpd->ar);
00155     
00156     UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
00157     
00158     /* exceptions */
00159     if (vpd->sa->spacetype==SPACE_OUTLINER) {
00160         /* don't rebuild full tree, since we're just changing our view */
00161         SpaceOops *soops= vpd->sa->spacedata.first;
00162         soops->storeflag |= SO_TREESTORE_REDRAW;
00163     }
00164 }
00165 
00166 /* cleanup temp customdata  */
00167 static void view_pan_exit(wmOperator *op)
00168 {
00169     if (op->customdata) {
00170         MEM_freeN(op->customdata);
00171         op->customdata= NULL;               
00172     }
00173 } 
00174  
00175 /* ------------------ Modal Drag Version (1) ---------------------- */
00176 
00177 /* for 'redo' only, with no user input */
00178 static int view_pan_exec(bContext *C, wmOperator *op)
00179 {
00180     if (!view_pan_init(C, op))
00181         return OPERATOR_CANCELLED;
00182     
00183     view_pan_apply(op);
00184     view_pan_exit(op);
00185     return OPERATOR_FINISHED;
00186 }
00187 
00188 /* set up modal operator and relevant settings */
00189 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
00190 {
00191     wmWindow *window= CTX_wm_window(C);
00192     v2dViewPanData *vpd;
00193     View2D *v2d;
00194     
00195     /* set up customdata */
00196     if (!view_pan_init(C, op))
00197         return OPERATOR_PASS_THROUGH;
00198     
00199     vpd= op->customdata;
00200     v2d= vpd->v2d;
00201     
00202     /* set initial settings */
00203     vpd->startx= vpd->lastx= event->x;
00204     vpd->starty= vpd->lasty= event->y;
00205     vpd->invoke_event= event->type;
00206     
00207     if (event->type == MOUSEPAN) {
00208         RNA_int_set(op->ptr, "deltax", event->prevx - event->x);
00209         RNA_int_set(op->ptr, "deltay", event->prevy - event->y);
00210         
00211         view_pan_apply(op);
00212         view_pan_exit(op);
00213         return OPERATOR_FINISHED;
00214     }
00215     
00216     RNA_int_set(op->ptr, "deltax", 0);
00217     RNA_int_set(op->ptr, "deltay", 0);
00218     
00219     if (v2d->keepofs & V2D_LOCKOFS_X)
00220         WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
00221     else if (v2d->keepofs & V2D_LOCKOFS_Y)
00222         WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
00223     else
00224         WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
00225     
00226     /* add temp handler */
00227     WM_event_add_modal_handler(C, op);
00228 
00229     return OPERATOR_RUNNING_MODAL;
00230 }
00231 
00232 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
00233 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
00234 {
00235     v2dViewPanData *vpd= op->customdata;
00236     
00237     /* execute the events */
00238     switch (event->type) {
00239         case MOUSEMOVE:
00240         {
00241             /* calculate new delta transform, then store mouse-coordinates for next-time */
00242             RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
00243             RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
00244             
00245             vpd->lastx= event->x;
00246             vpd->lasty= event->y;
00247             
00248             view_pan_apply(op);
00249         }
00250             break;
00251         /* XXX - Mode switching isn't implemented. See comments in 36818.
00252          * switch to zoom *
00253         case LEFTMOUSE:
00254             if (event->val==KM_PRESS) {
00255                 * calculate overall delta mouse-movement for redo *
00256                 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
00257                 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
00258                 
00259                 view_pan_exit(op);
00260                 WM_cursor_restore(CTX_wm_window(C));
00261                 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
00262                 return OPERATOR_FINISHED;
00263             }*/
00264             
00265         default:
00266             if (event->type == vpd->invoke_event || event->type==ESCKEY) {
00267                 if (event->val==KM_RELEASE) {
00268                     /* calculate overall delta mouse-movement for redo */
00269                     RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
00270                     RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
00271                     
00272                     view_pan_exit(op);
00273                     WM_cursor_restore(CTX_wm_window(C));
00274                     
00275                     return OPERATOR_FINISHED;
00276                 }
00277             }
00278             break;
00279     }
00280 
00281     return OPERATOR_RUNNING_MODAL;
00282 }
00283 
00284 static int view_pan_cancel(bContext *UNUSED(C), wmOperator *op)
00285 {
00286     view_pan_exit(op);
00287     return OPERATOR_CANCELLED;
00288 }
00289 
00290 static void VIEW2D_OT_pan(wmOperatorType *ot)
00291 {
00292     /* identifiers */
00293     ot->name= "Pan View";
00294     ot->description= "Pan the view";
00295     ot->idname= "VIEW2D_OT_pan";
00296     
00297     /* api callbacks */
00298     ot->exec= view_pan_exec;
00299     ot->invoke= view_pan_invoke;
00300     ot->modal= view_pan_modal;
00301     ot->cancel= view_pan_cancel;
00302     
00303     /* operator is modal */
00304     ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
00305     
00306     /* rna - must keep these in sync with the other operators */
00307     RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
00308     RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
00309 }
00310 
00311 /* ------------------ Scrollwheel Versions (2) ---------------------- */
00312 
00313 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
00314 static int view_scrollright_exec(bContext *C, wmOperator *op)
00315 {
00316     v2dViewPanData *vpd;
00317     
00318     /* initialise default settings (and validate if ok to run) */
00319     if (!view_pan_init(C, op))
00320         return OPERATOR_PASS_THROUGH;
00321         
00322     /* also, check if can pan in horizontal axis */
00323     vpd= op->customdata;
00324     if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
00325         view_pan_exit(op);
00326         return OPERATOR_PASS_THROUGH;
00327     }
00328     
00329     /* set RNA-Props - only movement in positive x-direction */
00330     RNA_int_set(op->ptr, "deltax", 20);
00331     RNA_int_set(op->ptr, "deltay", 0);
00332     
00333     /* apply movement, then we're done */
00334     view_pan_apply(op);
00335     view_pan_exit(op);
00336     
00337     return OPERATOR_FINISHED;
00338 }
00339 
00340 static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
00341 {
00342     /* identifiers */
00343     ot->name= "Scroll Right";
00344     ot->description= "Scroll the view right";
00345     ot->idname= "VIEW2D_OT_scroll_right";
00346     
00347     /* api callbacks */
00348     ot->exec= view_scrollright_exec;
00349     
00350     /* rna - must keep these in sync with the other operators */
00351     RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
00352     RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
00353 }
00354 
00355 
00356 
00357 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
00358 static int view_scrollleft_exec(bContext *C, wmOperator *op)
00359 {
00360     v2dViewPanData *vpd;
00361     
00362     /* initialise default settings (and validate if ok to run) */
00363     if (!view_pan_init(C, op))
00364         return OPERATOR_PASS_THROUGH;
00365         
00366     /* also, check if can pan in horizontal axis */
00367     vpd= op->customdata;
00368     if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
00369         view_pan_exit(op);
00370         return OPERATOR_PASS_THROUGH;
00371     }
00372     
00373     /* set RNA-Props - only movement in negative x-direction */
00374     RNA_int_set(op->ptr, "deltax", -20);
00375     RNA_int_set(op->ptr, "deltay", 0);
00376     
00377     /* apply movement, then we're done */
00378     view_pan_apply(op);
00379     view_pan_exit(op);
00380     
00381     return OPERATOR_FINISHED;
00382 }
00383 
00384 static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
00385 {
00386     /* identifiers */
00387     ot->name= "Scroll Left";
00388     ot->description= "Scroll the view left";
00389     ot->idname= "VIEW2D_OT_scroll_left";
00390     
00391     /* api callbacks */
00392     ot->exec= view_scrollleft_exec;
00393     
00394     /* rna - must keep these in sync with the other operators */
00395     RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
00396     RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
00397 }
00398 
00399 
00400 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
00401 static int view_scrolldown_exec(bContext *C, wmOperator *op)
00402 {
00403     v2dViewPanData *vpd;
00404     
00405     /* initialise default settings (and validate if ok to run) */
00406     if (!view_pan_init(C, op))
00407         return OPERATOR_PASS_THROUGH;
00408         
00409     /* also, check if can pan in vertical axis */
00410     vpd= op->customdata;
00411     if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
00412         view_pan_exit(op);
00413         return OPERATOR_PASS_THROUGH;
00414     }
00415     
00416     /* set RNA-Props */
00417     RNA_int_set(op->ptr, "deltax", 0);
00418     RNA_int_set(op->ptr, "deltay", -40);
00419     
00420     if(RNA_boolean_get(op->ptr, "page")) {
00421         ARegion *ar= CTX_wm_region(C);
00422         RNA_int_set(op->ptr, "deltay", ar->v2d.mask.ymin - ar->v2d.mask.ymax);
00423     }
00424     
00425     /* apply movement, then we're done */
00426     view_pan_apply(op);
00427     view_pan_exit(op);
00428     
00429     return OPERATOR_FINISHED;
00430 }
00431 
00432 static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
00433 {
00434     /* identifiers */
00435     ot->name= "Scroll Down";
00436     ot->description= "Scroll the view down";
00437     ot->idname= "VIEW2D_OT_scroll_down";
00438     
00439     /* api callbacks */
00440     ot->exec= view_scrolldown_exec;
00441     
00442     /* rna - must keep these in sync with the other operators */
00443     RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
00444     RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
00445     RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll down one page");
00446 }
00447 
00448 
00449 
00450 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
00451 static int view_scrollup_exec(bContext *C, wmOperator *op)
00452 {
00453     v2dViewPanData *vpd;
00454     
00455     /* initialise default settings (and validate if ok to run) */
00456     if (!view_pan_init(C, op))
00457         return OPERATOR_PASS_THROUGH;
00458         
00459     /* also, check if can pan in vertical axis */
00460     vpd= op->customdata;
00461     if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
00462         view_pan_exit(op);
00463         return OPERATOR_PASS_THROUGH;
00464     }
00465     
00466     /* set RNA-Props */
00467     RNA_int_set(op->ptr, "deltax", 0);
00468     RNA_int_set(op->ptr, "deltay", 40);
00469     
00470     if(RNA_boolean_get(op->ptr, "page")) {
00471         ARegion *ar= CTX_wm_region(C);
00472         RNA_int_set(op->ptr, "deltay", ar->v2d.mask.ymax - ar->v2d.mask.ymin);
00473     }
00474     
00475     /* apply movement, then we're done */
00476     view_pan_apply(op);
00477     view_pan_exit(op);
00478     
00479     return OPERATOR_FINISHED;
00480 }
00481 
00482 static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
00483 {
00484     /* identifiers */
00485     ot->name= "Scroll Up";
00486     ot->description= "Scroll the view up";
00487     ot->idname= "VIEW2D_OT_scroll_up";
00488     
00489     /* api callbacks */
00490     ot->exec= view_scrollup_exec;
00491     
00492     /* rna - must keep these in sync with the other operators */
00493     RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
00494     RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
00495     RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll up one page");
00496 }
00497 
00498 /* ********************************************************* */
00499 /* SINGLE-STEP VIEW ZOOMING OPERATOR                         */
00500 
00501 /*  This group of operators come in several forms:
00502  *      1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
00503  *      2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y)  // XXX this could be implemented...
00504  *      3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
00505  *
00506  *  In order to make sure this works, each operator must define the following RNA-Operator Props:
00507  *      zoomfacx, zoomfacy  - These two zoom factors allow for non-uniform scaling.
00508  *                            It is safe to scale by 0, as these factors are used to determine
00509  *                            amount to enlarge 'cur' by
00510  */
00511 
00512 /* ------------------ 'Shared' stuff ------------------------ */
00513 
00514 /* temp customdata for operator */
00515 typedef struct v2dViewZoomData {
00516     View2D *v2d;            /* view2d we're operating in */
00517     ARegion *ar;
00518 
00519     /* needed for continuous zoom */
00520     wmTimer *timer;
00521     double timer_lastdraw;
00522 
00523     int lastx, lasty;       /* previous x/y values of mouse in window */
00524     int invoke_event;       /* event type that invoked, for modal exits */
00525     float dx, dy;           /* running tally of previous delta values (for obtaining final zoom) */
00526     float mx_2d, my_2d;     /* initial mouse location in v2d coords */
00527 } v2dViewZoomData;
00528 
00529 
00530 /* initialise panning customdata */
00531 static int view_zoomdrag_init(bContext *C, wmOperator *op)
00532 {
00533     ARegion *ar= CTX_wm_region(C);
00534     v2dViewZoomData *vzd;
00535     View2D *v2d;
00536     
00537     /* regions now have v2d-data by default, so check for region */
00538     if (ar == NULL)
00539         return 0;
00540     v2d= &ar->v2d;
00541     
00542     /* check that 2d-view is zoomable */
00543     if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
00544         return 0;
00545     
00546     /* set custom-data for operator */
00547     vzd= MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
00548     op->customdata= vzd;
00549     
00550     /* set pointers to owners */
00551     vzd->v2d= v2d;
00552     vzd->ar = ar;
00553     
00554     return 1;
00555 }
00556 
00557 /* check if step-zoom can be applied */
00558 static int view_zoom_poll(bContext *C)
00559 {
00560     ARegion *ar= CTX_wm_region(C);
00561     View2D *v2d;
00562     
00563     /* check if there's a region in context to work with */
00564     if (ar == NULL)
00565         return 0;
00566     v2d= &ar->v2d;
00567     
00568     /* check that 2d-view is zoomable */
00569     if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
00570         return 0;
00571         
00572     /* view is zoomable */
00573     return 1;
00574 }
00575  
00576 /* apply transform to view (i.e. adjust 'cur' rect) */
00577 static void view_zoomstep_apply(bContext *C, wmOperator *op)
00578 {
00579     v2dViewZoomData *vzd= op->customdata;
00580     ARegion *ar= CTX_wm_region(C);
00581     View2D *v2d= &ar->v2d;
00582     float dx, dy, facx, facy;
00583     
00584     /* calculate amount to move view by, ensuring symmetry so the
00585      * old zoom level is restored after zooming back the same amount 
00586      */
00587     facx= RNA_float_get(op->ptr, "zoomfacx");
00588     facy= RNA_float_get(op->ptr, "zoomfacy");
00589 
00590     if (facx >= 0.0f) {
00591         dx= (v2d->cur.xmax - v2d->cur.xmin) * facx;
00592         dy= (v2d->cur.ymax - v2d->cur.ymin) * facy;
00593     }
00594     else {
00595         dx= ((v2d->cur.xmax - v2d->cur.xmin)/(1.0f + 2.0f*facx)) * facx;
00596         dy= ((v2d->cur.ymax - v2d->cur.ymin)/(1.0f + 2.0f*facy)) * facy;
00597     }
00598 
00599     /* only resize view on an axis if change is allowed */
00600     if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
00601         if (v2d->keepofs & V2D_LOCKOFS_X) {
00602             v2d->cur.xmax -= 2*dx;
00603         }
00604         else if (v2d->keepofs & V2D_KEEPOFS_X) {
00605             if (v2d->align & V2D_ALIGN_NO_POS_X)
00606                 v2d->cur.xmin += 2*dx;
00607             else
00608                 v2d->cur.xmax -= 2*dx;
00609         }
00610         else {
00611             if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00612                 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
00613                 float mval_faci = 1.0f - mval_fac;
00614                 float ofs= (mval_fac * dx) - (mval_faci * dx);
00615                 
00616                 v2d->cur.xmin += ofs + dx;
00617                 v2d->cur.xmax += ofs - dx;
00618             }
00619             else {
00620                 v2d->cur.xmin += dx;
00621                 v2d->cur.xmax -= dx;
00622             }
00623         }
00624     }
00625     if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
00626         if (v2d->keepofs & V2D_LOCKOFS_Y) {
00627             v2d->cur.ymax -= 2*dy;
00628         }
00629         else if (v2d->keepofs & V2D_KEEPOFS_Y) {
00630             if (v2d->align & V2D_ALIGN_NO_POS_Y)
00631                 v2d->cur.ymin += 2*dy;
00632             else
00633                 v2d->cur.ymax -= 2*dy;
00634         }
00635         else {
00636             if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00637                 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
00638                 float mval_faci = 1.0f - mval_fac;
00639                 float ofs= (mval_fac * dy) - (mval_faci * dy);
00640                 
00641                 v2d->cur.ymin += ofs + dy;
00642                 v2d->cur.ymax += ofs - dy;
00643             } 
00644             else {
00645                 v2d->cur.ymin += dy;
00646                 v2d->cur.ymax -= dy;
00647             }
00648         }
00649     }
00650 
00651     /* validate that view is in valid configuration after this operation */
00652     UI_view2d_curRect_validate(v2d);
00653 
00654     /* request updates to be done... */
00655     ED_region_tag_redraw(vzd->ar);
00656     UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
00657 }
00658 
00659 /* --------------- Individual Operators ------------------- */
00660 
00661 /* cleanup temp customdata  */
00662 static void view_zoomstep_exit(wmOperator *op)
00663 {
00664     if (op->customdata) {
00665         MEM_freeN(op->customdata);
00666         op->customdata= NULL;               
00667     }
00668 }
00669 
00670 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
00671 static int view_zoomin_exec(bContext *C, wmOperator *op)
00672 {
00673     /* check that there's an active region, as View2D data resides there */
00674     if (!view_zoom_poll(C))
00675         return OPERATOR_PASS_THROUGH;
00676     
00677     /* set RNA-Props - zooming in by uniform factor */
00678     RNA_float_set(op->ptr, "zoomfacx", 0.0375f);
00679     RNA_float_set(op->ptr, "zoomfacy", 0.0375f);
00680     
00681     /* apply movement, then we're done */
00682     view_zoomstep_apply(C, op);
00683     
00684     view_zoomstep_exit(op);
00685     
00686     return OPERATOR_FINISHED;
00687 }
00688 
00689 static int view_zoomin_invoke(bContext *C, wmOperator *op, wmEvent *event)
00690 {
00691     v2dViewZoomData *vzd;
00692     
00693     if (!view_zoomdrag_init(C, op))
00694         return OPERATOR_PASS_THROUGH;
00695     
00696     vzd= op->customdata;
00697     
00698     if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00699         ARegion *ar= CTX_wm_region(C);
00700         
00701         /* store initial mouse position (in view space) */
00702         UI_view2d_region_to_view(&ar->v2d, 
00703                 event->mval[0], event->mval[1],
00704                 &vzd->mx_2d, &vzd->my_2d);
00705     }
00706     
00707     return view_zoomin_exec(C, op);
00708 }
00709 
00710 static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
00711 {
00712     /* identifiers */
00713     ot->name= "Zoom In";
00714     ot->description= "Zoom in the view";
00715     ot->idname= "VIEW2D_OT_zoom_in";
00716     
00717     /* api callbacks */
00718     ot->invoke= view_zoomin_invoke;
00719 //  ot->exec= view_zoomin_exec;  // XXX, needs view_zoomdrag_init called first.
00720     ot->poll= view_zoom_poll;
00721     
00722     /* rna - must keep these in sync with the other operators */
00723     RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
00724     RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
00725 }
00726     
00727 /* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
00728 static int view_zoomout_exec(bContext *C, wmOperator *op)
00729 {
00730     /* check that there's an active region, as View2D data resides there */
00731     if (!view_zoom_poll(C))
00732         return OPERATOR_PASS_THROUGH;
00733     
00734     /* set RNA-Props - zooming in by uniform factor */
00735     RNA_float_set(op->ptr, "zoomfacx", -0.0375f);
00736     RNA_float_set(op->ptr, "zoomfacy", -0.0375f);
00737     
00738     /* apply movement, then we're done */
00739     view_zoomstep_apply(C, op);
00740 
00741     view_zoomstep_exit(op);
00742     
00743     return OPERATOR_FINISHED;
00744 }
00745 
00746 static int view_zoomout_invoke(bContext *C, wmOperator *op, wmEvent *event)
00747 {
00748     v2dViewZoomData *vzd;
00749     
00750     if (!view_zoomdrag_init(C, op))
00751         return OPERATOR_PASS_THROUGH;
00752 
00753     vzd= op->customdata;
00754     
00755     if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00756         ARegion *ar= CTX_wm_region(C);
00757         
00758         /* store initial mouse position (in view space) */
00759         UI_view2d_region_to_view(&ar->v2d, 
00760                 event->mval[0], event->mval[1],
00761                 &vzd->mx_2d, &vzd->my_2d);
00762     }
00763     
00764     return view_zoomout_exec(C, op);
00765 }
00766 
00767 static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
00768 {
00769     /* identifiers */
00770     ot->name= "Zoom Out";
00771     ot->description= "Zoom out the view";
00772     ot->idname= "VIEW2D_OT_zoom_out";
00773     
00774     /* api callbacks */
00775     ot->invoke= view_zoomout_invoke;
00776 //  ot->exec= view_zoomout_exec; // XXX, needs view_zoomdrag_init called first.
00777     ot->poll= view_zoom_poll;
00778     
00779     /* rna - must keep these in sync with the other operators */
00780     RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
00781     RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
00782 }
00783 
00784 /* ********************************************************* */
00785 /* DRAG-ZOOM OPERATOR                                    */
00786 
00787 /*  MMB Drag - allows non-uniform scaling by dragging mouse
00788  *
00789  *  In order to make sure this works, each operator must define the following RNA-Operator Props:
00790  *      deltax, deltay  - amounts to add to each side of the 'cur' rect
00791  */
00792 
00793 /* apply transform to view (i.e. adjust 'cur' rect) */
00794 static void view_zoomdrag_apply(bContext *C, wmOperator *op)
00795 {
00796     v2dViewZoomData *vzd= op->customdata;
00797     View2D *v2d= vzd->v2d;
00798     float dx, dy;
00799     
00800     /* get amount to move view by */
00801     dx= RNA_float_get(op->ptr, "deltax");
00802     dy= RNA_float_get(op->ptr, "deltay");
00803 
00804     /* continous zoom shouldn't move that fast... */
00805     if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
00806         double time= PIL_check_seconds_timer();
00807         float time_step= (float)(time - vzd->timer_lastdraw);
00808 
00809         dx *= time_step * 0.5f;
00810         dy *= time_step * 0.5f;
00811         
00812         vzd->timer_lastdraw= time;
00813     }
00814 
00815     /* only move view on an axis if change is allowed */
00816     if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
00817         if (v2d->keepofs & V2D_LOCKOFS_X) {
00818             v2d->cur.xmax -= 2*dx;
00819         }
00820         else {
00821             if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00822                 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
00823                 float mval_faci = 1.0f - mval_fac;
00824                 float ofs= (mval_fac * dx) - (mval_faci * dx);
00825                 
00826                 v2d->cur.xmin += ofs + dx;
00827                 v2d->cur.xmax += ofs - dx;
00828             }
00829             else {
00830                 v2d->cur.xmin += dx;
00831                 v2d->cur.xmax -= dx;
00832             }
00833         }
00834     }
00835     if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
00836         if (v2d->keepofs & V2D_LOCKOFS_Y) {
00837             v2d->cur.ymax -= 2*dy;
00838         }
00839         else {
00840             if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00841                 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
00842                 float mval_faci = 1.0f - mval_fac;
00843                 float ofs= (mval_fac * dy) - (mval_faci * dy);
00844                 
00845                 v2d->cur.ymin += ofs + dy;
00846                 v2d->cur.ymax += ofs - dy;
00847             }
00848             else {
00849                 v2d->cur.ymin += dy;
00850                 v2d->cur.ymax -= dy;
00851             }
00852         }
00853     }
00854     
00855     /* validate that view is in valid configuration after this operation */
00856     UI_view2d_curRect_validate(v2d);
00857     
00858     /* request updates to be done... */
00859     ED_region_tag_redraw(vzd->ar);
00860     UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
00861 }
00862 
00863 /* cleanup temp customdata  */
00864 static void view_zoomdrag_exit(bContext *C, wmOperator *op)
00865 {
00866     if (op->customdata) {
00867         v2dViewZoomData *vzd= op->customdata;
00868         
00869         if(vzd->timer)
00870             WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer);
00871         
00872         MEM_freeN(op->customdata);
00873         op->customdata= NULL;               
00874     }
00875 } 
00876 
00877 static int view_zoomdrag_cancel(bContext *C, wmOperator *op)
00878 {
00879     view_zoomdrag_exit(C, op);
00880 
00881     return OPERATOR_CANCELLED;
00882 }
00883 
00884 /* for 'redo' only, with no user input */
00885 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
00886 {
00887     if (!view_zoomdrag_init(C, op))
00888         return OPERATOR_PASS_THROUGH;
00889     
00890     view_zoomdrag_apply(C, op);
00891     view_zoomdrag_exit(C, op);
00892     return OPERATOR_FINISHED;
00893 }
00894 
00895 /* set up modal operator and relevant settings */
00896 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
00897 {
00898     wmWindow *window= CTX_wm_window(C);
00899     v2dViewZoomData *vzd;
00900     View2D *v2d;
00901     
00902     /* set up customdata */
00903     if (!view_zoomdrag_init(C, op))
00904         return OPERATOR_PASS_THROUGH;
00905     
00906     vzd= op->customdata;
00907     v2d= vzd->v2d;
00908     
00909     if (event->type == MOUSEZOOM) {
00910         float dx, dy, fac;
00911         
00912         vzd->lastx= event->prevx;
00913         vzd->lasty= event->prevy;
00914         
00915         /* As we have only 1D information (magnify value), feed both axes
00916          * with magnify information that is stored in x axis 
00917          */
00918         fac= 0.01f * (event->x - event->prevx);
00919         dx= fac * (v2d->cur.xmax - v2d->cur.xmin) / 10.0f;
00920         dy= fac * (v2d->cur.ymax - v2d->cur.ymin) / 10.0f;
00921 
00922         RNA_float_set(op->ptr, "deltax", dx);
00923         RNA_float_set(op->ptr, "deltay", dy);
00924         
00925         view_zoomdrag_apply(C, op);
00926         view_zoomdrag_exit(C, op);
00927         return OPERATOR_FINISHED;
00928     }   
00929     
00930     /* set initial settings */
00931     vzd->lastx= event->x;
00932     vzd->lasty= event->y;
00933     RNA_float_set(op->ptr, "deltax", 0);
00934     RNA_float_set(op->ptr, "deltay", 0);
00935     
00936     /* for modal exit test */
00937     vzd->invoke_event= event->type;
00938     
00939     if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
00940         ARegion *ar= CTX_wm_region(C);
00941         
00942         /* store initial mouse position (in view space) */
00943         UI_view2d_region_to_view(&ar->v2d, 
00944                 event->mval[0], event->mval[1],
00945                 &vzd->mx_2d, &vzd->my_2d);
00946     }
00947 
00948     if (v2d->keepofs & V2D_LOCKOFS_X)
00949         WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
00950     else if (v2d->keepofs & V2D_LOCKOFS_Y)
00951         WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
00952     else
00953         WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
00954     
00955     /* add temp handler */
00956     WM_event_add_modal_handler(C, op);
00957 
00958     if (U.viewzoom == USER_ZOOM_CONT) {
00959         /* needs a timer to continue redrawing */
00960         vzd->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
00961         vzd->timer_lastdraw= PIL_check_seconds_timer();
00962     }
00963 
00964     return OPERATOR_RUNNING_MODAL;
00965 }
00966 
00967 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
00968 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
00969 {
00970     v2dViewZoomData *vzd= op->customdata;
00971     View2D *v2d= vzd->v2d;
00972     
00973     /* execute the events */
00974     if (event->type == TIMER && event->customdata == vzd->timer) {
00975         view_zoomdrag_apply(C, op);
00976     }
00977     else if(event->type == MOUSEMOVE) {
00978         float dx, dy;
00979         
00980         /* calculate new delta transform, based on zooming mode */
00981         if (U.viewzoom == USER_ZOOM_SCALE) {
00982             /* 'scale' zooming */
00983             float dist;
00984             
00985             /* x-axis transform */
00986             dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
00987             dx= 1.0f - (fabsf(vzd->lastx - dist) + 2.0f) / (fabsf(event->x - dist) + 2.0f);
00988             dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
00989             
00990             /* y-axis transform */
00991             dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
00992             dy= 1.0f - (fabsf(vzd->lasty - dist) + 2.0f) / (fabsf(event->y - dist) + 2.0f);
00993             dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
00994         }
00995         else {
00996             /* 'continuous' or 'dolly' */
00997             float fac;
00998             
00999             /* x-axis transform */
01000             fac= 0.01f * (event->x - vzd->lastx);
01001             dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
01002             
01003             /* y-axis transform */
01004             fac= 0.01f * (event->y - vzd->lasty);
01005             dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
01006 #if 0
01007             /* continous zoom shouldn't move that fast... */
01008             if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
01009                 double time= PIL_check_seconds_timer();
01010                 float time_step= (float)(time - vzd->timer_lastdraw);
01011 
01012                 dx /= (0.1f / time_step);
01013                 dy /= (0.1f / time_step);
01014                 
01015                 vzd->timer_lastdraw= time;
01016             }
01017 #endif
01018         }
01019         
01020         /* set transform amount, and add current deltas to stored total delta (for redo) */
01021         RNA_float_set(op->ptr, "deltax", dx);
01022         RNA_float_set(op->ptr, "deltay", dy);
01023         vzd->dx += dx;
01024         vzd->dy += dy;
01025         
01026         /* store mouse coordinates for next time, if not doing continuous zoom
01027          *  - continuous zoom only depends on distance of mouse to starting point to determine rate of change
01028          */
01029         if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
01030             vzd->lastx= event->x;
01031             vzd->lasty= event->y;
01032         }
01033         
01034         /* apply zooming */
01035         view_zoomdrag_apply(C, op);
01036     } 
01037     else if (event->type == vzd->invoke_event || event->type==ESCKEY) {
01038         if (event->val == KM_RELEASE) {
01039             
01040             /* for redo, store the overall deltas - need to respect zoom-locks here... */
01041             if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
01042                 RNA_float_set(op->ptr, "deltax", vzd->dx);
01043             else
01044                 RNA_float_set(op->ptr, "deltax", 0);
01045                 
01046             if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
01047                 RNA_float_set(op->ptr, "deltay", vzd->dy);
01048             else
01049                 RNA_float_set(op->ptr, "deltay", 0);
01050             
01051             /* free customdata */
01052             view_zoomdrag_exit(C, op);
01053             WM_cursor_restore(CTX_wm_window(C));
01054             
01055             return OPERATOR_FINISHED;
01056         }
01057     }
01058 
01059     return OPERATOR_RUNNING_MODAL;
01060 }
01061 
01062 static void VIEW2D_OT_zoom(wmOperatorType *ot)
01063 {
01064     /* identifiers */
01065     ot->name= "Zoom 2D View";
01066     ot->description= "Zoom in/out the view";
01067     ot->idname= "VIEW2D_OT_zoom";
01068     
01069     /* api callbacks */
01070     ot->exec= view_zoomdrag_exec;
01071     ot->invoke= view_zoomdrag_invoke;
01072     ot->modal= view_zoomdrag_modal;
01073     ot->cancel= view_zoomdrag_cancel;
01074     
01075     ot->poll= view_zoom_poll;
01076     
01077     /* operator is repeatable */
01078     // ot->flag= OPTYPE_BLOCKING;
01079     
01080     /* rna - must keep these in sync with the other operators */
01081     RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
01082     RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
01083 }
01084 
01085 /* ********************************************************* */
01086 /* BORDER-ZOOM */
01087 
01088 /* The user defines a rect using standard borderselect tools, and we use this rect to 
01089  * define the new zoom-level of the view in the following ways:
01090  *  1) LEFTMOUSE - zoom in to view
01091  *  2) RIGHTMOUSE - zoom out of view
01092  *
01093  * Currently, these key mappings are hardcoded, but it shouldn't be too important to
01094  * have custom keymappings for this...
01095  */
01096  
01097 static int view_borderzoom_exec(bContext *C, wmOperator *op)
01098 {
01099     ARegion *ar= CTX_wm_region(C);
01100     View2D *v2d= &ar->v2d;
01101     rctf rect;
01102     int gesture_mode;
01103     
01104     /* convert coordinates of rect to 'tot' rect coordinates */
01105     UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
01106     UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
01107     
01108     /* check if zooming in/out view */
01109     gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
01110     
01111     if (gesture_mode == GESTURE_MODAL_IN) {
01112         /* zoom in: 
01113          *  - 'cur' rect will be defined by the coordinates of the border region 
01114          *  - just set the 'cur' rect to have the same coordinates as the border region
01115          *    if zoom is allowed to be changed
01116          */
01117         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
01118             v2d->cur.xmin= rect.xmin;
01119             v2d->cur.xmax= rect.xmax;
01120         }
01121         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
01122             v2d->cur.ymin= rect.ymin;
01123             v2d->cur.ymax= rect.ymax;
01124         }
01125     }
01126     else /* if (gesture_mode == GESTURE_MODAL_OUT) */ {
01127         /* zoom out:
01128          *  - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are, 
01129          *    but the 'cur' rect coordinates will need to be adjusted to take in more of the view
01130          *  - calculate zoom factor, and adjust using center-point
01131          */
01132         float zoom, center, size;
01133         
01134         // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
01135         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
01136             size= (v2d->cur.xmax - v2d->cur.xmin);
01137             zoom= size / (rect.xmax - rect.xmin);
01138             center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
01139             
01140             v2d->cur.xmin= center - (size * zoom);
01141             v2d->cur.xmax= center + (size * zoom);
01142         }
01143         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
01144             size= (v2d->cur.ymax - v2d->cur.ymin);
01145             zoom= size / (rect.ymax - rect.ymin);
01146             center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
01147             
01148             v2d->cur.ymin= center - (size * zoom);
01149             v2d->cur.ymax= center + (size * zoom);
01150         }
01151     }
01152     
01153     /* validate that view is in valid configuration after this operation */
01154     UI_view2d_curRect_validate(v2d);
01155     
01156     /* request updates to be done... */
01157     ED_region_tag_redraw(ar);
01158     UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
01159     
01160     return OPERATOR_FINISHED;
01161 } 
01162 
01163 static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
01164 {
01165     /* identifiers */
01166     ot->name= "Zoom to Border";
01167     ot->description= "Zoom in the view to the nearest item contained in the border";
01168     ot->idname= "VIEW2D_OT_zoom_border";
01169     
01170     /* api callbacks */
01171     ot->invoke= WM_border_select_invoke;
01172     ot->exec= view_borderzoom_exec;
01173     ot->modal= WM_border_select_modal;
01174     ot->cancel= WM_border_select_cancel;
01175     
01176     ot->poll= view_zoom_poll;
01177     
01178     /* rna */
01179     RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
01180     RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
01181     RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
01182     RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
01183     RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
01184 }
01185 
01186 /* ********************************************************* */
01187 /* SCROLLERS */
01188 
01189 /*  Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
01190  *      1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, 
01191  *          enlarge 'cur' rect on the relevant side 
01192  *      2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
01193  *
01194  *  In order to make sure this works, each operator must define the following RNA-Operator Props:
01195  *      deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
01196  */
01197 
01198 /* customdata for scroller-invoke data */
01199 typedef struct v2dScrollerMove {
01200     View2D *v2d;            /* View2D data that this operation affects */
01201     ARegion *ar;            /* region that the scroller is in */
01202     
01203     short scroller;         /* scroller that mouse is in ('h' or 'v') */
01204     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?)
01205     
01206     float fac;              /* view adjustment factor, based on size of region */
01207     float delta;            /* amount moved by mouse on axis of interest */
01208     
01209     float scrollbarwidth;   /* width of the scrollbar itself, used for page up/down clicks */
01210     int scrollbar_orig;      /* initial location of scrollbar x/y, mouse relative */
01211     
01212     int lastx, lasty;       /* previous mouse coordinates (in screen coordinates) for determining movement */
01213 } v2dScrollerMove;
01214 
01215 
01216 /* View2DScrollers is typedef'd in UI_view2d.h 
01217  * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
01218  * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
01219  *         For now, we don't need to have a separate (internal) header for structs like this...
01220  */
01221 struct View2DScrollers {    
01222         /* focus bubbles */
01223     int vert_min, vert_max; /* vertical scrollbar */
01224     int hor_min, hor_max;   /* horizontal scrollbar */
01225 };
01226 
01227 /* quick enum for vsm->zone (scroller handles) */
01228 enum {
01229     SCROLLHANDLE_MIN= -1,
01230     SCROLLHANDLE_BAR,
01231     SCROLLHANDLE_MAX,
01232     SCROLLHANDLE_MIN_OUTSIDE,
01233     SCROLLHANDLE_MAX_OUTSIDE
01234 } /*eV2DScrollerHandle_Zone*/;
01235 
01236 /* ------------------------ */
01237 
01238 /* check if mouse is within scroller handle 
01239  *  - mouse         =   relevant mouse coordinate in region space
01240  *  - sc_min, sc_max    =   extents of scroller 'groove' (potential available space for scroller)
01241  *  - sh_min, sh_max    =   positions of scrollbar handles
01242  */
01243 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
01244 {
01245     short in_min, in_max, in_bar, out_min, out_max, in_view=1;
01246     
01247     /* firstly, check if 
01248      *  - 'bubble' fills entire scroller 
01249      *  - 'bubble' completely out of view on either side 
01250      */
01251     if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
01252     if (sh_min == sh_max) {
01253         if (sh_min <= sc_min) in_view= 0;
01254         if (sh_max >= sc_max) in_view= 0;
01255     }
01256     else {
01257         if (sh_max <= sc_min) in_view= 0;
01258         if (sh_min >= sc_max) in_view= 0;
01259     }
01260     
01261     
01262     if (in_view == 0) {
01263         return SCROLLHANDLE_BAR;
01264     }
01265     
01266     /* check if mouse is in or past either handle */
01267     // TODO: check if these extents are still valid or not
01268     in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
01269     in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
01270     in_bar= ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) );
01271     out_min= mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE);
01272     out_max= mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE);
01273     
01274     if (in_bar)
01275         return SCROLLHANDLE_BAR;
01276     else if (in_max)
01277         return SCROLLHANDLE_MAX;
01278     else if (in_min)
01279         return SCROLLHANDLE_MIN;
01280     else if (out_min)
01281         return SCROLLHANDLE_MIN_OUTSIDE;              
01282     else if (out_max)
01283         return SCROLLHANDLE_MAX_OUTSIDE;
01284     
01285     /* unlikely to happen, though we just cover it in case */
01286     return SCROLLHANDLE_BAR;
01287 } 
01288 
01289 /* initialise customdata for scroller manipulation operator */
01290 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
01291 {
01292     v2dScrollerMove *vsm;
01293     View2DScrollers *scrollers;
01294     ARegion *ar= CTX_wm_region(C);
01295     View2D *v2d= &ar->v2d;
01296     float mask_size;
01297     
01298     /* set custom-data for operator */
01299     vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
01300     op->customdata= vsm;
01301     
01302     /* set general data */
01303     vsm->v2d= v2d;
01304     vsm->ar= ar;
01305     vsm->scroller= in_scroller;
01306 
01307     /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
01308     vsm->lastx = event->x;
01309     vsm->lasty = event->y;
01310     /* 'zone' depends on where mouse is relative to bubble 
01311      *  - zooming must be allowed on this axis, otherwise, default to pan
01312      */
01313     scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
01314 
01315 
01316     if (in_scroller == 'h') {
01317         /* horizontal scroller - calculate adjustment factor first */
01318         mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
01319         vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
01320         
01321         /* get 'zone' (i.e. which part of scroller is activated) */
01322         vsm->zone= mouse_in_scroller_handle(event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
01323         
01324         if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
01325             /* default to scroll, as handles not usable */
01326             vsm->zone= SCROLLHANDLE_BAR;
01327         }
01328 
01329         vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
01330         vsm->scrollbar_orig = ((scrollers->hor_max + scrollers->hor_min) / 2) + ar->winrct.xmin;
01331     }
01332     else {
01333         /* vertical scroller - calculate adjustment factor first */
01334         mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
01335         vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
01336         
01337         /* get 'zone' (i.e. which part of scroller is activated) */
01338         vsm->zone= mouse_in_scroller_handle(event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
01339             
01340         if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
01341             /* default to scroll, as handles not usable */
01342             vsm->zone= SCROLLHANDLE_BAR;
01343         }
01344         
01345         vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
01346         vsm->scrollbar_orig = ((scrollers->vert_max + scrollers->vert_min) / 2) +  + ar->winrct.ymin;
01347     }
01348     
01349     UI_view2d_scrollers_free(scrollers);
01350     ED_region_tag_redraw(ar);
01351 }
01352 
01353 /* cleanup temp customdata  */
01354 static void scroller_activate_exit(bContext *C, wmOperator *op)
01355 {
01356     if (op->customdata) {
01357         v2dScrollerMove *vsm= op->customdata;
01358 
01359         vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE|V2D_SCROLL_V_ACTIVE);
01360         
01361         MEM_freeN(op->customdata);
01362         op->customdata= NULL;       
01363         
01364         ED_region_tag_redraw(CTX_wm_region(C));
01365     }
01366 }
01367 
01368 static int scroller_activate_cancel(bContext *C, wmOperator *op)
01369 {
01370     scroller_activate_exit(C, op);
01371 
01372     return OPERATOR_CANCELLED;
01373 }
01374 
01375 /* apply transform to view (i.e. adjust 'cur' rect) */
01376 static void scroller_activate_apply(bContext *C, wmOperator *op)
01377 {
01378     v2dScrollerMove *vsm= op->customdata;
01379     View2D *v2d= vsm->v2d;
01380     float temp;
01381     
01382     /* calculate amount to move view by */
01383     temp= vsm->fac * vsm->delta;
01384     
01385     /* type of movement */
01386     switch (vsm->zone) {
01387         case SCROLLHANDLE_MIN:
01388             /* only expand view on axis if zoom is allowed */
01389             if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
01390                 v2d->cur.xmin -= temp;
01391             if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
01392                 v2d->cur.ymin -= temp;
01393             break;
01394             
01395         case SCROLLHANDLE_MAX:
01396             
01397             /* only expand view on axis if zoom is allowed */
01398             if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
01399                 v2d->cur.xmax += temp;
01400             if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
01401                 v2d->cur.ymax += temp;
01402             break;
01403             
01404         case SCROLLHANDLE_MIN_OUTSIDE:
01405         case SCROLLHANDLE_MAX_OUTSIDE:
01406         case SCROLLHANDLE_BAR:
01407         default:
01408             /* only move view on an axis if panning is allowed */
01409             if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
01410                 v2d->cur.xmin += temp;
01411                 v2d->cur.xmax += temp;
01412             }
01413             if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
01414                 v2d->cur.ymin += temp;
01415                 v2d->cur.ymax += temp;
01416             }
01417             break;
01418             
01419     }
01420     
01421     /* validate that view is in valid configuration after this operation */
01422     UI_view2d_curRect_validate(v2d);
01423     
01424     /* request updates to be done... */
01425     ED_region_tag_redraw(vsm->ar);
01426     UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
01427 }
01428 
01429 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
01430 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
01431 {
01432     v2dScrollerMove *vsm= op->customdata;
01433     
01434     /* execute the events */
01435     switch (event->type) {
01436         case MOUSEMOVE:
01437         {
01438             /* calculate new delta transform, then store mouse-coordinates for next-time */
01439             if (ELEM(vsm->zone, SCROLLHANDLE_BAR, SCROLLHANDLE_MAX)) {
01440                 /* if using bar (i.e. 'panning') or 'max' zoom widget */
01441                 switch (vsm->scroller) {
01442                     case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
01443                         vsm->delta= (float)(event->x - vsm->lastx);
01444                         break;
01445                     case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
01446                         vsm->delta= (float)(event->y - vsm->lasty);
01447                         break;
01448                 }
01449             }
01450             else if (vsm->zone == SCROLLHANDLE_MIN) {
01451                 /* using 'min' zoom widget */
01452                 switch (vsm->scroller) {
01453                     case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
01454                         vsm->delta= (float)(vsm->lastx - event->x);
01455                         break;
01456                     case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
01457                         vsm->delta= (float)(vsm->lasty - event->y);
01458                         break;
01459                 }
01460             }
01461             
01462             /* store previous coordinates */
01463             vsm->lastx= event->x;
01464             vsm->lasty= event->y;
01465             
01466             scroller_activate_apply(C, op);
01467         }
01468             break;
01469             
01470         case LEFTMOUSE:
01471         case MIDDLEMOUSE:
01472             if (event->val==KM_RELEASE) {
01473                 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
01474                 if (ELEM(vsm->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
01475                     if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE)
01476                         vsm->delta = -vsm->scrollbarwidth * 0.8f;
01477                     else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE)
01478                         vsm->delta = vsm->scrollbarwidth * 0.8f;
01479                     
01480                     scroller_activate_apply(C, op);
01481                     scroller_activate_exit(C, op);
01482                     return OPERATOR_FINISHED;
01483                 }
01484                 
01485                 /* otherwise, end the drag action  */
01486                 if (vsm->lastx || vsm->lasty) {
01487                     scroller_activate_exit(C, op);
01488                     return OPERATOR_FINISHED;
01489                 }
01490             }
01491             break;
01492 
01493     }
01494 
01495     return OPERATOR_RUNNING_MODAL;
01496 }
01497 
01498 
01499 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
01500 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
01501 {
01502     ARegion *ar= CTX_wm_region(C);
01503     View2D *v2d= &ar->v2d;
01504     short in_scroller= 0;
01505         
01506     /* check if mouse in scrollbars, if they're enabled */
01507     in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
01508     
01509     /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
01510     if (in_scroller) {
01511         v2dScrollerMove *vsm;
01512         
01513         /* initialise customdata */
01514         scroller_activate_init(C, op, event, in_scroller);
01515         vsm= (v2dScrollerMove *)op->customdata;
01516         
01517         /* support for quick jump to location - gtk and qt do this on linux */
01518         if (event->type == MIDDLEMOUSE) {
01519             switch (vsm->scroller) {
01520                 case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
01521                     vsm->delta= (float)(event->x - vsm->scrollbar_orig);
01522                     break;
01523                 case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
01524                     vsm->delta= (float)(event->y - vsm->scrollbar_orig);
01525                     break;
01526             }
01527             scroller_activate_apply(C, op);
01528 
01529             vsm->zone= SCROLLHANDLE_BAR;
01530         }
01531 
01532         /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
01533          * NOTE: see view2d.c for latest conditions, and keep this in sync with that
01534          */
01535         if (ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
01536             if ( ((vsm->scroller=='h') && (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL)==0) ||
01537                  ((vsm->scroller=='v') && (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL)==0) )
01538             {
01539                 /* switch to bar (i.e. no scaling gets handled) */
01540                 vsm->zone= SCROLLHANDLE_BAR;
01541             }
01542         }
01543         
01544         /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
01545         if (vsm->zone == SCROLLHANDLE_BAR) {
01546             if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
01547                  ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
01548             {
01549                 /* free customdata initialised */
01550                 scroller_activate_exit(C, op);
01551                 
01552                 /* can't catch this event for ourselves, so let it go to someone else? */
01553                 return OPERATOR_PASS_THROUGH;
01554             }           
01555         }
01556         
01557         /* zone is also inappropriate if scroller is not visible... */
01558         if ( ((vsm->scroller=='h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_HORIZONTAL_FULLR))) ||
01559              ((vsm->scroller=='v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE|V2D_SCROLL_VERTICAL_FULLR))) )
01560         {
01561             /* free customdata initialised */
01562             scroller_activate_exit(C, op);
01563                 
01564             /* can't catch this event for ourselves, so let it go to someone else? */
01565             /* XXX note: if handlers use mask rect to clip input, input will fail for this case */
01566             return OPERATOR_PASS_THROUGH;
01567         }
01568         
01569         /* activate the scroller */
01570         if (vsm->scroller=='h')
01571             v2d->scroll_ui |= V2D_SCROLL_H_ACTIVE;
01572         else
01573             v2d->scroll_ui |= V2D_SCROLL_V_ACTIVE;
01574         
01575         /* still ok, so can add */
01576         WM_event_add_modal_handler(C, op);
01577         return OPERATOR_RUNNING_MODAL;
01578     }
01579     else {
01580         /* not in scroller, so nothing happened... (pass through let's something else catch event) */
01581         return OPERATOR_PASS_THROUGH;
01582     }
01583 }
01584 
01585 /* LMB-Drag in Scrollers - not repeatable operator! */
01586 static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
01587 {
01588     /* identifiers */
01589     ot->name= "Scroller Activate";
01590     ot->description= "Scroll view by mouse click and drag";
01591     ot->idname= "VIEW2D_OT_scroller_activate";
01592 
01593     /* flags */
01594     ot->flag= OPTYPE_BLOCKING;
01595     
01596     /* api callbacks */
01597     ot->invoke= scroller_activate_invoke;
01598     ot->modal= scroller_activate_modal;
01599     ot->cancel= scroller_activate_cancel;
01600 
01601     ot->poll= view2d_poll;
01602 }
01603 
01604 /* ********************************************************* */
01605 /* RESET */
01606 
01607 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
01608 {
01609     uiStyle *style= UI_GetStyle();
01610     ARegion *ar= CTX_wm_region(C);
01611     View2D *v2d= &ar->v2d;
01612     int winx, winy;
01613 
01614     /* zoom 1.0 */
01615     winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
01616     winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
01617 
01618     v2d->cur.xmax= v2d->cur.xmin + winx;
01619     v2d->cur.ymax= v2d->cur.ymin + winy;
01620     
01621     /* align */
01622     if (v2d->align) {
01623         /* posx and negx flags are mutually exclusive, so watch out */
01624         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
01625             v2d->cur.xmax= 0.0f;
01626             v2d->cur.xmin= -winx*style->panelzoom;
01627         }
01628         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
01629             v2d->cur.xmax= winx*style->panelzoom;
01630             v2d->cur.xmin= 0.0f;
01631         }
01632 
01633         /* - posx and negx flags are mutually exclusive, so watch out */
01634         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
01635             v2d->cur.ymax= 0.0f;
01636             v2d->cur.ymin= -winy*style->panelzoom;
01637         }
01638         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
01639             v2d->cur.ymax= winy*style->panelzoom;
01640             v2d->cur.ymin= 0.0f;
01641         }
01642     }
01643 
01644     /* validate that view is in valid configuration after this operation */
01645     UI_view2d_curRect_validate(v2d);
01646     
01647     /* request updates to be done... */
01648     ED_region_tag_redraw(ar);
01649     UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
01650     
01651     return OPERATOR_FINISHED;
01652 }
01653  
01654 static void VIEW2D_OT_reset(wmOperatorType *ot)
01655 {
01656     /* identifiers */
01657     ot->name= "Reset View";
01658     ot->description= "Reset the view";
01659     ot->idname= "VIEW2D_OT_reset";
01660     
01661     /* api callbacks */
01662     ot->exec= reset_exec;
01663     ot->poll= view2d_poll;
01664 }
01665  
01666 /* ********************************************************* */
01667 /* Registration */
01668 
01669 void UI_view2d_operatortypes(void)
01670 {
01671     WM_operatortype_append(VIEW2D_OT_pan);
01672     
01673     WM_operatortype_append(VIEW2D_OT_scroll_left);
01674     WM_operatortype_append(VIEW2D_OT_scroll_right);
01675     WM_operatortype_append(VIEW2D_OT_scroll_up);
01676     WM_operatortype_append(VIEW2D_OT_scroll_down);
01677     
01678     WM_operatortype_append(VIEW2D_OT_zoom_in);
01679     WM_operatortype_append(VIEW2D_OT_zoom_out);
01680     
01681     WM_operatortype_append(VIEW2D_OT_zoom);
01682     WM_operatortype_append(VIEW2D_OT_zoom_border);
01683     
01684     WM_operatortype_append(VIEW2D_OT_scroller_activate);
01685 
01686     WM_operatortype_append(VIEW2D_OT_reset);
01687 }
01688 
01689 void UI_view2d_keymap(wmKeyConfig *keyconf)
01690 {
01691     wmKeyMap *keymap= WM_keymap_find(keyconf, "View2D", 0, 0);
01692     wmKeyMapItem *kmi;
01693 
01694     /* scrollers */
01695     WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
01696     WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", MIDDLEMOUSE, KM_PRESS, 0, 0);
01697 
01698     /* pan/scroll */
01699     WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
01700     WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
01701     
01702     WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
01703     
01704     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
01705     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
01706     
01707     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
01708     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
01709     
01710     /* zoom - single step */
01711     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
01712     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
01713     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
01714     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
01715     
01716     /* scroll up/down - no modifiers, only when zoom fails */
01717         /* these may fail if zoom is disallowed, in which case they should pass on event */
01718     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
01719     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
01720         /* these may be necessary if vertical scroll is disallowed */
01721     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
01722     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
01723     
01724     /* alternatives for page up/down to scroll */
01725 #if 0 // XXX disabled, since this causes conflicts with hotkeys in animation editors
01726         /* scroll up/down may fall through to left/right */
01727     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
01728     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
01729     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, 0, 0);
01730     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, 0, 0);
01731         /* shift for moving view left/right with page up/down */
01732     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
01733     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
01734 #endif
01735     
01736     /* zoom - drag */
01737     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
01738     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
01739     
01740     /* borderzoom - drag */
01741     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
01742 
01743     /* Alternative keymap for buttons listview */
01744     keymap= WM_keymap_find(keyconf, "View2D Buttons List", 0, 0);
01745 
01746     WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
01747     WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", MIDDLEMOUSE, KM_PRESS, 0, 0);
01748 
01749     WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
01750     WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
01751     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
01752     WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
01753     
01754     kmi = WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
01755     RNA_boolean_set(kmi->ptr, "page", TRUE);
01756     kmi = WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
01757     RNA_boolean_set(kmi->ptr, "page", TRUE);
01758     
01759     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
01760     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
01761     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
01762     WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
01763     WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);
01764 }
01765