svn merge -r39286:39298 https://svn.blender.org/svnroot/bf-blender/trunk/blender...
[blender.git] / source / blender / editors / interface / view2d_ops.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  * 
23  * Contributor(s): Blender Foundation, Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/interface/view2d_ops.c
29  *  \ingroup edinterface
30  */
31
32
33 #include <math.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_context.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50
51 #include "ED_screen.h"
52
53 #include "UI_view2d.h"
54
55 #include "PIL_time.h" /* USER_ZOOM_CONT */
56
57 static int view2d_poll(bContext *C)
58 {
59         ARegion *ar= CTX_wm_region(C);
60
61         return (ar != NULL) && (ar->v2d.flag & V2D_IS_INITIALISED);
62 }
63
64 /* ********************************************************* */
65 /* VIEW PANNING OPERATOR                                                                 */
66
67 /*      This group of operators come in several forms:
68  *              1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
69  *              2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
70  *
71  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
72  *              deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
73  */
74
75 /* ------------------ Shared 'core' stuff ---------------------- */
76  
77 /* temp customdata for operator */
78 typedef struct v2dViewPanData {
79         bScreen *sc;                    /* screen where view pan was initiated */
80         ScrArea *sa;                    /* area where view pan was initiated */
81         ARegion *ar;                    /* region where view pan was initiated */
82         View2D *v2d;                    /* view2d we're operating in */
83         
84         float facx, facy;               /* amount to move view relative to zoom */
85         
86                 /* options for version 1 */
87         int startx, starty;             /* mouse x/y values in window when operator was initiated */
88         int lastx, lasty;               /* previous x/y values of mouse in window */
89         int invoke_event;               /* event starting pan, for modal exit */
90         
91         short in_scroller;              /* for MMB in scrollers (old feature in past, but now not that useful) */
92 } v2dViewPanData;
93  
94 /* initialise panning customdata */
95 static int view_pan_init(bContext *C, wmOperator *op)
96 {
97         ARegion *ar= CTX_wm_region(C);
98         v2dViewPanData *vpd;
99         View2D *v2d;
100         float winx, winy;
101         
102         /* regions now have v2d-data by default, so check for region */
103         if (ar == NULL)
104                 return 0;
105                 
106         /* check if panning is allowed at all */
107         v2d= &ar->v2d;
108         if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y))
109                 return 0;
110         
111         /* set custom-data for operator */
112         vpd= MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
113         op->customdata= vpd;
114         
115         /* set pointers to owners */
116         vpd->sc= CTX_wm_screen(C);
117         vpd->sa= CTX_wm_area(C);
118         vpd->v2d= v2d;
119         vpd->ar = ar;
120         
121         /* calculate translation factor - based on size of view */
122         winx= (float)(ar->winrct.xmax - ar->winrct.xmin + 1);
123         winy= (float)(ar->winrct.ymax - ar->winrct.ymin + 1);
124         vpd->facx= (v2d->cur.xmax - v2d->cur.xmin) / winx;
125         vpd->facy= (v2d->cur.ymax - v2d->cur.ymin) / winy;
126         
127         return 1;
128 }
129
130 /* apply transform to view (i.e. adjust 'cur' rect) */
131 static void view_pan_apply(wmOperator *op)
132 {
133         v2dViewPanData *vpd= op->customdata;
134         View2D *v2d= vpd->v2d;
135         float dx, dy;
136         
137         /* calculate amount to move view by */
138         dx= vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
139         dy= vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
140         
141         /* only move view on an axis if change is allowed */
142         if ((v2d->keepofs & V2D_LOCKOFS_X)==0) {
143                 v2d->cur.xmin += dx;
144                 v2d->cur.xmax += dx;
145         }
146         if ((v2d->keepofs & V2D_LOCKOFS_Y)==0) {
147                 v2d->cur.ymin += dy;
148                 v2d->cur.ymax += dy;
149         }
150         
151         /* validate that view is in valid configuration after this operation */
152         UI_view2d_curRect_validate(v2d);
153         
154         /* request updates to be done... */
155         ED_region_tag_redraw(vpd->ar);
156         
157         UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
158         
159         /* exceptions */
160         if (vpd->sa->spacetype==SPACE_OUTLINER) {
161                 /* don't rebuild full tree, since we're just changing our view */
162                 SpaceOops *soops= vpd->sa->spacedata.first;
163                 soops->storeflag |= SO_TREESTORE_REDRAW;
164         }
165 }
166
167 /* cleanup temp customdata  */
168 static void view_pan_exit(wmOperator *op)
169 {
170         if (op->customdata) {
171                 MEM_freeN(op->customdata);
172                 op->customdata= NULL;                           
173         }
174
175  
176 /* ------------------ Modal Drag Version (1) ---------------------- */
177
178 /* for 'redo' only, with no user input */
179 static int view_pan_exec(bContext *C, wmOperator *op)
180 {
181         if (!view_pan_init(C, op))
182                 return OPERATOR_CANCELLED;
183         
184         view_pan_apply(op);
185         view_pan_exit(op);
186         return OPERATOR_FINISHED;
187 }
188
189 /* set up modal operator and relevant settings */
190 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
191 {
192         wmWindow *window= CTX_wm_window(C);
193         v2dViewPanData *vpd;
194         View2D *v2d;
195         
196         /* set up customdata */
197         if (!view_pan_init(C, op))
198                 return OPERATOR_PASS_THROUGH;
199         
200         vpd= op->customdata;
201         v2d= vpd->v2d;
202         
203         /* set initial settings */
204         vpd->startx= vpd->lastx= event->x;
205         vpd->starty= vpd->lasty= event->y;
206         vpd->invoke_event= event->type;
207         
208         if (event->type == MOUSEPAN) {
209                 RNA_int_set(op->ptr, "deltax", event->prevx - event->x);
210                 RNA_int_set(op->ptr, "deltay", event->prevy - event->y);
211                 
212                 view_pan_apply(op);
213                 view_pan_exit(op);
214                 return OPERATOR_FINISHED;
215         }
216         
217         RNA_int_set(op->ptr, "deltax", 0);
218         RNA_int_set(op->ptr, "deltay", 0);
219         
220         if (v2d->keepofs & V2D_LOCKOFS_X)
221                 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
222         else if (v2d->keepofs & V2D_LOCKOFS_Y)
223                 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
224         else
225                 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
226         
227         /* add temp handler */
228         WM_event_add_modal_handler(C, op);
229
230         return OPERATOR_RUNNING_MODAL;
231 }
232
233 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
234 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
235 {
236         v2dViewPanData *vpd= op->customdata;
237         
238         /* execute the events */
239         switch (event->type) {
240                 case MOUSEMOVE:
241                 {
242                         /* calculate new delta transform, then store mouse-coordinates for next-time */
243                         RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
244                         RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
245                         
246                         vpd->lastx= event->x;
247                         vpd->lasty= event->y;
248                         
249                         view_pan_apply(op);
250                 }
251                         break;
252                         
253                 case LEFTMOUSE:
254                         /* switch to zoom */
255                         if (event->val==KM_PRESS) {
256                                 /* calculate overall delta mouse-movement for redo */
257                                 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
258                                 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
259                                 
260                                 view_pan_exit(op);
261                                 WM_cursor_restore(CTX_wm_window(C));
262                                 
263                                 WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
264                                 return OPERATOR_FINISHED;
265                         }
266                         
267                 default:
268                         if (event->type == vpd->invoke_event || event->type==ESCKEY) {
269                                 if (event->val==KM_RELEASE) {
270                                         /* calculate overall delta mouse-movement for redo */
271                                         RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
272                                         RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
273                                         
274                                         view_pan_exit(op);
275                                         WM_cursor_restore(CTX_wm_window(C));
276                                         
277                                         return OPERATOR_FINISHED;
278                                 }
279                         }
280                         break;
281         }
282
283         return OPERATOR_RUNNING_MODAL;
284 }
285
286 static int view_pan_cancel(bContext *UNUSED(C), wmOperator *op)
287 {
288         view_pan_exit(op);
289         return OPERATOR_CANCELLED;
290 }
291
292 static void VIEW2D_OT_pan(wmOperatorType *ot)
293 {
294         /* identifiers */
295         ot->name= "Pan View";
296         ot->description= "Pan the view";
297         ot->idname= "VIEW2D_OT_pan";
298         
299         /* api callbacks */
300         ot->exec= view_pan_exec;
301         ot->invoke= view_pan_invoke;
302         ot->modal= view_pan_modal;
303         ot->cancel= view_pan_cancel;
304         
305         /* operator is modal */
306         ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
307         
308         /* rna - must keep these in sync with the other operators */
309         RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
310         RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
311 }
312
313 /* ------------------ Scrollwheel Versions (2) ---------------------- */
314
315 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
316 static int view_scrollright_exec(bContext *C, wmOperator *op)
317 {
318         v2dViewPanData *vpd;
319         
320         /* initialise default settings (and validate if ok to run) */
321         if (!view_pan_init(C, op))
322                 return OPERATOR_PASS_THROUGH;
323                 
324         /* also, check if can pan in horizontal axis */
325         vpd= op->customdata;
326         if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
327                 view_pan_exit(op);
328                 return OPERATOR_PASS_THROUGH;
329         }
330         
331         /* set RNA-Props - only movement in positive x-direction */
332         RNA_int_set(op->ptr, "deltax", 20);
333         RNA_int_set(op->ptr, "deltay", 0);
334         
335         /* apply movement, then we're done */
336         view_pan_apply(op);
337         view_pan_exit(op);
338         
339         return OPERATOR_FINISHED;
340 }
341
342 static void VIEW2D_OT_scroll_right(wmOperatorType *ot)
343 {
344         /* identifiers */
345         ot->name= "Scroll Right";
346         ot->description= "Scroll the view right";
347         ot->idname= "VIEW2D_OT_scroll_right";
348         
349         /* api callbacks */
350         ot->exec= view_scrollright_exec;
351         
352         /* rna - must keep these in sync with the other operators */
353         RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
354         RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
355 }
356
357
358
359 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
360 static int view_scrollleft_exec(bContext *C, wmOperator *op)
361 {
362         v2dViewPanData *vpd;
363         
364         /* initialise default settings (and validate if ok to run) */
365         if (!view_pan_init(C, op))
366                 return OPERATOR_PASS_THROUGH;
367                 
368         /* also, check if can pan in horizontal axis */
369         vpd= op->customdata;
370         if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
371                 view_pan_exit(op);
372                 return OPERATOR_PASS_THROUGH;
373         }
374         
375         /* set RNA-Props - only movement in negative x-direction */
376         RNA_int_set(op->ptr, "deltax", -20);
377         RNA_int_set(op->ptr, "deltay", 0);
378         
379         /* apply movement, then we're done */
380         view_pan_apply(op);
381         view_pan_exit(op);
382         
383         return OPERATOR_FINISHED;
384 }
385
386 static void VIEW2D_OT_scroll_left(wmOperatorType *ot)
387 {
388         /* identifiers */
389         ot->name= "Scroll Left";
390         ot->description= "Scroll the view left";
391         ot->idname= "VIEW2D_OT_scroll_left";
392         
393         /* api callbacks */
394         ot->exec= view_scrollleft_exec;
395         
396         /* rna - must keep these in sync with the other operators */
397         RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
398         RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
399 }
400
401
402 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
403 static int view_scrolldown_exec(bContext *C, wmOperator *op)
404 {
405         v2dViewPanData *vpd;
406         
407         /* initialise default settings (and validate if ok to run) */
408         if (!view_pan_init(C, op))
409                 return OPERATOR_PASS_THROUGH;
410                 
411         /* also, check if can pan in vertical axis */
412         vpd= op->customdata;
413         if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
414                 view_pan_exit(op);
415                 return OPERATOR_PASS_THROUGH;
416         }
417         
418         /* set RNA-Props */
419         RNA_int_set(op->ptr, "deltax", 0);
420         RNA_int_set(op->ptr, "deltay", -40);
421         
422         if(RNA_boolean_get(op->ptr, "page")) {
423                 ARegion *ar= CTX_wm_region(C);
424                 RNA_int_set(op->ptr, "deltay", ar->v2d.mask.ymin - ar->v2d.mask.ymax);
425         }
426         
427         /* apply movement, then we're done */
428         view_pan_apply(op);
429         view_pan_exit(op);
430         
431         return OPERATOR_FINISHED;
432 }
433
434 static void VIEW2D_OT_scroll_down(wmOperatorType *ot)
435 {
436         /* identifiers */
437         ot->name= "Scroll Down";
438         ot->description= "Scroll the view down";
439         ot->idname= "VIEW2D_OT_scroll_down";
440         
441         /* api callbacks */
442         ot->exec= view_scrolldown_exec;
443         
444         /* rna - must keep these in sync with the other operators */
445         RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
446         RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
447         RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll down one page.");
448 }
449
450
451
452 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
453 static int view_scrollup_exec(bContext *C, wmOperator *op)
454 {
455         v2dViewPanData *vpd;
456         
457         /* initialise default settings (and validate if ok to run) */
458         if (!view_pan_init(C, op))
459                 return OPERATOR_PASS_THROUGH;
460                 
461         /* also, check if can pan in vertical axis */
462         vpd= op->customdata;
463         if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
464                 view_pan_exit(op);
465                 return OPERATOR_PASS_THROUGH;
466         }
467         
468         /* set RNA-Props */
469         RNA_int_set(op->ptr, "deltax", 0);
470         RNA_int_set(op->ptr, "deltay", 40);
471         
472         if(RNA_boolean_get(op->ptr, "page")) {
473                 ARegion *ar= CTX_wm_region(C);
474                 RNA_int_set(op->ptr, "deltay", ar->v2d.mask.ymax - ar->v2d.mask.ymin);
475         }
476         
477         /* apply movement, then we're done */
478         view_pan_apply(op);
479         view_pan_exit(op);
480         
481         return OPERATOR_FINISHED;
482 }
483
484 static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
485 {
486         /* identifiers */
487         ot->name= "Scroll Up";
488         ot->description= "Scroll the view up";
489         ot->idname= "VIEW2D_OT_scroll_up";
490         
491         /* api callbacks */
492         ot->exec= view_scrollup_exec;
493         
494         /* rna - must keep these in sync with the other operators */
495         RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
496         RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
497         RNA_def_boolean(ot->srna, "page", 0, "Page", "Scroll up one page.");
498 }
499
500 /* ********************************************************* */
501 /* SINGLE-STEP VIEW ZOOMING OPERATOR                                             */
502
503 /*      This group of operators come in several forms:
504  *              1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
505  *              2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y)  // XXX this could be implemented...
506  *              3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
507  *
508  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
509  *              zoomfacx, zoomfacy      - These two zoom factors allow for non-uniform scaling.
510  *                                                        It is safe to scale by 0, as these factors are used to determine
511  *                                                        amount to enlarge 'cur' by
512  */
513
514 /* ------------------ 'Shared' stuff ------------------------ */
515
516 /* temp customdata for operator */
517 typedef struct v2dViewZoomData {
518         View2D *v2d;                    /* view2d we're operating in */
519         ARegion *ar;
520
521         /* needed for continuous zoom */
522         wmTimer *timer;
523         double timer_lastdraw;
524
525         int lastx, lasty;               /* previous x/y values of mouse in window */
526         int invoke_event;               /* event type that invoked, for modal exits */
527         float dx, dy;                   /* running tally of previous delta values (for obtaining final zoom) */
528         float mx_2d, my_2d;             /* initial mouse location in v2d coords */
529 } v2dViewZoomData;
530
531
532 /* initialise panning customdata */
533 static int view_zoomdrag_init(bContext *C, wmOperator *op)
534 {
535         ARegion *ar= CTX_wm_region(C);
536         v2dViewZoomData *vzd;
537         View2D *v2d;
538         
539         /* regions now have v2d-data by default, so check for region */
540         if (ar == NULL)
541                 return 0;
542         v2d= &ar->v2d;
543         
544         /* check that 2d-view is zoomable */
545         if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
546                 return 0;
547         
548         /* set custom-data for operator */
549         vzd= MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
550         op->customdata= vzd;
551         
552         /* set pointers to owners */
553         vzd->v2d= v2d;
554         vzd->ar = ar;
555         
556         return 1;
557 }
558
559 /* check if step-zoom can be applied */
560 static int view_zoom_poll(bContext *C)
561 {
562         ARegion *ar= CTX_wm_region(C);
563         View2D *v2d;
564         
565         /* check if there's a region in context to work with */
566         if (ar == NULL)
567                 return 0;
568         v2d= &ar->v2d;
569         
570         /* check that 2d-view is zoomable */
571         if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
572                 return 0;
573                 
574         /* view is zoomable */
575         return 1;
576 }
577  
578 /* apply transform to view (i.e. adjust 'cur' rect) */
579 static void view_zoomstep_apply(bContext *C, wmOperator *op)
580 {
581         v2dViewZoomData *vzd= op->customdata;
582         ARegion *ar= CTX_wm_region(C);
583         View2D *v2d= &ar->v2d;
584         float dx, dy, facx, facy;
585         
586         /* calculate amount to move view by, ensuring symmetry so the
587          * old zoom level is restored after zooming back the same amount 
588          */
589         facx= RNA_float_get(op->ptr, "zoomfacx");
590         facy= RNA_float_get(op->ptr, "zoomfacy");
591
592         if (facx >= 0.0f) {
593                 dx= (v2d->cur.xmax - v2d->cur.xmin) * facx;
594                 dy= (v2d->cur.ymax - v2d->cur.ymin) * facy;
595         }
596         else {
597                 dx= ((v2d->cur.xmax - v2d->cur.xmin)/(1.0f + 2.0f*facx)) * facx;
598                 dy= ((v2d->cur.ymax - v2d->cur.ymin)/(1.0f + 2.0f*facy)) * facy;
599         }
600
601         /* only resize view on an axis if change is allowed */
602         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
603                 if (v2d->keepofs & V2D_LOCKOFS_X) {
604                         v2d->cur.xmax -= 2*dx;
605                 }
606                 else if (v2d->keepofs & V2D_KEEPOFS_X) {
607                         if (v2d->align & V2D_ALIGN_NO_POS_X)
608                                 v2d->cur.xmin += 2*dx;
609                         else
610                                 v2d->cur.xmax -= 2*dx;
611                 }
612                 else {
613                         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
614                                 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
615                                 float mval_faci = 1.0f - mval_fac;
616                                 float ofs= (mval_fac * dx) - (mval_faci * dx);
617                                 
618                                 v2d->cur.xmin += ofs + dx;
619                                 v2d->cur.xmax += ofs - dx;
620                         }
621                         else {
622                                 v2d->cur.xmin += dx;
623                                 v2d->cur.xmax -= dx;
624                         }
625                 }
626         }
627         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
628                 if (v2d->keepofs & V2D_LOCKOFS_Y) {
629                         v2d->cur.ymax -= 2*dy;
630                 }
631                 else if (v2d->keepofs & V2D_KEEPOFS_Y) {
632                         if (v2d->align & V2D_ALIGN_NO_POS_Y)
633                                 v2d->cur.ymin += 2*dy;
634                         else
635                                 v2d->cur.ymax -= 2*dy;
636                 }
637                 else {
638                         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
639                                 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
640                                 float mval_faci = 1.0f - mval_fac;
641                                 float ofs= (mval_fac * dy) - (mval_faci * dy);
642                                 
643                                 v2d->cur.ymin += ofs + dy;
644                                 v2d->cur.ymax += ofs - dy;
645                         } 
646                         else {
647                                 v2d->cur.ymin += dy;
648                                 v2d->cur.ymax -= dy;
649                         }
650                 }
651         }
652
653         /* validate that view is in valid configuration after this operation */
654         UI_view2d_curRect_validate(v2d);
655
656         /* request updates to be done... */
657         ED_region_tag_redraw(vzd->ar);
658         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
659 }
660
661 /* --------------- Individual Operators ------------------- */
662
663 /* cleanup temp customdata  */
664 static void view_zoomstep_exit(wmOperator *op)
665 {
666         if (op->customdata) {
667                 MEM_freeN(op->customdata);
668                 op->customdata= NULL;                           
669         }
670 }
671
672 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
673 static int view_zoomin_exec(bContext *C, wmOperator *op)
674 {
675         /* check that there's an active region, as View2D data resides there */
676         if (!view_zoom_poll(C))
677                 return OPERATOR_PASS_THROUGH;
678         
679         /* set RNA-Props - zooming in by uniform factor */
680         RNA_float_set(op->ptr, "zoomfacx", 0.0375f);
681         RNA_float_set(op->ptr, "zoomfacy", 0.0375f);
682         
683         /* apply movement, then we're done */
684         view_zoomstep_apply(C, op);
685         
686         view_zoomstep_exit(op);
687         
688         return OPERATOR_FINISHED;
689 }
690
691 static int view_zoomin_invoke(bContext *C, wmOperator *op, wmEvent *event)
692 {
693         v2dViewZoomData *vzd;
694         
695         if (!view_zoomdrag_init(C, op))
696                 return OPERATOR_PASS_THROUGH;
697         
698         vzd= op->customdata;
699         
700         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
701                 ARegion *ar= CTX_wm_region(C);
702                 
703                 /* store initial mouse position (in view space) */
704                 UI_view2d_region_to_view(&ar->v2d, 
705                                 event->mval[0], event->mval[1],
706                                 &vzd->mx_2d, &vzd->my_2d);
707         }
708         
709         return view_zoomin_exec(C, op);
710 }
711
712 static void VIEW2D_OT_zoom_in(wmOperatorType *ot)
713 {
714         /* identifiers */
715         ot->name= "Zoom In";
716         ot->description= "Zoom in the view";
717         ot->idname= "VIEW2D_OT_zoom_in";
718         
719         /* api callbacks */
720         ot->invoke= view_zoomin_invoke;
721 //      ot->exec= view_zoomin_exec;  // XXX, needs view_zoomdrag_init called first.
722         ot->poll= view_zoom_poll;
723         
724         /* rna - must keep these in sync with the other operators */
725         RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
726         RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
727 }
728         
729 /* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
730 static int view_zoomout_exec(bContext *C, wmOperator *op)
731 {
732         /* check that there's an active region, as View2D data resides there */
733         if (!view_zoom_poll(C))
734                 return OPERATOR_PASS_THROUGH;
735         
736         /* set RNA-Props - zooming in by uniform factor */
737         RNA_float_set(op->ptr, "zoomfacx", -0.0375f);
738         RNA_float_set(op->ptr, "zoomfacy", -0.0375f);
739         
740         /* apply movement, then we're done */
741         view_zoomstep_apply(C, op);
742
743         view_zoomstep_exit(op);
744         
745         return OPERATOR_FINISHED;
746 }
747
748 static int view_zoomout_invoke(bContext *C, wmOperator *op, wmEvent *event)
749 {
750         v2dViewZoomData *vzd;
751         
752         if (!view_zoomdrag_init(C, op))
753                 return OPERATOR_PASS_THROUGH;
754
755         vzd= op->customdata;
756         
757         if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
758                 ARegion *ar= CTX_wm_region(C);
759                 
760                 /* store initial mouse position (in view space) */
761                 UI_view2d_region_to_view(&ar->v2d, 
762                                 event->mval[0], event->mval[1],
763                                 &vzd->mx_2d, &vzd->my_2d);
764         }
765         
766         return view_zoomout_exec(C, op);
767 }
768
769 static void VIEW2D_OT_zoom_out(wmOperatorType *ot)
770 {
771         /* identifiers */
772         ot->name= "Zoom Out";
773         ot->description= "Zoom out the view";
774         ot->idname= "VIEW2D_OT_zoom_out";
775         
776         /* api callbacks */
777         ot->invoke= view_zoomout_invoke;
778 //      ot->exec= view_zoomout_exec; // XXX, needs view_zoomdrag_init called first.
779         ot->poll= view_zoom_poll;
780         
781         /* rna - must keep these in sync with the other operators */
782         RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
783         RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
784 }
785
786 /* ********************************************************* */
787 /* DRAG-ZOOM OPERATOR                                                                    */
788
789 /*      MMB Drag - allows non-uniform scaling by dragging mouse
790  *
791  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
792  *              deltax, deltay  - amounts to add to each side of the 'cur' rect
793  */
794
795 /* apply transform to view (i.e. adjust 'cur' rect) */
796 static void view_zoomdrag_apply(bContext *C, wmOperator *op)
797 {
798         v2dViewZoomData *vzd= op->customdata;
799         View2D *v2d= vzd->v2d;
800         float dx, dy;
801         
802         /* get amount to move view by */
803         dx= RNA_float_get(op->ptr, "deltax");
804         dy= RNA_float_get(op->ptr, "deltay");
805
806         /* continous zoom shouldn't move that fast... */
807         if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
808                 double time= PIL_check_seconds_timer();
809                 float time_step= (float)(time - vzd->timer_lastdraw);
810
811                 dx *= time_step * 0.5f;
812                 dy *= time_step * 0.5f;
813                 
814                 vzd->timer_lastdraw= time;
815         }
816
817         /* only move view on an axis if change is allowed */
818         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
819                 if (v2d->keepofs & V2D_LOCKOFS_X) {
820                         v2d->cur.xmax -= 2*dx;
821                 }
822                 else {
823                         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
824                                 float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / (v2d->cur.xmax-v2d->cur.xmin);
825                                 float mval_faci = 1.0f - mval_fac;
826                                 float ofs= (mval_fac * dx) - (mval_faci * dx);
827                                 
828                                 v2d->cur.xmin += ofs + dx;
829                                 v2d->cur.xmax += ofs - dx;
830                         }
831                         else {
832                                 v2d->cur.xmin += dx;
833                                 v2d->cur.xmax -= dx;
834                         }
835                 }
836         }
837         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
838                 if (v2d->keepofs & V2D_LOCKOFS_Y) {
839                         v2d->cur.ymax -= 2*dy;
840                 }
841                 else {
842                         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
843                                 float mval_fac = (vzd->my_2d - v2d->cur.ymin) / (v2d->cur.ymax-v2d->cur.ymin);
844                                 float mval_faci = 1.0f - mval_fac;
845                                 float ofs= (mval_fac * dy) - (mval_faci * dy);
846                                 
847                                 v2d->cur.ymin += ofs + dy;
848                                 v2d->cur.ymax += ofs - dy;
849                         }
850                         else {
851                                 v2d->cur.ymin += dy;
852                                 v2d->cur.ymax -= dy;
853                         }
854                 }
855         }
856         
857         /* validate that view is in valid configuration after this operation */
858         UI_view2d_curRect_validate(v2d);
859         
860         /* request updates to be done... */
861         ED_region_tag_redraw(vzd->ar);
862         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
863 }
864
865 /* cleanup temp customdata  */
866 static void view_zoomdrag_exit(bContext *C, wmOperator *op)
867 {
868         if (op->customdata) {
869                 v2dViewZoomData *vzd= op->customdata;
870                 
871                 if(vzd->timer)
872                         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer);
873                 
874                 MEM_freeN(op->customdata);
875                 op->customdata= NULL;                           
876         }
877
878
879 static int view_zoomdrag_cancel(bContext *C, wmOperator *op)
880 {
881         view_zoomdrag_exit(C, op);
882
883         return OPERATOR_CANCELLED;
884 }
885
886 /* for 'redo' only, with no user input */
887 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
888 {
889         if (!view_zoomdrag_init(C, op))
890                 return OPERATOR_PASS_THROUGH;
891         
892         view_zoomdrag_apply(C, op);
893         view_zoomdrag_exit(C, op);
894         return OPERATOR_FINISHED;
895 }
896
897 /* set up modal operator and relevant settings */
898 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
899 {
900         wmWindow *window= CTX_wm_window(C);
901         v2dViewZoomData *vzd;
902         View2D *v2d;
903         
904         /* set up customdata */
905         if (!view_zoomdrag_init(C, op))
906                 return OPERATOR_PASS_THROUGH;
907         
908         vzd= op->customdata;
909         v2d= vzd->v2d;
910         
911         if (event->type == MOUSEZOOM) {
912                 float dx, dy, fac;
913                 
914                 vzd->lastx= event->prevx;
915                 vzd->lasty= event->prevy;
916                 
917                 /* As we have only 1D information (magnify value), feed both axes
918                  * with magnify information that is stored in x axis 
919                  */
920                 fac= 0.01f * (event->x - event->prevx);
921                 dx= fac * (v2d->cur.xmax - v2d->cur.xmin) / 10.0f;
922                 dy= fac * (v2d->cur.ymax - v2d->cur.ymin) / 10.0f;
923
924                 RNA_float_set(op->ptr, "deltax", dx);
925                 RNA_float_set(op->ptr, "deltay", dy);
926                 
927                 view_zoomdrag_apply(C, op);
928                 view_zoomdrag_exit(C, op);
929                 return OPERATOR_FINISHED;
930         }       
931         
932         /* set initial settings */
933         vzd->lastx= event->x;
934         vzd->lasty= event->y;
935         RNA_float_set(op->ptr, "deltax", 0);
936         RNA_float_set(op->ptr, "deltay", 0);
937         
938         /* for modal exit test */
939         vzd->invoke_event= event->type;
940         
941         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
942                 ARegion *ar= CTX_wm_region(C);
943                 
944                 /* store initial mouse position (in view space) */
945                 UI_view2d_region_to_view(&ar->v2d, 
946                                 event->mval[0], event->mval[1],
947                                 &vzd->mx_2d, &vzd->my_2d);
948         }
949
950         if (v2d->keepofs & V2D_LOCKOFS_X)
951                 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
952         else if (v2d->keepofs & V2D_LOCKOFS_Y)
953                 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
954         else
955                 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
956         
957         /* add temp handler */
958         WM_event_add_modal_handler(C, op);
959
960         if (U.viewzoom == USER_ZOOM_CONT) {
961                 /* needs a timer to continue redrawing */
962                 vzd->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
963                 vzd->timer_lastdraw= PIL_check_seconds_timer();
964         }
965
966         return OPERATOR_RUNNING_MODAL;
967 }
968
969 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
970 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
971 {
972         v2dViewZoomData *vzd= op->customdata;
973         View2D *v2d= vzd->v2d;
974         
975         /* execute the events */
976         if (event->type == TIMER && event->customdata == vzd->timer) {
977                 view_zoomdrag_apply(C, op);
978         }
979         else if(event->type == MOUSEMOVE) {
980                 float dx, dy;
981                 
982                 /* calculate new delta transform, based on zooming mode */
983                 if (U.viewzoom == USER_ZOOM_SCALE) {
984                         /* 'scale' zooming */
985                         float dist;
986                         
987                         /* x-axis transform */
988                         dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
989                         dx= 1.0f - ((float)fabs(vzd->lastx - dist) + 2.0f) / ((float)fabs(event->x - dist) + 2.0f);
990                         dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
991                         
992                         /* y-axis transform */
993                         dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
994                         dy= 1.0f - ((float)fabs(vzd->lasty - dist) + 2.0f) / ((float)fabs(event->y - dist) + 2.0f);
995                         dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
996                 }
997                 else {
998                         /* 'continuous' or 'dolly' */
999                         float fac;
1000                         
1001                         /* x-axis transform */
1002                         fac= 0.01f * (event->x - vzd->lastx);
1003                         dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
1004                         
1005                         /* y-axis transform */
1006                         fac= 0.01f * (event->y - vzd->lasty);
1007                         dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
1008 #if 0
1009                         /* continous zoom shouldn't move that fast... */
1010                         if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1011                                 double time= PIL_check_seconds_timer();
1012                                 float time_step= (float)(time - vzd->timer_lastdraw);
1013
1014                                 dx /= (0.1f / time_step);
1015                                 dy /= (0.1f / time_step);
1016                                 
1017                                 vzd->timer_lastdraw= time;
1018                         }
1019 #endif
1020                 }
1021                 
1022                 /* set transform amount, and add current deltas to stored total delta (for redo) */
1023                 RNA_float_set(op->ptr, "deltax", dx);
1024                 RNA_float_set(op->ptr, "deltay", dy);
1025                 vzd->dx += dx;
1026                 vzd->dy += dy;
1027                 
1028                 /* store mouse coordinates for next time, if not doing continuous zoom
1029                  *      - continuous zoom only depends on distance of mouse to starting point to determine rate of change
1030                  */
1031                 if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1032                         vzd->lastx= event->x;
1033                         vzd->lasty= event->y;
1034                 }
1035                 
1036                 /* apply zooming */
1037                 view_zoomdrag_apply(C, op);
1038         } 
1039         else if (event->type == vzd->invoke_event || event->type==ESCKEY) {
1040                 if (event->val == KM_RELEASE) {
1041                         
1042                         /* for redo, store the overall deltas - need to respect zoom-locks here... */
1043                         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
1044                                 RNA_float_set(op->ptr, "deltax", vzd->dx);
1045                         else
1046                                 RNA_float_set(op->ptr, "deltax", 0);
1047                                 
1048                         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
1049                                 RNA_float_set(op->ptr, "deltay", vzd->dy);
1050                         else
1051                                 RNA_float_set(op->ptr, "deltay", 0);
1052                         
1053                         /* free customdata */
1054                         view_zoomdrag_exit(C, op);
1055                         WM_cursor_restore(CTX_wm_window(C));
1056                         
1057                         return OPERATOR_FINISHED;
1058                 }
1059         }
1060
1061         return OPERATOR_RUNNING_MODAL;
1062 }
1063
1064 static void VIEW2D_OT_zoom(wmOperatorType *ot)
1065 {
1066         /* identifiers */
1067         ot->name= "Zoom 2D View";
1068         ot->description= "Zoom in/out the view";
1069         ot->idname= "VIEW2D_OT_zoom";
1070         
1071         /* api callbacks */
1072         ot->exec= view_zoomdrag_exec;
1073         ot->invoke= view_zoomdrag_invoke;
1074         ot->modal= view_zoomdrag_modal;
1075         ot->cancel= view_zoomdrag_cancel;
1076         
1077         ot->poll= view_zoom_poll;
1078         
1079         /* operator is repeatable */
1080         // ot->flag= OPTYPE_BLOCKING;
1081         
1082         /* rna - must keep these in sync with the other operators */
1083         RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1084         RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1085 }
1086
1087 /* ********************************************************* */
1088 /* BORDER-ZOOM */
1089
1090 /* The user defines a rect using standard borderselect tools, and we use this rect to 
1091  * define the new zoom-level of the view in the following ways:
1092  *      1) LEFTMOUSE - zoom in to view
1093  *      2) RIGHTMOUSE - zoom out of view
1094  *
1095  * Currently, these key mappings are hardcoded, but it shouldn't be too important to
1096  * have custom keymappings for this...
1097  */
1098  
1099 static int view_borderzoom_exec(bContext *C, wmOperator *op)
1100 {
1101         ARegion *ar= CTX_wm_region(C);
1102         View2D *v2d= &ar->v2d;
1103         rctf rect;
1104         int gesture_mode;
1105         
1106         /* convert coordinates of rect to 'tot' rect coordinates */
1107         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
1108         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
1109         
1110         /* check if zooming in/out view */
1111         gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
1112         
1113         if (gesture_mode == GESTURE_MODAL_IN) {
1114                 /* zoom in: 
1115                  *      - 'cur' rect will be defined by the coordinates of the border region 
1116                  *      - just set the 'cur' rect to have the same coordinates as the border region
1117                  *        if zoom is allowed to be changed
1118                  */
1119                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1120                         v2d->cur.xmin= rect.xmin;
1121                         v2d->cur.xmax= rect.xmax;
1122                 }
1123                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1124                         v2d->cur.ymin= rect.ymin;
1125                         v2d->cur.ymax= rect.ymax;
1126                 }
1127         }
1128         else /* if (gesture_mode == GESTURE_MODAL_OUT) */ {
1129                 /* zoom out:
1130                  *      - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are, 
1131                  *        but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1132                  *      - calculate zoom factor, and adjust using center-point
1133                  */
1134                 float zoom, center, size;
1135                 
1136                 // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
1137                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1138                         size= (v2d->cur.xmax - v2d->cur.xmin);
1139                         zoom= size / (rect.xmax - rect.xmin);
1140                         center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
1141                         
1142                         v2d->cur.xmin= center - (size * zoom);
1143                         v2d->cur.xmax= center + (size * zoom);
1144                 }
1145                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1146                         size= (v2d->cur.ymax - v2d->cur.ymin);
1147                         zoom= size / (rect.ymax - rect.ymin);
1148                         center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
1149                         
1150                         v2d->cur.ymin= center - (size * zoom);
1151                         v2d->cur.ymax= center + (size * zoom);
1152                 }
1153         }
1154         
1155         /* validate that view is in valid configuration after this operation */
1156         UI_view2d_curRect_validate(v2d);
1157         
1158         /* request updates to be done... */
1159         ED_region_tag_redraw(ar);
1160         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1161         
1162         return OPERATOR_FINISHED;
1163
1164
1165 static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
1166 {
1167         /* identifiers */
1168         ot->name= "Zoom to Border";
1169         ot->description= "Zoom in the view to the nearest item contained in the border";
1170         ot->idname= "VIEW2D_OT_zoom_border";
1171         
1172         /* api callbacks */
1173         ot->invoke= WM_border_select_invoke;
1174         ot->exec= view_borderzoom_exec;
1175         ot->modal= WM_border_select_modal;
1176         ot->cancel= WM_border_select_cancel;
1177         
1178         ot->poll= view_zoom_poll;
1179         
1180         /* rna */
1181         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
1182         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1183         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1184         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1185         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1186 }
1187
1188 /* ********************************************************* */
1189 /* SCROLLERS */
1190
1191 /*      Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
1192  *              1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, 
1193  *                      enlarge 'cur' rect on the relevant side 
1194  *              2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
1195  *
1196  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
1197  *              deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
1198  */
1199
1200 /* customdata for scroller-invoke data */
1201 typedef struct v2dScrollerMove {
1202         View2D *v2d;                    /* View2D data that this operation affects */
1203         ARegion *ar;                    /* region that the scroller is in */
1204         
1205         short scroller;                 /* scroller that mouse is in ('h' or 'v') */
1206         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?)
1207         
1208         float fac;                              /* view adjustment factor, based on size of region */
1209         float delta;                    /* amount moved by mouse on axis of interest */
1210         
1211         float scrollbarwidth;   /* width of the scrollbar itself, used for page up/down clicks */
1212         
1213         int lastx, lasty;               /* previous mouse coordinates (in screen coordinates) for determining movement */
1214 } v2dScrollerMove;
1215
1216
1217 /* View2DScrollers is typedef'd in UI_view2d.h 
1218  * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
1219  * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
1220  *                 For now, we don't need to have a separate (internal) header for structs like this...
1221  */
1222 struct View2DScrollers {        
1223                 /* focus bubbles */
1224         int vert_min, vert_max; /* vertical scrollbar */
1225         int hor_min, hor_max;   /* horizontal scrollbar */
1226 };
1227
1228 /* quick enum for vsm->zone (scroller handles) */
1229 enum {
1230         SCROLLHANDLE_MIN= -1,
1231         SCROLLHANDLE_BAR,
1232         SCROLLHANDLE_MAX,
1233         SCROLLHANDLE_MIN_OUTSIDE,
1234         SCROLLHANDLE_MAX_OUTSIDE
1235 } /*eV2DScrollerHandle_Zone*/;
1236
1237 /* ------------------------ */
1238
1239 /* check if mouse is within scroller handle 
1240  *      - mouse                 =       relevant mouse coordinate in region space
1241  *      - sc_min, sc_max        =       extents of scroller 'groove' (potential available space for scroller)
1242  *      - sh_min, sh_max        =       positions of scrollbar handles
1243  */
1244 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1245 {
1246         short in_min, in_max, in_bar, out_min, out_max, in_view=1;
1247         
1248         /* firstly, check if 
1249          *      - 'bubble' fills entire scroller 
1250          *      - 'bubble' completely out of view on either side 
1251          */
1252         if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
1253         if (sh_min == sh_max) {
1254                 if (sh_min <= sc_min) in_view= 0;
1255                 if (sh_max >= sc_max) in_view= 0;
1256         }
1257         else {
1258                 if (sh_max <= sc_min) in_view= 0;
1259                 if (sh_min >= sc_max) in_view= 0;
1260         }
1261         
1262         
1263         if (in_view == 0) {
1264                 return SCROLLHANDLE_BAR;
1265         }
1266         
1267         /* check if mouse is in or past either handle */
1268         // TODO: check if these extents are still valid or not
1269         in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
1270         in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
1271         in_bar= ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) );
1272         out_min= mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE);
1273         out_max= mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE);
1274         
1275         if (in_bar)
1276                 return SCROLLHANDLE_BAR;
1277         else if (in_max)
1278                 return SCROLLHANDLE_MAX;
1279         else if (in_min)
1280                 return SCROLLHANDLE_MIN;
1281         else if (out_min)
1282                 return SCROLLHANDLE_MIN_OUTSIDE;                          
1283         else if (out_max)
1284                 return SCROLLHANDLE_MAX_OUTSIDE;
1285         
1286         /* unlikely to happen, though we just cover it in case */
1287         return SCROLLHANDLE_BAR;
1288
1289
1290 /* initialise customdata for scroller manipulation operator */
1291 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
1292 {
1293         v2dScrollerMove *vsm;
1294         View2DScrollers *scrollers;
1295         ARegion *ar= CTX_wm_region(C);
1296         View2D *v2d= &ar->v2d;
1297         float mask_size;
1298         
1299         /* set custom-data for operator */
1300         vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
1301         op->customdata= vsm;
1302         
1303         /* set general data */
1304         vsm->v2d= v2d;
1305         vsm->ar= ar;
1306         vsm->scroller= in_scroller;
1307         
1308         /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1309         vsm->lastx = event->x;
1310         vsm->lasty = event->y;
1311         
1312         /* 'zone' depends on where mouse is relative to bubble 
1313          *      - zooming must be allowed on this axis, otherwise, default to pan
1314          */
1315         scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1316         if (in_scroller == 'h') {
1317                 /* horizontal scroller - calculate adjustment factor first */
1318                 mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
1319                 vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
1320                 
1321                 /* get 'zone' (i.e. which part of scroller is activated) */
1322                 vsm->zone= mouse_in_scroller_handle(event->mval[0], v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
1323                 
1324                 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1325                         /* default to scroll, as handles not usable */
1326                         vsm->zone= SCROLLHANDLE_BAR;
1327                 }
1328
1329                 vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
1330         }
1331         else {
1332                 /* vertical scroller - calculate adjustment factor first */
1333                 mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
1334                 vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
1335                 
1336                 /* get 'zone' (i.e. which part of scroller is activated) */
1337                 vsm->zone= mouse_in_scroller_handle(event->mval[1], v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
1338                         
1339                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1340                         /* default to scroll, as handles not usable */
1341                         vsm->zone= SCROLLHANDLE_BAR;
1342                 }
1343                 
1344                 vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
1345         }
1346         
1347         UI_view2d_scrollers_free(scrollers);
1348         ED_region_tag_redraw(ar);
1349 }
1350
1351 /* cleanup temp customdata  */
1352 static void scroller_activate_exit(bContext *C, wmOperator *op)
1353 {
1354         if (op->customdata) {
1355                 v2dScrollerMove *vsm= op->customdata;
1356
1357                 vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE|V2D_SCROLL_V_ACTIVE);
1358                 
1359                 MEM_freeN(op->customdata);
1360                 op->customdata= NULL;           
1361                 
1362                 ED_region_tag_redraw(CTX_wm_region(C));
1363         }
1364 }
1365
1366 static int scroller_activate_cancel(bContext *C, wmOperator *op)
1367 {
1368         scroller_activate_exit(C, op);
1369
1370         return OPERATOR_CANCELLED;
1371 }
1372
1373 /* apply transform to view (i.e. adjust 'cur' rect) */
1374 static void scroller_activate_apply(bContext *C, wmOperator *op)
1375 {
1376         v2dScrollerMove *vsm= op->customdata;
1377         View2D *v2d= vsm->v2d;
1378         float temp;
1379         
1380         /* calculate amount to move view by */
1381         temp= vsm->fac * vsm->delta;
1382         
1383         /* type of movement */
1384         switch (vsm->zone) {
1385                 case SCROLLHANDLE_MIN:
1386                         /* only expand view on axis if zoom is allowed */
1387                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1388                                 v2d->cur.xmin -= temp;
1389                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1390                                 v2d->cur.ymin -= temp;
1391                         break;
1392                         
1393                 case SCROLLHANDLE_MAX:
1394                         
1395                         /* only expand view on axis if zoom is allowed */
1396                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1397                                 v2d->cur.xmax += temp;
1398                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1399                                 v2d->cur.ymax += temp;
1400                         break;
1401                         
1402                 case SCROLLHANDLE_MIN_OUTSIDE:
1403                 case SCROLLHANDLE_MAX_OUTSIDE:
1404                 case SCROLLHANDLE_BAR:
1405                 default:
1406                         /* only move view on an axis if panning is allowed */
1407                         if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1408                                 v2d->cur.xmin += temp;
1409                                 v2d->cur.xmax += temp;
1410                         }
1411                         if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1412                                 v2d->cur.ymin += temp;
1413                                 v2d->cur.ymax += temp;
1414                         }
1415                         break;
1416                         
1417         }
1418         
1419         /* validate that view is in valid configuration after this operation */
1420         UI_view2d_curRect_validate(v2d);
1421         
1422         /* request updates to be done... */
1423         ED_region_tag_redraw(vsm->ar);
1424         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1425 }
1426
1427 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
1428 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
1429 {
1430         v2dScrollerMove *vsm= op->customdata;
1431         
1432         /* execute the events */
1433         switch (event->type) {
1434                 case MOUSEMOVE:
1435                 {
1436                         /* calculate new delta transform, then store mouse-coordinates for next-time */
1437                         if (ELEM(vsm->zone, SCROLLHANDLE_BAR, SCROLLHANDLE_MAX)) {
1438                                 /* if using bar (i.e. 'panning') or 'max' zoom widget */
1439                                 switch (vsm->scroller) {
1440                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1441                                                 vsm->delta= (float)(event->x - vsm->lastx);
1442                                                 break;
1443                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1444                                                 vsm->delta= (float)(event->y - vsm->lasty);
1445                                                 break;
1446                                 }
1447                         }
1448                         else if (vsm->zone == SCROLLHANDLE_MIN) {
1449                                 /* using 'min' zoom widget */
1450                                 switch (vsm->scroller) {
1451                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
1452                                                 vsm->delta= (float)(vsm->lastx - event->x);
1453                                                 break;
1454                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
1455                                                 vsm->delta= (float)(vsm->lasty - event->y);
1456                                                 break;
1457                                 }
1458                         }
1459                         
1460                         /* store previous coordinates */
1461                         vsm->lastx= event->x;
1462                         vsm->lasty= event->y;
1463                         
1464                         scroller_activate_apply(C, op);
1465                 }
1466                         break;
1467                         
1468                 case LEFTMOUSE:
1469                         if (event->val==KM_RELEASE) {
1470                                 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
1471                                 if (ELEM(vsm->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
1472                                         if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE)
1473                                                 vsm->delta = -vsm->scrollbarwidth * 0.8f;
1474                                         else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE)
1475                                                 vsm->delta = vsm->scrollbarwidth * 0.8f;
1476                                         
1477                                         scroller_activate_apply(C, op);
1478                                         scroller_activate_exit(C, op);
1479                                         return OPERATOR_FINISHED;
1480                                 }
1481                                 
1482                                 /* otherwise, end the drag action  */
1483                                 if (vsm->lastx || vsm->lasty) {
1484                                         scroller_activate_exit(C, op);
1485                                         return OPERATOR_FINISHED;
1486                                 }
1487                         }
1488                         break;
1489         }
1490
1491         return OPERATOR_RUNNING_MODAL;
1492 }
1493
1494
1495 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
1496 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1497 {
1498         ARegion *ar= CTX_wm_region(C);
1499         View2D *v2d= &ar->v2d;
1500         short in_scroller= 0;
1501                 
1502         /* check if mouse in scrollbars, if they're enabled */
1503         in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
1504         
1505         /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
1506         if (in_scroller) {
1507                 v2dScrollerMove *vsm;
1508                 
1509                 /* initialise customdata */
1510                 scroller_activate_init(C, op, event, in_scroller);
1511                 vsm= (v2dScrollerMove *)op->customdata;
1512                 
1513                 /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
1514                  * NOTE: see view2d.c for latest conditions, and keep this in sync with that
1515                  */
1516                 if (ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1517                         if ( ((vsm->scroller=='h') && (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL)==0) ||
1518                                  ((vsm->scroller=='v') && (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL)==0) )
1519                         {
1520                                 /* switch to bar (i.e. no scaling gets handled) */
1521                                 vsm->zone= SCROLLHANDLE_BAR;
1522                         }
1523                 }
1524                 
1525                 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
1526                 if (vsm->zone == SCROLLHANDLE_BAR) {
1527                         if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
1528                                  ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
1529                         {
1530                                 /* free customdata initialised */
1531                                 scroller_activate_exit(C, op);
1532                                 
1533                                 /* can't catch this event for ourselves, so let it go to someone else? */
1534                                 return OPERATOR_PASS_THROUGH;
1535                         }                       
1536                 }
1537                 
1538                 /* zone is also inappropriate if scroller is not visible... */
1539                 if ( ((vsm->scroller=='h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_HORIZONTAL_FULLR))) ||
1540                          ((vsm->scroller=='v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE|V2D_SCROLL_VERTICAL_FULLR))) )
1541                 {
1542                         /* free customdata initialised */
1543                         scroller_activate_exit(C, op);
1544                                 
1545                         /* can't catch this event for ourselves, so let it go to someone else? */
1546                         /* XXX note: if handlers use mask rect to clip input, input will fail for this case */
1547                         return OPERATOR_PASS_THROUGH;
1548                 }
1549                 
1550                 /* activate the scroller */
1551                 if (vsm->scroller=='h')
1552                         v2d->scroll_ui |= V2D_SCROLL_H_ACTIVE;
1553                 else
1554                         v2d->scroll_ui |= V2D_SCROLL_V_ACTIVE;
1555                 
1556                 /* still ok, so can add */
1557                 WM_event_add_modal_handler(C, op);
1558                 return OPERATOR_RUNNING_MODAL;
1559         }
1560         else {
1561                 /* not in scroller, so nothing happened... (pass through let's something else catch event) */
1562                 return OPERATOR_PASS_THROUGH;
1563         }
1564 }
1565
1566 /* LMB-Drag in Scrollers - not repeatable operator! */
1567 static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
1568 {
1569         /* identifiers */
1570         ot->name= "Scroller Activate";
1571         ot->description= "Scroll view by mouse click and drag";
1572         ot->idname= "VIEW2D_OT_scroller_activate";
1573
1574         /* flags */
1575         ot->flag= OPTYPE_BLOCKING;
1576         
1577         /* api callbacks */
1578         ot->invoke= scroller_activate_invoke;
1579         ot->modal= scroller_activate_modal;
1580         ot->cancel= scroller_activate_cancel;
1581
1582         ot->poll= view2d_poll;
1583 }
1584
1585 /* ********************************************************* */
1586 /* RESET */
1587
1588 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
1589 {
1590         uiStyle *style= U.uistyles.first;
1591         ARegion *ar= CTX_wm_region(C);
1592         View2D *v2d= &ar->v2d;
1593         int winx, winy;
1594
1595         /* zoom 1.0 */
1596         winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
1597         winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
1598
1599         v2d->cur.xmax= v2d->cur.xmin + winx;
1600         v2d->cur.ymax= v2d->cur.ymin + winy;
1601         
1602         /* align */
1603         if (v2d->align) {
1604                 /* posx and negx flags are mutually exclusive, so watch out */
1605                 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1606                         v2d->cur.xmax= 0.0f;
1607                         v2d->cur.xmin= -winx*style->panelzoom;
1608                 }
1609                 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
1610                         v2d->cur.xmax= winx*style->panelzoom;
1611                         v2d->cur.xmin= 0.0f;
1612                 }
1613
1614                 /* - posx and negx flags are mutually exclusive, so watch out */
1615                 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1616                         v2d->cur.ymax= 0.0f;
1617                         v2d->cur.ymin= -winy*style->panelzoom;
1618                 }
1619                 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
1620                         v2d->cur.ymax= winy*style->panelzoom;
1621                         v2d->cur.ymin= 0.0f;
1622                 }
1623         }
1624
1625         /* validate that view is in valid configuration after this operation */
1626         UI_view2d_curRect_validate(v2d);
1627         
1628         /* request updates to be done... */
1629         ED_region_tag_redraw(ar);
1630         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1631         
1632         return OPERATOR_FINISHED;
1633 }
1634  
1635 static void VIEW2D_OT_reset(wmOperatorType *ot)
1636 {
1637         /* identifiers */
1638         ot->name= "Reset View";
1639         ot->description= "Reset the view";
1640         ot->idname= "VIEW2D_OT_reset";
1641         
1642         /* api callbacks */
1643         ot->exec= reset_exec;
1644         ot->poll= view2d_poll;
1645 }
1646  
1647 /* ********************************************************* */
1648 /* Registration */
1649
1650 void UI_view2d_operatortypes(void)
1651 {
1652         WM_operatortype_append(VIEW2D_OT_pan);
1653         
1654         WM_operatortype_append(VIEW2D_OT_scroll_left);
1655         WM_operatortype_append(VIEW2D_OT_scroll_right);
1656         WM_operatortype_append(VIEW2D_OT_scroll_up);
1657         WM_operatortype_append(VIEW2D_OT_scroll_down);
1658         
1659         WM_operatortype_append(VIEW2D_OT_zoom_in);
1660         WM_operatortype_append(VIEW2D_OT_zoom_out);
1661         
1662         WM_operatortype_append(VIEW2D_OT_zoom);
1663         WM_operatortype_append(VIEW2D_OT_zoom_border);
1664         
1665         WM_operatortype_append(VIEW2D_OT_scroller_activate);
1666
1667         WM_operatortype_append(VIEW2D_OT_reset);
1668 }
1669
1670 void UI_view2d_keymap(wmKeyConfig *keyconf)
1671 {
1672         wmKeyMap *keymap= WM_keymap_find(keyconf, "View2D", 0, 0);
1673         
1674         /* pan/scroll */
1675         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1676         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
1677         
1678         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1679         
1680         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
1681         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
1682         
1683         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
1684         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
1685         
1686         /* zoom - single step */
1687         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
1688         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
1689         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1690         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1691         
1692         /* scroll up/down - no modifiers, only when zoom fails */
1693                 /* these may fail if zoom is disallowed, in which case they should pass on event */
1694         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1695         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1696                 /* these may be necessary if vertical scroll is disallowed */
1697         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1698         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
1699         
1700         /* alternatives for page up/down to scroll */
1701 #if 0 // XXX disabled, since this causes conflicts with hotkeys in animation editors
1702                 /* scroll up/down may fall through to left/right */
1703         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1704         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1705         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, 0, 0);
1706         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, 0, 0);
1707                 /* shift for moving view left/right with page up/down */
1708         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
1709         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
1710 #endif
1711         
1712         /* zoom - drag */
1713         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1714         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1715         
1716         /* borderzoom - drag */
1717         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
1718         
1719         /* scrollers */
1720         WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1721
1722         /* Alternative keymap for buttons listview */
1723         keymap= WM_keymap_find(keyconf, "View2D Buttons List", 0, 0);
1724         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1725         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1726         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1727         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1728         
1729         RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "page", 1);
1730         RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "page", 1);
1731         
1732         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1733         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1734         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1735         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1736         WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);
1737         WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1738 }
1739