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