Progress indicators for threaded jobs
[blender.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 /* the price we pay for not exposting structs :( */
1318 void UI_view2d_grid_size(View2DGrid *grid, float *r_dx, float *r_dy)
1319 {
1320         *r_dx= grid->dx;
1321         *r_dy= grid->dy;
1322 }
1323
1324 /* free temporary memory used for drawing grid */
1325 void UI_view2d_grid_free(View2DGrid *grid)
1326 {
1327         /* only free if there's a grid */
1328         if (grid)
1329                 MEM_freeN(grid);
1330 }
1331
1332 /* *********************************************************************** */
1333 /* Scrollers */
1334
1335 /* View2DScrollers is typedef'd in UI_view2d.h 
1336  * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. 
1337  *                 For now, we don't need to have a separate (internal) header for structs like this...
1338  */
1339 struct View2DScrollers {
1340                 /* focus bubbles */
1341         int vert_min, vert_max; /* vertical scrollbar */
1342         int hor_min, hor_max;   /* horizontal scrollbar */
1343         
1344         rcti hor, vert;                 /* exact size of slider backdrop */
1345         int horfull, vertfull;  /* set if sliders are full, we don't draw them */
1346         
1347         /* scales */
1348         View2DGrid *grid;               /* grid for coordinate drawing */
1349         short xunits, xclamp;   /* units and clamping options for x-axis */
1350         short yunits, yclamp;   /* units and clamping options for y-axis */
1351 };
1352
1353 /* Calculate relevant scroller properties */
1354 View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
1355 {
1356         View2DScrollers *scrollers;
1357         rcti vert, hor;
1358         float fac1, fac2, totsize, scrollsize;
1359         int scroll= view2d_scroll_mapped(v2d->scroll);
1360         
1361         /* scrollers is allocated here... */
1362         scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
1363         
1364         vert= v2d->vert;
1365         hor= v2d->hor;
1366         
1367         /* slider rects need to be smaller than region */
1368         hor.xmin+=4;
1369         hor.xmax-=4;
1370         if (scroll & V2D_SCROLL_BOTTOM)
1371                 hor.ymin+=4;
1372         else
1373                 hor.ymax-=4;
1374         
1375         if (scroll & V2D_SCROLL_LEFT)
1376                 vert.xmin+=4;
1377         else
1378                 vert.xmax-=4;
1379         vert.ymin+=4;
1380         vert.ymax-=4;
1381         
1382         CLAMP(vert.ymin, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
1383         CLAMP(hor.xmin, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
1384         
1385         /* store in scrollers, used for drawing */
1386         scrollers->vert= vert;
1387         scrollers->hor= hor;
1388         
1389         /* scroller 'buttons':
1390          *      - These should always remain within the visible region of the scrollbar
1391          *      - They represent the region of 'tot' that is visible in 'cur'
1392          */
1393         
1394         /* horizontal scrollers */
1395         if (scroll & V2D_SCROLL_HORIZONTAL) {
1396                 /* scroller 'button' extents */
1397                 totsize= v2d->tot.xmax - v2d->tot.xmin;
1398                 scrollsize= (float)(hor.xmax - hor.xmin);
1399                 if(totsize==0.0f) totsize = 1.0f; /* avoid divide by zero */
1400                 
1401                 fac1= (v2d->cur.xmin - v2d->tot.xmin) / totsize;
1402                 if(fac1<=0.0f)
1403                         scrollers->hor_min= hor.xmin;
1404                 else
1405                         scrollers->hor_min= (int)(hor.xmin + (fac1 * scrollsize));
1406                 
1407                 fac2= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
1408                 if(fac2>=1.0f)
1409                         scrollers->hor_max= hor.xmax;
1410                 else
1411                         scrollers->hor_max= (int)(hor.xmin + (fac2 * scrollsize));
1412                 
1413                 /* prevent inverted sliders */
1414                 if (scrollers->hor_min > scrollers->hor_max) 
1415                         scrollers->hor_min= scrollers->hor_max;
1416                 /* prevent sliders from being too small, and disappearing */
1417                 if ((scrollers->hor_max - scrollers->hor_min) < V2D_SCROLLER_HANDLE_SIZE) {
1418                         scrollers->hor_max= scrollers->hor_min + V2D_SCROLLER_HANDLE_SIZE;
1419
1420                         CLAMP(scrollers->hor_max, hor.xmin+V2D_SCROLLER_HANDLE_SIZE, hor.xmax);
1421                         CLAMP(scrollers->hor_min, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
1422                 }
1423                 
1424                 /* check whether sliders can disappear due to the full-range being used */
1425                 if(v2d->keeptot) {
1426                         if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
1427                                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_FULLR;
1428                                 scrollers->horfull= 1;
1429                         }
1430                         else    
1431                                 v2d->scroll &= ~V2D_SCROLL_HORIZONTAL_FULLR;
1432                 }
1433         }
1434         
1435         /* vertical scrollers */
1436         if (scroll & V2D_SCROLL_VERTICAL) {
1437                 /* scroller 'button' extents */
1438                 totsize= v2d->tot.ymax - v2d->tot.ymin;
1439                 scrollsize= (float)(vert.ymax - vert.ymin);
1440                 if(totsize==0.0f) totsize = 1.0f; /* avoid divide by zero */
1441                 
1442                 fac1= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
1443                 if(fac1<=0.0f)
1444                         scrollers->vert_min= vert.ymin;
1445                 else
1446                         scrollers->vert_min= (int)(vert.ymin + (fac1 * scrollsize));
1447                 
1448                 fac2= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
1449                 if(fac2>=1.0f)
1450                         scrollers->vert_max= vert.ymax;
1451                 else
1452                         scrollers->vert_max= (int)(vert.ymin + (fac2 * scrollsize));
1453                 
1454                 /* prevent inverted sliders */
1455                 if (scrollers->vert_min > scrollers->vert_max) 
1456                         scrollers->vert_min= scrollers->vert_max;
1457                 /* prevent sliders from being too small, and disappearing */
1458                 if ((scrollers->vert_max - scrollers->vert_min) < V2D_SCROLLER_HANDLE_SIZE) {
1459                         
1460                         scrollers->vert_max= scrollers->vert_min + V2D_SCROLLER_HANDLE_SIZE;
1461                         
1462                         CLAMP(scrollers->vert_max, vert.ymin+V2D_SCROLLER_HANDLE_SIZE, vert.ymax);
1463                         CLAMP(scrollers->vert_min, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
1464                 }
1465
1466                 /* check whether sliders can disappear due to the full-range being used */
1467                 if(v2d->keeptot) {
1468                         if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
1469                                 v2d->scroll |= V2D_SCROLL_VERTICAL_FULLR;
1470                                 scrollers->vertfull= 1;
1471                         }
1472                         else    
1473                                 v2d->scroll &= ~V2D_SCROLL_VERTICAL_FULLR;
1474                 }
1475         }
1476         
1477         /* grid markings on scrollbars */
1478         if (scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
1479                 /* store clamping */
1480                 scrollers->xclamp= xclamp;
1481                 scrollers->xunits= xunits;
1482                 scrollers->yclamp= yclamp;
1483                 scrollers->yunits= yunits;
1484                 
1485                 scrollers->grid= UI_view2d_grid_calc(C, v2d, xunits, xclamp, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
1486         }
1487         
1488         /* return scrollers */
1489         return scrollers;
1490 }
1491
1492 /* Print scale marking along a time scrollbar */
1493 static void scroll_printstr(View2DScrollers *scrollers, Scene *scene, float x, float y, float val, int power, short unit, char dir)
1494 {
1495         int len;
1496         char str[32];
1497         
1498         /* adjust the scale unit to work ok */
1499         if (dir == 'v') {
1500                 /* here we bump up the power by factor of 10, as 
1501                  * rotation values (hence 'degrees') are divided by 10 to 
1502                  * be able to show the curves at the same time
1503                  */
1504                 if (ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME)) {
1505                         power += 1;
1506                         val *= 10;
1507                 }
1508         }
1509         
1510         /* get string to print */
1511         ANIM_timecode_string_from_frame(str, scene, power, (unit == V2D_UNIT_SECONDS), val);
1512         
1513         /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
1514         len= strlen(str);
1515         if (dir == 'h') {
1516                 /* seconds/timecode display has slightly longer strings... */
1517                 if (unit == V2D_UNIT_SECONDS)
1518                         x-= 3*len;
1519                 else
1520                         x-= 4*len;
1521         }
1522         
1523         /* Add degree sympbol to end of string for vertical scrollbar? */
1524         if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
1525                 str[len]= 186;
1526                 str[len+1]= 0;
1527         }
1528         
1529         /* draw it */
1530         BLF_draw_default(x, y, 0.0f, str);
1531 }
1532
1533 /* Draw scrollbars in the given 2d-region */
1534 void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
1535 {
1536         Scene *scene= CTX_data_scene(C);
1537         rcti vert, hor;
1538         int scroll= view2d_scroll_mapped(v2d->scroll);
1539         
1540         /* make copies of rects for less typing */
1541         vert= vs->vert;
1542         hor= vs->hor;
1543         
1544         /* horizontal scrollbar */
1545         if (scroll & V2D_SCROLL_HORIZONTAL) {
1546                 /* only draw scrollbar when it doesn't fill the entire space */
1547                 if(vs->horfull==0) {
1548                         bTheme *btheme= U.themes.first;
1549                         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1550                         rcti slider;
1551                         int state;
1552                         
1553                         slider.xmin= vs->hor_min;
1554                         slider.xmax= vs->hor_max;
1555                         slider.ymin= hor.ymin;
1556                         slider.ymax= hor.ymax;
1557                         
1558                         state= (v2d->scroll_ui & V2D_SCROLL_H_ACTIVE)?UI_SCROLL_PRESSED:0;
1559                         
1560                         /* show zoom handles if:
1561                          *      - zooming on x-axis is allowed (no scroll otherwise)
1562                          *      - slider bubble is large enough (no overdraw confusion)
1563                          *      - scale is shown on the scroller 
1564                          *        (workaround to make sure that button windows don't show these, 
1565                          *              and only the time-grids with their zoomability can do so)
1566                          */
1567                         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0 && 
1568                                 (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) &&
1569                                 (slider.xmax - slider.xmin > V2D_SCROLLER_HANDLE_SIZE))
1570                         {
1571                                 state |= UI_SCROLL_ARROWS;
1572                         }
1573                         
1574                         uiWidgetScrollDraw(&wcol, &hor, &slider, state);
1575                 }
1576                 
1577                 /* scale indicators */
1578                 if ((scroll & V2D_SCROLL_SCALE_HORIZONTAL) && (vs->grid)) {
1579                         View2DGrid *grid= vs->grid;
1580                         float fac, dfac, fac2, val;
1581                         
1582                         /* the numbers: convert grid->startx and -dx to scroll coordinates 
1583                          *      - fac is x-coordinate to draw to
1584                          *      - dfac is gap between scale markings
1585                          */
1586                         fac= (grid->startx - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1587                         fac= (float)hor.xmin + fac*(hor.xmax - hor.xmin);
1588                         
1589                         dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
1590                         dfac= dfac * (hor.xmax - hor.xmin);
1591                         
1592                         /* set starting value, and text color */
1593                         UI_ThemeColor(TH_TEXT);
1594                         val= grid->startx;
1595                         
1596                         /* if we're clamping to whole numbers only, make sure entries won't be repeated */
1597                         if (vs->xclamp == V2D_GRID_CLAMP) {
1598                                 while (grid->dx < 0.9999f) {
1599                                         grid->dx *= 2.0f;
1600                                         dfac *= 2.0f;
1601                                 }
1602                         }
1603                         if (vs->xunits == V2D_UNIT_FRAMES)
1604                                 grid->powerx= 1;
1605                         
1606                         /* draw numbers in the appropriate range */
1607                         if (dfac > 0.0f) {
1608                                 float h= 2.0f+(float)(hor.ymin);
1609                                 
1610                                 for (; fac < hor.xmax-10; fac+=dfac, val+=grid->dx) {
1611                                         
1612                                         /* make prints look nicer for scrollers */
1613                                         if(fac < hor.xmin+10)
1614                                                 continue;
1615                                         
1616                                         switch (vs->xunits) {                                                   
1617                                                 case V2D_UNIT_FRAMES:           /* frames (as whole numbers)*/
1618                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMES, 'h');
1619                                                         break;
1620                                                         
1621                                                 case V2D_UNIT_FRAMESCALE:       /* frames (not always as whole numbers) */
1622                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMESCALE, 'h');
1623                                                         break;
1624                                                 
1625                                                 case V2D_UNIT_SECONDS:          /* seconds */
1626                                                         fac2= val/(float)FPS;
1627                                                         scroll_printstr(vs, scene, fac, h, fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
1628                                                         break;
1629                                                         
1630                                                 case V2D_UNIT_SECONDSSEQ:       /* seconds with special calculations (only used for sequencer only) */
1631                                                 {
1632                                                         float time;
1633                                                         
1634                                                         fac2= val/(float)FPS;
1635                                                         time= (float)floor(fac2);
1636                                                         fac2= fac2-time;
1637                                                         
1638                                                         scroll_printstr(vs, scene, fac, h, time+(float)FPS*fac2/100.0f, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
1639                                                 }
1640                                                         break;
1641                                                         
1642                                                 case V2D_UNIT_DEGREES:          /* Graph Editor for rotation Drivers */
1643                                                         /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
1644                                                         scroll_printstr(vs, scene, fac, h, val, grid->powerx, V2D_UNIT_DEGREES, 'v');
1645                                                         break;
1646                                         }
1647                                 }
1648                         }
1649                 }
1650         }
1651         
1652         /* vertical scrollbar */
1653         if (scroll & V2D_SCROLL_VERTICAL) {
1654                 /* only draw scrollbar when it doesn't fill the entire space */
1655                 if(vs->vertfull==0) {
1656                         bTheme *btheme= U.themes.first;
1657                         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1658                         rcti slider;
1659                         int state;
1660                         
1661                         slider.xmin= vert.xmin;
1662                         slider.xmax= vert.xmax;
1663                         slider.ymin= vs->vert_min;
1664                         slider.ymax= vs->vert_max;
1665                         
1666                         state= (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE)?UI_SCROLL_PRESSED:0;
1667                         
1668                         /* show zoom handles if:
1669                          *      - zooming on y-axis is allowed (no scroll otherwise)
1670                          *      - slider bubble is large enough (no overdraw confusion)
1671                          *      - scale is shown on the scroller 
1672                          *        (workaround to make sure that button windows don't show these, 
1673                          *              and only the time-grids with their zoomability can do so)
1674                          */
1675                         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0 && 
1676                                 (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) &&
1677                                 (slider.ymax - slider.ymin > V2D_SCROLLER_HANDLE_SIZE))
1678                         {
1679                                 state |= UI_SCROLL_ARROWS;
1680                         }
1681                                 
1682                         uiWidgetScrollDraw(&wcol, &vert, &slider, state);
1683                 }
1684                 
1685                 
1686                 /* scale indiators */
1687                 if ((scroll & V2D_SCROLL_SCALE_VERTICAL) && (vs->grid)) {
1688                         View2DGrid *grid= vs->grid;
1689                         float fac, dfac, val;
1690                         
1691                         /* the numbers: convert grid->starty and dy to scroll coordinates 
1692                          *      - fac is y-coordinate to draw to
1693                          *      - dfac is gap between scale markings
1694                          *      - these involve a correction for horizontal scrollbar
1695                          *        NOTE: it's assumed that that scrollbar is there if this is involved!
1696                          */
1697                         fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1698                         fac= (vert.ymin + V2D_SCROLL_HEIGHT) + fac*(vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1699                         
1700                         dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
1701                         dfac= dfac * (vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1702                         
1703                         /* set starting value, and text color */
1704                         UI_ThemeColor(TH_TEXT);
1705                         val= grid->starty;
1706                         
1707                         /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
1708                         if (vs->yclamp == V2D_GRID_CLAMP)
1709                                 fac += 0.5f * dfac;
1710                                 
1711                         /* draw vertical steps */
1712                         if (dfac > 0.0f) {
1713                                 
1714                                 BLF_rotation_default(90.0f);
1715                                 BLF_enable_default(BLF_ROTATION);
1716
1717                                 for (; fac < vert.ymax-10; fac+= dfac, val += grid->dy) {
1718                                         
1719                                         /* make prints look nicer for scrollers */
1720                                         if(fac < vert.ymin+10)
1721                                                 continue;
1722                                         
1723                                         scroll_printstr(vs, scene, (float)(vert.xmax)-2.0f, fac, val, grid->powery, vs->yunits, 'v');
1724                                 }
1725                                 
1726                                 BLF_disable_default(BLF_ROTATION);
1727                         }
1728                 }       
1729         }
1730         
1731 }
1732
1733 /* free temporary memory used for drawing scrollers */
1734 void UI_view2d_scrollers_free(View2DScrollers *scrollers)
1735 {
1736         /* need to free grid as well... */
1737         if (scrollers->grid) MEM_freeN(scrollers->grid);
1738         MEM_freeN(scrollers);
1739 }
1740
1741 /* *********************************************************************** */
1742 /* List View Utilities */
1743
1744 /* Get the view-coordinates of the nominated cell 
1745  *      - columnwidth, rowheight        = size of each 'cell'
1746  *      - startx, starty                        = coordinates (in 'tot' rect space) that the list starts from
1747  *                                                        This should be (0,0) for most views. However, for those where the starting row was offsetted
1748  *                                                        (like for Animation Editor channel lists, to make the first entry more visible), these will be 
1749  *                                                        the min-coordinates of the first item.
1750  *      - column, row                           = the 2d-corodinates (in 2D-view / 'tot' rect space) the cell exists at
1751  *      - rect                                  = coordinates of the cell (passed as single var instead of 4 separate, as it's more useful this way)
1752  */
1753 void UI_view2d_listview_cell_to_view(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, int column, int row, rctf *rect)
1754 {
1755         /* sanity checks */
1756         if ELEM(NULL, v2d, rect)
1757                 return;
1758         if ((columnwidth <= 0) && (rowheight <= 0)) {
1759                 rect->xmin= rect->xmax= 0.0f;
1760                 rect->ymin= rect->ymax= 0.0f;
1761                 return;
1762         }
1763         
1764         /* x-coordinates */
1765         rect->xmin= startx + (float)(columnwidth * column);
1766         rect->xmax= startx + (float)(columnwidth * (column + 1));
1767         
1768         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
1769                 /* simply negate the values for the coordinates if in negative half */
1770                 rect->xmin = -rect->xmin;
1771                 rect->xmax = -rect->xmax;
1772         }
1773         
1774         /* y-coordinates */
1775         rect->ymin= starty + (float)(rowheight * row);
1776         rect->ymax= starty + (float)(rowheight * (row + 1));
1777         
1778         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
1779                 /* simply negate the values for the coordinates if in negative half */
1780                 rect->ymin = -rect->ymin;
1781                 rect->ymax = -rect->ymax;
1782         }
1783 }
1784
1785 /* Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in.
1786  *      - columnwidth, rowheight        = size of each 'cell'
1787  *      - startx, starty                        = coordinates (in 'tot' rect space) that the list starts from
1788  *                                                        This should be (0,0) for most views. However, for those where the starting row was offsetted
1789  *                                                        (like for Animation Editor channel lists, to make the first entry more visible), these will be 
1790  *                                                        the min-coordinates of the first item.
1791  *      - viewx, viewy                  = 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for
1792  *      - column, row                           = the 'coordinates' of the relevant 'cell'
1793  */
1794 void UI_view2d_listview_view_to_cell(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
1795                                                 float viewx, float viewy, int *column, int *row)
1796 {
1797         /* adjust view coordinates to be all positive ints, corrected for the start offset */
1798         const int x= (int)(floor(fabs(viewx) + 0.5f) - startx); 
1799         const int y= (int)(floor(fabs(viewy) + 0.5f) - starty);
1800         
1801         /* sizes must not be negative */
1802         if ( (v2d == NULL) || ((columnwidth <= 0) && (rowheight <= 0)) ) {
1803                 if (column) *column= 0;
1804                 if (row) *row= 0;
1805                 
1806                 return;
1807         }
1808         
1809         /* get column */
1810         if ((column) && (columnwidth > 0))
1811                 *column= x / columnwidth;
1812         else if (column)
1813                 *column= 0;
1814         
1815         /* get row */
1816         if ((row) && (rowheight > 0))
1817                 *row= y / rowheight;
1818         else if (row)
1819                 *row= 0;
1820 }
1821
1822 /* Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect 
1823  *      - columnwidth, rowheight        = size of each 'cell'
1824  *      - startx, starty                        = coordinates that the list starts from, which should be (0,0) for most views
1825  *      - column/row_min/max            = the starting and ending column/row indices
1826  */
1827 void UI_view2d_listview_visible_cells(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
1828                                                 int *column_min, int *column_max, int *row_min, int *row_max)
1829 {
1830         /* using 'cur' rect coordinates, call the cell-getting function to get the cells for this */
1831         if (v2d) {
1832                 /* min */
1833                 UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
1834                                         v2d->cur.xmin, v2d->cur.ymin, column_min, row_min);
1835                                         
1836                 /* max*/
1837                 UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
1838                                         v2d->cur.xmax, v2d->cur.ymax, column_max, row_max);
1839         }
1840 }
1841
1842 /* *********************************************************************** */
1843 /* Coordinate Conversions */
1844
1845 /* Convert from screen/region space to 2d-View space 
1846  *      
1847  *      - x,y                   = coordinates to convert
1848  *      - viewx,viewy           = resultant coordinates
1849  */
1850 void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
1851 {
1852         float div, ofs;
1853
1854         if (viewx) {
1855                 div= (float)(v2d->mask.xmax - v2d->mask.xmin);
1856                 ofs= (float)v2d->mask.xmin;
1857                 
1858                 *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
1859         }
1860
1861         if (viewy) {
1862                 div= (float)(v2d->mask.ymax - v2d->mask.ymin);
1863                 ofs= (float)v2d->mask.ymin;
1864                 
1865                 *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
1866         }
1867 }
1868
1869 /* Convert from 2d-View space to screen/region space
1870  *      - Coordinates are clamped to lie within bounds of region
1871  *
1872  *      - x,y                           = coordinates to convert
1873  *      - regionx,regiony       = resultant coordinates 
1874  */
1875 void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *regionx, int *regiony)
1876 {
1877         /* set initial value in case coordinate lies outside of bounds */
1878         if (regionx)
1879                 *regionx= V2D_IS_CLIPPED;
1880         if (regiony)
1881                 *regiony= V2D_IS_CLIPPED;
1882         
1883         /* express given coordinates as proportional values */
1884         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1885         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1886         
1887         /* check if values are within bounds */
1888         if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
1889                 if (regionx)
1890                         *regionx= (int)(v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin));
1891                 if (regiony)
1892                         *regiony= (int)(v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin));
1893         }
1894 }
1895
1896 /* Convert from 2d-view space to screen/region space
1897  *      - Coordinates are NOT clamped to lie within bounds of region
1898  *
1899  *      - x,y                           = coordinates to convert
1900  *      - regionx,regiony       = resultant coordinates 
1901  */
1902 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, int *regionx, int *regiony)
1903 {
1904         /* step 1: express given coordinates as proportional values */
1905         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1906         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1907         
1908         /* step 2: convert proportional distances to screen coordinates  */
1909         x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
1910         y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
1911         
1912         /* although we don't clamp to lie within region bounds, we must avoid exceeding size of ints */
1913         if (regionx) {
1914                 if (x < INT_MIN) *regionx= INT_MIN;
1915                 else if(x > INT_MAX) *regionx= INT_MAX;
1916                 else *regionx= (int)x;
1917         }
1918         if (regiony) {
1919                 if (y < INT_MIN) *regiony= INT_MIN;
1920                 else if(y > INT_MAX) *regiony= INT_MAX;
1921                 else *regiony= (int)y;
1922         }
1923 }
1924
1925 /* *********************************************************************** */
1926 /* Utilities */
1927
1928 /* View2D data by default resides in region, so get from region stored in context */
1929 View2D *UI_view2d_fromcontext(const bContext *C)
1930 {
1931         ScrArea *area= CTX_wm_area(C);
1932         ARegion *region= CTX_wm_region(C);
1933
1934         if (area == NULL) return NULL;
1935         if (region == NULL) return NULL;
1936         return &(region->v2d);
1937 }
1938
1939 /* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
1940 View2D *UI_view2d_fromcontext_rwin(const bContext *C)
1941 {
1942         ScrArea *area= CTX_wm_area(C);
1943         ARegion *region= CTX_wm_region(C);
1944
1945         if (area == NULL) return NULL;
1946         if (region == NULL) return NULL;
1947         if (region->regiontype!=RGN_TYPE_WINDOW) {
1948                 ARegion *ar= area->regionbase.first;
1949                 for(; ar; ar= ar->next)
1950                         if(ar->regiontype==RGN_TYPE_WINDOW)
1951                                 return &(ar->v2d);
1952                 return NULL;
1953         }
1954         return &(region->v2d);
1955 }
1956
1957
1958 /* Calculate the scale per-axis of the drawing-area
1959  *      - Is used to inverse correct drawing of icons, etc. that need to follow view 
1960  *        but not be affected by scale
1961  *
1962  *      - x,y   = scale on each axis
1963  */
1964 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
1965 {
1966         if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1967         if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1968 }
1969
1970 /* Check if mouse is within scrollers
1971  *      - Returns appropriate code for match
1972  *              'h' = in horizontal scroller
1973  *              'v' = in vertical scroller
1974  *              0 = not in scroller
1975  *      
1976  *      - x,y   = mouse coordinates in screen (not region) space
1977  */
1978 short UI_view2d_mouse_in_scrollers (const bContext *C, View2D *v2d, int x, int y)
1979 {
1980         ARegion *ar= CTX_wm_region(C);
1981         int co[2];
1982         int scroll= view2d_scroll_mapped(v2d->scroll);
1983         
1984         /* clamp x,y to region-coordinates first */
1985         co[0]= x - ar->winrct.xmin;
1986         co[1]= y - ar->winrct.ymin;
1987         
1988         /* check if within scrollbars */
1989         if (scroll & V2D_SCROLL_HORIZONTAL) {
1990                 if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h';
1991         }
1992         if (scroll & V2D_SCROLL_VERTICAL) {
1993                 if (IN_2D_VERT_SCROLL(v2d, co)) return 'v';
1994         }       
1995         
1996         /* not found */
1997         return 0;
1998 }
1999
2000 /* ******************* view2d text drawing cache ******************** */
2001
2002 /* assumes caches are used correctly, so for time being no local storage in v2d */
2003 static ListBase strings= {NULL, NULL};
2004
2005 typedef struct View2DString {
2006         struct View2DString *next, *prev;
2007         float col[4];
2008         char str[128]; 
2009         short mval[2];
2010         rcti rect;
2011 } View2DString;
2012
2013
2014 void UI_view2d_text_cache_add(View2D *v2d, float x, float y, char *str)
2015 {
2016         int mval[2];
2017         
2018         UI_view2d_view_to_region(v2d, x, y, mval, mval+1);
2019         
2020         if(mval[0]!=V2D_IS_CLIPPED && mval[1]!=V2D_IS_CLIPPED) {
2021                 /* use calloc, rect has to be zeroe'd */
2022                 View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
2023                 
2024                 BLI_addtail(&strings, v2s);
2025                 BLI_strncpy(v2s->str, str, 128);
2026                 v2s->mval[0]= mval[0];
2027                 v2s->mval[1]= mval[1];
2028                 glGetFloatv(GL_CURRENT_COLOR, v2s->col);
2029         }
2030 }
2031
2032 /* no clip (yet) */
2033 void UI_view2d_text_cache_rectf(View2D *v2d, rctf *rect, char *str)
2034 {
2035         View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
2036         
2037         UI_view2d_to_region_no_clip(v2d, rect->xmin, rect->ymin, &v2s->rect.xmin, &v2s->rect.ymin);
2038         UI_view2d_to_region_no_clip(v2d, rect->xmax, rect->ymax, &v2s->rect.xmax, &v2s->rect.ymax);
2039         
2040         BLI_addtail(&strings, v2s);
2041         BLI_strncpy(v2s->str, str, 128);
2042         glGetFloatv(GL_CURRENT_COLOR, v2s->col);
2043 }
2044
2045
2046 void UI_view2d_text_cache_draw(ARegion *ar)
2047 {
2048         View2DString *v2s;
2049         
2050         // glMatrixMode(GL_PROJECTION);
2051         // glPushMatrix();
2052         // glMatrixMode(GL_MODELVIEW);
2053         // glPushMatrix();
2054         ED_region_pixelspace(ar);
2055         
2056         for(v2s= strings.first; v2s; v2s= v2s->next) {
2057                 glColor3fv(v2s->col);
2058                 if(v2s->rect.xmin==v2s->rect.xmax)
2059                         BLF_draw_default((float)v2s->mval[0], (float)v2s->mval[1], 0.0, v2s->str);
2060                 else {
2061                         int xofs=0, yofs;
2062                         
2063                         yofs= ceil( 0.5f*(v2s->rect.ymax - v2s->rect.ymin - BLF_height_default("28")));
2064                         if(yofs<1) yofs= 1;
2065                         
2066                         BLF_clipping_default(v2s->rect.xmin-4, v2s->rect.ymin-4, v2s->rect.xmax+4, v2s->rect.ymax+4);
2067                         BLF_enable_default(BLF_CLIPPING);
2068                         BLF_draw_default(v2s->rect.xmin+xofs, v2s->rect.ymin+yofs, 0.0f, v2s->str);
2069                         BLF_disable_default(BLF_CLIPPING);
2070                 }
2071         }
2072         
2073         // glMatrixMode(GL_PROJECTION);
2074         // glPopMatrix();
2075         // glMatrixMode(GL_MODELVIEW);
2076         // glPopMatrix();
2077         
2078         if(strings.first) 
2079                 BLI_freelistN(&strings);
2080 }
2081
2082
2083 /* ******************************************************** */
2084
2085