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