BugFix:
[blender-staging.git] / source / blender / editors / interface / view2d.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <float.h>
29 #include <limits.h>
30 #include <math.h>
31 #include <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_scene_types.h"
36 #include "DNA_userdef_types.h"
37
38 #include "BLI_blenlib.h"
39
40 #include "BKE_context.h"
41 #include "BKE_global.h"
42 #include "BKE_utildefines.h"
43
44 #include "WM_api.h"
45
46 #include "BIF_gl.h"
47 #include "BIF_glutil.h"
48
49 #include "BLF_api.h"
50
51 #include "ED_anim_api.h"
52 #include "ED_screen.h"
53
54 #include "UI_interface.h"
55 #include "UI_view2d.h"
56
57 #include "interface_intern.h"
58
59 /* *********************************************************************** */
60
61 /* helper to allow scrollbars to dynamically hide
62  *      - returns a copy of the scrollbar settings with the flags to display 
63  *        horizontal/vertical scrollbars removed
64  *      - input scroll value is the v2d->scroll var
65  *      - hide flags are set per region at drawtime
66  */
67 static int view2d_scroll_mapped(int scroll)
68 {
69         if(scroll & V2D_SCROLL_HORIZONTAL_HIDE)
70                 scroll &= ~(V2D_SCROLL_HORIZONTAL);
71         if(scroll & V2D_SCROLL_VERTICAL_HIDE)
72                 scroll &= ~(V2D_SCROLL_VERTICAL);
73         return scroll;
74 }
75
76 /* called each time cur changes, to dynamically update masks */
77 static void view2d_masks(View2D *v2d)
78 {
79         int scroll;
80         
81         /* mask - view frame */
82         v2d->mask.xmin= v2d->mask.ymin= 0;
83         v2d->mask.xmax= v2d->winx - 1;  /* -1 yes! masks are pixels */
84         v2d->mask.ymax= v2d->winy - 1;
85
86 #if 0
87         v2d->scroll &= ~(V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_VERTICAL_HIDE);
88         /* check size if: */
89         if (v2d->scroll & V2D_SCROLL_HORIZONTAL)
90                 if(!(v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL))
91                         if (v2d->tot.xmax-v2d->tot.xmin <= v2d->cur.xmax-v2d->cur.xmin)
92                                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
93         if (v2d->scroll & V2D_SCROLL_VERTICAL)
94                 if(!(v2d->scroll & V2D_SCROLL_SCALE_VERTICAL))
95                         if (v2d->tot.ymax-v2d->tot.ymin <= v2d->cur.ymax-v2d->cur.ymin)
96                                 v2d->scroll |= V2D_SCROLL_VERTICAL_HIDE;
97 #endif
98         scroll= view2d_scroll_mapped(v2d->scroll);
99         
100         /* scrollers shrink mask area, but should be based off regionsize 
101          *      - they can only be on one to two edges of the region they define
102          *      - if they overlap, they must not occupy the corners (which are reserved for other widgets)
103          */
104         if (scroll) {
105                 /* vertical scroller */
106                 if (scroll & V2D_SCROLL_LEFT) {
107                         /* on left-hand edge of region */
108                         v2d->vert= v2d->mask;
109                         v2d->vert.xmax= V2D_SCROLL_WIDTH;
110                         v2d->mask.xmin= v2d->vert.xmax + 1;
111                 }
112                 else if (scroll & V2D_SCROLL_RIGHT) {
113                         /* on right-hand edge of region */
114                         v2d->vert= v2d->mask;
115                         v2d->vert.xmax++; /* one pixel extra... was leaving a minor gap... */
116                         v2d->vert.xmin= v2d->vert.xmax - V2D_SCROLL_WIDTH;
117                         v2d->mask.xmax= v2d->vert.xmin - 1;
118                 }
119                 
120                 /* horizontal scroller */
121                 if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
122                         /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
123                         v2d->hor= v2d->mask;
124                         v2d->hor.ymax= V2D_SCROLL_HEIGHT;
125                         v2d->mask.ymin= v2d->hor.ymax + 1;
126                 }
127                 else if (scroll & V2D_SCROLL_TOP) {
128                         /* on upper edge of region */
129                         v2d->hor= v2d->mask;
130                         v2d->hor.ymin= v2d->hor.ymax - V2D_SCROLL_HEIGHT;
131                         v2d->mask.ymax= v2d->hor.ymin - 1;
132                 }
133                 
134                 /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */
135                 if (scroll & V2D_SCROLL_VERTICAL) {
136                         /* just set y min/max for vertical scroller to y min/max of mask as appropriate */
137                         if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
138                                 /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
139                                 v2d->vert.ymin= v2d->mask.ymin;
140                         }
141                         else if (scroll & V2D_SCROLL_TOP) {
142                                 /* on upper edge of region */
143                                 v2d->vert.ymax= v2d->mask.ymax;
144                         }
145                 }
146         }
147         
148 }
149
150 /* Refresh and Validation */
151
152 /* Initialise all relevant View2D data (including view rects if first time) and/or refresh mask sizes after view resize
153  *      - for some of these presets, it is expected that the region will have defined some
154  *        additional settings necessary for the customisation of the 2D viewport to its requirements
155  *      - this function should only be called from region init() callbacks, where it is expected that
156  *        this is called before UI_view2d_size_update(), as this one checks that the rects are properly initialised. 
157  */
158 void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
159 {
160         short tot_changed= 0, init= 0;
161         uiStyle *style= U.uistyles.first;
162
163         /* initialise data if there is a need for such */
164         if ((v2d->flag & V2D_IS_INITIALISED) == 0) {
165                 /* set initialised flag so that View2D doesn't get reinitialised next time again */
166                 v2d->flag |= V2D_IS_INITIALISED;
167
168                 init= 1;
169                 
170                 /* see eView2D_CommonViewTypes in UI_view2d.h for available view presets */
171                 switch (type) {
172                         /* 'standard view' - optimum setup for 'standard' view behaviour, that should be used new views as basis for their
173                          *      own unique View2D settings, which should be used instead of this in most cases...
174                          */
175                         case V2D_COMMONVIEW_STANDARD:
176                         {
177                                 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
178                                 v2d->keepzoom= (V2D_KEEPASPECT|V2D_LIMITZOOM);
179                                 v2d->minzoom= 0.01f;
180                                 v2d->maxzoom= 1000.0f;
181                                 
182                                 /* tot rect and cur should be same size, and aligned using 'standard' OpenGL coordinates for now 
183                                  *      - region can resize 'tot' later to fit other data
184                                  *      - keeptot is only within bounds, as strict locking is not that critical
185                                  *      - view is aligned for (0,0) -> (winx-1, winy-1) setup
186                                  */
187                                 v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
188                                 v2d->keeptot= V2D_KEEPTOT_BOUNDS;
189                                 
190                                 v2d->tot.xmin= v2d->tot.ymin= 0.0f;
191                                 v2d->tot.xmax= (float)(winx - 1);
192                                 v2d->tot.ymax= (float)(winy - 1);
193                                 
194                                 v2d->cur= v2d->tot;
195                                 
196                                 /* scrollers - should we have these by default? */
197                                 // XXX for now, we don't override this, or set it either!
198                         }
199                                 break;
200                         
201                         /* 'list/channel view' - zoom, aspect ratio, and alignment restrictions are set here */
202                         case V2D_COMMONVIEW_LIST:
203                         {
204                                 /* zoom + aspect ratio are locked */
205                                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
206                                 v2d->minzoom= v2d->maxzoom= 1.0f;
207                                 
208                                 /* tot rect has strictly regulated placement, and must only occur in +/- quadrant */
209                                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
210                                 v2d->keeptot = V2D_KEEPTOT_STRICT;
211                                 tot_changed= 1;
212                                 
213                                 /* scroller settings are currently not set here... that is left for regions... */
214                         }
215                                 break;
216                                 
217                         /* 'stack view' - practically the same as list/channel view, except is located in the pos y half instead. 
218                          *      zoom, aspect ratio, and alignment restrictions are set here */
219                         case V2D_COMMONVIEW_STACK:
220                         {
221                                 /* zoom + aspect ratio are locked */
222                                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
223                                 v2d->minzoom= v2d->maxzoom= 1.0f;
224                                 
225                                 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
226                                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
227                                 v2d->keeptot = V2D_KEEPTOT_STRICT;
228                                 tot_changed= 1;
229                                 
230                                 /* scroller settings are currently not set here... that is left for regions... */
231                         }
232                                 break;
233                                 
234                         /* 'header' regions - zoom, aspect ratio, alignment, and panning restrictions are set here */
235                         case V2D_COMMONVIEW_HEADER:
236                         {
237                                 /* zoom + aspect ratio are locked */
238                                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
239                                 v2d->minzoom= v2d->maxzoom= 1.0f;
240                                 v2d->min[0]= v2d->max[0]= (float)(winx-1);
241                                 v2d->min[1]= v2d->max[1]= (float)(winy-1);
242                                 
243                                 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
244                                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
245                                 v2d->keeptot = V2D_KEEPTOT_STRICT;
246                                 tot_changed= 1;
247                                 
248                                 /* panning in y-axis is prohibited */
249                                 v2d->keepofs= V2D_LOCKOFS_Y;
250                                 
251                                 /* absolutely no scrollers allowed */
252                                 v2d->scroll= 0;
253                                 
254                                 /* pixel offsets need to be applied for smooth UI controls */
255                                 v2d->flag |= (V2D_PIXELOFS_X|V2D_PIXELOFS_Y);
256                         }
257                                 break;
258                         
259                         /* panels view, with horizontal/vertical align */
260                         case V2D_COMMONVIEW_PANELS_UI:
261                         {
262                                 float panelzoom= (style) ? style->panelzoom : 1.0f;
263                                 
264                                 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
265                                 v2d->keepzoom= (V2D_KEEPASPECT|V2D_LIMITZOOM|V2D_KEEPZOOM);
266                                 v2d->minzoom= 0.5f;
267                                 v2d->maxzoom= 2.0f;
268                                 //tot_changed= 1;
269                                 
270                                 v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
271                                 v2d->keeptot= V2D_KEEPTOT_BOUNDS;
272                                 
273                                 v2d->scroll |= (V2D_SCROLL_RIGHT|V2D_SCROLL_BOTTOM);
274                                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
275                                 v2d->scroll &= ~V2D_SCROLL_VERTICAL_HIDE;
276
277                                 v2d->tot.xmin= 0.0f;
278                                 v2d->tot.xmax= winx;
279                                 
280                                 v2d->tot.ymax= 0.0f;
281                                 v2d->tot.ymin= -winy;
282                                 
283                                 v2d->cur.xmin= 0.0f;
284                                 /* bad workaround for keeping zoom level with scrollers */
285                                 v2d->cur.xmax= (winx - V2D_SCROLL_WIDTH)*panelzoom;
286                                 
287                                 v2d->cur.ymax= 0.0f;
288                                 v2d->cur.ymin= (-winy)*panelzoom;
289                         }
290                                 break;
291                                 
292                                 /* other view types are completely defined using their own settings already */
293                         default:
294                                 /* we don't do anything here, as settings should be fine, but just make sure that rect */
295                                 break;  
296                 }
297         }
298         
299         /* store view size */
300         v2d->winx= winx;
301         v2d->winy= winy;
302         
303         /* set masks */
304         view2d_masks(v2d);
305         
306         /* set 'tot' rect before setting cur? */
307         if (tot_changed) 
308                 UI_view2d_totRect_set_resize(v2d, winx, winy, !init);
309         else
310                 UI_view2d_curRect_validate_resize(v2d, !init);
311 }
312
313 /* Ensure View2D rects remain in a viable configuration 
314  *      - cur is not allowed to be: larger than max, smaller than min, or outside of tot
315  */
316 // XXX pre2.5 -> this used to be called  test_view2d()
317 void UI_view2d_curRect_validate_resize(View2D *v2d, int resize)
318 {
319         float totwidth, totheight, curwidth, curheight, width, height;
320         float winx, winy;
321         rctf *cur, *tot;
322         
323         /* use mask as size of region that View2D resides in, as it takes into account scrollbars already  */
324         winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
325         winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
326         
327         /* get pointers to rcts for less typing */
328         cur= &v2d->cur;
329         tot= &v2d->tot;
330         
331         /* we must satisfy the following constraints (in decreasing order of importance):
332          *      - alignment restrictions are respected
333          *      - cur must not fall outside of tot
334          *      - axis locks (zoom and offset) must be maintained
335          *      - zoom must not be excessive (check either sizes or zoom values)
336          *      - aspect ratio should be respected (NOTE: this is quite closely realted to zoom too)
337          */
338         
339         /* Step 1: if keepzoom, adjust the sizes of the rects only
340          *      - firstly, we calculate the sizes of the rects
341          *      - curwidth and curheight are saved as reference... modify width and height values here
342          */
343         totwidth= tot->xmax - tot->xmin;
344         totheight= tot->ymax - tot->ymin;
345         curwidth= width= cur->xmax - cur->xmin;
346         curheight= height= cur->ymax - cur->ymin;
347         
348         /* if zoom is locked, size on the appropriate axis is reset to mask size */
349         if (v2d->keepzoom & V2D_LOCKZOOM_X)
350                 width= winx;
351         if (v2d->keepzoom & V2D_LOCKZOOM_Y)
352                 height= winy;
353                 
354         /* values used to divide, so make it safe 
355          * NOTE: width and height must use FLT_MIN instead of 1, otherwise it is impossible to
356          *              get enough resolution in Graph Editor for editing some curves
357          */
358         if(width < FLT_MIN) width= 1;
359         if(height < FLT_MIN) height= 1;
360         if(winx < 1) winx= 1;
361         if(winy < 1) winy= 1;
362         
363         /* V2D_LIMITZOOM indicates that zoom level should be preserved when the window size changes */
364         if (resize && (v2d->keepzoom & V2D_KEEPZOOM)) {
365                 float zoom, oldzoom;
366
367                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
368                         zoom= winx / width;
369                         oldzoom= v2d->oldwinx / curwidth;
370
371                         if(oldzoom != zoom)
372                                 width *= zoom/oldzoom;
373                 }
374
375                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
376                         zoom= winy / height;
377                         oldzoom= v2d->oldwiny / curheight;
378
379                         if(oldzoom != zoom)
380                                 height *= zoom/oldzoom;
381                 }
382         }
383         /* keepzoom (V2D_LIMITZOOM set), indicates that zoom level on each axis must not exceed limits 
384          * NOTE: in general, it is not expected that the lock-zoom will be used in conjunction with this
385          */
386         else if (v2d->keepzoom & V2D_LIMITZOOM) {
387                 float zoom, fac;
388                 
389                 /* check if excessive zoom on x-axis */
390                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
391                         zoom= winx / width;
392                         if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
393                                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
394                                 width *= fac;
395                         }
396                 }
397                 
398                 /* check if excessive zoom on y-axis */
399                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
400                         zoom= winy / height;
401                         if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
402                                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
403                                 height *= fac;
404                         }
405                 }
406         }
407         else {
408                 /* make sure sizes don't exceed that of the min/max sizes (even though we're not doing zoom clamping) */
409                 CLAMP(width, v2d->min[0], v2d->max[0]);
410                 CLAMP(height, v2d->min[1], v2d->max[1]);
411         }
412         
413         /* check if we should restore aspect ratio (if view size changed) */
414         if (v2d->keepzoom & V2D_KEEPASPECT) {
415                 short do_x=0, do_y=0, do_cur, do_win;
416                 float curRatio, winRatio;
417                 
418                 /* when a window edge changes, the aspect ratio can't be used to
419                  * find which is the best new 'cur' rect. thats why it stores 'old' 
420                  */
421                 if (winx != v2d->oldwinx) do_x= 1;
422                 if (winy != v2d->oldwiny) do_y= 1;
423                 
424                 curRatio= height / width;
425                 winRatio= winy / winx;
426                 
427                 /* both sizes change (area/region maximised)  */
428                 if (do_x == do_y) {
429                         if (do_x && do_y) {
430                                 /* here is 1,1 case, so all others must be 0,0 */
431                                 if (ABS(winx - v2d->oldwinx) > ABS(winy - v2d->oldwiny)) do_y= 0;
432                                 else do_x= 0;
433                         }
434                         else if (winRatio > 1.0f) do_x= 0; 
435                         else do_x= 1;
436                 }
437                 do_cur= do_x;
438                 do_win= do_y;
439                 
440                 if (do_cur) {
441                         if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winx != v2d->oldwinx)) {
442                                 /* special exception for Outliner (and later channel-lists):
443                                  *      - The view may be moved left to avoid contents being pushed out of view when view shrinks. 
444                                  *      - The keeptot code will make sure cur->xmin will not be less than tot->xmin (which cannot be allowed)
445                                  *      - width is not adjusted for changed ratios here...
446                                  */
447                                 if (winx < v2d->oldwinx) {
448                                         float temp = v2d->oldwinx - winx;
449                                         
450                                         cur->xmin -= temp;
451                                         cur->xmax -= temp;
452                                         
453                                         /* width does not get modified, as keepaspect here is just set to make 
454                                          * sure visible area adjusts to changing view shape! 
455                                          */
456                                 }
457                         }
458                         else {
459                                 /* portrait window: correct for x */
460                                 width= height / winRatio;
461                         }
462                 }
463                 else {
464                         if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winy != v2d->oldwiny)) {
465                                 /* special exception for Outliner (and later channel-lists):
466                                  *      - Currently, no actions need to be taken here...
467                                  */
468
469                                 if (winy < v2d->oldwiny) {
470                                         float temp = v2d->oldwiny - winy;
471                                         
472                                         cur->ymin += temp;
473                                         cur->ymax += temp;
474                                 }
475
476                         }
477                         else {
478                                 /* landscape window: correct for y */
479                                 height = width * winRatio;
480                         }
481                 }
482                 
483                 /* store region size for next time */
484                 v2d->oldwinx= (short)winx; 
485                 v2d->oldwiny= (short)winy;
486         }
487         
488         /* Step 2: apply new sizes to cur rect, but need to take into account alignment settings here... */
489         if ((width != curwidth) || (height != curheight)) {
490                 float temp, dh;
491                 
492                 /* resize from centerpoint, unless otherwise specified */
493                 if (width != curwidth) {
494                         if (v2d->keepofs & V2D_LOCKOFS_X) {
495                                 cur->xmax += width - (cur->xmax - cur->xmin);
496                         }
497                         else if (v2d->keepofs & V2D_KEEPOFS_X) {
498                                 if(v2d->align & V2D_ALIGN_NO_POS_X)
499                                         cur->xmin -= width - (cur->xmax - cur->xmin);
500                                 else
501                                         cur->xmax += width - (cur->xmax - cur->xmin);
502                         }
503                         else {
504                                 temp= (cur->xmax + cur->xmin) * 0.5f;
505                                 dh= width * 0.5f;
506                                 
507                                 cur->xmin = temp - dh;
508                                 cur->xmax = temp + dh;
509                         }
510                 }
511                 if (height != curheight) {
512                         if (v2d->keepofs & V2D_LOCKOFS_Y) {
513                                 cur->ymax += height - (cur->ymax - cur->ymin);
514                         }
515                         else if (v2d->keepofs & V2D_KEEPOFS_Y) {
516                                 if(v2d->align & V2D_ALIGN_NO_POS_Y)
517                                         cur->ymin -= height - (cur->ymax - cur->ymin);
518                                 else
519                                         cur->ymax += height - (cur->ymax - cur->ymin);
520                         }
521                         else {
522                                 temp= (cur->ymax + cur->ymin) * 0.5f;
523                                 dh= height * 0.5f;
524                                 
525                                 cur->ymin = temp - dh;
526                                 cur->ymax = temp + dh;
527                         }
528                 }
529         }
530         
531         /* Step 3: adjust so that it doesn't fall outside of bounds of 'tot' */
532         if (v2d->keeptot) {
533                 float temp, diff;
534                 
535                 /* recalculate extents of cur */
536                 curwidth= cur->xmax - cur->xmin;
537                 curheight= cur->ymax - cur->ymin;
538                 
539                 /* width */
540                 if ( (curwidth > totwidth) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_X|V2D_LIMITZOOM)) ) {
541                         /* if zoom doesn't have to be maintained, just clamp edges */
542                         if (cur->xmin < tot->xmin) cur->xmin= tot->xmin;
543                         if (cur->xmax > tot->xmax) cur->xmax= tot->xmax;
544                 }
545                 else if (v2d->keeptot == V2D_KEEPTOT_STRICT) {
546                         /* This is an exception for the outliner (and later channel-lists, headers) 
547                          *      - must clamp within tot rect (absolutely no excuses)
548                          *      --> therefore, cur->xmin must not be less than tot->xmin
549                          */
550                         if (cur->xmin < tot->xmin) {
551                                 /* move cur across so that it sits at minimum of tot */
552                                 temp= tot->xmin - cur->xmin;
553                                 
554                                 cur->xmin += temp;
555                                 cur->xmax += temp;
556                         }
557                         else if (cur->xmax > tot->xmax) {
558                                 /* - only offset by difference of cur-xmax and tot-xmax if that would not move 
559                                  *      cur-xmin to lie past tot-xmin
560                                  * - otherwise, simply shift to tot-xmin???
561                                  */
562                                 temp= cur->xmax - tot->xmax;
563                                 
564                                 if ((cur->xmin - temp) < tot->xmin) {
565                                         /* only offset by difference from cur-min and tot-min */
566                                         temp= cur->xmin - tot->xmin;
567                                         
568                                         cur->xmin -= temp;
569                                         cur->xmax -= temp;
570                                 }
571                                 else {
572                                         cur->xmin -= temp;
573                                         cur->xmax -= temp;
574                                 }
575                         }
576                 }
577                 else {
578                         /* This here occurs when:
579                          *      - width too big, but maintaining zoom (i.e. widths cannot be changed)
580                          *      - width is OK, but need to check if outside of boundaries
581                          * 
582                          * So, resolution is to just shift view by the gap between the extremities.
583                          * We favour moving the 'minimum' across, as that's origin for most things
584                          * (XXX - in the past, max was favoured... if there are bugs, swap!)
585                          */
586                         if ((cur->xmin < tot->xmin) && (cur->xmax > tot->xmax)) {
587                                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
588                                 temp= (tot->xmax + tot->xmin) * 0.5f;
589                                 diff= curheight * 0.5f;
590                                 
591                                 cur->xmin= temp - diff;
592                                 cur->xmax= temp + diff;
593                         }
594                         else if (cur->xmin < tot->xmin) {
595                                 /* move cur across so that it sits at minimum of tot */
596                                 temp= tot->xmin - cur->xmin;
597                                 
598                                 cur->xmin += temp;
599                                 cur->xmax += temp;
600                         }
601                         else if (cur->xmax > tot->xmax) {
602                                 /* - only offset by difference of cur-xmax and tot-xmax if that would not move 
603                                  *      cur-xmin to lie past tot-xmin
604                                  * - otherwise, simply shift to tot-xmin???
605                                  */
606                                 temp= cur->xmax - tot->xmax;
607                                 
608                                 if ((cur->xmin - temp) < tot->xmin) {
609                                         /* only offset by difference from cur-min and tot-min */
610                                         temp= cur->xmin - tot->xmin;
611                                         
612                                         cur->xmin -= temp;
613                                         cur->xmax -= temp;
614                                 }
615                                 else {
616                                         cur->xmin -= temp;
617                                         cur->xmax -= temp;
618                                 }
619                         }
620                 }
621                 
622                 /* height */
623                 if ( (curheight > totheight) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_Y|V2D_LIMITZOOM)) ) {
624                         /* if zoom doesn't have to be maintained, just clamp edges */
625                         if (cur->ymin < tot->ymin) cur->ymin= tot->ymin;
626                         if (cur->ymax > tot->ymax) cur->ymax= tot->ymax;
627                 }
628                 else {
629                         /* This here occurs when:
630                          *      - height too big, but maintaining zoom (i.e. heights cannot be changed)
631                          *      - height is OK, but need to check if outside of boundaries
632                          * 
633                          * So, resolution is to just shift view by the gap between the extremities.
634                          * We favour moving the 'minimum' across, as that's origin for most things
635                          */
636                         if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
637                                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
638                                 temp= (tot->ymax + tot->ymin) * 0.5f;
639                                 diff= curheight * 0.5f;
640                                 
641                                 cur->ymin= temp - diff;
642                                 cur->ymax= temp + diff;
643                         }
644                         else if (cur->ymin < tot->ymin) {
645                                 /* there's still space remaining, so shift up */
646                                 temp= tot->ymin - cur->ymin;
647                                 
648                                 cur->ymin += temp;
649                                 cur->ymax += temp;
650                         }
651                         else if (cur->ymax > tot->ymax) {
652                                 /* there's still space remaining, so shift down */
653                                 temp= cur->ymax - tot->ymax;
654                                 
655                                 cur->ymin -= temp;
656                                 cur->ymax -= temp;
657                         }
658                 }
659         }
660         
661         /* Step 4: Make sure alignment restrictions are respected */
662         if (v2d->align) {
663                 /* If alignment flags are set (but keeptot is not), they must still be respected, as although
664                  * they don't specify any particular bounds to stay within, they do define ranges which are 
665                  * invalid.
666                  *
667                  * Here, we only check to make sure that on each axis, the 'cur' rect doesn't stray into these 
668                  * invalid zones, otherwise we offset.
669                  */
670                 
671                 /* handle width - posx and negx flags are mutually exclusive, so watch out */
672                 if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
673                         /* width is in negative-x half */
674                         if (v2d->cur.xmax > 0) {
675                                 v2d->cur.xmin -= v2d->cur.xmax;
676                                 v2d->cur.xmax= 0.0f;
677                         }
678                 }
679                 else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
680                         /* width is in positive-x half */
681                         if (v2d->cur.xmin < 0) {
682                                 v2d->cur.xmax -= v2d->cur.xmin;
683                                 v2d->cur.xmin = 0.0f;
684                         }
685                 }
686                 
687                 /* handle height - posx and negx flags are mutually exclusive, so watch out */
688                 if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
689                         /* height is in negative-y half */
690                         if (v2d->cur.ymax > 0) {
691                                 v2d->cur.ymin -= v2d->cur.ymax;
692                                 v2d->cur.ymax = 0.0f;
693                         }
694                 }
695                 else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
696                         /* height is in positive-y half */
697                         if (v2d->cur.ymin < 0) {
698                                 v2d->cur.ymax -= v2d->cur.ymin;
699                                 v2d->cur.ymin = 0.0f;
700                         }
701                 }
702         }
703         
704         /* set masks */
705         view2d_masks(v2d);
706 }
707
708 void UI_view2d_curRect_validate(View2D *v2d)
709 {
710         UI_view2d_curRect_validate_resize(v2d, 0);
711 }
712
713 /* ------------------ */
714
715 /* Called by menus to activate it, or by view2d operators to make sure 'related' views stay in synchrony */
716 void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
717 {
718         ScrArea *sa;
719         ARegion *ar;
720         
721         /* don't continue if no view syncing to be done */
722         if ((v2dcur->flag & (V2D_VIEWSYNC_SCREEN_TIME|V2D_VIEWSYNC_AREA_VERTICAL)) == 0)
723                 return;
724                 
725         /* check if doing within area syncing (i.e. channels/vertical) */
726         if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) {
727                 for (ar= area->regionbase.first; ar; ar= ar->next) {
728                         /* don't operate on self */
729                         if (v2dcur != &ar->v2d) {
730                                 /* only if view has vertical locks enabled */
731                                 if (ar->v2d.flag & V2D_VIEWSYNC_AREA_VERTICAL) {
732                                         if (flag == V2D_LOCK_COPY) {
733                                                 /* other views with locks on must copy active */
734                                                 ar->v2d.cur.ymin= v2dcur->cur.ymin;
735                                                 ar->v2d.cur.ymax= v2dcur->cur.ymax;
736                                         }
737                                         else { /* V2D_LOCK_SET */
738                                                 /* active must copy others */
739                                                 v2dcur->cur.ymin= ar->v2d.cur.ymin;
740                                                 v2dcur->cur.ymax= ar->v2d.cur.ymax;
741                                         }
742                                         
743                                         /* region possibly changed, so refresh */
744                                         ED_region_tag_redraw(ar);
745                                 }
746                         }
747                 }
748         }
749         
750         /* check if doing whole screen syncing (i.e. time/horizontal) */
751         if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
752                 for (sa= screen->areabase.first; sa; sa= sa->next) {
753                         for (ar= sa->regionbase.first; ar; ar= ar->next) {
754                                 /* don't operate on self */
755                                 if (v2dcur != &ar->v2d) {
756                                         /* only if view has horizontal locks enabled */
757                                         if (ar->v2d.flag & V2D_VIEWSYNC_SCREEN_TIME) {
758                                                 if (flag == V2D_LOCK_COPY) {
759                                                         /* other views with locks on must copy active */
760                                                         ar->v2d.cur.xmin= v2dcur->cur.xmin;
761                                                         ar->v2d.cur.xmax= v2dcur->cur.xmax;
762                                                 }
763                                                 else { /* V2D_LOCK_SET */
764                                                         /* active must copy others */
765                                                         v2dcur->cur.xmin= ar->v2d.cur.xmin;
766                                                         v2dcur->cur.xmax= ar->v2d.cur.xmax;
767                                                 }
768                                                 
769                                                 /* region possibly changed, so refresh */
770                                                 ED_region_tag_redraw(ar);
771                                         }
772                                 }
773                         }
774                 }
775         }
776 }
777
778
779 /* Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot) 
780  * This does not take into account if zooming the view on an axis will improve the view (if allowed)
781  */
782 void UI_view2d_curRect_reset (View2D *v2d)
783 {
784         float width, height;
785         
786         /* assume width and height of 'cur' rect by default, should be same size as mask */
787         width= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
788         height= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
789         
790         /* handle width - posx and negx flags are mutually exclusive, so watch out */
791         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
792                 /* width is in negative-x half */
793                 v2d->cur.xmin= (float)-width;
794                 v2d->cur.xmax= 0.0f;
795         }
796         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
797                 /* width is in positive-x half */
798                 v2d->cur.xmin= 0.0f;
799                 v2d->cur.xmax= (float)width;
800         }
801         else {
802                 /* width is centered around x==0 */
803                 const float dx= (float)width / 2.0f;
804                 
805                 v2d->cur.xmin= -dx;
806                 v2d->cur.xmax= dx;
807         }
808         
809         /* handle height - posx and negx flags are mutually exclusive, so watch out */
810         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
811                 /* height is in negative-y half */
812                 v2d->cur.ymin= (float)-height;
813                 v2d->cur.ymax= 0.0f;
814         }
815         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
816                 /* height is in positive-y half */
817                 v2d->cur.ymin= 0.0f;
818                 v2d->cur.ymax= (float)height;
819         }
820         else {
821                 /* height is centered around y==0 */
822                 const float dy= (float)height / 2.0f;
823                 
824                 v2d->cur.ymin= -dy;
825                 v2d->cur.ymax= dy;
826         }
827 }
828
829 /* ------------------ */
830
831 /* Change the size of the maximum viewable area (i.e. 'tot' rect) */
832 void UI_view2d_totRect_set_resize (View2D *v2d, int width, int height, int resize)
833 {
834         int scroll= view2d_scroll_mapped(v2d->scroll);
835         
836         /* don't do anything if either value is 0 */
837         width= abs(width);
838         height= abs(height);
839         
840         /* hrumf! */
841         /* XXX: there are work arounds for this in the panel and file browse code. */
842         if(scroll & V2D_SCROLL_HORIZONTAL) 
843                 width -= V2D_SCROLL_WIDTH;
844         if(scroll & V2D_SCROLL_VERTICAL) 
845                 height -= V2D_SCROLL_HEIGHT;
846         
847         if (ELEM3(0, v2d, width, height)) {
848                 if (G.f & G_DEBUG)
849                         printf("Error: View2D totRect set exiting: v2d=%p width=%d height=%d \n", v2d, width, height); // XXX temp debug info
850                 return;
851         }
852         
853         /* handle width - posx and negx flags are mutually exclusive, so watch out */
854         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
855                 /* width is in negative-x half */
856                 v2d->tot.xmin= (float)-width;
857                 v2d->tot.xmax= 0.0f;
858         }
859         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
860                 /* width is in positive-x half */
861                 v2d->tot.xmin= 0.0f;
862                 v2d->tot.xmax= (float)width;
863         }
864         else {
865                 /* width is centered around x==0 */
866                 const float dx= (float)width / 2.0f;
867                 
868                 v2d->tot.xmin= -dx;
869                 v2d->tot.xmax= dx;
870         }
871         
872         /* handle height - posx and negx flags are mutually exclusive, so watch out */
873         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
874                 /* height is in negative-y half */
875                 v2d->tot.ymin= (float)-height;
876                 v2d->tot.ymax= 0.0f;
877         }
878         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
879                 /* height is in positive-y half */
880                 v2d->tot.ymin= 0.0f;
881                 v2d->tot.ymax= (float)height;
882         }
883         else {
884                 /* height is centered around y==0 */
885                 const float dy= (float)height / 2.0f;
886                 
887                 v2d->tot.ymin= -dy;
888                 v2d->tot.ymax= dy;
889         }
890         
891         /* make sure that 'cur' rect is in a valid state as a result of these changes */
892         UI_view2d_curRect_validate_resize(v2d, resize);
893 }
894
895 void UI_view2d_totRect_set(View2D *v2d, int width, int height)
896 {
897         UI_view2d_totRect_set_resize(v2d, width, height, 0);
898 }
899
900 int UI_view2d_tab_set(View2D *v2d, int tab)
901 {
902         float default_offset[2]= {0.0f, 0.0f};
903         float *offset, *new_offset;
904         int changed= 0;
905
906         /* if tab changed, change offset */
907         if(tab != v2d->tab_cur && v2d->tab_offset) {
908                 if(tab < v2d->tab_num)
909                         offset= &v2d->tab_offset[tab*2];
910                 else
911                         offset= default_offset;
912
913                 v2d->cur.xmax += offset[0] - v2d->cur.xmin;
914                 v2d->cur.xmin= offset[0];
915
916                 v2d->cur.ymin += offset[1] - v2d->cur.ymax;
917                 v2d->cur.ymax= offset[1];
918
919                 /* validation should happen in subsequent totRect_set */
920
921                 changed= 1;
922         }
923
924         /* resize array if needed */
925         if(tab >= v2d->tab_num) {
926                 new_offset= MEM_callocN(sizeof(float)*(tab+1)*2, "view2d tab offset");
927
928                 if(v2d->tab_offset) {
929                         memcpy(new_offset, v2d->tab_offset, sizeof(float)*v2d->tab_num*2);
930                         MEM_freeN(v2d->tab_offset);
931                 }
932
933                 v2d->tab_offset= new_offset;
934                 v2d->tab_num= tab+1;
935         }
936
937         /* set current tab and offset */
938         v2d->tab_cur= tab;
939         v2d->tab_offset[2*tab+0]= v2d->cur.xmin;
940         v2d->tab_offset[2*tab+1]= v2d->cur.ymax;
941
942         return changed;
943 }
944
945 /* *********************************************************************** */
946 /* View Matrix Setup */
947
948 /* mapping function to ensure 'cur' draws extended over the area where sliders are */
949 static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
950 {
951         *curmasked= v2d->cur;
952         
953         if (view2d_scroll_mapped(v2d->scroll)) {
954                 float dx= (v2d->cur.xmax-v2d->cur.xmin)/((float)(v2d->mask.xmax-v2d->mask.xmin+1));
955                 float dy= (v2d->cur.ymax-v2d->cur.ymin)/((float)(v2d->mask.ymax-v2d->mask.ymin+1));
956                 
957                 if (v2d->mask.xmin != 0)
958                         curmasked->xmin -= dx*(float)v2d->mask.xmin;
959                 if (v2d->mask.xmax+1 != v2d->winx)
960                         curmasked->xmax += dx*(float)(v2d->winx - v2d->mask.xmax-1);
961                 
962                 if (v2d->mask.ymin != 0)
963                         curmasked->ymin -= dy*(float)v2d->mask.ymin;
964                 if (v2d->mask.ymax+1 != v2d->winy)
965                         curmasked->ymax += dy*(float)(v2d->winy - v2d->mask.ymax-1);
966                 
967         }
968 }
969
970 /* Set view matrices to use 'cur' rect as viewing frame for View2D drawing */
971 void UI_view2d_view_ortho(const bContext *C, View2D *v2d)
972 {
973         rctf curmasked;
974         float xofs, yofs;
975         
976         /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing, 
977          * but only applied where requsted
978          */
979         /* XXX ton: fix this! */
980         xofs= 0.0; // (v2d->flag & V2D_PIXELOFS_X) ? 0.375f : 0.0f;
981         yofs= 0.0; // (v2d->flag & V2D_PIXELOFS_Y) ? 0.375f : 0.0f;
982
983         /* XXX brecht: instead of zero at least use a tiny offset, otherwise
984          * pixel rounding is effectively random due to float inaccuracy */
985         xofs= 0.001f*(v2d->cur.xmax - v2d->cur.xmin)/(v2d->mask.xmax - v2d->mask.xmin);
986         yofs= 0.001f*(v2d->cur.ymax - v2d->cur.ymin)/(v2d->mask.ymax - v2d->mask.ymin);
987         
988         /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
989         view2d_map_cur_using_mask(v2d, &curmasked);
990         
991         /* set matrix on all appropriate axes */
992         wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
993         
994         /* XXX is this necessary? */
995         glLoadIdentity();
996 }
997
998 /* Set view matrices to only use one axis of 'cur' only
999  *      - xaxis         = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x)
1000  */
1001 void UI_view2d_view_orthoSpecial(const bContext *C, View2D *v2d, short xaxis)
1002 {
1003         ARegion *ar= CTX_wm_region(C);
1004         rctf curmasked;
1005         float xofs, yofs;
1006         
1007         /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing, 
1008          * but only applied where requsted
1009          */
1010         /* XXX temp (ton) */
1011         xofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_X) ? 0.375f : 0.0f;
1012         yofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_Y) ? 0.375f : 0.0f;
1013         
1014         /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
1015         view2d_map_cur_using_mask(v2d, &curmasked);
1016         
1017         /* only set matrix with 'cur' coordinates on relevant axes */
1018         if (xaxis)
1019                 wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, -yofs, ar->winy-yofs);
1020         else
1021                 wmOrtho2(-xofs, ar->winx-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
1022                 
1023         /* XXX is this necessary? */
1024         glLoadIdentity();
1025
1026
1027
1028 /* Restore view matrices after drawing */
1029 void UI_view2d_view_restore(const bContext *C)
1030 {
1031         ARegion *ar= CTX_wm_region(C);
1032         int width= ar->winrct.xmax-ar->winrct.xmin+1;
1033         int height= ar->winrct.ymax-ar->winrct.ymin+1;
1034         
1035         wmOrtho2(0.0f, (float)width, 0.0f, (float)height);
1036         glLoadIdentity();
1037         
1038         //      ED_region_pixelspace(CTX_wm_region(C));
1039 }
1040
1041 /* *********************************************************************** */
1042 /* Gridlines */
1043
1044 /* View2DGrid is typedef'd in UI_view2d.h */
1045 struct View2DGrid {
1046         float dx, dy;                   /* stepsize (in pixels) between gridlines */
1047         float startx, starty;   /* initial coordinates to start drawing grid from */
1048         int powerx, powery;             /* step as power of 10 */
1049 };
1050
1051 /* --------------- */
1052
1053 /* try to write step as a power of 10 */
1054 static void step_to_grid(float *step, int *power, int unit)
1055 {
1056         const float loga= (float)log10(*step);
1057         float rem;
1058         
1059         *power= (int)(loga);
1060         
1061         rem= loga - (*power);
1062         rem= (float)pow(10.0, rem);
1063         
1064         if (loga < 0.0f) {
1065                 if (rem < 0.2f) rem= 0.2f;
1066                 else if(rem < 0.5f) rem= 0.5f;
1067                 else rem= 1.0f;
1068                 
1069                 *step= rem * (float)pow(10.0, (*power));
1070                 
1071                 /* for frames, we want 1.0 frame intervals only */
1072                 if (unit == V2D_UNIT_FRAMES) {
1073                         rem = 1.0f;
1074                         *step = 1.0f;
1075                 }
1076                 
1077                 /* prevents printing 1.0 2.0 3.0 etc */
1078                 if (rem == 1.0f) (*power)++;    
1079         }
1080         else {
1081                 if (rem < 2.0f) rem= 2.0f;
1082                 else if(rem < 5.0f) rem= 5.0f;
1083                 else rem= 10.0f;
1084                 
1085                 *step= rem * (float)pow(10.0, (*power));
1086                 
1087                 (*power)++;
1088                 /* prevents printing 1.0, 2.0, 3.0, etc. */
1089                 if (rem == 10.0f) (*power)++;   
1090         }
1091 }
1092
1093 /* Intialise settings necessary for drawing gridlines in a 2d-view 
1094  *      - Currently, will return pointer to View2DGrid struct that needs to 
1095  *        be freed with UI_view2d_grid_free()
1096  *      - Is used for scrollbar drawing too (for units drawing)
1097  *      - Units + clamping args will be checked, to make sure they are valid values that can be used
1098  *        so it is very possible that we won't return grid at all!
1099  *      
1100  *      - xunits,yunits = V2D_UNIT_*  grid steps in seconds or frames 
1101  *      - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals
1102  *      - winx                  = width of region we're drawing to
1103  *      - winy                  = height of region we're drawing into
1104  */
1105 View2DGrid *UI_view2d_grid_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp, int winx, int winy)
1106 {
1107         Scene *scene= CTX_data_scene(C);
1108         View2DGrid *grid;
1109         float space, pixels, seconddiv;
1110         int secondgrid;
1111         
1112         /* check that there are at least some workable args */
1113         if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) && ELEM(V2D_ARG_DUMMY, yunits, yclamp))
1114                 return NULL;
1115         
1116         /* grid here is allocated... */
1117         grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
1118         
1119         /* rule: gridstep is minimal GRIDSTEP pixels */
1120         if (xunits == V2D_UNIT_SECONDS) {
1121                 secondgrid= 1;
1122                 seconddiv= (float)(0.01 * FPS);
1123         }
1124         else {
1125                 secondgrid= 0;
1126                 seconddiv= 1.0f;
1127         }
1128         
1129         /* calculate x-axis grid scale (only if both args are valid) */
1130         if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
1131                 space= v2d->cur.xmax - v2d->cur.xmin;
1132                 pixels= (float)(v2d->mask.xmax - v2d->mask.xmin);
1133                 
1134                 grid->dx= (U.v2d_min_gridsize * space) / (seconddiv * pixels);
1135                 step_to_grid(&grid->dx, &grid->powerx, xunits);
1136                 grid->dx *= seconddiv;
1137                 
1138                 if (xclamp == V2D_GRID_CLAMP) {
1139                         if (grid->dx < 0.1f) grid->dx= 0.1f;
1140                         grid->powerx-= 2;
1141                         if (grid->powerx < -2) grid->powerx= -2;
1142                 }
1143         }
1144         
1145         /* calculate y-axis grid scale (only if both args are valid) */
1146         if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
1147                 space= v2d->cur.ymax - v2d->cur.ymin;
1148                 pixels= (float)winy;
1149                 
1150                 grid->dy= U.v2d_min_gridsize * space / pixels;
1151                 step_to_grid(&grid->dy, &grid->powery, yunits);
1152                 
1153                 if (yclamp == V2D_GRID_CLAMP) {
1154                         if (grid->dy < 1.0f) grid->dy= 1.0f;
1155                         if (grid->powery < 1) grid->powery= 1;
1156                 }
1157         }
1158         
1159         /* calculate start position */
1160         if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
1161                 grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - (float)fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
1162                 if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
1163         }
1164         else
1165                 grid->startx= v2d->cur.xmin;
1166                 
1167         if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
1168                 grid->starty= (v2d->cur.ymin - (float)fmod(v2d->cur.ymin, grid->dy));
1169                 if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
1170         }
1171         else
1172                 grid->starty= v2d->cur.ymin;
1173         
1174         return grid;
1175 }
1176
1177 /* Draw gridlines in the given 2d-region */
1178 void UI_view2d_grid_draw(const bContext *C, View2D *v2d, View2DGrid *grid, int flag)
1179 {
1180         float vec1[2], vec2[2];
1181         int a, step;
1182         
1183         /* check for grid first, as it may not exist */
1184         if (grid == NULL)
1185                 return;
1186         
1187         /* vertical lines */
1188         if (flag & V2D_VERTICAL_LINES) {
1189                 /* initialise initial settings */
1190                 vec1[0]= vec2[0]= grid->startx;
1191                 vec1[1]= grid->starty;
1192                 vec2[1]= v2d->cur.ymax;
1193                 
1194                 /* minor gridlines */
1195                 step= (v2d->mask.xmax - v2d->mask.xmin + 1) / U.v2d_min_gridsize;
1196                 UI_ThemeColor(TH_GRID);
1197                 
1198                 for (a=0; a<step; a++) {
1199                         glBegin(GL_LINE_STRIP);
1200                                 glVertex2fv(vec1); 
1201                                 glVertex2fv(vec2);
1202                         glEnd();
1203                         
1204                         vec2[0]= vec1[0]+= grid->dx;
1205                 }
1206                 
1207                 /* major gridlines */
1208                 vec2[0]= vec1[0]-= 0.5f*grid->dx;
1209                 UI_ThemeColorShade(TH_GRID, 16);
1210                 
1211                 step++;
1212                 for (a=0; a<=step; a++) {
1213                         glBegin(GL_LINE_STRIP);
1214                                 glVertex2fv(vec1); 
1215                                 glVertex2fv(vec2);
1216                         glEnd();
1217                         
1218                         vec2[0]= vec1[0]-= grid->dx;
1219                 }
1220         }
1221         
1222         /* horizontal lines */
1223         if (flag & V2D_HORIZONTAL_LINES) {
1224                 /* only major gridlines */
1225                 vec1[1]= vec2[1]= grid->starty;
1226                 vec1[0]= grid->startx;
1227                 vec2[0]= v2d->cur.xmax;
1228                 
1229                 step= (v2d->mask.ymax - v2d->mask.ymin + 1) / U.v2d_min_gridsize;
1230                 
1231                 UI_ThemeColor(TH_GRID);
1232                 for (a=0; a<=step; a++) {
1233                         glBegin(GL_LINE_STRIP);
1234                                 glVertex2fv(vec1); 
1235                                 glVertex2fv(vec2);
1236                         glEnd();
1237                         
1238                         vec2[1]= vec1[1]+= grid->dy;
1239                 }
1240                 
1241                 /* fine grid lines */
1242                 vec2[1]= vec1[1]-= 0.5f*grid->dy;
1243                 step++;
1244                 
1245                 if (flag & V2D_HORIZONTAL_FINELINES) { 
1246                         UI_ThemeColorShade(TH_GRID, 16);
1247                         for (a=0; a<step; a++) {
1248                                 glBegin(GL_LINE_STRIP);
1249                                         glVertex2fv(vec1); 
1250                                         glVertex2fv(vec2);
1251                                 glEnd();
1252                                 
1253                                 vec2[1]= vec1[1]-= grid->dy;
1254                         }
1255                 }
1256         }
1257         
1258         /* Axes are drawn as darker lines */
1259         UI_ThemeColorShade(TH_GRID, -50);
1260         
1261         /* horizontal axis */
1262         if (flag & V2D_HORIZONTAL_AXIS) {
1263                 vec1[0]= v2d->cur.xmin;
1264                 vec2[0]= v2d->cur.xmax;
1265                 vec1[1]= vec2[1]= 0.0f;
1266                 
1267                 glBegin(GL_LINE_STRIP);
1268                         glVertex2fv(vec1);
1269                         glVertex2fv(vec2);
1270                 glEnd();
1271         }
1272         
1273         /* vertical axis */
1274         if (flag & V2D_VERTICAL_AXIS) {
1275                 vec1[1]= v2d->cur.ymin;
1276                 vec2[1]= v2d->cur.ymax;
1277                 vec1[0]= vec2[0]= 0.0f;
1278                 
1279                 glBegin(GL_LINE_STRIP);
1280                         glVertex2fv(vec1); 
1281                         glVertex2fv(vec2);
1282                 glEnd();
1283         }
1284 }
1285
1286 /* Draw a constant grid in given 2d-region */
1287 void UI_view2d_constant_grid_draw(const bContext *C, View2D *v2d)
1288 {
1289         float start, step= 25.0f;
1290
1291         UI_ThemeColorShade(TH_BACK, -10);
1292         
1293         start= v2d->cur.xmin - (float)fmod(v2d->cur.xmin, step);
1294         
1295         glBegin(GL_LINES);
1296         for(; start<v2d->cur.xmax; start+=step) {
1297                 glVertex2f(start, v2d->cur.ymin);
1298                 glVertex2f(start, v2d->cur.ymax);
1299         }
1300
1301         start= v2d->cur.ymin - (float)fmod(v2d->cur.ymin, step);
1302         for(; start<v2d->cur.ymax; start+=step) {
1303                 glVertex2f(v2d->cur.xmin, start);
1304                 glVertex2f(v2d->cur.xmax, start);
1305         }
1306         
1307         /* X and Y axis */
1308         UI_ThemeColorShade(TH_BACK, -18);
1309         glVertex2f(0.0f, v2d->cur.ymin);
1310         glVertex2f(0.0f, v2d->cur.ymax);
1311         glVertex2f(v2d->cur.xmin, 0.0f);
1312         glVertex2f(v2d->cur.xmax, 0.0f);
1313         
1314         glEnd();
1315 }
1316
1317 /* free temporary memory used for drawing grid */
1318 void UI_view2d_grid_free(View2DGrid *grid)
1319 {
1320         /* only free if there's a grid */
1321         if (grid)
1322                 MEM_freeN(grid);
1323 }
1324
1325 /* *********************************************************************** */
1326 /* Scrollers */
1327
1328 /* View2DScrollers is typedef'd in UI_view2d.h 
1329  * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. 
1330  *                 For now, we don't need to have a separate (internal) header for structs like this...
1331  */
1332 struct View2DScrollers {
1333                 /* focus bubbles */
1334         int vert_min, vert_max; /* vertical scrollbar */
1335         int hor_min, hor_max;   /* horizontal scrollbar */
1336         
1337         rcti hor, vert;                 /* exact size of slider backdrop */
1338         int horfull, vertfull;  /* set if sliders are full, we don't draw them */
1339         
1340         /* scales */
1341         View2DGrid *grid;               /* grid for coordinate drawing */
1342         short xunits, xclamp;   /* units and clamping options for x-axis */
1343         short yunits, yclamp;   /* units and clamping options for y-axis */
1344 };
1345
1346 /* Calculate relevant scroller properties */
1347 View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
1348 {
1349         View2DScrollers *scrollers;
1350         rcti vert, hor;
1351         float fac1, fac2, totsize, scrollsize;
1352         int scroll= view2d_scroll_mapped(v2d->scroll);
1353         
1354         /* scrollers is allocated here... */
1355         scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
1356         
1357         vert= v2d->vert;
1358         hor= v2d->hor;
1359         
1360         /* slider rects need to be smaller than region */
1361         hor.xmin+=4;
1362         hor.xmax-=4;
1363         if (scroll & V2D_SCROLL_BOTTOM)
1364                 hor.ymin+=4;
1365         else
1366                 hor.ymax-=4;
1367         
1368         if (scroll & V2D_SCROLL_LEFT)
1369                 vert.xmin+=4;
1370         else
1371                 vert.xmax-=4;
1372         vert.ymin+=4;
1373         vert.ymax-=4;
1374         
1375         CLAMP(vert.ymin, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
1376         CLAMP(hor.xmin, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
1377         
1378         /* store in scrollers, used for drawing */
1379         scrollers->vert= vert;
1380         scrollers->hor= hor;
1381         
1382         /* scroller 'buttons':
1383          *      - These should always remain within the visible region of the scrollbar
1384          *      - They represent the region of 'tot' that is visible in 'cur'
1385          */
1386         
1387         /* horizontal scrollers */
1388         if (scroll & V2D_SCROLL_HORIZONTAL) {
1389                 /* scroller 'button' extents */
1390                 totsize= v2d->tot.xmax - v2d->tot.xmin;
1391                 scrollsize= (float)(hor.xmax - hor.xmin);
1392                 
1393                 fac1= (v2d->cur.xmin - v2d->tot.xmin) / totsize;
1394                 if(fac1<=0.0f)
1395                         scrollers->hor_min= hor.xmin;
1396                 else
1397                         scrollers->hor_min= (int)(hor.xmin + (fac1 * scrollsize));
1398                 
1399                 fac2= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
1400                 if(fac2>=1.0f)
1401                         scrollers->hor_max= hor.xmax;
1402                 else
1403                         scrollers->hor_max= (int)(hor.xmin + (fac2 * scrollsize));
1404                 
1405                 /* prevent inverted sliders */
1406                 if (scrollers->hor_min > scrollers->hor_max) 
1407                         scrollers->hor_min= scrollers->hor_max;
1408                 /* prevent sliders from being too small, and disappearing */
1409                 if ((scrollers->hor_max - scrollers->hor_min) < V2D_SCROLLER_HANDLE_SIZE) {
1410                         scrollers->hor_max= scrollers->hor_min + V2D_SCROLLER_HANDLE_SIZE;
1411
1412                         CLAMP(scrollers->hor_max, hor.xmin+V2D_SCROLLER_HANDLE_SIZE, hor.xmax);
1413                         CLAMP(scrollers->hor_min, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
1414                 }
1415                 
1416                 /* check whether sliders can disappear due to the full-range being used */
1417                 if(v2d->keeptot) {
1418                         if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
1419                                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_FULLR;
1420                                 scrollers->horfull= 1;
1421                         }
1422                         else    
1423                                 v2d->scroll &= ~V2D_SCROLL_HORIZONTAL_FULLR;
1424                 }
1425         }
1426         
1427         /* vertical scrollers */
1428         if (scroll & V2D_SCROLL_VERTICAL) {
1429                 /* scroller 'button' extents */
1430                 totsize= v2d->tot.ymax - v2d->tot.ymin;
1431                 scrollsize= (float)(vert.ymax - vert.ymin);
1432                 
1433                 fac1= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
1434                 if(fac1<=0.0f)
1435                         scrollers->vert_min= vert.ymin;
1436                 else
1437                         scrollers->vert_min= (int)(vert.ymin + (fac1 * scrollsize));
1438                 
1439                 fac2= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
1440                 if(fac2>=1.0f)
1441                         scrollers->vert_max= vert.ymax;
1442                 else
1443                         scrollers->vert_max= (int)(vert.ymin + (fac2 * scrollsize));
1444                 
1445                 /* prevent inverted sliders */
1446                 if (scrollers->vert_min > scrollers->vert_max) 
1447                         scrollers->vert_min= scrollers->vert_max;
1448                 /* prevent sliders from being too small, and disappearing */
1449                 if ((scrollers->vert_max - scrollers->vert_min) < V2D_SCROLLER_HANDLE_SIZE) {
1450                         
1451                         scrollers->vert_max= scrollers->vert_min + V2D_SCROLLER_HANDLE_SIZE;
1452                         
1453                         CLAMP(scrollers->vert_max, vert.ymin+V2D_SCROLLER_HANDLE_SIZE, vert.ymax);
1454                         CLAMP(scrollers->vert_min, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
1455                 }
1456
1457                 /* check whether sliders can disappear due to the full-range being used */
1458                 if(v2d->keeptot) {
1459                         if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
1460                                 v2d->scroll |= V2D_SCROLL_VERTICAL_FULLR;
1461                                 scrollers->vertfull= 1;
1462                         }
1463                         else    
1464                                 v2d->scroll &= ~V2D_SCROLL_VERTICAL_FULLR;
1465                 }
1466         }
1467         
1468         /* grid markings on scrollbars */
1469         if (scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
1470                 /* store clamping */
1471                 scrollers->xclamp= xclamp;
1472                 scrollers->xunits= xunits;
1473                 scrollers->yclamp= yclamp;
1474                 scrollers->yunits= yunits;
1475                 
1476                 scrollers->grid= UI_view2d_grid_calc(C, v2d, xunits, xclamp, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
1477         }
1478         
1479         /* return scrollers */
1480         return scrollers;
1481 }
1482
1483 /* Print scale marking along a time scrollbar */
1484 static void scroll_printstr(View2DScrollers *scrollers, Scene *scene, float x, float y, float val, int power, short unit, char dir)
1485 {
1486         int len;
1487         char str[32];
1488         
1489         /* adjust the scale unit to work ok */
1490         if (dir == 'v') {
1491                 /* here we bump up the power by factor of 10, as 
1492                  * rotation values (hence 'degrees') are divided by 10 to 
1493                  * be able to show the curves at the same time
1494                  */
1495                 if (ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME)) {
1496                         power += 1;
1497                         val *= 10;
1498                 }
1499         }
1500         
1501         /* get string to print */
1502         ANIM_timecode_string_from_frame(str, scene, power, (unit == V2D_UNIT_SECONDS), val);
1503         
1504         /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
1505         len= strlen(str);
1506         if (dir == 'h') {
1507                 /* seconds/timecode display has slightly longer strings... */
1508                 if (unit == V2D_UNIT_SECONDS)
1509                         x-= 3*len;
1510                 else
1511                         x-= 4*len;
1512         }
1513         
1514         /* Add degree sympbol to end of string for vertical scrollbar? */
1515         if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
1516                 str[len]= 186;
1517                 str[len+1]= 0;
1518         }
1519         
1520         /* draw it */
1521         BLF_draw_default(x, y, 0.0f, str);
1522 }
1523
1524 /* Draw scrollbars in the given 2d-region */
1525 void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
1526 {
1527         Scene *scene= CTX_data_scene(C);
1528         rcti vert, hor;
1529         int scroll= view2d_scroll_mapped(v2d->scroll);
1530         
1531         /* make copies of rects for less typing */
1532         vert= vs->vert;
1533         hor= vs->hor;
1534         
1535         /* horizontal scrollbar */
1536         if (scroll & V2D_SCROLL_HORIZONTAL) {
1537                 /* only draw scrollbar when it doesn't fill the entire space */
1538                 if(vs->horfull==0) {
1539                         bTheme *btheme= U.themes.first;
1540                         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1541                         rcti slider;
1542                         int state;
1543                         
1544                         slider.xmin= vs->hor_min;
1545                         slider.xmax= vs->hor_max;
1546                         slider.ymin= hor.ymin;
1547                         slider.ymax= hor.ymax;
1548                         
1549                         state= (v2d->scroll_ui & V2D_SCROLL_H_ACTIVE)?UI_SCROLL_PRESSED:0;
1550                         
1551                         /* show zoom handles if:
1552                          *      - zooming on x-axis is allowed (no scroll otherwise)
1553                          *      - slider bubble is large enough (no overdraw confusion)
1554                          *      - scale is shown on the scroller 
1555                          *        (workaround to make sure that button windows don't show these, 
1556                          *              and only the time-grids with their zoomability can do so)
1557                          */
1558                         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0 && 
1559                                 (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) &&
1560                                 (slider.xmax - slider.xmin > V2D_SCROLLER_HANDLE_SIZE))
1561                         {
1562                                 state |= UI_SCROLL_ARROWS;
1563                         }
1564                         
1565                         uiWidgetScrollDraw(&wcol, &hor, &slider, state);
1566                 }
1567                 
1568                 /* scale indicators */
1569                 if ((scroll & V2D_SCROLL_SCALE_HORIZONTAL) && (vs->grid)) {
1570                         View2DGrid *grid= vs->grid;
1571                         float fac, dfac, fac2, val;
1572                         
1573                         /* the numbers: convert grid->startx and -dx to scroll coordinates 
1574                          *      - fac is x-coordinate to draw to
1575                          *      - dfac is gap between scale markings
1576                          */
1577                         fac= (grid->startx - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1578                         fac= (float)hor.xmin + fac*(hor.xmax - hor.xmin);
1579                         
1580                         dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
1581                         dfac= dfac * (hor.xmax - hor.xmin);
1582                         
1583                         /* set starting value, and text color */
1584                         UI_ThemeColor(TH_TEXT);
1585                         val= grid->startx;
1586                         
1587                         /* if we're clamping to whole numbers only, make sure entries won't be repeated */
1588                         if (vs->xclamp == V2D_GRID_CLAMP) {
1589                                 while (grid->dx < 0.9999f) {
1590                                         grid->dx *= 2.0f;
1591                                         dfac *= 2.0f;
1592                                 }
1593                         }
1594                         if (vs->xunits == V2D_UNIT_FRAMES)
1595                                 grid->powerx= 1;
1596                         
1597                         /* draw numbers in the appropriate range */
1598                         if (dfac > 0.0f) {
1599                                 float h= 2.0f+(float)(hor.ymin);
1600                                 
1601                                 for (; fac < hor.xmax-10; fac+=dfac, val+=grid->dx) {
1602                                         
1603                                         /* make prints look nicer for scrollers */
1604                                         if(fac < hor.xmin+10)
1605                                                 continue;
1606                                         
1607                                         switch (vs->xunits) {                                                   
1608                                                 case V2D_UNIT_FRAMES:           /* frames (as whole numbers)*/
1609                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMES, 'h');
1610                                                         break;
1611                                                         
1612                                                 case V2D_UNIT_FRAMESCALE:       /* frames (not always as whole numbers) */
1613                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMESCALE, 'h');
1614                                                         break;
1615                                                 
1616                                                 case V2D_UNIT_SECONDS:          /* seconds */
1617                                                         fac2= val/(float)FPS;
1618                                                         scroll_printstr(vs, scene, fac, h, fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
1619                                                         break;
1620                                                         
1621                                                 case V2D_UNIT_SECONDSSEQ:       /* seconds with special calculations (only used for sequencer only) */
1622                                                 {
1623                                                         float time;
1624                                                         
1625                                                         fac2= val/(float)FPS;
1626                                                         time= (float)floor(fac2);
1627                                                         fac2= fac2-time;
1628                                                         
1629                                                         scroll_printstr(vs, scene, fac, h, time+(float)FPS*fac2/100.0f, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
1630                                                 }
1631                                                         break;
1632                                                         
1633                                                 case V2D_UNIT_DEGREES:          /* Graph Editor for rotation Drivers */
1634                                                         /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
1635                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_DEGREES, 'v');
1636                                                         break;
1637                                         }
1638                                 }
1639                         }
1640                 }
1641         }
1642         
1643         /* vertical scrollbar */
1644         if (scroll & V2D_SCROLL_VERTICAL) {
1645                 /* only draw scrollbar when it doesn't fill the entire space */
1646                 if(vs->vertfull==0) {
1647                         bTheme *btheme= U.themes.first;
1648                         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1649                         rcti slider;
1650                         int state;
1651                         
1652                         slider.xmin= vert.xmin;
1653                         slider.xmax= vert.xmax;
1654                         slider.ymin= vs->vert_min;
1655                         slider.ymax= vs->vert_max;
1656                         
1657                         state= (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE)?UI_SCROLL_PRESSED:0;
1658                         
1659                         /* show zoom handles if:
1660                          *      - zooming on y-axis is allowed (no scroll otherwise)
1661                          *      - slider bubble is large enough (no overdraw confusion)
1662                          *      - scale is shown on the scroller 
1663                          *        (workaround to make sure that button windows don't show these, 
1664                          *              and only the time-grids with their zoomability can do so)
1665                          */
1666                         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0 && 
1667                                 (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) &&
1668                                 (slider.ymax - slider.ymin > V2D_SCROLLER_HANDLE_SIZE))
1669                         {
1670                                 state |= UI_SCROLL_ARROWS;
1671                         }
1672                                 
1673                         uiWidgetScrollDraw(&wcol, &vert, &slider, state);
1674                 }
1675                 
1676                 
1677                 /* scale indiators */
1678                 if ((scroll & V2D_SCROLL_SCALE_VERTICAL) && (vs->grid)) {
1679                         View2DGrid *grid= vs->grid;
1680                         float fac, dfac, val;
1681                         
1682                         /* the numbers: convert grid->starty and dy to scroll coordinates 
1683                          *      - fac is y-coordinate to draw to
1684                          *      - dfac is gap between scale markings
1685                          *      - these involve a correction for horizontal scrollbar
1686                          *        NOTE: it's assumed that that scrollbar is there if this is involved!
1687                          */
1688                         fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1689                         fac= (vert.ymin + V2D_SCROLL_HEIGHT) + fac*(vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1690                         
1691                         dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
1692                         dfac= dfac * (vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1693                         
1694                         /* set starting value, and text color */
1695                         UI_ThemeColor(TH_TEXT);
1696                         val= grid->starty;
1697                         
1698                         /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
1699                         if (vs->yclamp == V2D_GRID_CLAMP)
1700                                 fac += 0.5f * dfac;
1701                                 
1702                         /* draw vertical steps */
1703                         if (dfac > 0.0f) {
1704                                 
1705                                 BLF_rotation_default(90.0f);
1706                                 BLF_enable_default(BLF_ROTATION);
1707
1708                                 for (; fac < vert.ymax-10; fac+= dfac, val += grid->dy) {
1709                                         
1710                                         /* make prints look nicer for scrollers */
1711                                         if(fac < vert.ymin+10)
1712                                                 continue;
1713                                         
1714                                         scroll_printstr(vs, scene, (float)(vert.xmax)-2.0f, fac, val, grid->powery, vs->yunits, 'v');
1715                                 }
1716                                 
1717                                 BLF_disable_default(BLF_ROTATION);
1718                         }
1719                 }       
1720         }
1721         
1722 }
1723
1724 /* free temporary memory used for drawing scrollers */
1725 void UI_view2d_scrollers_free(View2DScrollers *scrollers)
1726 {
1727         /* need to free grid as well... */
1728         if (scrollers->grid) MEM_freeN(scrollers->grid);
1729         MEM_freeN(scrollers);
1730 }
1731
1732 /* *********************************************************************** */
1733 /* List View Utilities */
1734
1735 /* Get the view-coordinates of the nominated cell 
1736  *      - columnwidth, rowheight        = size of each 'cell'
1737  *      - startx, starty                        = coordinates (in 'tot' rect space) that the list starts from
1738  *                                                        This should be (0,0) for most views. However, for those where the starting row was offsetted
1739  *                                                        (like for Animation Editor channel lists, to make the first entry more visible), these will be 
1740  *                                                        the min-coordinates of the first item.
1741  *      - column, row                           = the 2d-corodinates (in 2D-view / 'tot' rect space) the cell exists at
1742  *      - rect                                  = coordinates of the cell (passed as single var instead of 4 separate, as it's more useful this way)
1743  */
1744 void UI_view2d_listview_cell_to_view(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, int column, int row, rctf *rect)
1745 {
1746         /* sanity checks */
1747         if ELEM(NULL, v2d, rect)
1748                 return;
1749         if ((columnwidth <= 0) && (rowheight <= 0)) {
1750                 rect->xmin= rect->xmax= 0.0f;
1751                 rect->ymin= rect->ymax= 0.0f;
1752                 return;
1753         }
1754         
1755         /* x-coordinates */
1756         rect->xmin= startx + (float)(columnwidth * column);
1757         rect->xmax= startx + (float)(columnwidth * (column + 1));
1758         
1759         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1760                 /* simply negate the values for the coordinates if in negative half */
1761                 rect->xmin = -rect->xmin;
1762                 rect->xmax = -rect->xmax;
1763         }
1764         
1765         /* y-coordinates */
1766         rect->ymin= starty + (float)(rowheight * row);
1767         rect->ymax= starty + (float)(rowheight * (row + 1));
1768         
1769         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1770                 /* simply negate the values for the coordinates if in negative half */
1771                 rect->ymin = -rect->ymin;
1772                 rect->ymax = -rect->ymax;
1773         }
1774 }
1775
1776 /* Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in.
1777  *      - columnwidth, rowheight        = size of each 'cell'
1778  *      - startx, starty                        = coordinates (in 'tot' rect space) that the list starts from
1779  *                                                        This should be (0,0) for most views. However, for those where the starting row was offsetted
1780  *                                                        (like for Animation Editor channel lists, to make the first entry more visible), these will be 
1781  *                                                        the min-coordinates of the first item.
1782  *      - viewx, viewy                  = 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for
1783  *      - column, row                           = the 'coordinates' of the relevant 'cell'
1784  */
1785 void UI_view2d_listview_view_to_cell(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
1786                                                 float viewx, float viewy, int *column, int *row)
1787 {
1788         /* adjust view coordinates to be all positive ints, corrected for the start offset */
1789         const int x= (int)(floor(fabs(viewx) + 0.5f) - startx); 
1790         const int y= (int)(floor(fabs(viewy) + 0.5f) - starty);
1791         
1792         /* sizes must not be negative */
1793         if ( (v2d == NULL) || ((columnwidth <= 0) && (rowheight <= 0)) ) {
1794                 if (column) *column= 0;
1795                 if (row) *row= 0;
1796                 
1797                 return;
1798         }
1799         
1800         /* get column */
1801         if ((column) && (columnwidth > 0))
1802                 *column= x / columnwidth;
1803         else if (column)
1804                 *column= 0;
1805         
1806         /* get row */
1807         if ((row) && (rowheight > 0))
1808                 *row= y / rowheight;
1809         else if (row)
1810                 *row= 0;
1811 }
1812
1813 /* Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect 
1814  *      - columnwidth, rowheight        = size of each 'cell'
1815  *      - startx, starty                        = coordinates that the list starts from, which should be (0,0) for most views
1816  *      - column/row_min/max            = the starting and ending column/row indices
1817  */
1818 void UI_view2d_listview_visible_cells(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
1819                                                 int *column_min, int *column_max, int *row_min, int *row_max)
1820 {
1821         /* using 'cur' rect coordinates, call the cell-getting function to get the cells for this */
1822         if (v2d) {
1823                 /* min */
1824                 UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
1825                                         v2d->cur.xmin, v2d->cur.ymin, column_min, row_min);
1826                                         
1827                 /* max*/
1828                 UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
1829                                         v2d->cur.xmax, v2d->cur.ymax, column_max, row_max);
1830         }
1831 }
1832
1833 /* *********************************************************************** */
1834 /* Coordinate Conversions */
1835
1836 /* Convert from screen/region space to 2d-View space 
1837  *      
1838  *      - x,y                   = coordinates to convert
1839  *      - viewx,viewy           = resultant coordinates
1840  */
1841 void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
1842 {
1843         float div, ofs;
1844
1845         if (viewx) {
1846                 div= (float)(v2d->mask.xmax - v2d->mask.xmin);
1847                 ofs= (float)v2d->mask.xmin;
1848                 
1849                 *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
1850         }
1851
1852         if (viewy) {
1853                 div= (float)(v2d->mask.ymax - v2d->mask.ymin);
1854                 ofs= (float)v2d->mask.ymin;
1855                 
1856                 *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
1857         }
1858 }
1859
1860 /* Convert from 2d-View space to screen/region space
1861  *      - Coordinates are clamped to lie within bounds of region
1862  *
1863  *      - x,y                           = coordinates to convert
1864  *      - regionx,regiony       = resultant coordinates 
1865  */
1866 void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *regionx, int *regiony)
1867 {
1868         /* set initial value in case coordinate lies outside of bounds */
1869         if (regionx)
1870                 *regionx= V2D_IS_CLIPPED;
1871         if (regiony)
1872                 *regiony= V2D_IS_CLIPPED;
1873         
1874         /* express given coordinates as proportional values */
1875         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1876         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1877         
1878         /* check if values are within bounds */
1879         if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
1880                 if (regionx)
1881                         *regionx= (int)(v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin));
1882                 if (regiony)
1883                         *regiony= (int)(v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin));
1884         }
1885 }
1886
1887 /* Convert from 2d-view space to screen/region space
1888  *      - Coordinates are NOT clamped to lie within bounds of region
1889  *
1890  *      - x,y                           = coordinates to convert
1891  *      - regionx,regiony       = resultant coordinates 
1892  */
1893 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, int *regionx, int *regiony)
1894 {
1895         /* step 1: express given coordinates as proportional values */
1896         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1897         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1898         
1899         /* step 2: convert proportional distances to screen coordinates  */
1900         x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
1901         y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
1902         
1903         /* although we don't clamp to lie within region bounds, we must avoid exceeding size of ints */
1904         if (regionx) {
1905                 if (x < INT_MIN) *regionx= INT_MIN;
1906                 else if(x > INT_MAX) *regionx= INT_MAX;
1907                 else *regionx= (int)x;
1908         }
1909         if (regiony) {
1910                 if (y < INT_MIN) *regiony= INT_MIN;
1911                 else if(y > INT_MAX) *regiony= INT_MAX;
1912                 else *regiony= (int)y;
1913         }
1914 }
1915
1916 /* *********************************************************************** */
1917 /* Utilities */
1918
1919 /* View2D data by default resides in region, so get from region stored in context */
1920 View2D *UI_view2d_fromcontext(const bContext *C)
1921 {
1922         ScrArea *area= CTX_wm_area(C);
1923         ARegion *region= CTX_wm_region(C);
1924
1925         if (area == NULL) return NULL;
1926         if (region == NULL) return NULL;
1927         return &(region->v2d);
1928 }
1929
1930 /* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
1931 View2D *UI_view2d_fromcontext_rwin(const bContext *C)
1932 {
1933         ScrArea *area= CTX_wm_area(C);
1934         ARegion *region= CTX_wm_region(C);
1935
1936         if (area == NULL) return NULL;
1937         if (region == NULL) return NULL;
1938         if (region->regiontype!=RGN_TYPE_WINDOW) {
1939                 ARegion *ar= area->regionbase.first;
1940                 for(; ar; ar= ar->next)
1941                         if(ar->regiontype==RGN_TYPE_WINDOW)
1942                                 return &(ar->v2d);
1943                 return NULL;
1944         }
1945         return &(region->v2d);
1946 }
1947
1948
1949 /* Calculate the scale per-axis of the drawing-area
1950  *      - Is used to inverse correct drawing of icons, etc. that need to follow view 
1951  *        but not be affected by scale
1952  *
1953  *      - x,y   = scale on each axis
1954  */
1955 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
1956 {
1957         if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1958         if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1959 }
1960
1961 /* Check if mouse is within scrollers
1962  *      - Returns appropriate code for match
1963  *              'h' = in horizontal scroller
1964  *              'v' = in vertical scroller
1965  *              0 = not in scroller
1966  *      
1967  *      - x,y   = mouse coordinates in screen (not region) space
1968  */
1969 short UI_view2d_mouse_in_scrollers (const bContext *C, View2D *v2d, int x, int y)
1970 {
1971         ARegion *ar= CTX_wm_region(C);
1972         int co[2];
1973         int scroll= view2d_scroll_mapped(v2d->scroll);
1974         
1975         /* clamp x,y to region-coordinates first */
1976         co[0]= x - ar->winrct.xmin;
1977         co[1]= y - ar->winrct.ymin;
1978         
1979         /* check if within scrollbars */
1980         if (scroll & V2D_SCROLL_HORIZONTAL) {
1981                 if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h';
1982         }
1983         if (scroll & V2D_SCROLL_VERTICAL) {
1984                 if (IN_2D_VERT_SCROLL(v2d, co)) return 'v';
1985         }       
1986         
1987         /* not found */
1988         return 0;
1989 }
1990
1991 /* ******************* view2d text drawing cache ******************** */
1992
1993 /* assumes caches are used correctly, so for time being no local storage in v2d */
1994 static ListBase strings= {NULL, NULL};
1995
1996 typedef struct View2DString {
1997         struct View2DString *next, *prev;
1998         float col[4];
1999         char str[128]; 
2000         short mval[2];
2001         rcti rect;
2002 } View2DString;
2003
2004
2005 void UI_view2d_text_cache_add(View2D *v2d, float x, float y, char *str)
2006 {
2007         int mval[2];
2008         
2009         UI_view2d_view_to_region(v2d, x, y, mval, mval+1);
2010         
2011         if(mval[0]!=V2D_IS_CLIPPED && mval[1]!=V2D_IS_CLIPPED) {
2012                 /* use calloc, rect has to be zeroe'd */
2013                 View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
2014                 
2015                 BLI_addtail(&strings, v2s);
2016                 BLI_strncpy(v2s->str, str, 128);
2017                 v2s->mval[0]= mval[0];
2018                 v2s->mval[1]= mval[1];
2019                 glGetFloatv(GL_CURRENT_COLOR, v2s->col);
2020         }
2021 }
2022
2023 /* no clip (yet) */
2024 void UI_view2d_text_cache_rectf(View2D *v2d, rctf *rect, char *str)
2025 {
2026         View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
2027         
2028         UI_view2d_to_region_no_clip(v2d, rect->xmin, rect->ymin, &v2s->rect.xmin, &v2s->rect.ymin);
2029         UI_view2d_to_region_no_clip(v2d, rect->xmax, rect->ymax, &v2s->rect.xmax, &v2s->rect.ymax);
2030         
2031         BLI_addtail(&strings, v2s);
2032         BLI_strncpy(v2s->str, str, 128);
2033         glGetFloatv(GL_CURRENT_COLOR, v2s->col);
2034 }
2035
2036
2037 void UI_view2d_text_cache_draw(ARegion *ar)
2038 {
2039         View2DString *v2s;
2040         
2041         // glMatrixMode(GL_PROJECTION);
2042         // glPushMatrix();
2043         // glMatrixMode(GL_MODELVIEW);
2044         // glPushMatrix();
2045         ED_region_pixelspace(ar);
2046         
2047         for(v2s= strings.first; v2s; v2s= v2s->next) {
2048                 glColor3fv(v2s->col);
2049                 if(v2s->rect.xmin==v2s->rect.xmax)
2050                         BLF_draw_default((float)v2s->mval[0], (float)v2s->mval[1], 0.0, v2s->str);
2051                 else {
2052                         int xofs=0, yofs;
2053                         
2054                         yofs= ceil( 0.5f*(v2s->rect.ymax - v2s->rect.ymin - BLF_height_default("28")));
2055                         if(yofs<1) yofs= 1;
2056                         
2057                         BLF_clipping_default(v2s->rect.xmin-4, v2s->rect.ymin-4, v2s->rect.xmax+4, v2s->rect.ymax+4);
2058                         BLF_enable_default(BLF_CLIPPING);
2059                         BLF_draw_default(v2s->rect.xmin+xofs, v2s->rect.ymin+yofs, 0.0f, v2s->str);
2060                         BLF_disable_default(BLF_CLIPPING);
2061                 }
2062         }
2063         
2064         // glMatrixMode(GL_PROJECTION);
2065         // glPopMatrix();
2066         // glMatrixMode(GL_MODELVIEW);
2067         // glPopMatrix();
2068         
2069         if(strings.first) 
2070                 BLI_freelistN(&strings);
2071 }
2072
2073
2074 /* ******************************************************** */
2075
2076