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 <string.h>
29 #include <math.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_scene_types.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_space_types.h"
36 #include "DNA_view2d_types.h"
37
38 #include "BLI_blenlib.h"
39
40 #include "BKE_global.h"
41 #include "BKE_utildefines.h"
42
43 #include "WM_api.h"
44
45 #include "BIF_gl.h"
46 #include "BIF_glutil.h"
47
48 #include "ED_screen.h"
49
50 #include "UI_resources.h"
51 #include "UI_text.h"
52 #include "UI_view2d.h"
53
54 #include "UI_interface.h"
55 #include "interface.h"
56
57 /* *********************************************************************** */
58 /* Refresh and Validation */
59
60 /* Adjust mask size in response to view size changes 
61  *      - This should only be called in region init() callbacks, which are
62  *        called when the region is resized or area changes...
63  */
64 // XXX pre2.5 -> this used to be called  calc_scrollrcts()
65 void UI_view2d_size_update(View2D *v2d, int winx, int winy)
66 {
67         v2d->winx= winx;
68         v2d->winy= winy;
69         
70         /* mask - view frame */
71         v2d->mask.xmin= v2d->mask.ymin= 0;
72         v2d->mask.xmax= winx - 1;       /* -1 yes! masks are pixels */
73         v2d->mask.ymax= winy - 1;
74         
75         /* scrollbars shrink mask area, but should be based off regionsize 
76          *      - they can only be on one edge of the region they define
77          */
78         if (v2d->scroll) {
79                 /* vertical scrollbar */
80                 if (v2d->scroll & V2D_SCROLL_LEFT) {
81                         /* on left-hand edge of region */
82                         v2d->vert= v2d->mask;
83                         v2d->vert.xmax= V2D_SCROLL_WIDTH;
84                         v2d->mask.xmin= v2d->vert.xmax + 1;
85                 }
86                 else if (v2d->scroll & V2D_SCROLL_RIGHT) {
87                         /* on right-hand edge of region */
88                         v2d->vert= v2d->mask;
89                         v2d->vert.xmin= v2d->vert.xmax - V2D_SCROLL_WIDTH;
90                         v2d->mask.xmax= v2d->vert.xmin - 1;
91                 }
92                 
93                 /* horizontal scrollbar */
94                 if (v2d->scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
95                         /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the ohter is for standard) */
96                         v2d->hor= v2d->mask;
97                         v2d->hor.ymax= V2D_SCROLL_HEIGHT;
98                         v2d->mask.ymin= v2d->hor.ymax + 1;
99                 }
100                 else if (v2d->scroll & V2D_SCROLL_TOP) {
101                         /* on upper edge of region */
102                         v2d->hor= v2d->mask;
103                         v2d->hor.ymin= v2d->hor.ymax - V2D_SCROLL_HEIGHT;
104                         v2d->mask.ymax= v2d->hor.ymin - 1;
105                 }
106         }
107         
108         /* cope with unitialized veriables for simple cases, like header or outliner */
109         if(v2d->tot.xmin==v2d->tot.xmax || v2d->cur.xmin==v2d->cur.xmax) {
110                 if(v2d->keepzoom) {
111                         BLI_init_rctf(&v2d->tot, v2d->mask.xmin, v2d->mask.xmax, v2d->mask.ymin, v2d->mask.ymax);
112                         BLI_init_rctf(&v2d->cur, v2d->mask.xmin, v2d->mask.xmax, v2d->mask.ymin, v2d->mask.ymax);
113                         
114                         v2d->min[0]= v2d->max[0]= winx;
115                         v2d->min[1]= v2d->max[1]= winy;
116                         v2d->minzoom= 1.0f;
117                         v2d->maxzoom= 1.0f;
118                 }
119         }
120         
121         /* make sure that 'cur' rect is in a valid state as a result of these changes */
122         UI_view2d_curRect_validate(v2d);
123 }
124
125 /* Ensure View2D rects remain in a viable configuration 
126  *      - cur is not allowed to be: larger than max, smaller than min, or outside of tot
127  */
128 // XXX pre2.5 -> this used to be called  test_view2d()
129 void UI_view2d_curRect_validate(View2D *v2d)
130 {
131         float totwidth, totheight, curwidth, curheight, width, height;
132         float winx, winy;
133         rctf *cur, *tot;
134         
135         /* use mask as size of region that View2D resides in, as it takes into account scrollbars already  */
136         winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
137         winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
138         
139         /* get pointers to rcts for less typing */
140         cur= &v2d->cur;
141         tot= &v2d->tot;
142         
143         /* we must satisfy the following constraints (in decreasing order of importance):
144          *      - cur must not fall outside of tot
145          *      - axis locks (zoom and offset) must be maintained
146          *      - zoom must not be excessive (check either sizes or zoom values)
147          *      - aspect ratio should be respected (NOTE: this is quite closely realted to zoom too)
148          */
149         
150         /* Step 1: if keepzoom, adjust the sizes of the rects only
151          *      - firstly, we calculate the sizes of the rects
152          *      - curwidth and curheight are saved as reference... modify width and height values here
153          */
154         totwidth= tot->xmax - tot->xmin;
155         totheight= tot->ymax - tot->ymin;
156         curwidth= width= cur->xmax - cur->xmin;
157         curheight= height= cur->ymax - cur->ymin;
158         
159         /* if zoom is locked, size on the appropriate axis is reset to mask size */
160         if (v2d->keepzoom & V2D_LOCKZOOM_X)
161                 width= winx;
162         if (v2d->keepzoom & V2D_LOCKZOOM_Y)
163                 height= winy;
164                 
165         /* keepzoom (V2D_KEEPZOOM set), indicates that zoom level on each axis must not exceed limits 
166          * NOTE: in general, it is not expected that the lock-zoom will be used in conjunction with this
167          */
168         if (v2d->keepzoom & V2D_KEEPZOOM) {
169                 float zoom, fac;
170                 
171                 /* check if excessive zoom on x-axis */
172                 if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
173                         zoom= winx / width;
174                         if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
175                                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
176                                 width *= fac;
177                         }
178                 }
179                 
180                 /* check if excessive zoom on y-axis */
181                 if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
182                         zoom= winy / height;
183                         if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
184                                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
185                                 height *= fac;
186                         }
187                 }
188         }
189         else {
190                 /* make sure sizes don't exceed that of the min/max sizes (even though we're not doing zoom clamping) */
191                 CLAMP(width, v2d->min[0], v2d->max[0]);
192                 CLAMP(height, v2d->min[1], v2d->max[1]);
193         }
194         
195         /* check if we should restore aspect ratio (if view size changed) */
196         if (v2d->keepaspect) {
197                 short do_x=0, do_y=0, do_cur, do_win;
198                 float curRatio, winRatio;
199                 
200                 /* when a window edge changes, the aspect ratio can't be used to
201                  * find which is the best new 'cur' rect. thats why it stores 'old' 
202                  */
203                 if (winx != v2d->oldwinx) do_x= 1;
204                 if (winy != v2d->oldwiny) do_y= 1;
205                 
206                 curRatio= height / width;
207                 winRatio= winy / winx;
208                 
209                 /* both sizes change (area/region maximised)  */
210                 if (do_x == do_y) {
211                         if (do_x && do_y) {
212                                 /* here is 1,1 case, so all others must be 0,0 */
213                                 if (ABS(winx - v2d->oldwinx) > ABS(winy - v2d->oldwiny)) do_y= 0;
214                                 else do_x= 0;
215                         }
216                         else if (winRatio > 1.0f) do_x= 0; 
217                         else do_x= 1;
218                 }
219                 do_cur= do_x;
220                 do_win= do_y;
221                 
222                 if (do_cur) {
223                         if ((v2d->keeptot == 2) && (winx != v2d->oldwinx)) {
224                                 /* special exception for Outliner (and later channel-lists):
225                                  *      - The view may be moved left to avoid contents being pushed out of view when view shrinks. 
226                                  *      - The keeptot code will make sure cur->xmin will not be less than tot->xmin (which cannot be allowed)
227                                  *      - width is not adjusted for changed ratios here...
228                                  */
229                                 if (winx < v2d->oldwinx) {
230                                         float temp = v2d->oldwinx - winx;
231                                         
232                                         cur->xmin -= temp;
233                                         cur->xmax -= temp;
234                                         
235                                         /* width does not get modified, as keepaspect here is just set to make 
236                                          * sure visible area adjusts to changing view shape! 
237                                          */
238                                 }
239                         }
240                         else {
241                                 /* portrait window: correct for x */
242                                 width= height / winRatio;
243                         }
244                 }
245                 else {
246                         /* landscape window: correct for y */
247                         height = width * winRatio;
248                 }
249                 
250                 /* store region size for next time */
251                 v2d->oldwinx= winx; 
252                 v2d->oldwiny= winy;
253         }
254         
255         /* Step 2: apply new sizes of cur rect to cur rect */
256         if ((width != curwidth) || (height != curheight)) {
257                 float temp, dh;
258                 
259                 /* resize around 'center' of frame */
260                 if (width != curwidth) {
261                         temp= (cur->xmax + cur->xmin) * 0.5f;
262                         dh= width * 0.5f;
263                         
264                         cur->xmin = temp - dh;
265                         cur->xmax = temp + dh;
266                 }
267                 if (height != curheight) {
268                         temp= (cur->ymax + cur->ymin) * 0.5f;
269                         dh= height * 0.5f;
270                         
271                         cur->ymin = temp - dh;
272                         cur->ymax = temp + dh;
273                 }
274         }
275         
276         /* Step 3: adjust so that it doesn't fall outside of bounds of tot */
277         if (v2d->keeptot) {
278                 float temp, diff;
279                 
280                 /* recalculate extents of cur */
281                 curwidth= cur->xmax - cur->xmin;
282                 curheight= cur->ymax - cur->ymin;
283                 
284                 /* width */
285                 if ( (curwidth > totwidth) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_X)) ) {
286                         /* if zoom doesn't have to be maintained, just clamp edges */
287                         if (cur->xmin < tot->xmin) cur->xmin= tot->xmin;
288                         if (cur->xmax > tot->xmax) cur->xmax= tot->xmax;
289                 }
290                 else if (v2d->keeptot == 2) {
291                         /* This is an exception for the outliner (and later channel-lists) 
292                          *      - must clamp within tot rect (absolutely no excuses)
293                          *      --> therefore, cur->xmin must not be less than tot->xmin
294                          */
295                         if (cur->xmin < tot->xmin) {
296                                 /* move cur across so that it sits at minimum of tot */
297                                 temp= tot->xmin - cur->xmin;
298                                 
299                                 cur->xmin += temp;
300                                 cur->xmax += temp;
301                         }
302                         else if (cur->xmax > tot->xmax) {
303                                 /* - only offset by difference of cur-xmax and tot-xmax if that would not move 
304                                  *      cur-xmin to lie past tot-xmin
305                                  * - otherwise, simply shift to tot-xmin???
306                                  */
307                                 temp= cur->xmax - tot->xmax;
308                                 
309                                 if ((cur->xmin - temp) < tot->xmin) {
310                                         /* only offset by difference from cur-min and tot-min */
311                                         temp= cur->xmin - tot->xmin;
312                                         
313                                         cur->xmin -= temp;
314                                         cur->xmax -= temp;
315                                 }
316                                 else {
317                                         cur->xmin -= temp;
318                                         cur->xmax -= temp;
319                                 }
320                         }
321                 }
322                 else {
323                         /* This here occurs when:
324                          *      - width too big, but maintaining zoom (i.e. widths cannot be changed)
325                          *      - width is OK, but need to check if outside of boundaries
326                          * 
327                          * So, resolution is to just shift view by the gap between the extremities.
328                          * We favour moving the 'minimum' across, as that's origin for most things
329                          * (XXX - in the past, max was favoured... if there are bugs, swap!)
330                          */
331                         if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
332                                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
333                                 temp= (tot->ymax + tot->ymin) * 0.5f;
334                                 diff= curheight * 0.5f;
335                                 
336                                 cur->ymin= temp - diff;
337                                 cur->ymax= temp + diff;
338                         }
339                         else if (cur->xmin > tot->xmin) {
340                                 /* there's still space remaining, so shift left */
341                                 temp= cur->xmin - tot->xmin;
342                                 
343                                 cur->xmin -= temp;
344                                 cur->xmax -= temp;
345                         }
346                         else if (cur->xmax < tot->xmax) {
347                                 /* there's still space remaining, so shift right */
348                                 temp= tot->xmax - cur->xmax;
349                                 
350                                 cur->xmin += temp;
351                                 cur->xmax += temp;
352                         }
353                 }
354                 
355                 /* height */
356                 if ( (curheight > totheight) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_Y)) ) {
357                         /* if zoom doesn't have to be maintained, just clamp edges */
358                         if (cur->ymin < tot->ymin) cur->ymin= tot->ymin;
359                         if (cur->ymax > tot->ymax) cur->ymax= tot->ymax;
360                 }
361                 else {
362                         /* This here occurs when:
363                          *      - height too big, but maintaining zoom (i.e. heights cannot be changed)
364                          *      - height is OK, but need to check if outside of boundaries
365                          * 
366                          * So, resolution is to just shift view by the gap between the extremities.
367                          * We favour moving the 'minimum' across, as that's origin for most things
368                          */
369                         if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
370                                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
371                                 temp= (tot->ymax + tot->ymin) * 0.5f;
372                                 diff= curheight * 0.5f;
373                                 
374                                 cur->ymin= temp - diff;
375                                 cur->ymax= temp + diff;
376                         }
377                         else if (cur->ymin < tot->ymin) {
378                                 /* there's still space remaining, so shift up */
379                                 temp= tot->ymin - cur->ymin;
380                                 
381                                 cur->ymin += temp;
382                                 cur->ymax += temp;
383                         }
384                         else if (cur->ymax > tot->ymax) {
385                                 /* there's still space remaining, so shift down */
386                                 temp= cur->ymax - tot->ymax;
387                                 
388                                 cur->ymin -= temp;
389                                 cur->ymax -= temp;
390                         }
391                 }
392         }
393         
394 }
395
396 /* ------------------ */
397
398 /* Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot) */
399 void UI_view2d_curRect_reset (View2D *v2d)
400 {
401         float width, height;
402         
403         /* assume width and height of 'cur' rect by default, should be same size as mask */
404         width= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
405         height= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
406         
407         /* handle width - posx and negx flags are mutually exclusive, so watch out */
408         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
409                 /* width is in negative-x half */
410                 v2d->cur.xmin= (float)-width;
411                 v2d->cur.xmax= 0.0f;
412         }
413         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
414                 /* width is in positive-x half */
415                 v2d->cur.xmin= 0.0f;
416                 v2d->cur.xmax= (float)width;
417         }
418         else {
419                 /* width is centered around x==0 */
420                 const float dx= (float)width / 2.0f;
421                 
422                 v2d->cur.xmin= -dx;
423                 v2d->cur.xmax= dx;
424         }
425         
426         /* handle height - posx and negx flags are mutually exclusive, so watch out */
427         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
428                 /* height is in negative-y half */
429                 v2d->cur.ymin= (float)-height;
430                 v2d->cur.ymax= 0.0f;
431         }
432         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
433                 /* height is in positive-y half */
434                 v2d->cur.ymin= 0.0f;
435                 v2d->cur.ymax= (float)height;
436         }
437         else {
438                 /* height is centered around y==0 */
439                 const float dy= (float)height / 2.0f;
440                 
441                 v2d->cur.ymin= -dy;
442                 v2d->cur.ymax= dy;
443         }
444 }
445
446 /* Change the size of the maximum viewable area (i.e. 'tot' rect) */
447 void UI_view2d_totRect_set (View2D *v2d, int width, int height)
448 {
449         /* don't do anything if either value is 0 */
450         if (ELEM3(0, v2d, width, height))
451                 return;
452         
453         /* handle width - posx and negx flags are mutually exclusive, so watch out */
454         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
455                 /* width is in negative-x half */
456                 v2d->tot.xmin= (float)-width;
457                 v2d->tot.xmax= 0.0f;
458         }
459         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
460                 /* width is in positive-x half */
461                 v2d->tot.xmin= 0.0f;
462                 v2d->tot.xmax= (float)width;
463         }
464         else {
465                 /* width is centered around x==0 */
466                 const float dx= (float)width / 2.0f;
467                 
468                 v2d->tot.xmin= -dx;
469                 v2d->tot.xmax= dx;
470         }
471         
472         /* handle height - posx and negx flags are mutually exclusive, so watch out */
473         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
474                 /* height is in negative-y half */
475                 v2d->tot.ymin= (float)-height;
476                 v2d->tot.ymax= 0.0f;
477         }
478         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
479                 /* height is in positive-y half */
480                 v2d->tot.ymin= 0.0f;
481                 v2d->tot.ymax= (float)height;
482         }
483         else {
484                 /* height is centered around y==0 */
485                 const float dy= (float)height / 2.0f;
486                 
487                 v2d->tot.ymin= -dy;
488                 v2d->tot.ymax= dy;
489         }
490         
491         /* make sure that 'cur' rect is in a valid state as a result of these changes */
492         UI_view2d_curRect_validate(v2d);
493 }
494
495 /* *********************************************************************** */
496 /* View Matrix Setup */
497
498 /* mapping function to ensure 'cur' draws extended over the area were sliders are */
499 static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
500 {
501         *curmasked= v2d->cur;
502         
503         if (v2d->scroll) {
504                 float dx= ((float)(v2d->mask.xmax-v2d->mask.xmin+1))/(v2d->cur.xmax-v2d->cur.xmin);
505                 float dy= ((float)(v2d->mask.ymax-v2d->mask.ymin+1))/(v2d->cur.ymax-v2d->cur.ymin);
506                 
507                 if (v2d->mask.xmin != 0)
508                         curmasked->xmin -= dx*(float)v2d->mask.xmin;
509                 if (v2d->mask.xmax+1 != v2d->winx)
510                         curmasked->xmax += dx*(float)(v2d->winx - v2d->mask.xmax-1);
511                 
512                 if (v2d->mask.ymin != 0)
513                         curmasked->ymin -= dy*(float)v2d->mask.ymin;
514                 if (v2d->mask.ymax+1 != v2d->winy)
515                         curmasked->ymax += dy*(float)(v2d->winy - v2d->mask.ymax-1);
516                 
517         }
518 }
519
520 /* Set view matrices to use 'cur' rect as viewing frame for View2D drawing 
521 *       - this assumes viewport/scissor been set for the region, taking scrollbars into account
522 */
523
524 void UI_view2d_view_ortho(const bContext *C, View2D *v2d)
525 {
526         rctf curmasked;
527         
528         /* set the matrix - pixel offsets (-0.375) for 1:1 correspondance are not applied, 
529          * as they were causing some unwanted offsets when drawing 
530          */
531         view2d_map_cur_using_mask(v2d, &curmasked);
532         wmOrtho2(C->window, curmasked.xmin, curmasked.xmax, curmasked.ymin, curmasked.ymax);
533         
534         /* XXX is this necessary? */
535         wmLoadIdentity(C->window);
536 }
537
538 /* Set view matrices to only use one axis of 'cur' only
539  *      - this assumes viewport/scissor been set for the region, taking scrollbars into account
540  *
541  *      - xaxis         = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x)
542  */
543 void UI_view2d_view_orthoSpecial(const bContext *C, View2D *v2d, short xaxis)
544 {
545         ARegion *ar= C->region;
546         rctf curmasked;
547         
548         /* set the matrix - pixel offsets (-0.375) for 1:1 correspondance are not applied, 
549          * as they were causing some unwanted offsets when drawing 
550          */
551         view2d_map_cur_using_mask(v2d, &curmasked);
552         if (xaxis)
553                 wmOrtho2(C->window, curmasked.xmin, curmasked.xmax, 0, ar->winy);
554         else
555                 wmOrtho2(C->window, 0, ar->winx, curmasked.ymin, curmasked.ymax);
556                 
557         /* XXX is this necessary? */
558         wmLoadIdentity(C->window);
559
560
561
562 /* Restore view matrices after drawing */
563 void UI_view2d_view_restore(const bContext *C)
564 {
565         ED_region_pixelspace(C, C->region);
566 }
567
568 /* allowing horizontal pan */
569 void UI_view2d_header_default(View2D *v2d)
570 {
571         v2d->keepaspect= 1;
572         v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM);
573         v2d->keepofs = V2D_LOCKOFS_Y;
574         v2d->keeptot = 2; // this keeps the view in place when region size changes...
575         v2d->align = V2D_ALIGN_NO_NEG_X;
576         
577 }
578
579 /* *********************************************************************** */
580 /* Gridlines */
581
582 /* minimum pixels per gridstep */
583 #define MINGRIDSTEP     35
584
585 /* View2DGrid is typedef'd in UI_view2d.h */
586 struct View2DGrid {
587         float dx, dy;                   /* stepsize (in pixels) between gridlines */
588         float startx, starty;   /* initial coordinates to start drawing grid from */
589         int powerx, powery;             /* step as power of 10 */
590 };
591
592 /* --------------- */
593
594 /* try to write step as a power of 10 */
595 static void step_to_grid(float *step, int *power, int unit)
596 {
597         const float loga= log10(*step);
598         float rem;
599         
600         *power= (int)(loga);
601         
602         rem= loga - (*power);
603         rem= pow(10.0, rem);
604         
605         if (loga < 0.0) {
606                 if (rem < 0.2) rem= 0.2;
607                 else if(rem < 0.5) rem= 0.5;
608                 else rem= 1.0;
609                 
610                 *step= rem * pow(10.0, (float)(*power));
611                 
612                 /* for frames, we want 1.0 frame intervals only */
613                 if (unit == V2D_UNIT_FRAMES) {
614                         rem = 1.0;
615                         *step = 1.0;
616                 }
617                 
618                 /* prevents printing 1.0 2.0 3.0 etc */
619                 if (rem == 1.0) (*power)++;     
620         }
621         else {
622                 if (rem < 2.0) rem= 2.0;
623                 else if(rem < 5.0) rem= 5.0;
624                 else rem= 10.0;
625                 
626                 *step= rem * pow(10.0, (float)(*power));
627                 
628                 (*power)++;
629                 /* prevents printing 1.0, 2.0, 3.0, etc. */
630                 if (rem == 10.0) (*power)++;    
631         }
632 }
633
634 /* Intialise settings necessary for drawing gridlines in a 2d-view 
635  *      - Currently, will return pointer to View2DGrid struct that needs to 
636  *        be freed with UI_view2d_grid_free()
637  *      - Is used for scrollbar drawing too (for units drawing)
638  *      
639  *      - unit  = V2D_UNIT_*  grid steps in seconds or frames 
640  *      - clamp = V2D_CLAMP_* only show whole-number intervals
641  *      - winx  = width of region we're drawing to
642  *      - winy  = height of region we're drawing into
643  */
644 View2DGrid *UI_view2d_grid_calc(const bContext *C, View2D *v2d, short unit, short clamp, int winx, int winy)
645 {
646         View2DGrid *grid;
647         float space, pixels, seconddiv;
648         int secondgrid;
649         
650         /* grid here is allocated... */
651         grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
652         
653         /* rule: gridstep is minimal GRIDSTEP pixels */
654         if (unit == V2D_UNIT_SECONDS) {
655                 secondgrid= 1;
656                 seconddiv= 0.01f * FPS;
657         }
658         else {
659                 secondgrid= 0;
660                 seconddiv= 1.0f;
661         }
662         
663         /* calculate x-axis grid scale */
664         space= v2d->cur.xmax - v2d->cur.xmin;
665         pixels= v2d->mask.xmax - v2d->mask.xmin;
666         
667         grid->dx= (MINGRIDSTEP * space) / (seconddiv * pixels);
668         step_to_grid(&grid->dx, &grid->powerx, unit);
669         grid->dx *= seconddiv;
670         
671         if (clamp == V2D_GRID_CLAMP) {
672                 if (grid->dx < 0.1f) grid->dx= 0.1f;
673                 grid->powerx-= 2;
674                 if (grid->powerx < -2) grid->powerx= -2;
675         }
676         
677         /* calculate y-axis grid scale */
678         space= (v2d->cur.ymax - v2d->cur.ymin);
679         pixels= winy;
680         
681         grid->dy= MINGRIDSTEP * space / pixels;
682         step_to_grid(&grid->dy, &grid->powery, unit);
683         
684         if (clamp == V2D_GRID_CLAMP) {
685                 if (grid->dy < 1.0f) grid->dy= 1.0f;
686                 if (grid->powery < 1) grid->powery= 1;
687         }
688         
689         /* calculate start position */
690         grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
691         if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
692         
693         grid->starty= (v2d->cur.ymin - fmod(v2d->cur.ymin, grid->dy));
694         if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
695
696         return grid;
697 }
698
699 /* Draw gridlines in the given 2d-region */
700 void UI_view2d_grid_draw(const bContext *C, View2D *v2d, View2DGrid *grid, int flag)
701 {
702         float vec1[2], vec2[2];
703         int a, step;
704         
705         /* vertical lines */
706         if (flag & V2D_VERTICAL_LINES) {
707                 /* initialise initial settings */
708                 vec1[0]= vec2[0]= grid->startx;
709                 vec1[1]= grid->starty;
710                 vec2[1]= v2d->cur.ymax;
711                 
712                 /* minor gridlines */
713                 step= (v2d->mask.xmax - v2d->mask.xmin + 1) / MINGRIDSTEP;
714                 UI_ThemeColor(TH_GRID);
715                 
716                 for (a=0; a<step; a++) {
717                         glBegin(GL_LINE_STRIP);
718                                 glVertex2fv(vec1); 
719                                 glVertex2fv(vec2);
720                         glEnd();
721                         
722                         vec2[0]= vec1[0]+= grid->dx;
723                 }
724                 
725                 /* major gridlines */
726                 vec2[0]= vec1[0]-= 0.5f*grid->dx;
727                 UI_ThemeColorShade(TH_GRID, 16);
728                 
729                 step++;
730                 for (a=0; a<=step; a++) {
731                         glBegin(GL_LINE_STRIP);
732                                 glVertex2fv(vec1); 
733                                 glVertex2fv(vec2);
734                         glEnd();
735                         
736                         vec2[0]= vec1[0]-= grid->dx;
737                 }
738         }
739         
740         /* horizontal lines */
741         if (flag & V2D_HORIZONTAL_LINES) {
742                 /* only major gridlines */
743                 vec1[0]= grid->startx;
744                 vec1[1]= vec2[1]= grid->starty;
745                 vec2[0]= v2d->cur.xmax;
746                 
747                 step= (v2d->mask.ymax - v2d->mask.ymax + 1) / MINGRIDSTEP;
748                 
749                 UI_ThemeColor(TH_GRID);
750                 for (a=0; a<=step; a++) {
751                         glBegin(GL_LINE_STRIP);
752                                 glVertex2fv(vec1); 
753                                 glVertex2fv(vec2);
754                         glEnd();
755                         
756                         vec2[1]= vec1[1]+= grid->dy;
757                 }
758                 
759                 /* fine grid lines */
760                 // er... only in IPO-Editor it seems (how to expose this in nice way)?
761                 vec2[1]= vec1[1]-= 0.5f*grid->dy;
762                 step++;
763                 
764 #if 0
765                 if (curarea->spacetype==SPACE_IPO) { 
766                         UI_ThemeColorShade(TH_GRID, 16);
767                         for (a=0; a<step; a++) {
768                                 glBegin(GL_LINE_STRIP);
769                                         glVertex2fv(vec1); 
770                                         glVertex2fv(vec2);
771                                 glEnd();
772                                 
773                                 vec2[1]= vec1[1]-= grid->dy;
774                         }
775                 }
776 #endif
777         }
778         
779         /* Axes are drawn as darker lines */
780         UI_ThemeColorShade(TH_GRID, -50);
781         
782         /* horizontal axis */
783         if (flag & V2D_HORIZONTAL_AXIS) {
784                 vec1[0]= v2d->cur.xmin;
785                 vec2[0]= v2d->cur.xmax;
786                 vec1[1]= vec2[1]= 0.0f;
787                 
788                 glBegin(GL_LINE_STRIP);
789                         glVertex2fv(vec1);
790                         glVertex2fv(vec2);
791                 glEnd();
792         }
793         
794         /* vertical axis */
795         if (flag & V2D_VERTICAL_AXIS) {
796                 vec1[1]= v2d->cur.ymin;
797                 vec2[1]= v2d->cur.ymax;
798                 vec1[0]= vec2[0]= 0.0f;
799                 
800                 glBegin(GL_LINE_STRIP);
801                         glVertex2fv(vec1); 
802                         glVertex2fv(vec2);
803                 glEnd();
804         }
805 }
806
807 /* free temporary memory used for drawing grid */
808 void UI_view2d_grid_free(View2DGrid *grid)
809 {
810         MEM_freeN(grid);
811 }
812
813 /* *********************************************************************** */
814 /* Scrollbars */
815
816 /* View2DScrollers is typedef'd in UI_view2d.h 
817  * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. 
818  *                 For now, we don't need to have a separate (internal) header for structs like this...
819  */
820 struct View2DScrollers {        
821                 /* focus bubbles */
822         int vert_min, vert_max; /* vertical scrollbar */
823         int hor_min, hor_max;   /* horizontal scrollbar */
824         
825                 /* scales */
826         View2DGrid *grid;               /* grid for coordinate drawing */
827         short xunits, xclamp;   /* units and clamping options for x-axis */
828         short yunits, yclamp;   /* units and clamping options for y-axis */
829 };
830
831 /* Calculate relevant scroller properties */
832 View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
833 {
834         View2DScrollers *scrollers;
835         rcti vert, hor;
836         float fac, totsize, scrollsize;
837         
838         vert= v2d->vert;
839         hor= v2d->hor;
840         
841         /* scrollers is allocated here... */
842         scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
843         
844         /* scroller 'buttons':
845          *      - These should always remain within the visible region of the scrollbar
846          *      - They represent the region of 'tot' that is visible in 'cur'
847          */
848         
849         /* horizontal scrollers */
850         if (v2d->scroll & (V2D_SCROLL_HORIZONTAL|V2D_SCROLL_HORIZONTAL_O)) {
851                 /* scroller 'button' extents */
852                 totsize= v2d->tot.xmax - v2d->tot.xmin;
853                 scrollsize= hor.xmax - hor.xmin;
854                 
855                 fac= (v2d->cur.xmin- v2d->tot.xmin) / totsize;
856                 scrollers->hor_min= hor.xmin + (fac * scrollsize);
857                 
858                 fac= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
859                 scrollers->hor_max= hor.xmin + (fac * scrollsize);
860                 
861                 if (scrollers->hor_min > scrollers->hor_max) 
862                         scrollers->hor_min= scrollers->hor_max;
863         }
864         
865         /* vertical scrollers */
866         if (v2d->scroll & V2D_SCROLL_VERTICAL) {
867                 /* scroller 'button' extents */
868                 totsize= v2d->tot.ymax - v2d->tot.ymin;
869                 scrollsize= vert.ymax - vert.ymin;
870                 
871                 fac= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
872                 scrollers->vert_min= vert.ymin + (fac * scrollsize);
873                 
874                 fac= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
875                 scrollers->vert_max= vert.ymin + (fac * scrollsize);
876                 
877                 if (scrollers->vert_min > scrollers->vert_max) 
878                         scrollers->vert_min= scrollers->vert_max;
879         }
880         
881         /* grid markings on scrollbars */
882         if (v2d->scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
883                 /* store clamping */
884                 scrollers->xclamp= xclamp;
885                 scrollers->xunits= xunits;
886                 scrollers->yclamp= yclamp;
887                 scrollers->yunits= yunits;
888                 
889                 /* calculate grid only if clamping + units are valid arguments */
890                 if ( !((xclamp == V2D_ARG_DUMMY) && (xunits == V2D_ARG_DUMMY) && (yclamp == V2D_ARG_DUMMY) && (yunits == V2D_ARG_DUMMY)) ) { 
891                         /* if both axes show scale, give priority to horizontal.. */
892                         // FIXME: this doesn't do justice to the vertical scroller calculations...
893                         if (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL)
894                                 scrollers->grid= UI_view2d_grid_calc(C, v2d, xunits, xclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
895                         else if (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL)
896                                 scrollers->grid= UI_view2d_grid_calc(C, v2d, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
897                 }
898         }
899         
900         /* return scrollers */
901         return scrollers;
902 }
903
904 /* XXX */
905 extern void ui_rasterpos_safe(float x, float y, float aspect);
906
907 /* Print scale marking along a time scrollbar */
908 static void scroll_printstr(View2DScrollers *scrollers, float x, float y, float val, int power, short unit, char dir)
909 {
910         int len;
911         char str[32];
912         
913         /* adjust the scale unit to work ok */
914         if (dir == 'v') {
915                 /* here we bump up the power by factor of 10, as 
916                  * rotation values (hence 'degrees') are divided by 10 to 
917                  * be able to show the curves at the same time
918                  */
919                 if ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME) {
920                         power += 1;
921                         val *= 10;
922                 }
923         }
924         
925         /* get string to print */
926         if (unit == V2D_UNIT_SECONDS) {
927                 /* SMPTE timecode style:
928                  *      - In general, minutes and seconds should be shown, as most clips will be
929                  *        within this length. Hours will only be included if relevant.
930                  *      - Only show frames when zoomed in enough for them to be relevant 
931                  *        (using separator convention of ';' for frames, ala QuickTime).
932                  *        When showing frames, use slightly different display to avoid confusion with mm:ss format
933                  */
934                 int hours=0, minutes=0, seconds=0, frames=0;
935                 char neg[2]= "";
936                 
937                 /* get values */
938                 if (val < 0) {
939                         /* correction for negative values */
940                         sprintf(neg, "-");
941                         val = -val;
942                 }
943                 if (val >= 3600) {
944                         /* hours */
945                         /* XXX should we only display a single digit for hours since clips are 
946                          *         VERY UNLIKELY to be more than 1-2 hours max? However, that would 
947                          *         go against conventions...
948                          */
949                         hours= (int)val / 3600;
950                         val= fmod(val, 3600);
951                 }
952                 if (val >= 60) {
953                         /* minutes */
954                         minutes= (int)val / 60;
955                         val= fmod(val, 60);
956                 }
957                 if (power <= 0) {
958                         /* seconds + frames
959                          *      Frames are derived from 'fraction' of second. We need to perform some additional rounding
960                          *      to cope with 'half' frames, etc., which should be fine in most cases
961                          */
962                         seconds= (int)val;
963                         frames= (int)floor( ((val - seconds) * FPS) + 0.5f );
964                 }
965                 else {
966                         /* seconds (with pixel offset) */
967                         seconds= (int)floor(val + 0.375f);
968                 }
969                 
970                 /* print timecode to temp string buffer */
971                 if (power <= 0) {
972                         /* include "frames" in display */
973                         if (hours) sprintf(str, "%s%02d:%02d:%02d;%02d", neg, hours, minutes, seconds, frames);
974                         else if (minutes) sprintf(str, "%s%02d:%02d;%02d", neg, minutes, seconds, frames);
975                         else sprintf(str, "%s%d;%02d", neg, seconds, frames);
976                 }
977                 else {
978                         /* don't include 'frames' in display */
979                         if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
980                         else sprintf(str, "%s%02d:%02d", neg, minutes, seconds);
981                 }
982         }
983         else {
984                 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
985                 if (power <= 0) sprintf(str, "%.*f", 1-power, val);
986                 else sprintf(str, "%d", (int)floor(val + 0.375f));
987         }
988         
989         /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
990         len= strlen(str);
991         if (dir == 'h') {
992                 /* seconds/timecode display has slightly longer strings... */
993                 if (unit == V2D_UNIT_SECONDS)
994                         x-= 3*len;
995                 else
996                         x-= 4*len;
997         }
998         
999         /* Add degree sympbol to end of string for vertical scrollbar? */
1000         if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
1001                 str[len]= 186;
1002                 str[len+1]= 0;
1003         }
1004         
1005         /* draw it */
1006         ui_rasterpos_safe(x, y, 1.0);
1007         UI_DrawString(G.fonts, str, 0); // XXX check this again when new text-drawing api is done
1008 }
1009
1010 /* local define for radius of scroller 'button' caps */
1011 #define V2D_SCROLLCAP_RAD       5
1012
1013 /* Draw scrollbars in the given 2d-region */
1014 void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
1015 {
1016         const int darker= -50, dark= -10, light= 20, lighter= 50;
1017         rcti vert, hor;
1018         
1019         /* make copies of rects for less typing */
1020         vert= v2d->vert;
1021         hor= v2d->hor;
1022         
1023         /* horizontal scrollbar */
1024         if (v2d->scroll & (V2D_SCROLL_HORIZONTAL|V2D_SCROLL_HORIZONTAL_O)) {
1025                 /* scroller backdrop */
1026                 UI_ThemeColorShade(TH_SHADE1, light);
1027                 glRecti(hor.xmin,  hor.ymin,  hor.xmax,  hor.ymax);
1028                 
1029                 /* scroller 'button' 
1030                  *      - if view is zoomable in x, draw handles too 
1031                  *      - handles are drawn darker
1032                  */
1033                 if (v2d->keepzoom & V2D_LOCKZOOM_X) {
1034                         /* draw base bar as rounded shape */
1035                         UI_ThemeColorShade(TH_SHADE1, dark);
1036                         
1037                         uiSetRoundBox(15);
1038                         gl_round_box_shade(GL_POLYGON, 
1039                                         vs->hor_min, hor.ymin+2, 
1040                                         vs->hor_max, hor.ymax-2, 
1041                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1042                 }
1043                 else {
1044                         /* base bar drawn as shaded rect */
1045                         UI_ThemeColorShade(TH_SHADE1, dark);
1046                         uiSetRoundBox(0);
1047                         gl_round_box_shade(GL_POLYGON, 
1048                                         vs->hor_min, hor.ymin+2, 
1049                                         vs->hor_max, hor.ymax-2, 
1050                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1051                         
1052                         /* handles draw darker */
1053                         // XXX handles are drawn with the two last args set to same values, otherwise, max appears darker than min
1054                         UI_ThemeColorShade(TH_SHADE1, darker);
1055                         
1056                         /* 'minimum' handle */
1057                         uiSetRoundBox(9);
1058                         gl_round_box_shade(GL_POLYGON, 
1059                                         vs->hor_min-V2D_SCROLLER_HANDLE_SIZE, hor.ymin+2, 
1060                                         vs->hor_min+V2D_SCROLLER_HANDLE_SIZE, hor.ymax-2, 
1061                                         V2D_SCROLLCAP_RAD, 0.07f, -0.07f);
1062                                         //V2D_SCROLLCAP_RAD, 0, 0);
1063                         
1064                         /* maximum handle */
1065                         uiSetRoundBox(6);
1066                         gl_round_box_shade(GL_POLYGON, 
1067                                         vs->hor_max-V2D_SCROLLER_HANDLE_SIZE, hor.ymin+2, 
1068                                         vs->hor_max+V2D_SCROLLER_HANDLE_SIZE, hor.ymax-2, 
1069                                         V2D_SCROLLCAP_RAD, 0.07f, -0.07f);
1070                                         //V2D_SCROLLCAP_RAD, 0, 0);
1071                 }
1072                 
1073                 /* scale indicators */
1074                 // XXX will need to update the font drawing when the new stuff comes in
1075                 if (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) {
1076                         View2DGrid *grid= vs->grid;
1077                         float fac, dfac, fac2, val;
1078                         
1079                         /* the numbers: convert grid->startx and -dx to scroll coordinates 
1080                          *      - fac is x-coordinate to draw to
1081                          *      - dfac is gap between scale markings
1082                          */
1083                         fac= (grid->startx- v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1084                         fac= hor.xmin + fac*(hor.xmax - hor.xmin);
1085                         
1086                         dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
1087                         dfac= dfac * (hor.xmax - hor.xmin);
1088                         
1089                         /* set starting value, and text color */
1090                         UI_ThemeColor(TH_TEXT);
1091                         val= grid->startx;
1092                         
1093                         /* if we're clamping to whole numbers only, make sure entries won't be repeated */
1094                         if (vs->xclamp == V2D_GRID_CLAMP) {
1095                                 while (grid->dx < 0.9999f) {
1096                                         grid->dx *= 2.0f;
1097                                         dfac *= 2.0f;
1098                                 }
1099                         }
1100                         if (vs->xunits == V2D_UNIT_FRAMES)
1101                                 grid->powerx= 1;
1102                         
1103                         /* draw numbers in the appropriate range */
1104                         if (dfac != 0.0f) {
1105                                 for (; fac < hor.xmax; fac+=dfac, val+=grid->dx) {
1106                                         switch (vs->xunits) {
1107                                                 case V2D_UNIT_FRAMES:           /* frames (as whole numbers)*/
1108                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_FRAMES, 'h');
1109                                                         break;
1110                                                 
1111                                                 case V2D_UNIT_SECONDS:          /* seconds */
1112                                                         fac2= val/FPS;
1113                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
1114                                                         break;
1115                                                         
1116                                                 case V2D_UNIT_SECONDSSEQ:       /* seconds with special calculations (only used for sequencer only) */
1117                                                 {
1118                                                         float time;
1119                                                         
1120                                                         fac2= val/FPS;
1121                                                         time= floor(fac2);
1122                                                         fac2= fac2-time;
1123                                                         
1124                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), time+FPS*fac2/100.0, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
1125                                                 }
1126                                                         break;
1127                                                         
1128                                                 case V2D_UNIT_DEGREES:          /* IPO-Editor for rotation IPO-Drivers */
1129                                                         /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
1130                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_DEGREES, 'v');
1131                                                         break;
1132                                         }
1133                                 }
1134                         }
1135                 }
1136                 
1137                 /* decoration outer bevel line */
1138                 UI_ThemeColorShade(TH_SHADE1, lighter);
1139                 if (v2d->scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O))
1140                         sdrawline(hor.xmin, hor.ymax, hor.xmax, hor.ymax);
1141                 else if (v2d->scroll & V2D_SCROLL_TOP)
1142                         sdrawline(hor.xmin, hor.ymin, hor.xmax, hor.ymin);
1143         }
1144         
1145         /* vertical scrollbar */
1146         if (v2d->scroll & V2D_SCROLL_VERTICAL) {
1147                 /* scroller backdrop  */
1148                 UI_ThemeColorShade(TH_SHADE1, light);
1149                 glRecti(vert.xmin,  vert.ymin,  vert.xmax,  vert.ymax);
1150                 
1151                 /* scroller 'button' 
1152                  *      - if view is zoomable in y, draw handles too 
1153                  *      - handles are drawn darker
1154                  */
1155                 // XXX gl_round_box_shade is currently hardcoded to do horizontal buts only... need a vertical region
1156                 if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
1157                         /* draw base bar as rounded shape */
1158                         UI_ThemeColorShade(TH_SHADE1, dark);
1159                         
1160                         uiSetRoundBox(15);
1161                         gl_round_box_shade(GL_POLYGON, 
1162                                         vert.xmin+2, vs->vert_min, 
1163                                         vert.xmax-2, vs->vert_max, 
1164                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1165                 }
1166                 else {
1167                         /* for now, draw base bar as unshaded rect */
1168                         // FIXME: this should be shaded horizontally too
1169                         UI_ThemeColorShade(TH_SHADE1, dark);
1170                         glRecti(vert.xmin+2, vs->vert_min,  
1171                                         vert.xmax-2, vs->vert_max);
1172                         
1173                         /* handles draw darker */
1174                         // XXX handles are drawn with the two last args set to same values, otherwise, max appears darker than min
1175                         UI_ThemeColorShade(TH_SHADE1, darker);
1176                         
1177                         /* 'minimum' handle */
1178                         uiSetRoundBox(12);
1179                         gl_round_box_shade(GL_POLYGON, 
1180                                         vert.xmin+2, vs->vert_min-V2D_SCROLLER_HANDLE_SIZE, 
1181                                         vert.xmax-2, vs->vert_min+V2D_SCROLLER_HANDLE_SIZE, 
1182                                         V2D_SCROLLCAP_RAD, 0, 0);
1183                         
1184                         /* maximum handle */
1185                         uiSetRoundBox(3);
1186                         gl_round_box_shade(GL_POLYGON, 
1187                                         vert.xmin+2, vs->vert_max-V2D_SCROLLER_HANDLE_SIZE, 
1188                                         vert.xmax-2, vs->vert_max+V2D_SCROLLER_HANDLE_SIZE, 
1189                                         V2D_SCROLLCAP_RAD, 0, 0);
1190                 }
1191                 
1192                 /* scale indiators */
1193                 // XXX will need to update the font drawing when the new stuff comes in
1194                 if (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) {
1195                         View2DGrid *grid= vs->grid;
1196                         float fac, dfac, val;
1197                         
1198                         /* the numbers: convert grid->starty and dy to scroll coordinates 
1199                          *      - fac is y-coordinate to draw to
1200                          *      - dfac is gap between scale markings
1201                          *      - these involve a correction for horizontal scrollbar
1202                          *        NOTE: it's assumed that that scrollbar is there if this is involved!
1203                          */
1204                         fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1205                         fac= (vert.ymin + V2D_SCROLL_HEIGHT) + fac*(vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1206                         
1207                         dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
1208                         dfac= dfac * (vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1209                         
1210                         /* set starting value, and text color */
1211                         UI_ThemeColor(TH_TEXT);
1212                         val= grid->starty;
1213                         
1214                         /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
1215                         // XXX only relevant to Sequencer, so need to review this when we port that code
1216                         if (vs->yclamp == V2D_GRID_CLAMP)
1217                                 fac += 0.5f * dfac;
1218                                 
1219                         /* draw vertical steps */
1220                         for (; fac < vert.ymax; fac+= dfac, val += grid->dy) {
1221                                 scroll_printstr(vs, (float)(vert.xmax)-14.0, fac, val, grid->powery, vs->yunits, 'v');
1222                         }                       
1223                 }       
1224                 
1225                 /* decoration outer bevel line */
1226                 UI_ThemeColorShade(TH_SHADE1, lighter);
1227                 if (v2d->scroll & V2D_SCROLL_RIGHT)
1228                         sdrawline(vert.xmin, vert.ymin, vert.xmin, vert.ymax);
1229                 else if (v2d->scroll & V2D_SCROLL_LEFT)
1230                         sdrawline(vert.xmax, vert.ymin, vert.xmax, vert.ymax);
1231         }
1232 }
1233
1234 /* free temporary memory used for drawing scrollers */
1235 void UI_view2d_scrollers_free(View2DScrollers *scrollers)
1236 {
1237         /* need to free grid as well... */
1238         if (scrollers->grid) MEM_freeN(scrollers->grid);
1239         MEM_freeN(scrollers);
1240 }
1241
1242 /* *********************************************************************** */
1243 /* Coordinate Conversions */
1244
1245 /* Convert from screen/region space to 2d-View space 
1246  *      
1247  *      - x,y                   = coordinates to convert
1248  *      - viewx,viewy           = resultant coordinates
1249  */
1250 void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
1251 {
1252         float div, ofs;
1253
1254         if (viewx) {
1255                 div= v2d->mask.xmax - v2d->mask.xmin;
1256                 ofs= v2d->mask.xmin;
1257                 
1258                 *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
1259         }
1260
1261         if (viewy) {
1262                 div= v2d->mask.ymax - v2d->mask.ymin;
1263                 ofs= v2d->mask.ymin;
1264                 
1265                 *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
1266         }
1267 }
1268
1269 /* Convert from 2d-View space to screen/region space
1270  *      - Coordinates are clamped to lie within bounds of region
1271  *
1272  *      - x,y                           = coordinates to convert
1273  *      - regionx,regiony       = resultant coordinates 
1274  */
1275 void UI_view2d_view_to_region(View2D *v2d, float x, float y, short *regionx, short *regiony)
1276 {
1277         /* set initial value in case coordinate lies outside of bounds */
1278         if (regionx)
1279                 *regionx= V2D_IS_CLIPPED;
1280         if (regiony)
1281                 *regiony= V2D_IS_CLIPPED;
1282         
1283         /* express given coordinates as proportional values */
1284         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1285         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1286         
1287         /* check if values are within bounds */
1288         if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
1289                 if (regionx)
1290                         *regionx= v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin);
1291                 if (regiony)
1292                         *regiony= v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin);
1293         }
1294 }
1295
1296 /* Convert from 2d-view space to screen/region space
1297  *      - Coordinates are NOT clamped to lie within bounds of region
1298  *
1299  *      - x,y                           = coordinates to convert
1300  *      - regionx,regiony       = resultant coordinates 
1301  */
1302 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, short *regionx, short *regiony)
1303 {
1304         /* step 1: express given coordinates as proportional values */
1305         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1306         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1307         
1308         /* step 2: convert proportional distances to screen coordinates  */
1309         x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
1310         y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
1311         
1312         /* although we don't clamp to lie within region bounds, we must avoid exceeding size of shorts */
1313         if (regionx) {
1314                 if (x < -32760) *regionx= -32760;
1315                 else if(x > 32760) *regionx= 32760;
1316                 else *regionx= x;
1317         }
1318         if (regiony) {
1319                 if (y < -32760) *regiony= -32760;
1320                 else if(y > 32760) *regiony= 32760;
1321                 else *regiony= y;
1322         }
1323 }
1324
1325 /* *********************************************************************** */
1326 /* Utilities */
1327
1328 /* View2D data by default resides in region, so get from region stored in context */
1329 View2D *UI_view2d_fromcontext(const bContext *C)
1330 {
1331         if (C->area == NULL) return NULL;
1332         if (C->region == NULL) return NULL;
1333         return &(C->region->v2d);
1334 }
1335
1336 /* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
1337 View2D *UI_view2d_fromcontext_rwin(const bContext *C)
1338 {
1339         if (C->area == NULL) return NULL;
1340         if (C->region == NULL) return NULL;
1341         if (C->region->regiontype!=RGN_TYPE_WINDOW) {
1342                 ARegion *ar= C->area->regionbase.first;
1343                 for(; ar; ar= ar->next)
1344                         if(ar->regiontype==RGN_TYPE_WINDOW)
1345                                 return &(ar->v2d);
1346                 return NULL;
1347         }
1348         return &(C->region->v2d);
1349 }
1350
1351
1352 /* Calculate the scale per-axis of the drawing-area
1353  *      - Is used to inverse correct drawing of icons, etc. that need to follow view 
1354  *        but not be affected by scale
1355  *
1356  *      - x,y   = scale on each axis
1357  */
1358 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
1359 {
1360         if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1361         if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1362 }