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