Bugfix #27181
[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->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
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->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
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 /* for 'redo' only, with no user input */
880 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
881 {
882         if (!view_zoomdrag_init(C, op))
883                 return OPERATOR_PASS_THROUGH;
884         
885         view_zoomdrag_apply(C, op);
886         view_zoomdrag_exit(C, op);
887         return OPERATOR_FINISHED;
888 }
889
890 /* set up modal operator and relevant settings */
891 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
892 {
893         wmWindow *window= CTX_wm_window(C);
894         v2dViewZoomData *vzd;
895         View2D *v2d;
896         
897         /* set up customdata */
898         if (!view_zoomdrag_init(C, op))
899                 return OPERATOR_PASS_THROUGH;
900         
901         vzd= op->customdata;
902         v2d= vzd->v2d;
903         
904         if (event->type == MOUSEZOOM) {
905                 float dx, dy, fac;
906                 
907                 vzd->lastx= event->prevx;
908                 vzd->lasty= event->prevy;
909                 
910                 /* As we have only 1D information (magnify value), feed both axes
911                  * with magnify information that is stored in x axis 
912                  */
913                 fac= 0.01f * (event->x - event->prevx);
914                 dx= fac * (v2d->cur.xmax - v2d->cur.xmin) / 10.0f;
915                 dy= fac * (v2d->cur.ymax - v2d->cur.ymin) / 10.0f;
916
917                 RNA_float_set(op->ptr, "deltax", dx);
918                 RNA_float_set(op->ptr, "deltay", dy);
919                 
920                 view_zoomdrag_apply(C, op);
921                 view_zoomdrag_exit(C, op);
922                 return OPERATOR_FINISHED;
923         }       
924         
925         /* set initial settings */
926         vzd->lastx= event->x;
927         vzd->lasty= event->y;
928         RNA_float_set(op->ptr, "deltax", 0);
929         RNA_float_set(op->ptr, "deltay", 0);
930         
931         /* for modal exit test */
932         vzd->invoke_event= event->type;
933         
934         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
935                 ARegion *ar= CTX_wm_region(C);
936                 
937                 /* store initial mouse position (in view space) */
938                 UI_view2d_region_to_view(&ar->v2d, 
939                                 event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
940                                 &vzd->mx_2d, &vzd->my_2d);
941         }
942
943         if (v2d->keepofs & V2D_LOCKOFS_X)
944                 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
945         else if (v2d->keepofs & V2D_LOCKOFS_Y)
946                 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
947         else
948                 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
949         
950         /* add temp handler */
951         WM_event_add_modal_handler(C, op);
952
953         if (U.viewzoom == USER_ZOOM_CONT) {
954                 /* needs a timer to continue redrawing */
955                 vzd->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
956                 vzd->timer_lastdraw= PIL_check_seconds_timer();
957         }
958
959         return OPERATOR_RUNNING_MODAL;
960 }
961
962 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
963 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
964 {
965         v2dViewZoomData *vzd= op->customdata;
966         View2D *v2d= vzd->v2d;
967         
968         /* execute the events */
969         if (event->type == TIMER && event->customdata == vzd->timer) {
970                 view_zoomdrag_apply(C, op);
971         }
972         else if(event->type == MOUSEMOVE) {
973                 float dx, dy;
974                 
975                 /* calculate new delta transform, based on zooming mode */
976                 if (U.viewzoom == USER_ZOOM_SCALE) {
977                         /* 'scale' zooming */
978                         float dist;
979                         
980                         /* x-axis transform */
981                         dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
982                         dx= 1.0f - ((float)fabs(vzd->lastx - dist) + 2.0f) / ((float)fabs(event->x - dist) + 2.0f);
983                         dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
984                         
985                         /* y-axis transform */
986                         dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
987                         dy= 1.0f - ((float)fabs(vzd->lasty - dist) + 2.0f) / ((float)fabs(event->y - dist) + 2.0f);
988                         dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
989                 }
990                 else {
991                         /* 'continuous' or 'dolly' */
992                         float fac;
993                         
994                         /* x-axis transform */
995                         fac= 0.01f * (event->x - vzd->lastx);
996                         dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
997                         
998                         /* y-axis transform */
999                         fac= 0.01f * (event->y - vzd->lasty);
1000                         dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
1001 #if 0
1002                         /* continous zoom shouldn't move that fast... */
1003                         if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1004                                 double time= PIL_check_seconds_timer();
1005                                 float time_step= (float)(time - vzd->timer_lastdraw);
1006
1007                                 dx /= (0.1f / time_step);
1008                                 dy /= (0.1f / time_step);
1009                                 
1010                                 vzd->timer_lastdraw= time;
1011                         }
1012 #endif
1013                 }
1014                 
1015                 /* set transform amount, and add current deltas to stored total delta (for redo) */
1016                 RNA_float_set(op->ptr, "deltax", dx);
1017                 RNA_float_set(op->ptr, "deltay", dy);
1018                 vzd->dx += dx;
1019                 vzd->dy += dy;
1020                 
1021                 /* store mouse coordinates for next time, if not doing continuous zoom
1022                  *      - continuous zoom only depends on distance of mouse to starting point to determine rate of change
1023                  */
1024                 if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
1025                         vzd->lastx= event->x;
1026                         vzd->lasty= event->y;
1027                 }
1028                 
1029                 /* apply zooming */
1030                 view_zoomdrag_apply(C, op);
1031         } 
1032         else if (event->type == vzd->invoke_event || event->type==ESCKEY) {
1033                 if (event->val == KM_RELEASE) {
1034                         
1035                         /* for redo, store the overall deltas - need to respect zoom-locks here... */
1036                         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
1037                                 RNA_float_set(op->ptr, "deltax", vzd->dx);
1038                         else
1039                                 RNA_float_set(op->ptr, "deltax", 0);
1040                                 
1041                         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
1042                                 RNA_float_set(op->ptr, "deltay", vzd->dy);
1043                         else
1044                                 RNA_float_set(op->ptr, "deltay", 0);
1045                         
1046                         /* free customdata */
1047                         view_zoomdrag_exit(C, op);
1048                         WM_cursor_restore(CTX_wm_window(C));
1049                         
1050                         return OPERATOR_FINISHED;
1051                 }
1052         }
1053
1054         return OPERATOR_RUNNING_MODAL;
1055 }
1056
1057 static void VIEW2D_OT_zoom(wmOperatorType *ot)
1058 {
1059         /* identifiers */
1060         ot->name= "Zoom 2D View";
1061         ot->description= "Zoom in/out the view";
1062         ot->idname= "VIEW2D_OT_zoom";
1063         
1064         /* api callbacks */
1065         ot->exec= view_zoomdrag_exec;
1066         ot->invoke= view_zoomdrag_invoke;
1067         ot->modal= view_zoomdrag_modal;
1068         
1069         ot->poll= view_zoom_poll;
1070         
1071         /* operator is repeatable */
1072         // ot->flag= OPTYPE_BLOCKING;
1073         
1074         /* rna - must keep these in sync with the other operators */
1075         RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
1076         RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
1077 }
1078
1079 /* ********************************************************* */
1080 /* BORDER-ZOOM */
1081
1082 /* The user defines a rect using standard borderselect tools, and we use this rect to 
1083  * define the new zoom-level of the view in the following ways:
1084  *      1) LEFTMOUSE - zoom in to view
1085  *      2) RIGHTMOUSE - zoom out of view
1086  *
1087  * Currently, these key mappings are hardcoded, but it shouldn't be too important to
1088  * have custom keymappings for this...
1089  */
1090  
1091 static int view_borderzoom_exec(bContext *C, wmOperator *op)
1092 {
1093         ARegion *ar= CTX_wm_region(C);
1094         View2D *v2d= &ar->v2d;
1095         rctf rect;
1096         int gesture_mode;
1097         
1098         /* convert coordinates of rect to 'tot' rect coordinates */
1099         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
1100         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
1101         
1102         /* check if zooming in/out view */
1103         gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
1104         
1105         if (gesture_mode == GESTURE_MODAL_IN) {
1106                 /* zoom in: 
1107                  *      - 'cur' rect will be defined by the coordinates of the border region 
1108                  *      - just set the 'cur' rect to have the same coordinates as the border region
1109                  *        if zoom is allowed to be changed
1110                  */
1111                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1112                         v2d->cur.xmin= rect.xmin;
1113                         v2d->cur.xmax= rect.xmax;
1114                 }
1115                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1116                         v2d->cur.ymin= rect.ymin;
1117                         v2d->cur.ymax= rect.ymax;
1118                 }
1119         }
1120         else /* if (gesture_mode == GESTURE_MODAL_OUT) */ {
1121                 /* zoom out:
1122                  *      - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are, 
1123                  *        but the 'cur' rect coordinates will need to be adjusted to take in more of the view
1124                  *      - calculate zoom factor, and adjust using center-point
1125                  */
1126                 float zoom, center, size;
1127                 
1128                 // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
1129                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
1130                         size= (v2d->cur.xmax - v2d->cur.xmin);
1131                         zoom= size / (rect.xmax - rect.xmin);
1132                         center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
1133                         
1134                         v2d->cur.xmin= center - (size * zoom);
1135                         v2d->cur.xmax= center + (size * zoom);
1136                 }
1137                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
1138                         size= (v2d->cur.ymax - v2d->cur.ymin);
1139                         zoom= size / (rect.ymax - rect.ymin);
1140                         center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
1141                         
1142                         v2d->cur.ymin= center - (size * zoom);
1143                         v2d->cur.ymax= center + (size * zoom);
1144                 }
1145         }
1146         
1147         /* validate that view is in valid configuration after this operation */
1148         UI_view2d_curRect_validate(v2d);
1149         
1150         /* request updates to be done... */
1151         ED_region_tag_redraw(ar);
1152         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1153         
1154         return OPERATOR_FINISHED;
1155
1156
1157 static void VIEW2D_OT_zoom_border(wmOperatorType *ot)
1158 {
1159         /* identifiers */
1160         ot->name= "Zoom to Border";
1161         ot->description= "Zoom in the view to the nearest item contained in the border";
1162         ot->idname= "VIEW2D_OT_zoom_border";
1163         
1164         /* api callbacks */
1165         ot->invoke= WM_border_select_invoke;
1166         ot->exec= view_borderzoom_exec;
1167         ot->modal= WM_border_select_modal;
1168         
1169         ot->poll= view_zoom_poll;
1170         
1171         /* rna */
1172         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
1173         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1174         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1175         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1176         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1177 }
1178
1179 /* ********************************************************* */
1180 /* SCROLLERS */
1181
1182 /*      Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
1183  *              1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, 
1184  *                      enlarge 'cur' rect on the relevant side 
1185  *              2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
1186  *
1187  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
1188  *              deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
1189  */
1190
1191 /* customdata for scroller-invoke data */
1192 typedef struct v2dScrollerMove {
1193         View2D *v2d;                    /* View2D data that this operation affects */
1194         ARegion *ar;                    /* region that the scroller is in */
1195         
1196         short scroller;                 /* scroller that mouse is in ('h' or 'v') */
1197         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?)
1198         
1199         float fac;                              /* view adjustment factor, based on size of region */
1200         float delta;                    /* amount moved by mouse on axis of interest */
1201         
1202         float scrollbarwidth;   /* width of the scrollbar itself, used for page up/down clicks */
1203         
1204         int lastx, lasty;               /* previous mouse coordinates (in screen coordinates) for determining movement */
1205 } v2dScrollerMove;
1206
1207
1208 /* View2DScrollers is typedef'd in UI_view2d.h 
1209  * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
1210  * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
1211  *                 For now, we don't need to have a separate (internal) header for structs like this...
1212  */
1213 struct View2DScrollers {        
1214                 /* focus bubbles */
1215         int vert_min, vert_max; /* vertical scrollbar */
1216         int hor_min, hor_max;   /* horizontal scrollbar */
1217 };
1218
1219 /* quick enum for vsm->zone (scroller handles) */
1220 enum {
1221         SCROLLHANDLE_MIN= -1,
1222         SCROLLHANDLE_BAR,
1223         SCROLLHANDLE_MAX,
1224         SCROLLHANDLE_MIN_OUTSIDE,
1225         SCROLLHANDLE_MAX_OUTSIDE
1226 } /*eV2DScrollerHandle_Zone*/;
1227
1228 /* ------------------------ */
1229
1230 /* check if mouse is within scroller handle 
1231  *      - mouse                 =       relevant mouse coordinate in region space
1232  *      - sc_min, sc_max        =       extents of scroller 'groove' (potential available space for scroller)
1233  *      - sh_min, sh_max        =       positions of scrollbar handles
1234  */
1235 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
1236 {
1237         short in_min, in_max, in_bar, out_min, out_max, in_view=1;
1238         
1239         /* firstly, check if 
1240          *      - 'bubble' fills entire scroller 
1241          *      - 'bubble' completely out of view on either side 
1242          */
1243         if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
1244         if (sh_min == sh_max) {
1245                 if (sh_min <= sc_min) in_view= 0;
1246                 if (sh_max >= sc_max) in_view= 0;
1247         }
1248         else {
1249                 if (sh_max <= sc_min) in_view= 0;
1250                 if (sh_min >= sc_max) in_view= 0;
1251         }
1252         
1253         
1254         if (in_view == 0) {
1255                 return SCROLLHANDLE_BAR;
1256         }
1257         
1258         /* check if mouse is in or past either handle */
1259         // TODO: check if these extents are still valid or not
1260         in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
1261         in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
1262         in_bar= ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) );
1263         out_min= mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE);
1264         out_max= mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE);
1265         
1266         if (in_bar)
1267                 return SCROLLHANDLE_BAR;
1268         else if (in_max)
1269                 return SCROLLHANDLE_MAX;
1270         else if (in_min)
1271                 return SCROLLHANDLE_MIN;
1272         else if (out_min)
1273                 return SCROLLHANDLE_MIN_OUTSIDE;                          
1274         else if (out_max)
1275                 return SCROLLHANDLE_MAX_OUTSIDE;
1276         
1277         /* unlikely to happen, though we just cover it in case */
1278         return SCROLLHANDLE_BAR;
1279
1280
1281 /* initialise customdata for scroller manipulation operator */
1282 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
1283 {
1284         v2dScrollerMove *vsm;
1285         View2DScrollers *scrollers;
1286         ARegion *ar= CTX_wm_region(C);
1287         View2D *v2d= &ar->v2d;
1288         float mask_size;
1289         int x, y;
1290         
1291         /* set custom-data for operator */
1292         vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
1293         op->customdata= vsm;
1294         
1295         /* set general data */
1296         vsm->v2d= v2d;
1297         vsm->ar= ar;
1298         vsm->scroller= in_scroller;
1299         
1300         /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1301         vsm->lastx = event->x;
1302         vsm->lasty = event->y;
1303         x= event->x - ar->winrct.xmin;
1304         y= event->y - ar->winrct.ymin;
1305         
1306         /* 'zone' depends on where mouse is relative to bubble 
1307          *      - zooming must be allowed on this axis, otherwise, default to pan
1308          */
1309         scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1310         if (in_scroller == 'h') {
1311                 /* horizontal scroller - calculate adjustment factor first */
1312                 mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
1313                 vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
1314                 
1315                 /* get 'zone' (i.e. which part of scroller is activated) */
1316                 vsm->zone= mouse_in_scroller_handle(x, v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max); 
1317                 
1318                 if ((v2d->keepzoom & V2D_LOCKZOOM_X) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1319                         /* default to scroll, as handles not usable */
1320                         vsm->zone= SCROLLHANDLE_BAR;
1321                 }
1322
1323                 vsm->scrollbarwidth = scrollers->hor_max - scrollers->hor_min;
1324         }
1325         else {
1326                 /* vertical scroller - calculate adjustment factor first */
1327                 mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
1328                 vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
1329                 
1330                 /* get 'zone' (i.e. which part of scroller is activated) */
1331                 vsm->zone= mouse_in_scroller_handle(y, v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max); 
1332                         
1333                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y) && ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1334                         /* default to scroll, as handles not usable */
1335                         vsm->zone= SCROLLHANDLE_BAR;
1336                 }
1337                 
1338                 vsm->scrollbarwidth = scrollers->vert_max - scrollers->vert_min;
1339         }
1340         
1341         UI_view2d_scrollers_free(scrollers);
1342         ED_region_tag_redraw(ar);
1343 }
1344
1345 /* cleanup temp customdata  */
1346 static void scroller_activate_exit(bContext *C, wmOperator *op)
1347 {
1348         if (op->customdata) {
1349                 v2dScrollerMove *vsm= op->customdata;
1350
1351                 vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE|V2D_SCROLL_V_ACTIVE);
1352                 
1353                 MEM_freeN(op->customdata);
1354                 op->customdata= NULL;           
1355                 
1356                 ED_region_tag_redraw(CTX_wm_region(C));
1357         }
1358
1359
1360 /* apply transform to view (i.e. adjust 'cur' rect) */
1361 static void scroller_activate_apply(bContext *C, wmOperator *op)
1362 {
1363         v2dScrollerMove *vsm= op->customdata;
1364         View2D *v2d= vsm->v2d;
1365         float temp;
1366         
1367         /* calculate amount to move view by */
1368         temp= vsm->fac * vsm->delta;
1369         
1370         /* type of movement */
1371         switch (vsm->zone) {
1372                 case SCROLLHANDLE_MIN:
1373                         /* only expand view on axis if zoom is allowed */
1374                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1375                                 v2d->cur.xmin -= temp;
1376                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1377                                 v2d->cur.ymin -= temp;
1378                         break;
1379                         
1380                 case SCROLLHANDLE_MAX:
1381                         
1382                         /* only expand view on axis if zoom is allowed */
1383                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1384                                 v2d->cur.xmax += temp;
1385                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1386                                 v2d->cur.ymax += temp;
1387                         break;
1388                         
1389                 case SCROLLHANDLE_MIN_OUTSIDE:
1390                 case SCROLLHANDLE_MAX_OUTSIDE:
1391                 case SCROLLHANDLE_BAR:
1392                 default:
1393                         /* only move view on an axis if panning is allowed */
1394                         if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1395                                 v2d->cur.xmin += temp;
1396                                 v2d->cur.xmax += temp;
1397                         }
1398                         if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1399                                 v2d->cur.ymin += temp;
1400                                 v2d->cur.ymax += temp;
1401                         }
1402                         break;
1403                         
1404         }
1405         
1406         /* validate that view is in valid configuration after this operation */
1407         UI_view2d_curRect_validate(v2d);
1408         
1409         /* request updates to be done... */
1410         ED_region_tag_redraw(vsm->ar);
1411         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1412 }
1413
1414 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
1415 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
1416 {
1417         v2dScrollerMove *vsm= op->customdata;
1418         
1419         /* execute the events */
1420         switch (event->type) {
1421                 case MOUSEMOVE:
1422                 {
1423                         /* calculate new delta transform, then store mouse-coordinates for next-time */
1424                         if (ELEM(vsm->zone, SCROLLHANDLE_BAR, SCROLLHANDLE_MAX)) {
1425                                 /* if using bar (i.e. 'panning') or 'max' zoom widget */
1426                                 switch (vsm->scroller) {
1427                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1428                                                 vsm->delta= (float)(event->x - vsm->lastx);
1429                                                 break;
1430                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1431                                                 vsm->delta= (float)(event->y - vsm->lasty);
1432                                                 break;
1433                                 }
1434                         }
1435                         else if (vsm->zone == SCROLLHANDLE_MIN) {
1436                                 /* using 'min' zoom widget */
1437                                 switch (vsm->scroller) {
1438                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
1439                                                 vsm->delta= (float)(vsm->lastx - event->x);
1440                                                 break;
1441                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
1442                                                 vsm->delta= (float)(vsm->lasty - event->y);
1443                                                 break;
1444                                 }
1445                         }
1446                         
1447                         /* store previous coordinates */
1448                         vsm->lastx= event->x;
1449                         vsm->lasty= event->y;
1450                         
1451                         scroller_activate_apply(C, op);
1452                 }
1453                         break;
1454                         
1455                 case LEFTMOUSE:
1456                         if (event->val==KM_RELEASE) {
1457                                 /* single-click was in empty space outside bubble, so scroll by 1 'page' */
1458                                 if (ELEM(vsm->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
1459                                         if (vsm->zone == SCROLLHANDLE_MIN_OUTSIDE)
1460                                                 vsm->delta = -vsm->scrollbarwidth * 0.8f;
1461                                         else if (vsm->zone == SCROLLHANDLE_MAX_OUTSIDE)
1462                                                 vsm->delta = vsm->scrollbarwidth * 0.8f;
1463                                         
1464                                         scroller_activate_apply(C, op);
1465                                         scroller_activate_exit(C, op);
1466                                         return OPERATOR_FINISHED;
1467                                 }
1468                                 
1469                                 /* otherwise, end the drag action  */
1470                                 if (vsm->lastx || vsm->lasty) {
1471                                         scroller_activate_exit(C, op);
1472                                         return OPERATOR_FINISHED;
1473                                 }
1474                         }
1475                         break;
1476         }
1477
1478         return OPERATOR_RUNNING_MODAL;
1479 }
1480
1481
1482 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
1483 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1484 {
1485         ARegion *ar= CTX_wm_region(C);
1486         View2D *v2d= &ar->v2d;
1487         short in_scroller= 0;
1488                 
1489         /* check if mouse in scrollbars, if they're enabled */
1490         in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
1491         
1492         /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
1493         if (in_scroller) {
1494                 v2dScrollerMove *vsm;
1495                 
1496                 /* initialise customdata */
1497                 scroller_activate_init(C, op, event, in_scroller);
1498                 vsm= (v2dScrollerMove *)op->customdata;
1499                 
1500                 /* check if zoom zones are inappropriate (i.e. zoom widgets not shown), so cannot continue
1501                  * NOTE: see view2d.c for latest conditions, and keep this in sync with that
1502                  */
1503                 if (ELEM(vsm->zone, SCROLLHANDLE_MIN, SCROLLHANDLE_MAX)) {
1504                         if ( ((vsm->scroller=='h') && (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL)==0) ||
1505                                  ((vsm->scroller=='v') && (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL)==0) )
1506                         {
1507                                 /* switch to bar (i.e. no scaling gets handled) */
1508                                 vsm->zone= SCROLLHANDLE_BAR;
1509                         }
1510                 }
1511                 
1512                 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
1513                 if (vsm->zone == SCROLLHANDLE_BAR) {
1514                         if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
1515                                  ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
1516                         {
1517                                 /* free customdata initialised */
1518                                 scroller_activate_exit(C, op);
1519                                 
1520                                 /* can't catch this event for ourselves, so let it go to someone else? */
1521                                 return OPERATOR_PASS_THROUGH;
1522                         }                       
1523                 }
1524                 
1525                 /* zone is also inappropriate if scroller is not visible... */
1526                 if ( ((vsm->scroller=='h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_HORIZONTAL_FULLR))) ||
1527                          ((vsm->scroller=='v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE|V2D_SCROLL_VERTICAL_FULLR))) )
1528                 {
1529                         /* free customdata initialised */
1530                         scroller_activate_exit(C, op);
1531                                 
1532                         /* can't catch this event for ourselves, so let it go to someone else? */
1533                         /* XXX note: if handlers use mask rect to clip input, input will fail for this case */
1534                         return OPERATOR_PASS_THROUGH;
1535                 }
1536                 
1537                 /* activate the scroller */
1538                 if (vsm->scroller=='h')
1539                         v2d->scroll_ui |= V2D_SCROLL_H_ACTIVE;
1540                 else
1541                         v2d->scroll_ui |= V2D_SCROLL_V_ACTIVE;
1542                 
1543                 /* still ok, so can add */
1544                 WM_event_add_modal_handler(C, op);
1545                 return OPERATOR_RUNNING_MODAL;
1546         }
1547         else {
1548                 /* not in scroller, so nothing happened... (pass through let's something else catch event) */
1549                 return OPERATOR_PASS_THROUGH;
1550         }
1551 }
1552
1553 /* LMB-Drag in Scrollers - not repeatable operator! */
1554 static void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
1555 {
1556         /* identifiers */
1557         ot->name= "Scroller Activate";
1558         ot->description= "Scroll view by mouse click and drag";
1559         ot->idname= "VIEW2D_OT_scroller_activate";
1560
1561         /* flags */
1562         ot->flag= OPTYPE_BLOCKING;
1563         
1564         /* api callbacks */
1565         ot->invoke= scroller_activate_invoke;
1566         ot->modal= scroller_activate_modal;
1567         ot->poll= view2d_poll;
1568 }
1569
1570 /* ********************************************************* */
1571 /* RESET */
1572
1573 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
1574 {
1575         uiStyle *style= U.uistyles.first;
1576         ARegion *ar= CTX_wm_region(C);
1577         View2D *v2d= &ar->v2d;
1578         int winx, winy;
1579
1580         /* zoom 1.0 */
1581         winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
1582         winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
1583
1584         v2d->cur.xmax= v2d->cur.xmin + winx;
1585         v2d->cur.ymax= v2d->cur.ymin + winy;
1586         
1587         /* align */
1588         if (v2d->align) {
1589                 /* posx and negx flags are mutually exclusive, so watch out */
1590                 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1591                         v2d->cur.xmax= 0.0f;
1592                         v2d->cur.xmin= -winx*style->panelzoom;
1593                 }
1594                 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
1595                         v2d->cur.xmax= winx*style->panelzoom;
1596                         v2d->cur.xmin= 0.0f;
1597                 }
1598
1599                 /* - posx and negx flags are mutually exclusive, so watch out */
1600                 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1601                         v2d->cur.ymax= 0.0f;
1602                         v2d->cur.ymin= -winy*style->panelzoom;
1603                 }
1604                 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
1605                         v2d->cur.ymax= winy*style->panelzoom;
1606                         v2d->cur.ymin= 0.0f;
1607                 }
1608         }
1609
1610         /* validate that view is in valid configuration after this operation */
1611         UI_view2d_curRect_validate(v2d);
1612         
1613         /* request updates to be done... */
1614         ED_region_tag_redraw(ar);
1615         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1616         
1617         return OPERATOR_FINISHED;
1618 }
1619  
1620 static void VIEW2D_OT_reset(wmOperatorType *ot)
1621 {
1622         /* identifiers */
1623         ot->name= "Reset View";
1624         ot->description= "Reset the view";
1625         ot->idname= "VIEW2D_OT_reset";
1626         
1627         /* api callbacks */
1628         ot->exec= reset_exec;
1629         ot->poll= view2d_poll;
1630 }
1631  
1632 /* ********************************************************* */
1633 /* Registration */
1634
1635 void UI_view2d_operatortypes(void)
1636 {
1637         WM_operatortype_append(VIEW2D_OT_pan);
1638         
1639         WM_operatortype_append(VIEW2D_OT_scroll_left);
1640         WM_operatortype_append(VIEW2D_OT_scroll_right);
1641         WM_operatortype_append(VIEW2D_OT_scroll_up);
1642         WM_operatortype_append(VIEW2D_OT_scroll_down);
1643         
1644         WM_operatortype_append(VIEW2D_OT_zoom_in);
1645         WM_operatortype_append(VIEW2D_OT_zoom_out);
1646         
1647         WM_operatortype_append(VIEW2D_OT_zoom);
1648         WM_operatortype_append(VIEW2D_OT_zoom_border);
1649         
1650         WM_operatortype_append(VIEW2D_OT_scroller_activate);
1651
1652         WM_operatortype_append(VIEW2D_OT_reset);
1653 }
1654
1655 void UI_view2d_keymap(wmKeyConfig *keyconf)
1656 {
1657         wmKeyMap *keymap= WM_keymap_find(keyconf, "View2D", 0, 0);
1658         
1659         /* pan/scroll */
1660         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1661         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
1662         
1663         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1664         
1665         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
1666         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
1667         
1668         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
1669         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
1670         
1671         /* zoom - single step */
1672         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
1673         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
1674         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1675         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1676         
1677         /* scroll up/down - no modifiers, only when zoom fails */
1678                 /* these may fail if zoom is disallowed, in which case they should pass on event */
1679         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1680         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1681                 /* these may be necessary if vertical scroll is disallowed */
1682         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1683         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
1684         
1685         /* alternatives for page up/down to scroll */
1686 #if 0 // XXX disabled, since this causes conflicts with hotkeys in animation editors
1687                 /* scroll up/down may fall through to left/right */
1688         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0);
1689         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0);
1690         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, 0, 0);
1691         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, 0, 0);
1692                 /* shift for moving view left/right with page up/down */
1693         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0);
1694         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);
1695 #endif
1696         
1697         /* zoom - drag */
1698         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1699         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1700         
1701         /* borderzoom - drag */
1702         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
1703         
1704         /* scrollers */
1705         WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1706
1707         /* Alternative keymap for buttons listview */
1708         keymap= WM_keymap_find(keyconf, "View2D Buttons List", 0, 0);
1709         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1710         WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MOUSEPAN, 0, 0, 0);
1711         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
1712         WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
1713         
1714         RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "page", 1);
1715         RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "page", 1);
1716         
1717         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1718         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MOUSEZOOM, 0, 0, 0);
1719         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
1720         WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
1721         WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);
1722         WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1723 }
1724