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