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