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