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