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