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