View2D: Border-Zoom tool
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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_scene_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_space_types.h"
35 #include "DNA_userdef_types.h"
36 #include "DNA_vec_types.h"
37 #include "DNA_view2d_types.h"
38
39 #include "BLI_blenlib.h"
40
41 #include "BKE_context.h"
42 #include "BKE_utildefines.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "BIF_gl.h"
51
52 #include "ED_screen.h"
53
54 #include "UI_resources.h"
55 #include "UI_view2d.h"
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         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         /* set custom-data for operator */
99         vpd= MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
100         op->customdata= vpd;
101         
102         /* set pointers to owners */
103         vpd->sc= CTX_wm_screen(C);
104         vpd->sa= CTX_wm_area(C);
105         vpd->v2d= v2d= &ar->v2d;
106         
107         /* calculate translation factor - based on size of view */
108         winx= (float)(ar->winrct.xmax - ar->winrct.xmin);
109         winy= (float)(ar->winrct.ymax - ar->winrct.ymin);
110         vpd->facx= (v2d->cur.xmax - v2d->cur.xmin) / winx;
111         vpd->facy= (v2d->cur.ymax - v2d->cur.ymin) / winy;
112         
113         return 1;
114 }
115
116 /* apply transform to view (i.e. adjust 'cur' rect) */
117 static void view_pan_apply(bContext *C, wmOperator *op)
118 {
119         v2dViewPanData *vpd= op->customdata;
120         View2D *v2d= vpd->v2d;
121         float dx, dy;
122         
123         /* calculate amount to move view by */
124         dx= vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
125         dy= vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
126         
127         /* only move view on an axis if change is allowed */
128         if ((v2d->keepofs & V2D_LOCKOFS_X)==0) {
129                 v2d->cur.xmin += dx;
130                 v2d->cur.xmax += dx;
131         }
132         if ((v2d->keepofs & V2D_LOCKOFS_Y)==0) {
133                 v2d->cur.ymin += dy;
134                 v2d->cur.ymax += dy;
135         }
136         
137         /* validate that view is in valid configuration after this operation */
138         UI_view2d_curRect_validate(v2d);
139         
140         /* request updates to be done... */
141         ED_area_tag_redraw(vpd->sa);
142         UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
143 }
144
145 /* cleanup temp customdata  */
146 static void view_pan_exit(bContext *C, wmOperator *op)
147 {
148         if (op->customdata) {
149                 MEM_freeN(op->customdata);
150                 op->customdata= NULL;                           
151         }
152
153  
154 /* ------------------ Modal Drag Version (1) ---------------------- */
155
156 /* for 'redo' only, with no user input */
157 static int view_pan_exec(bContext *C, wmOperator *op)
158 {
159         if (!view_pan_init(C, op))
160                 return OPERATOR_CANCELLED;
161         
162         view_pan_apply(C, op);
163         view_pan_exit(C, op);
164         return OPERATOR_FINISHED;
165 }
166
167 /* set up modal operator and relevant settings */
168 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
169 {
170         wmWindow *window= CTX_wm_window(C);
171         v2dViewPanData *vpd;
172         View2D *v2d;
173         
174         /* set up customdata */
175         if (!view_pan_init(C, op))
176                 return OPERATOR_CANCELLED;
177         
178         vpd= op->customdata;
179         v2d= vpd->v2d;
180         
181         /* set initial settings */
182         vpd->startx= vpd->lastx= event->x;
183         vpd->starty= vpd->lasty= event->y;
184         RNA_int_set(op->ptr, "deltax", 0);
185         RNA_int_set(op->ptr, "deltay", 0);
186         
187         if (v2d->keepofs & V2D_LOCKOFS_X)
188                 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
189         else if (v2d->keepofs & V2D_LOCKOFS_Y)
190                 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
191         else
192                 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
193         
194         /* add temp handler */
195         WM_event_add_modal_handler(C, &window->handlers, op);
196
197         return OPERATOR_RUNNING_MODAL;
198 }
199
200 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
201 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
202 {
203         v2dViewPanData *vpd= op->customdata;
204         
205         /* execute the events */
206         switch (event->type) {
207                 case MOUSEMOVE:
208                 {
209                         /* calculate new delta transform, then store mouse-coordinates for next-time */
210                         RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
211                         RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
212                         
213                         vpd->lastx= event->x;
214                         vpd->lasty= event->y;
215                         
216                         view_pan_apply(C, op);
217                 }
218                         break;
219                         
220                 case MIDDLEMOUSE:
221                         if (event->val==0) {
222                                 /* calculate overall delta mouse-movement for redo */
223                                 RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
224                                 RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
225                                 
226                                 view_pan_exit(C, op);
227                                 WM_cursor_restore(CTX_wm_window(C));
228                                 
229                                 return OPERATOR_FINISHED;
230                         }
231                         break;
232         }
233
234         return OPERATOR_RUNNING_MODAL;
235 }
236
237 void ED_View2D_OT_view_pan(wmOperatorType *ot)
238 {
239         PropertyRNA *prop;
240         
241         /* identifiers */
242         ot->name= "Pan View";
243         ot->idname= "ED_View2D_OT_view_pan";
244         
245         /* api callbacks */
246         ot->exec= view_pan_exec;
247         ot->invoke= view_pan_invoke;
248         ot->modal= view_pan_modal;
249         
250         /* operator is repeatable */
251         ot->flag= OPTYPE_REGISTER;
252         
253         /* rna - must keep these in sync with the other operators */
254         prop= RNA_def_property(ot->srna, "deltax", PROP_INT, PROP_NONE);
255         prop= RNA_def_property(ot->srna, "deltay", PROP_INT, PROP_NONE);
256 }
257
258 /* ------------------ Scrollwheel Versions (2) ---------------------- */
259
260 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
261 static int view_scrollright_exec(bContext *C, wmOperator *op)
262 {
263         /* initialise default settings (and validate if ok to run) */
264         if (!view_pan_init(C, op))
265                 return OPERATOR_CANCELLED;
266         
267         /* set RNA-Props - only movement in positive x-direction */
268         RNA_int_set(op->ptr, "deltax", 20);
269         RNA_int_set(op->ptr, "deltay", 0);
270         
271         /* apply movement, then we're done */
272         view_pan_apply(C, op);
273         view_pan_exit(C, op);
274         
275         return OPERATOR_FINISHED;
276 }
277
278 void ED_View2D_OT_view_scrollright(wmOperatorType *ot)
279 {
280         PropertyRNA *prop;
281         
282         /* identifiers */
283         ot->name= "Scroll Right";
284         ot->idname= "ED_View2D_OT_view_rightscroll";
285         
286         /* api callbacks */
287         ot->exec= view_scrollright_exec;
288         
289         /* operator is repeatable */
290         ot->flag= OPTYPE_REGISTER;
291         
292         /* rna - must keep these in sync with the other operators */
293         prop= RNA_def_property(ot->srna, "deltax", PROP_INT, PROP_NONE);
294         prop= RNA_def_property(ot->srna, "deltay", PROP_INT, PROP_NONE);
295 }
296
297
298
299 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
300 static int view_scrollleft_exec(bContext *C, wmOperator *op)
301 {
302         /* initialise default settings (and validate if ok to run) */
303         if (!view_pan_init(C, op))
304                 return OPERATOR_CANCELLED;
305         
306         /* set RNA-Props - only movement in negative x-direction */
307         RNA_int_set(op->ptr, "deltax", -20);
308         RNA_int_set(op->ptr, "deltay", 0);
309         
310         /* apply movement, then we're done */
311         view_pan_apply(C, op);
312         view_pan_exit(C, op);
313         
314         return OPERATOR_FINISHED;
315 }
316
317 void ED_View2D_OT_view_scrollleft(wmOperatorType *ot)
318 {
319         PropertyRNA *prop;
320         
321         /* identifiers */
322         ot->name= "Scroll Left";
323         ot->idname= "ED_View2D_OT_view_leftscroll";
324         
325         /* api callbacks */
326         ot->exec= view_scrollleft_exec;
327         
328         /* operator is repeatable */
329         ot->flag= OPTYPE_REGISTER;
330         
331         /* rna - must keep these in sync with the other operators */
332         prop= RNA_def_property(ot->srna, "deltax", PROP_INT, PROP_NONE);
333         prop= RNA_def_property(ot->srna, "deltay", PROP_INT, PROP_NONE);
334 }
335
336 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
337 static int view_scrolldown_exec(bContext *C, wmOperator *op)
338 {
339         /* initialise default settings (and validate if ok to run) */
340         if (!view_pan_init(C, op))
341                 return OPERATOR_CANCELLED;
342         
343         /* set RNA-Props - only movement in positive x-direction */
344         RNA_int_set(op->ptr, "deltax", 0);
345         RNA_int_set(op->ptr, "deltay", -20);
346         
347         /* apply movement, then we're done */
348         view_pan_apply(C, op);
349         view_pan_exit(C, op);
350         
351         return OPERATOR_FINISHED;
352 }
353
354 void ED_View2D_OT_view_scrolldown(wmOperatorType *ot)
355 {
356         PropertyRNA *prop;
357         
358         /* identifiers */
359         ot->name= "Scroll Down";
360         ot->idname= "ED_View2D_OT_view_downscroll";
361         
362         /* api callbacks */
363         ot->exec= view_scrolldown_exec;
364         
365         /* operator is repeatable */
366         ot->flag= OPTYPE_REGISTER;
367         
368         /* rna - must keep these in sync with the other operators */
369         prop= RNA_def_property(ot->srna, "deltax", PROP_INT, PROP_NONE);
370         prop= RNA_def_property(ot->srna, "deltay", PROP_INT, PROP_NONE);
371 }
372
373
374
375 /* this operator only needs this single callback, where it callsthe view_pan_*() methods */
376 static int view_scrollup_exec(bContext *C, wmOperator *op)
377 {
378         /* initialise default settings (and validate if ok to run) */
379         if (!view_pan_init(C, op))
380                 return OPERATOR_CANCELLED;
381         
382         /* set RNA-Props - only movement in negative x-direction */
383         RNA_int_set(op->ptr, "deltax", 0);
384         RNA_int_set(op->ptr, "deltay", 20);
385         
386         /* apply movement, then we're done */
387         view_pan_apply(C, op);
388         view_pan_exit(C, op);
389         
390         return OPERATOR_FINISHED;
391 }
392
393 void ED_View2D_OT_view_scrollup(wmOperatorType *ot)
394 {
395         PropertyRNA *prop;
396         
397         /* identifiers */
398         ot->name= "Scroll Up";
399         ot->idname= "ED_View2D_OT_view_upscroll";
400         
401         /* api callbacks */
402         ot->exec= view_scrollup_exec;
403         
404         /* operator is repeatable */
405         ot->flag= OPTYPE_REGISTER;
406         
407         /* rna - must keep these in sync with the other operators */
408         prop= RNA_def_property(ot->srna, "deltax", PROP_INT, PROP_NONE);
409         prop= RNA_def_property(ot->srna, "deltay", PROP_INT, PROP_NONE);
410 }
411
412 /* ********************************************************* */
413 /* SINGLE-STEP VIEW ZOOMING OPERATOR                                             */
414
415 /*      This group of operators come in several forms:
416  *              1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
417  *              2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y)  // XXX this could be implemented...
418  *              3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
419  *
420  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
421  *              zoomfacx, zoomfacy      - These two zoom factors allow for non-uniform scaling.
422  *                                                        It is safe to scale by 0, as these factors are used to determine
423  *                                                        amount to enlarge 'cur' by
424  */
425
426 /* ------------------ 'Shared' stuff ------------------------ */
427  
428 /* check if step-zoom can be applied */
429 static short view_zoomstep_ok(bContext *C)
430 {
431         ARegion *ar= CTX_wm_region(C);
432         View2D *v2d;
433         
434         /* check if there's a region in context to work with */
435         if (ar == NULL)
436                 return 0;
437         v2d= &ar->v2d;
438         
439         /* check that 2d-view is zoomable */
440         if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
441                 return 0;
442                 
443         /* view is zoomable */
444         return 1;
445 }
446  
447 /* apply transform to view (i.e. adjust 'cur' rect) */
448 static void view_zoomstep_apply(bContext *C, wmOperator *op)
449 {
450         ARegion *ar= CTX_wm_region(C);
451         View2D *v2d= &ar->v2d;
452         float dx, dy;
453         
454         /* calculate amount to move view by */
455         dx= (v2d->cur.xmax - v2d->cur.xmin) * (float)RNA_float_get(op->ptr, "zoomfacx");
456         dy= (v2d->cur.ymax - v2d->cur.ymin) * (float)RNA_float_get(op->ptr, "zoomfacy");
457         
458         /* only move view on an axis if change is allowed */
459         if ((v2d->keepzoom & V2D_LOCKOFS_X)==0) {
460                 v2d->cur.xmin += dx;
461                 v2d->cur.xmax -= dx;
462         }
463         if ((v2d->keepzoom & V2D_LOCKOFS_Y)==0) {
464                 v2d->cur.ymin += dy;
465                 v2d->cur.ymax -= dy;
466         }
467         
468         /* validate that view is in valid configuration after this operation */
469         UI_view2d_curRect_validate(v2d);
470         
471         /* request updates to be done... */
472         ED_area_tag_redraw(CTX_wm_area(C));
473         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
474 }
475
476 /* --------------- Individual Operators ------------------- */
477
478 /* this operator only needs this single callback, where it calls the view_zoom_*() methods */
479 static int view_zoomin_exec(bContext *C, wmOperator *op)
480 {
481         /* check that there's an active region, as View2D data resides there */
482         if (!view_zoomstep_ok(C))
483                 return OPERATOR_CANCELLED;
484         
485         /* set RNA-Props - zooming in by uniform factor */
486         RNA_float_set(op->ptr, "zoomfacx", 0.0375f);
487         RNA_float_set(op->ptr, "zoomfacy", 0.0375f);
488         
489         /* apply movement, then we're done */
490         view_zoomstep_apply(C, op);
491         
492         return OPERATOR_FINISHED;
493 }
494
495 void ED_View2D_OT_view_zoomin(wmOperatorType *ot)
496 {
497         PropertyRNA *prop;
498         
499         /* identifiers */
500         ot->name= "Zoom In";
501         ot->idname= "ED_View2D_OT_view_zoomin";
502         
503         /* api callbacks */
504         ot->exec= view_zoomin_exec;
505         
506         /* operator is repeatable */
507         ot->flag= OPTYPE_REGISTER;
508         
509         /* rna - must keep these in sync with the other operators */
510         prop= RNA_def_property(ot->srna, "zoomfacx", PROP_FLOAT, PROP_NONE);
511         prop= RNA_def_property(ot->srna, "zoomfacy", PROP_FLOAT, PROP_NONE);
512 }
513
514
515
516 /* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
517 static int view_zoomout_exec(bContext *C, wmOperator *op)
518 {
519         /* check that there's an active region, as View2D data resides there */
520         if (!view_zoomstep_ok(C))
521                 return OPERATOR_CANCELLED;
522         
523         /* set RNA-Props - zooming in by uniform factor */
524         RNA_float_set(op->ptr, "zoomfacx", -0.0375f);
525         RNA_float_set(op->ptr, "zoomfacy", -0.0375f);
526         
527         /* apply movement, then we're done */
528         view_zoomstep_apply(C, op);
529         
530         return OPERATOR_FINISHED;
531 }
532
533 void ED_View2D_OT_view_zoomout(wmOperatorType *ot)
534 {
535         PropertyRNA *prop;
536         
537         /* identifiers */
538         ot->name= "Zoom Out";
539         ot->idname= "ED_View2D_OT_view_zoomout";
540         
541         /* api callbacks */
542         ot->exec= view_zoomout_exec;
543         
544         /* operator is repeatable */
545         ot->flag= OPTYPE_REGISTER;
546         
547         /* rna - must keep these in sync with the other operators */
548         prop= RNA_def_property(ot->srna, "zoomfacx", PROP_FLOAT, PROP_NONE);
549         prop= RNA_def_property(ot->srna, "zoomfacy", PROP_FLOAT, PROP_NONE);
550 }
551
552 /* ********************************************************* */
553 /* DRAG-ZOOM OPERATOR                                                                    */
554
555 /*      MMB Drag - allows non-uniform scaling by dragging mouse
556  *
557  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
558  *              deltax, deltay  - amounts to add to each side of the 'cur' rect
559  */
560  
561 /* ------------------ Shared 'core' stuff ---------------------- */
562  
563 /* temp customdata for operator */
564 typedef struct v2dViewZoomData {
565         View2D *v2d;                    /* view2d we're operating in */
566         
567         int lastx, lasty;               /* previous x/y values of mouse in window */
568         float dx, dy;                   /* running tally of previous delta values (for obtaining final zoom) */
569 } v2dViewZoomData;
570  
571 /* initialise panning customdata */
572 static int view_zoomdrag_init(bContext *C, wmOperator *op)
573 {
574         ARegion *ar= CTX_wm_region(C);
575         v2dViewZoomData *vzd;
576         View2D *v2d;
577         
578         /* regions now have v2d-data by default, so check for region */
579         if (ar == NULL)
580                 return 0;
581         v2d= &ar->v2d;
582         
583         /* check that 2d-view is zoomable */
584         if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
585                 return 0;
586         
587         /* set custom-data for operator */
588         vzd= MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
589         op->customdata= vzd;
590         
591         /* set pointers to owners */
592         vzd->v2d= v2d;
593         
594         return 1;
595 }
596
597 /* apply transform to view (i.e. adjust 'cur' rect) */
598 static void view_zoomdrag_apply(bContext *C, wmOperator *op)
599 {
600         v2dViewZoomData *vzd= op->customdata;
601         View2D *v2d= vzd->v2d;
602         float dx, dy;
603         
604         /* get amount to move view by */
605         dx= RNA_float_get(op->ptr, "deltax");
606         dy= RNA_float_get(op->ptr, "deltay");
607         
608         /* only move view on an axis if change is allowed */
609         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
610                 v2d->cur.xmin += dx;
611                 v2d->cur.xmax -= dx;
612         }
613         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
614                 v2d->cur.ymin += dy;
615                 v2d->cur.ymax -= dy;
616         }
617         
618         /* validate that view is in valid configuration after this operation */
619         UI_view2d_curRect_validate(v2d);
620         
621         /* request updates to be done... */
622         ED_area_tag_redraw(CTX_wm_area(C));
623         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
624 }
625
626 /* cleanup temp customdata  */
627 static void view_zoomdrag_exit(bContext *C, wmOperator *op)
628 {
629         if (op->customdata) {
630                 MEM_freeN(op->customdata);
631                 op->customdata= NULL;                           
632         }
633
634
635 /* for 'redo' only, with no user input */
636 static int view_zoomdrag_exec(bContext *C, wmOperator *op)
637 {
638         if (!view_zoomdrag_init(C, op))
639                 return OPERATOR_CANCELLED;
640         
641         view_zoomdrag_apply(C, op);
642         view_zoomdrag_exit(C, op);
643         return OPERATOR_FINISHED;
644 }
645
646 /* set up modal operator and relevant settings */
647 static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
648 {
649         wmWindow *window= CTX_wm_window(C);
650         v2dViewZoomData *vzd;
651         View2D *v2d;
652         
653         /* set up customdata */
654         if (!view_zoomdrag_init(C, op))
655                 return OPERATOR_CANCELLED;
656         
657         vzd= op->customdata;
658         v2d= vzd->v2d;
659         
660         /* set initial settings */
661         vzd->lastx= event->x;
662         vzd->lasty= event->y;
663         RNA_float_set(op->ptr, "deltax", 0);
664         RNA_float_set(op->ptr, "deltay", 0);
665         
666         if (v2d->keepofs & V2D_LOCKOFS_X)
667                 WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
668         else if (v2d->keepofs & V2D_LOCKOFS_Y)
669                 WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
670         else
671                 WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
672         
673         /* add temp handler */
674         WM_event_add_modal_handler(C, &window->handlers, op);
675
676         return OPERATOR_RUNNING_MODAL;
677 }
678
679 /* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
680 static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
681 {
682         v2dViewZoomData *vzd= op->customdata;
683         View2D *v2d= vzd->v2d;
684         
685         /* execute the events */
686         switch (event->type) {
687                 case MOUSEMOVE:
688                 {
689                         float dx, dy;
690                         
691                         /* calculate new delta transform, based on zooming mode */
692                         if (U.viewzoom == USER_ZOOM_SCALE) {
693                                 /* 'scale' zooming */
694                                 float dist;
695                                 
696                                 /* x-axis transform */
697                                 dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
698                                 dx= 1.0f - (fabs(vzd->lastx - dist) + 2.0f) / (fabs(event->x - dist) + 2.0f);
699                                 dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
700                                 
701                                 /* y-axis transform */
702                                 dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
703                                 dy= 1.0f - (fabs(vzd->lasty - dist) + 2.0) / (fabs(event->y - dist) + 2.0f);
704                                 dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
705                         }
706                         else {
707                                 /* 'continuous' or 'dolly' */
708                                 float fac;
709                                 
710                                 /* x-axis transform */
711                                 fac= 0.01f * (event->x - vzd->lastx);
712                                 dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
713                                 
714                                 /* y-axis transform */
715                                 fac= 0.01f * (event->y - vzd->lasty);
716                                 dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
717                                 
718                                 /* continous zoom shouldn't move that fast... */
719                                 if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
720                                         dx /= 20.0f;
721                                         dy /= 20.0f;
722                                 }
723                         }
724                         
725                         /* set transform amount, and add current deltas to stored total delta (for redo) */
726                         RNA_float_set(op->ptr, "deltax", dx);
727                         RNA_float_set(op->ptr, "deltay", dy);
728                         vzd->dx += dx;
729                         vzd->dy += dy;
730                         
731                         /* store mouse coordinates for next time, if not doing continuous zoom
732                          *      - continuous zoom only depends on distance of mouse to starting point to determine rate of change
733                          */
734                         if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
735                                 vzd->lastx= event->x;
736                                 vzd->lasty= event->y;
737                         }
738                         
739                         /* apply zooming */
740                         view_zoomdrag_apply(C, op);
741                 }
742                         break;
743                         
744                 case MIDDLEMOUSE:
745                         if (event->val==0) {
746                                 /* for redo, store the overall deltas - need to respect zoom-locks here... */
747                                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
748                                         RNA_float_set(op->ptr, "deltax", vzd->dx);
749                                 else
750                                         RNA_float_set(op->ptr, "deltax", 0);
751                                         
752                                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
753                                         RNA_float_set(op->ptr, "deltay", vzd->dy);
754                                 else
755                                         RNA_float_set(op->ptr, "deltay", 0);
756                                 
757                                 /* free customdata */
758                                 view_zoomdrag_exit(C, op);
759                                 WM_cursor_restore(CTX_wm_window(C));
760                                 
761                                 return OPERATOR_FINISHED;
762                         }
763                         break;
764         }
765
766         return OPERATOR_RUNNING_MODAL;
767 }
768
769 void ED_View2D_OT_view_zoom(wmOperatorType *ot)
770 {
771         PropertyRNA *prop;
772         
773         /* identifiers */
774         ot->name= "Zoom View";
775         ot->idname= "ED_View2D_OT_view_zoom";
776         
777         /* api callbacks */
778         ot->exec= view_zoomdrag_exec;
779         ot->invoke= view_zoomdrag_invoke;
780         ot->modal= view_zoomdrag_modal;
781         
782         /* operator is repeatable */
783         ot->flag= OPTYPE_REGISTER;
784         
785         /* rna - must keep these in sync with the other operators */
786         prop= RNA_def_property(ot->srna, "deltax", PROP_FLOAT, PROP_NONE);
787         prop= RNA_def_property(ot->srna, "deltay", PROP_FLOAT, PROP_NONE);
788 }
789
790 /* ********************************************************* */
791 /* BORDER-ZOOM */
792
793 /* The user defines a rect using standard borderselect tools, and we use this rect to 
794  * define the new zoom-level of the view in the following ways:
795  *      1) LEFTMOUSE - zoom in to view
796  *      2) RIGHTMOUSE - zoom out of view
797  *
798  * Currently, these key mappings are hardcoded, but it shouldn't be too important to
799  * have custom keymappings for this...
800  */
801  
802 static int view_borderzoom_exec(bContext *C, wmOperator *op)
803 {
804         ARegion *ar= CTX_wm_region(C);
805         View2D *v2d= &ar->v2d;
806         rctf rect;
807         int event_type;
808         
809         /* convert coordinates of rect to 'tot' rect coordinates */
810         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
811         UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
812         
813         /* check if zooming in/out view */
814         // XXX hardcoded for now!
815         event_type= RNA_int_get(op->ptr, "event_type");
816         
817         if (event_type == LEFTMOUSE) {
818                 /* zoom in: 
819                  *      - 'cur' rect will be defined by the coordinates of the border region 
820                  *      - just set the 'cur' rect to have the same coordinates as the border region
821                  *        if zoom is allowed to be changed
822                  */
823                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
824                         v2d->cur.xmin= rect.xmin;
825                         v2d->cur.xmax= rect.xmax;
826                 }
827                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
828                         v2d->cur.ymin= rect.ymin;
829                         v2d->cur.ymax= rect.ymax;
830                 }
831         }
832         else {
833                 /* zoom out:
834                  *      - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are, 
835                  *        but the 'cur' rect coordinates will need to be adjusted to take in more of the view
836                  *      - calculate zoom factor, and adjust using center-point
837                  */
838                 float zoom, center, size;
839                 
840                 // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
841                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
842                         size= (v2d->cur.xmax - v2d->cur.xmin);
843                         zoom= size / (rect.xmax - rect.xmin);
844                         center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
845                         
846                         v2d->cur.xmin= center - (size * zoom);
847                         v2d->cur.xmax= center + (size * zoom);
848                 }
849                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
850                         size= (v2d->cur.ymax - v2d->cur.ymin);
851                         zoom= size / (rect.ymax - rect.ymin);
852                         center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
853                         
854                         v2d->cur.ymin= center - (size * zoom);
855                         v2d->cur.ymax= center + (size * zoom);
856                 }
857         }
858         
859         /* validate that view is in valid configuration after this operation */
860         UI_view2d_curRect_validate(v2d);
861         
862         /* request updates to be done... */
863         ED_area_tag_redraw(CTX_wm_area(C));
864         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
865         
866         return OPERATOR_FINISHED;
867
868
869 void ED_View2D_OT_view_borderzoom(wmOperatorType *ot)
870 {
871         /* identifiers */
872         ot->name= "Zoom to Border";
873         ot->idname= "ED_View2D_OT_view_borderzoom";
874         
875         /* api callbacks */
876         ot->invoke= WM_border_select_invoke;
877         ot->exec= view_borderzoom_exec;
878         ot->modal= WM_border_select_modal;
879         
880         ot->poll= ED_operator_areaactive;
881         
882         /* rna */
883         RNA_def_property(ot->srna, "event_type", PROP_INT, PROP_NONE);
884         RNA_def_property(ot->srna, "xmin", PROP_INT, PROP_NONE);
885         RNA_def_property(ot->srna, "xmax", PROP_INT, PROP_NONE);
886         RNA_def_property(ot->srna, "ymin", PROP_INT, PROP_NONE);
887         RNA_def_property(ot->srna, "ymax", PROP_INT, PROP_NONE);
888 }
889
890 /* ********************************************************* */
891 /* SCROLLERS */
892
893 /*      Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
894  *              1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, 
895  *                      enlarge 'cur' rect on the relevant side 
896  *              2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
897  *
898  *      In order to make sure this works, each operator must define the following RNA-Operator Props:
899  *              deltax, deltay  - define how much to move view by (relative to zoom-correction factor)
900  */
901
902 /* customdata for scroller-invoke data */
903 typedef struct v2dScrollerMove {
904         View2D *v2d;                    /* View2D data that this operation affects */
905         
906         short scroller;                 /* scroller that mouse is in ('h' or 'v') */
907         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?)
908         
909         float fac;                              /* view adjustment factor, based on size of region */
910         float delta;                    /* amount moved by mouse on axis of interest */
911         
912         int lastx, lasty;               /* previous mouse coordinates (in screen coordinates) for determining movement */
913 } v2dScrollerMove;
914
915
916 /* View2DScrollers is typedef'd in UI_view2d.h 
917  * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
918  * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
919  *                 For now, we don't need to have a separate (internal) header for structs like this...
920  */
921 struct View2DScrollers {        
922                 /* focus bubbles */
923         int vert_min, vert_max; /* vertical scrollbar */
924         int hor_min, hor_max;   /* horizontal scrollbar */
925 };
926
927 /* quick enum for vsm->zone (scroller handles) */
928 enum {
929         SCROLLHANDLE_MIN= -1,
930         SCROLLHANDLE_BAR,
931         SCROLLHANDLE_MAX
932 } eV2DScrollerHandle_Zone;
933
934 /* ------------------------ */
935
936 /* check if mouse is within scroller handle 
937  *      - mouse                 =       relevant mouse coordinate in region space
938  *      - sc_min, sc_max        =       extents of scroller
939  *      - sh_min, sh_max        =       positions of scroller handles
940  */
941 static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
942 {
943         short in_min, in_max, in_view=1;
944         
945         /* firstly, check if 
946          *      - 'bubble' fills entire scroller 
947          *      - 'bubble' completely out of view on either side 
948          */
949         if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
950         if (sh_min == sh_max) {
951                 if (sh_min <= sc_min) in_view= 0;
952                 if (sh_max >= sc_max) in_view= 0;
953         }
954         else {
955                 if (sh_max <= sc_min) in_view= 0;
956                 if (sh_min >= sc_max) in_view= 0;
957         }
958         
959         
960         if (in_view == 0) {
961                 /* handles are only activated if the mouse is within the relative quater lengths of the scroller */
962                 int qLen = (sc_max + sc_min) / 4;
963                 
964                 if (mouse >= (sc_max - qLen))
965                         return SCROLLHANDLE_MAX;
966                 else if (mouse <= qLen)
967                         return SCROLLHANDLE_MIN;
968                 else
969                         return SCROLLHANDLE_BAR;
970         }
971         
972         /* check if mouse is in or past either handle */
973         in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
974         in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
975         
976         /* check if overlap --> which means user clicked on bar, as bar is within handles region */
977         if (in_max && in_min)
978                 return SCROLLHANDLE_BAR;
979         else if (in_max)
980                 return SCROLLHANDLE_MAX;
981         else if (in_min)
982                 return SCROLLHANDLE_MIN;
983                 
984         /* unlikely to happen, though we just cover it in case */
985         return SCROLLHANDLE_BAR;
986
987
988 /* initialise customdata for scroller manipulation operator */
989 static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
990 {
991         v2dScrollerMove *vsm;
992         View2DScrollers *scrollers;
993         ARegion *ar= CTX_wm_region(C);
994         View2D *v2d= &ar->v2d;
995         float mask_size;
996         int x, y;
997         
998         /* set custom-data for operator */
999         vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
1000         op->customdata= vsm;
1001         
1002         /* set general data */
1003         vsm->v2d= v2d;
1004         vsm->scroller= in_scroller;
1005         
1006         /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
1007         vsm->lastx = event->x;
1008         vsm->lasty = event->y;
1009         x= event->x - ar->winrct.xmin;
1010         y= event->y - ar->winrct.ymin;
1011         
1012         /* 'zone' depends on where mouse is relative to bubble 
1013          *      - zooming must be allowed on this axis, otherwise, default to pan
1014          */
1015         scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1016         if (in_scroller == 'h') {
1017                 /* horizontal scroller - calculate adjustment factor first */
1018                 mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
1019                 vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
1020                 
1021                 /* get 'zone' (i.e. which part of scroller is activated) */
1022                 if (v2d->keepzoom & V2D_LOCKZOOM_X) {
1023                         /* default to scroll, as handles not usable */
1024                         vsm->zone= SCROLLHANDLE_BAR;
1025                 }
1026                 else {
1027                         /* check which handle we're in */
1028                         vsm->zone= mouse_in_scroller_handle(x, v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max); 
1029                 }
1030         }
1031         else {
1032                 /* vertical scroller - calculate adjustment factor first */
1033                 mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
1034                 vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
1035                 
1036                 /* get 'zone' (i.e. which part of scroller is activated) */
1037                 if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
1038                         /* default to scroll, as handles not usable */
1039                         vsm->zone= SCROLLHANDLE_BAR;
1040                 }
1041                 else {
1042                         /* check which handle we're in */
1043                         vsm->zone= mouse_in_scroller_handle(y, v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max); 
1044                 }
1045         }
1046         UI_view2d_scrollers_free(scrollers);
1047 }
1048
1049 /* cleanup temp customdata  */
1050 static void scroller_activate_exit(bContext *C, wmOperator *op)
1051 {
1052         if (op->customdata) {
1053                 MEM_freeN(op->customdata);
1054                 op->customdata= NULL;                           
1055         }
1056
1057
1058 /* apply transform to view (i.e. adjust 'cur' rect) */
1059 static void scroller_activate_apply(bContext *C, wmOperator *op)
1060 {
1061         v2dScrollerMove *vsm= op->customdata;
1062         View2D *v2d= vsm->v2d;
1063         float temp;
1064         
1065         /* calculate amount to move view by */
1066         temp= vsm->fac * vsm->delta;
1067         
1068         /* type of movement */
1069         switch (vsm->zone) {
1070                 case SCROLLHANDLE_MIN:
1071                         /* only expand view on axis if zoom is allowed */
1072                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1073                                 v2d->cur.xmin -= temp;
1074                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1075                                 v2d->cur.ymin -= temp;
1076                         break;
1077                 
1078                 case SCROLLHANDLE_MAX:
1079                         /* only expand view on axis if zoom is allowed */
1080                         if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
1081                                 v2d->cur.xmax += temp;
1082                         if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
1083                                 v2d->cur.ymax += temp;
1084                         break;
1085                 
1086                 default: /* SCROLLHANDLE_BAR */
1087                         /* only move view on an axis if panning is allowed */
1088                         if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
1089                                 v2d->cur.xmin += temp;
1090                                 v2d->cur.xmax += temp;
1091                         }
1092                         if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
1093                                 v2d->cur.ymin += temp;
1094                                 v2d->cur.ymax += temp;
1095                         }
1096                         break;
1097         }
1098         
1099         /* validate that view is in valid configuration after this operation */
1100         UI_view2d_curRect_validate(v2d);
1101         
1102         /* request updates to be done... */
1103         ED_area_tag_redraw(CTX_wm_area(C));
1104         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
1105 }
1106
1107 /* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
1108 static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
1109 {
1110         v2dScrollerMove *vsm= op->customdata;
1111         
1112         /* execute the events */
1113         switch (event->type) {
1114                 case MOUSEMOVE:
1115                 {
1116                         /* calculate new delta transform, then store mouse-coordinates for next-time */
1117                         if (vsm->zone != SCROLLHANDLE_MIN) {
1118                                 /* if using bar (i.e. 'panning') or 'max' zoom widget */
1119                                 switch (vsm->scroller) {
1120                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
1121                                                 vsm->delta= (float)(event->x - vsm->lastx);
1122                                                 break;
1123                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
1124                                                 vsm->delta= (float)(event->y - vsm->lasty);
1125                                                 break;
1126                                 }
1127                         }
1128                         else {
1129                                 /* using 'min' zoom widget */
1130                                 switch (vsm->scroller) {
1131                                         case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
1132                                                 vsm->delta= (float)(vsm->lastx - event->x);
1133                                                 break;
1134                                         case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
1135                                                 vsm->delta= (float)(vsm->lasty - event->y);
1136                                                 break;
1137                                 }
1138                         }
1139                         
1140                         /* store previous coordinates */
1141                         vsm->lastx= event->x;
1142                         vsm->lasty= event->y;
1143                         
1144                         scroller_activate_apply(C, op);
1145                 }
1146                         break;
1147                         
1148                 case LEFTMOUSE:
1149                         if (event->val==0) {
1150                                 scroller_activate_exit(C, op);
1151                                 return OPERATOR_FINISHED;
1152                         }
1153                         break;
1154         }
1155
1156         return OPERATOR_RUNNING_MODAL;
1157 }
1158
1159
1160 /* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
1161 static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1162 {
1163         ARegion *ar= CTX_wm_region(C);
1164         View2D *v2d= NULL;
1165         short in_scroller= 0;
1166         
1167         /* firstly, check context to see if mouse is actually in region */
1168         // XXX isn't this the job of poll() callbacks which can't check events, but only context?
1169         if (ar == NULL) 
1170                 return OPERATOR_PASS_THROUGH;//OPERATOR_CANCELLED;
1171         else
1172                 v2d= &ar->v2d;
1173                 
1174         /* check if mouse in scrollbars, if they're enabled */
1175         in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
1176         
1177         /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
1178         if (in_scroller) {
1179                 v2dScrollerMove *vsm;
1180                 
1181                 /* initialise customdata */
1182                 scroller_activate_init(C, op, event, in_scroller);
1183                 vsm= (v2dScrollerMove *)op->customdata;
1184                 
1185                 /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
1186                 if (vsm->zone == SCROLLHANDLE_BAR) {
1187                         if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
1188                                  ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
1189                         {
1190                                 /* free customdata initialised */
1191                                 scroller_activate_exit(C, op);
1192                                 
1193                                 /* can't catch this event for ourselves, so let it go to someone else? */
1194                                 return OPERATOR_PASS_THROUGH;
1195                         }                       
1196                 }
1197                 
1198                 /* still ok, so can add */
1199                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1200                 return OPERATOR_RUNNING_MODAL;
1201         }
1202         else {
1203                 /* not in scroller, so nothing happened... (pass through let's something else catch event) */
1204                 return OPERATOR_PASS_THROUGH;
1205         }
1206 }
1207
1208 /* LMB-Drag in Scrollers - not repeatable operator! */
1209 void ED_View2D_OT_scroller_activate(wmOperatorType *ot)
1210 {
1211         /* identifiers */
1212         ot->name= "Scroller Activate";
1213         ot->idname= "ED_View2D_OT_scroller_activate";
1214         
1215         /* api callbacks */
1216         ot->invoke= scroller_activate_invoke;
1217         ot->modal= scroller_activate_modal;
1218 }
1219  
1220 /* ********************************************************* */
1221 /* Registration */
1222
1223 void ui_view2d_operatortypes(void)
1224 {
1225         WM_operatortype_append(ED_View2D_OT_view_pan);
1226         
1227         WM_operatortype_append(ED_View2D_OT_view_scrollleft);
1228         WM_operatortype_append(ED_View2D_OT_view_scrollright);
1229         WM_operatortype_append(ED_View2D_OT_view_scrollup);
1230         WM_operatortype_append(ED_View2D_OT_view_scrolldown);
1231         
1232         WM_operatortype_append(ED_View2D_OT_view_zoomin);
1233         WM_operatortype_append(ED_View2D_OT_view_zoomout);
1234         
1235         WM_operatortype_append(ED_View2D_OT_view_zoom);
1236         WM_operatortype_append(ED_View2D_OT_view_borderzoom);
1237         
1238         WM_operatortype_append(ED_View2D_OT_scroller_activate);
1239 }
1240
1241 void UI_view2d_keymap(wmWindowManager *wm)
1242 {
1243         ListBase *keymap= WM_keymap_listbase(wm, "View2D", 0, 0);
1244         
1245         /* pan/scroll */
1246         WM_keymap_add_item(keymap, "ED_View2D_OT_view_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
1247         
1248         WM_keymap_add_item(keymap, "ED_View2D_OT_view_rightscroll", WHEELDOWNMOUSE, KM_ANY, KM_CTRL, 0);
1249         WM_keymap_add_item(keymap, "ED_View2D_OT_view_leftscroll", WHEELUPMOUSE, KM_ANY, KM_CTRL, 0);
1250         
1251         WM_keymap_add_item(keymap, "ED_View2D_OT_view_downscroll", WHEELDOWNMOUSE, KM_ANY, KM_SHIFT, 0);
1252         WM_keymap_add_item(keymap, "ED_View2D_OT_view_upscroll", WHEELUPMOUSE, KM_ANY, KM_SHIFT, 0);
1253         
1254         /* zoom - single step */
1255         WM_keymap_add_item(keymap, "ED_View2D_OT_view_zoomout", WHEELUPMOUSE, KM_ANY, 0, 0);
1256         WM_keymap_add_item(keymap, "ED_View2D_OT_view_zoomin", WHEELDOWNMOUSE, KM_ANY, 0, 0);
1257         WM_keymap_add_item(keymap, "ED_View2D_OT_view_zoomout", PADMINUS, KM_PRESS, 0, 0);
1258         WM_keymap_add_item(keymap, "ED_View2D_OT_view_zoomin", PADPLUSKEY, KM_PRESS, 0, 0);
1259         
1260         /* zoom - drag */
1261         WM_keymap_add_item(keymap, "ED_View2D_OT_view_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
1262         
1263         /* borderzoom - drag */
1264         WM_keymap_add_item(keymap, "ED_View2D_OT_view_borderzoom", ZKEY, KM_PRESS, KM_SHIFT, 0);
1265         
1266         /* scrollers */
1267         WM_keymap_add_item(keymap, "ED_View2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
1268 }
1269