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