2.5 - Assorted View2D related fixes
[blender-staging.git] / source / blender / editors / interface / view2d.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 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 /* ------------------ */
447
448 /* Change the size of the maximum viewable area (i.e. 'tot' rect) */
449 void UI_view2d_totRect_set (View2D *v2d, int width, int height)
450 {
451         /* don't do anything if either value is 0 */
452         if (ELEM3(0, v2d, width, height))
453                 return;
454         
455         /* handle width - posx and negx flags are mutually exclusive, so watch out */
456         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
457                 /* width is in negative-x half */
458                 v2d->tot.xmin= (float)-width;
459                 v2d->tot.xmax= 0.0f;
460         }
461         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
462                 /* width is in positive-x half */
463                 v2d->tot.xmin= 0.0f;
464                 v2d->tot.xmax= (float)width;
465         }
466         else {
467                 /* width is centered around x==0 */
468                 const float dx= (float)width / 2.0f;
469                 
470                 v2d->tot.xmin= -dx;
471                 v2d->tot.xmax= dx;
472         }
473         
474         /* handle height - posx and negx flags are mutually exclusive, so watch out */
475         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
476                 /* height is in negative-y half */
477                 v2d->tot.ymin= (float)-height;
478                 v2d->tot.ymax= 0.0f;
479         }
480         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
481                 /* height is in positive-y half */
482                 v2d->tot.ymin= 0.0f;
483                 v2d->tot.ymax= (float)height;
484         }
485         else {
486                 /* height is centered around y==0 */
487                 const float dy= (float)height / 2.0f;
488                 
489                 v2d->tot.ymin= -dy;
490                 v2d->tot.ymax= dy;
491         }
492         
493         /* make sure that 'cur' rect is in a valid state as a result of these changes */
494         UI_view2d_curRect_validate(v2d);
495 }
496
497 /* *********************************************************************** */
498 /* View Matrix Setup */
499
500 /* mapping function to ensure 'cur' draws extended over the area where sliders are */
501 static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
502 {
503         *curmasked= v2d->cur;
504         
505         /* currently, the following 'hack' is only necessary for Outliner, and will cause
506          * errors in all other views...
507          */
508         if ((v2d->scroll) && (v2d->keeptot==2)) {
509                 float dx= ((float)(v2d->mask.xmax-v2d->mask.xmin+1))/(v2d->cur.xmax-v2d->cur.xmin);
510                 float dy= ((float)(v2d->mask.ymax-v2d->mask.ymin+1))/(v2d->cur.ymax-v2d->cur.ymin);
511                 
512                 if (v2d->mask.xmin != 0)
513                         curmasked->xmin -= dx*(float)v2d->mask.xmin;
514                 if (v2d->mask.xmax+1 != v2d->winx)
515                         curmasked->xmax += dx*(float)(v2d->winx - v2d->mask.xmax-1);
516                 
517                 if (v2d->mask.ymin != 0)
518                         curmasked->ymin -= dy*(float)v2d->mask.ymin;
519                 if (v2d->mask.ymax+1 != v2d->winy)
520                         curmasked->ymax += dy*(float)(v2d->winy - v2d->mask.ymax-1);
521                 
522         }
523 }
524
525 /* Set view matrices to use 'cur' rect as viewing frame for View2D drawing 
526 *       - this assumes viewport/scissor been set for the region, taking scrollbars into account
527 */
528
529 void UI_view2d_view_ortho(const bContext *C, View2D *v2d)
530 {
531         rctf curmasked;
532         
533         /* set the matrix - pixel offsets (-0.375) for 1:1 correspondance are not applied, 
534          * as they were causing some unwanted offsets when drawing 
535          */
536         view2d_map_cur_using_mask(v2d, &curmasked);
537         wmOrtho2(C->window, curmasked.xmin, curmasked.xmax, curmasked.ymin, curmasked.ymax);
538         
539         /* XXX is this necessary? */
540         wmLoadIdentity(C->window);
541 }
542
543 /* Set view matrices to only use one axis of 'cur' only
544  *      - this assumes viewport/scissor been set for the region, taking scrollbars into account
545  *
546  *      - xaxis         = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x)
547  */
548 void UI_view2d_view_orthoSpecial(const bContext *C, View2D *v2d, short xaxis)
549 {
550         ARegion *ar= C->region;
551         rctf curmasked;
552         
553         /* set the matrix - pixel offsets (-0.375) for 1:1 correspondance are not applied, 
554          * as they were causing some unwanted offsets when drawing 
555          */
556         view2d_map_cur_using_mask(v2d, &curmasked);
557         if (xaxis)
558                 wmOrtho2(C->window, curmasked.xmin, curmasked.xmax, 0, ar->winy);
559         else
560                 wmOrtho2(C->window, 0, ar->winx, curmasked.ymin, curmasked.ymax);
561                 
562         /* XXX is this necessary? */
563         wmLoadIdentity(C->window);
564
565
566
567 /* Restore view matrices after drawing */
568 void UI_view2d_view_restore(const bContext *C)
569 {
570         ED_region_pixelspace(C, C->region);
571 }
572
573 /* allowing horizontal pan */
574 void UI_view2d_header_default(View2D *v2d)
575 {
576         v2d->keepaspect= 1;
577         v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM);
578         v2d->keepofs = V2D_LOCKOFS_Y;
579         v2d->keeptot = 2; // this keeps the view in place when region size changes...
580         v2d->align = V2D_ALIGN_NO_NEG_X;
581         
582 }
583
584 /* *********************************************************************** */
585 /* Gridlines */
586
587 /* minimum pixels per gridstep */
588 #define MINGRIDSTEP     35
589
590 /* View2DGrid is typedef'd in UI_view2d.h */
591 struct View2DGrid {
592         float dx, dy;                   /* stepsize (in pixels) between gridlines */
593         float startx, starty;   /* initial coordinates to start drawing grid from */
594         int powerx, powery;             /* step as power of 10 */
595 };
596
597 /* --------------- */
598
599 /* try to write step as a power of 10 */
600 static void step_to_grid(float *step, int *power, int unit)
601 {
602         const float loga= log10(*step);
603         float rem;
604         
605         *power= (int)(loga);
606         
607         rem= loga - (*power);
608         rem= pow(10.0, rem);
609         
610         if (loga < 0.0) {
611                 if (rem < 0.2) rem= 0.2;
612                 else if(rem < 0.5) rem= 0.5;
613                 else rem= 1.0;
614                 
615                 *step= rem * pow(10.0, (float)(*power));
616                 
617                 /* for frames, we want 1.0 frame intervals only */
618                 if (unit == V2D_UNIT_FRAMES) {
619                         rem = 1.0;
620                         *step = 1.0;
621                 }
622                 
623                 /* prevents printing 1.0 2.0 3.0 etc */
624                 if (rem == 1.0) (*power)++;     
625         }
626         else {
627                 if (rem < 2.0) rem= 2.0;
628                 else if(rem < 5.0) rem= 5.0;
629                 else rem= 10.0;
630                 
631                 *step= rem * pow(10.0, (float)(*power));
632                 
633                 (*power)++;
634                 /* prevents printing 1.0, 2.0, 3.0, etc. */
635                 if (rem == 10.0) (*power)++;    
636         }
637 }
638
639 /* Intialise settings necessary for drawing gridlines in a 2d-view 
640  *      - Currently, will return pointer to View2DGrid struct that needs to 
641  *        be freed with UI_view2d_grid_free()
642  *      - Is used for scrollbar drawing too (for units drawing)
643  *      
644  *      - unit  = V2D_UNIT_*  grid steps in seconds or frames 
645  *      - clamp = V2D_CLAMP_* only show whole-number intervals
646  *      - winx  = width of region we're drawing to
647  *      - winy  = height of region we're drawing into
648  */
649 View2DGrid *UI_view2d_grid_calc(const bContext *C, View2D *v2d, short unit, short clamp, int winx, int winy)
650 {
651         View2DGrid *grid;
652         float space, pixels, seconddiv;
653         int secondgrid;
654         
655         /* grid here is allocated... */
656         grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
657         
658         /* rule: gridstep is minimal GRIDSTEP pixels */
659         if (unit == V2D_UNIT_SECONDS) {
660                 secondgrid= 1;
661                 seconddiv= 0.01f * FPS;
662         }
663         else {
664                 secondgrid= 0;
665                 seconddiv= 1.0f;
666         }
667         
668         /* calculate x-axis grid scale */
669         space= v2d->cur.xmax - v2d->cur.xmin;
670         pixels= v2d->mask.xmax - v2d->mask.xmin;
671         
672         grid->dx= (MINGRIDSTEP * space) / (seconddiv * pixels);
673         step_to_grid(&grid->dx, &grid->powerx, unit);
674         grid->dx *= seconddiv;
675         
676         if (clamp == V2D_GRID_CLAMP) {
677                 if (grid->dx < 0.1f) grid->dx= 0.1f;
678                 grid->powerx-= 2;
679                 if (grid->powerx < -2) grid->powerx= -2;
680         }
681         
682         /* calculate y-axis grid scale */
683         space= v2d->cur.ymax - v2d->cur.ymin;
684         pixels= winy;
685         
686         grid->dy= MINGRIDSTEP * space / pixels;
687         step_to_grid(&grid->dy, &grid->powery, unit);
688         
689         if (clamp == V2D_GRID_CLAMP) {
690                 if (grid->dy < 1.0f) grid->dy= 1.0f;
691                 if (grid->powery < 1) grid->powery= 1;
692         }
693         
694         /* calculate start position */
695         grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
696         if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
697         
698         grid->starty= (v2d->cur.ymin - fmod(v2d->cur.ymin, grid->dy));
699         if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
700
701         return grid;
702 }
703
704 /* Draw gridlines in the given 2d-region */
705 void UI_view2d_grid_draw(const bContext *C, View2D *v2d, View2DGrid *grid, int flag)
706 {
707         float vec1[2], vec2[2];
708         int a, step;
709         
710         /* vertical lines */
711         if (flag & V2D_VERTICAL_LINES) {
712                 /* initialise initial settings */
713                 vec1[0]= vec2[0]= grid->startx;
714                 vec1[1]= grid->starty;
715                 vec2[1]= v2d->cur.ymax;
716                 
717                 /* minor gridlines */
718                 step= (v2d->mask.xmax - v2d->mask.xmin + 1) / MINGRIDSTEP;
719                 UI_ThemeColor(TH_GRID);
720                 
721                 for (a=0; a<step; a++) {
722                         glBegin(GL_LINE_STRIP);
723                                 glVertex2fv(vec1); 
724                                 glVertex2fv(vec2);
725                         glEnd();
726                         
727                         vec2[0]= vec1[0]+= grid->dx;
728                 }
729                 
730                 /* major gridlines */
731                 vec2[0]= vec1[0]-= 0.5f*grid->dx;
732                 UI_ThemeColorShade(TH_GRID, 16);
733                 
734                 step++;
735                 for (a=0; a<=step; a++) {
736                         glBegin(GL_LINE_STRIP);
737                                 glVertex2fv(vec1); 
738                                 glVertex2fv(vec2);
739                         glEnd();
740                         
741                         vec2[0]= vec1[0]-= grid->dx;
742                 }
743         }
744         
745         /* horizontal lines */
746         if (flag & V2D_HORIZONTAL_LINES) {
747                 puts("draw horizontal lines");
748                 /* only major gridlines */
749                 vec1[1]= vec2[1]= grid->starty;
750                 vec1[0]= grid->startx;
751                 vec2[0]= v2d->cur.xmax;
752                 
753                 step= (v2d->mask.ymax - v2d->mask.ymin + 1) / MINGRIDSTEP;
754                 
755                 UI_ThemeColor(TH_GRID);
756                 for (a=0; a<=step; a++) {
757                         printf("\t a = %d \n", a);
758                         glBegin(GL_LINE_STRIP);
759                                 glVertex2fv(vec1); 
760                                 glVertex2fv(vec2);
761                         glEnd();
762                         
763                         vec2[1]= vec1[1]+= grid->dy;
764                 }
765                 
766                 /* fine grid lines */
767                 vec2[1]= vec1[1]-= 0.5f*grid->dy;
768                 step++;
769                 
770                 if (flag & V2D_HORIZONTAL_FINELINES) { 
771                         UI_ThemeColorShade(TH_GRID, 16);
772                         for (a=0; a<step; a++) {
773                                 glBegin(GL_LINE_STRIP);
774                                         glVertex2fv(vec1); 
775                                         glVertex2fv(vec2);
776                                 glEnd();
777                                 
778                                 vec2[1]= vec1[1]-= grid->dy;
779                         }
780                 }
781         }
782         
783         /* Axes are drawn as darker lines */
784         UI_ThemeColorShade(TH_GRID, -50);
785         
786         /* horizontal axis */
787         if (flag & V2D_HORIZONTAL_AXIS) {
788                 vec1[0]= v2d->cur.xmin;
789                 vec2[0]= v2d->cur.xmax;
790                 vec1[1]= vec2[1]= 0.0f;
791                 
792                 glBegin(GL_LINE_STRIP);
793                         glVertex2fv(vec1);
794                         glVertex2fv(vec2);
795                 glEnd();
796         }
797         
798         /* vertical axis */
799         if (flag & V2D_VERTICAL_AXIS) {
800                 vec1[1]= v2d->cur.ymin;
801                 vec2[1]= v2d->cur.ymax;
802                 vec1[0]= vec2[0]= 0.0f;
803                 
804                 glBegin(GL_LINE_STRIP);
805                         glVertex2fv(vec1); 
806                         glVertex2fv(vec2);
807                 glEnd();
808         }
809 }
810
811 /* free temporary memory used for drawing grid */
812 void UI_view2d_grid_free(View2DGrid *grid)
813 {
814         MEM_freeN(grid);
815 }
816
817 /* *********************************************************************** */
818 /* Scrollbars */
819
820 /* View2DScrollers is typedef'd in UI_view2d.h 
821  * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. 
822  *                 For now, we don't need to have a separate (internal) header for structs like this...
823  */
824 struct View2DScrollers {        
825                 /* focus bubbles */
826         int vert_min, vert_max; /* vertical scrollbar */
827         int hor_min, hor_max;   /* horizontal scrollbar */
828         
829                 /* scales */
830         View2DGrid *grid;               /* grid for coordinate drawing */
831         short xunits, xclamp;   /* units and clamping options for x-axis */
832         short yunits, yclamp;   /* units and clamping options for y-axis */
833 };
834
835 /* Calculate relevant scroller properties */
836 View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
837 {
838         View2DScrollers *scrollers;
839         rcti vert, hor;
840         float fac, totsize, scrollsize;
841         
842         vert= v2d->vert;
843         hor= v2d->hor;
844         
845         /* scrollers is allocated here... */
846         scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
847         
848         /* scroller 'buttons':
849          *      - These should always remain within the visible region of the scrollbar
850          *      - They represent the region of 'tot' that is visible in 'cur'
851          */
852         
853         /* horizontal scrollers */
854         if (v2d->scroll & (V2D_SCROLL_HORIZONTAL|V2D_SCROLL_HORIZONTAL_O)) {
855                 /* scroller 'button' extents */
856                 totsize= v2d->tot.xmax - v2d->tot.xmin;
857                 scrollsize= hor.xmax - hor.xmin;
858                 
859                 fac= (v2d->cur.xmin- v2d->tot.xmin) / totsize;
860                 scrollers->hor_min= hor.xmin + (fac * scrollsize);
861                 
862                 fac= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
863                 scrollers->hor_max= hor.xmin + (fac * scrollsize);
864                 
865                 if (scrollers->hor_min > scrollers->hor_max) 
866                         scrollers->hor_min= scrollers->hor_max;
867         }
868         
869         /* vertical scrollers */
870         if (v2d->scroll & V2D_SCROLL_VERTICAL) {
871                 /* scroller 'button' extents */
872                 totsize= v2d->tot.ymax - v2d->tot.ymin;
873                 scrollsize= vert.ymax - vert.ymin;
874                 
875                 fac= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
876                 scrollers->vert_min= vert.ymin + (fac * scrollsize);
877                 
878                 fac= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
879                 scrollers->vert_max= vert.ymin + (fac * scrollsize);
880                 
881                 if (scrollers->vert_min > scrollers->vert_max) 
882                         scrollers->vert_min= scrollers->vert_max;
883         }
884         
885         /* grid markings on scrollbars */
886         if (v2d->scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
887                 /* store clamping */
888                 scrollers->xclamp= xclamp;
889                 scrollers->xunits= xunits;
890                 scrollers->yclamp= yclamp;
891                 scrollers->yunits= yunits;
892                 
893                 /* calculate grid only if clamping + units are valid arguments */
894                 if ( !((xclamp == V2D_ARG_DUMMY) && (xunits == V2D_ARG_DUMMY) && (yclamp == V2D_ARG_DUMMY) && (yunits == V2D_ARG_DUMMY)) ) { 
895                         /* if both axes show scale, give priority to horizontal.. */
896                         // FIXME: this doesn't do justice to the vertical scroller calculations...
897                         if ((v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) && ELEM(V2D_ARG_DUMMY, xclamp, xunits)==0)
898                                 scrollers->grid= UI_view2d_grid_calc(C, v2d, xunits, xclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
899                         else if (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL && ELEM(V2D_ARG_DUMMY, yclamp, yunits)==0)
900                                 scrollers->grid= UI_view2d_grid_calc(C, v2d, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
901                 }
902         }
903         
904         /* return scrollers */
905         return scrollers;
906 }
907
908 /* XXX */
909 extern void ui_rasterpos_safe(float x, float y, float aspect);
910
911 /* Print scale marking along a time scrollbar */
912 static void scroll_printstr(View2DScrollers *scrollers, float x, float y, float val, int power, short unit, char dir)
913 {
914         int len;
915         char str[32];
916         
917         /* adjust the scale unit to work ok */
918         if (dir == 'v') {
919                 /* here we bump up the power by factor of 10, as 
920                  * rotation values (hence 'degrees') are divided by 10 to 
921                  * be able to show the curves at the same time
922                  */
923                 if ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME) {
924                         power += 1;
925                         val *= 10;
926                 }
927         }
928         
929         /* get string to print */
930         if (unit == V2D_UNIT_SECONDS) {
931                 /* SMPTE timecode style:
932                  *      - In general, minutes and seconds should be shown, as most clips will be
933                  *        within this length. Hours will only be included if relevant.
934                  *      - Only show frames when zoomed in enough for them to be relevant 
935                  *        (using separator convention of ';' for frames, ala QuickTime).
936                  *        When showing frames, use slightly different display to avoid confusion with mm:ss format
937                  */
938                 int hours=0, minutes=0, seconds=0, frames=0;
939                 char neg[2]= "";
940                 
941                 /* get values */
942                 if (val < 0) {
943                         /* correction for negative values */
944                         sprintf(neg, "-");
945                         val = -val;
946                 }
947                 if (val >= 3600) {
948                         /* hours */
949                         /* XXX should we only display a single digit for hours since clips are 
950                          *         VERY UNLIKELY to be more than 1-2 hours max? However, that would 
951                          *         go against conventions...
952                          */
953                         hours= (int)val / 3600;
954                         val= fmod(val, 3600);
955                 }
956                 if (val >= 60) {
957                         /* minutes */
958                         minutes= (int)val / 60;
959                         val= fmod(val, 60);
960                 }
961                 if (power <= 0) {
962                         /* seconds + frames
963                          *      Frames are derived from 'fraction' of second. We need to perform some additional rounding
964                          *      to cope with 'half' frames, etc., which should be fine in most cases
965                          */
966                         seconds= (int)val;
967                         frames= (int)floor( ((val - seconds) * FPS) + 0.5f );
968                 }
969                 else {
970                         /* seconds (with pixel offset) */
971                         seconds= (int)floor(val + 0.375f);
972                 }
973                 
974                 /* print timecode to temp string buffer */
975                 if (power <= 0) {
976                         /* include "frames" in display */
977                         if (hours) sprintf(str, "%s%02d:%02d:%02d;%02d", neg, hours, minutes, seconds, frames);
978                         else if (minutes) sprintf(str, "%s%02d:%02d;%02d", neg, minutes, seconds, frames);
979                         else sprintf(str, "%s%d;%02d", neg, seconds, frames);
980                 }
981                 else {
982                         /* don't include 'frames' in display */
983                         if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
984                         else sprintf(str, "%s%02d:%02d", neg, minutes, seconds);
985                 }
986         }
987         else {
988                 /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
989                 if (power <= 0) sprintf(str, "%.*f", 1-power, val);
990                 else sprintf(str, "%d", (int)floor(val + 0.375f));
991         }
992         
993         /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
994         len= strlen(str);
995         if (dir == 'h') {
996                 /* seconds/timecode display has slightly longer strings... */
997                 if (unit == V2D_UNIT_SECONDS)
998                         x-= 3*len;
999                 else
1000                         x-= 4*len;
1001         }
1002         
1003         /* Add degree sympbol to end of string for vertical scrollbar? */
1004         if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
1005                 str[len]= 186;
1006                 str[len+1]= 0;
1007         }
1008         
1009         /* draw it */
1010         ui_rasterpos_safe(x, y, 1.0);
1011         UI_DrawString(G.fonts, str, 0); // XXX check this again when new text-drawing api is done
1012 }
1013
1014 /* local define for radius of scroller 'button' caps */
1015 #define V2D_SCROLLCAP_RAD       5
1016
1017 /* Draw scrollbars in the given 2d-region */
1018 void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
1019 {
1020         const int darker= -50, dark= -10, light= 20, lighter= 50;
1021         rcti vert, hor;
1022         
1023         /* make copies of rects for less typing */
1024         vert= v2d->vert;
1025         hor= v2d->hor;
1026         
1027         /* horizontal scrollbar */
1028         if (v2d->scroll & (V2D_SCROLL_HORIZONTAL|V2D_SCROLL_HORIZONTAL_O)) {
1029                 /* scroller backdrop */
1030                 UI_ThemeColorShade(TH_SHADE1, light);
1031                 glRecti(hor.xmin,  hor.ymin,  hor.xmax,  hor.ymax);
1032                 
1033                 /* scroller 'button' 
1034                  *      - if view is zoomable in x, draw handles too 
1035                  *      - handles are drawn darker
1036                  */
1037                 if (v2d->keepzoom & V2D_LOCKZOOM_X) {
1038                         /* draw base bar as rounded shape */
1039                         UI_ThemeColorShade(TH_SHADE1, dark);
1040                         
1041                         uiSetRoundBox(15);
1042                         gl_round_box_shade(GL_POLYGON, 
1043                                         vs->hor_min, hor.ymin+2, 
1044                                         vs->hor_max, hor.ymax-2, 
1045                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1046                 }
1047                 else {
1048                         /* base bar drawn as shaded rect */
1049                         UI_ThemeColorShade(TH_SHADE1, dark);
1050                         uiSetRoundBox(0);
1051                         gl_round_box_shade(GL_POLYGON, 
1052                                         vs->hor_min, hor.ymin+2, 
1053                                         vs->hor_max, hor.ymax-2, 
1054                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1055                         
1056                         /* handles draw darker */
1057                         // XXX handles are drawn with the two last args set to same values, otherwise, max appears darker than min
1058                         UI_ThemeColorShade(TH_SHADE1, darker);
1059                         
1060                         /* 'minimum' handle */
1061                         uiSetRoundBox(9);
1062                         gl_round_box_shade(GL_POLYGON, 
1063                                         vs->hor_min-V2D_SCROLLER_HANDLE_SIZE, hor.ymin+2, 
1064                                         vs->hor_min+V2D_SCROLLER_HANDLE_SIZE, hor.ymax-2, 
1065                                         V2D_SCROLLCAP_RAD, 0.07f, -0.07f);
1066                                         //V2D_SCROLLCAP_RAD, 0, 0);
1067                         
1068                         /* maximum handle */
1069                         uiSetRoundBox(6);
1070                         gl_round_box_shade(GL_POLYGON, 
1071                                         vs->hor_max-V2D_SCROLLER_HANDLE_SIZE, hor.ymin+2, 
1072                                         vs->hor_max+V2D_SCROLLER_HANDLE_SIZE, hor.ymax-2, 
1073                                         V2D_SCROLLCAP_RAD, 0.07f, -0.07f);
1074                                         //V2D_SCROLLCAP_RAD, 0, 0);
1075                 }
1076                 
1077                 /* scale indicators */
1078                 // XXX will need to update the font drawing when the new stuff comes in
1079                 if (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) {
1080                         View2DGrid *grid= vs->grid;
1081                         float fac, dfac, fac2, val;
1082                         
1083                         /* the numbers: convert grid->startx and -dx to scroll coordinates 
1084                          *      - fac is x-coordinate to draw to
1085                          *      - dfac is gap between scale markings
1086                          */
1087                         fac= (grid->startx- v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1088                         fac= hor.xmin + fac*(hor.xmax - hor.xmin);
1089                         
1090                         dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
1091                         dfac= dfac * (hor.xmax - hor.xmin);
1092                         
1093                         /* set starting value, and text color */
1094                         UI_ThemeColor(TH_TEXT);
1095                         val= grid->startx;
1096                         
1097                         /* if we're clamping to whole numbers only, make sure entries won't be repeated */
1098                         if (vs->xclamp == V2D_GRID_CLAMP) {
1099                                 while (grid->dx < 0.9999f) {
1100                                         grid->dx *= 2.0f;
1101                                         dfac *= 2.0f;
1102                                 }
1103                         }
1104                         if (vs->xunits == V2D_UNIT_FRAMES)
1105                                 grid->powerx= 1;
1106                         
1107                         /* draw numbers in the appropriate range */
1108                         if (dfac != 0.0f) {
1109                                 for (; fac < hor.xmax; fac+=dfac, val+=grid->dx) {
1110                                         switch (vs->xunits) {
1111                                                 case V2D_UNIT_FRAMES:           /* frames (as whole numbers)*/
1112                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_FRAMES, 'h');
1113                                                         break;
1114                                                 
1115                                                 case V2D_UNIT_SECONDS:          /* seconds */
1116                                                         fac2= val/FPS;
1117                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
1118                                                         break;
1119                                                         
1120                                                 case V2D_UNIT_SECONDSSEQ:       /* seconds with special calculations (only used for sequencer only) */
1121                                                 {
1122                                                         float time;
1123                                                         
1124                                                         fac2= val/FPS;
1125                                                         time= floor(fac2);
1126                                                         fac2= fac2-time;
1127                                                         
1128                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), time+FPS*fac2/100.0, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
1129                                                 }
1130                                                         break;
1131                                                         
1132                                                 case V2D_UNIT_DEGREES:          /* IPO-Editor for rotation IPO-Drivers */
1133                                                         /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
1134                                                         scroll_printstr(vs, fac, 3.0+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_DEGREES, 'v');
1135                                                         break;
1136                                         }
1137                                 }
1138                         }
1139                 }
1140                 
1141                 /* decoration outer bevel line */
1142                 UI_ThemeColorShade(TH_SHADE1, lighter);
1143                 if (v2d->scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O))
1144                         sdrawline(hor.xmin, hor.ymax, hor.xmax, hor.ymax);
1145                 else if (v2d->scroll & V2D_SCROLL_TOP)
1146                         sdrawline(hor.xmin, hor.ymin, hor.xmax, hor.ymin);
1147         }
1148         
1149         /* vertical scrollbar */
1150         if (v2d->scroll & V2D_SCROLL_VERTICAL) {
1151                 /* scroller backdrop  */
1152                 UI_ThemeColorShade(TH_SHADE1, light);
1153                 glRecti(vert.xmin,  vert.ymin,  vert.xmax,  vert.ymax);
1154                 
1155                 /* scroller 'button' 
1156                  *      - if view is zoomable in y, draw handles too 
1157                  *      - handles are drawn darker
1158                  */
1159                 // XXX gl_round_box_shade is currently hardcoded to do horizontal buts only... need a vertical region
1160                 if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
1161                         /* draw base bar as rounded shape */
1162                         UI_ThemeColorShade(TH_SHADE1, dark);
1163                         
1164                         uiSetRoundBox(15);
1165                         gl_round_box_shade(GL_POLYGON, 
1166                                         vert.xmin+2, vs->vert_min, 
1167                                         vert.xmax-2, vs->vert_max, 
1168                                         V2D_SCROLLCAP_RAD, 0.05f, -0.05f);
1169                 }
1170                 else {
1171                         /* for now, draw base bar as unshaded rect */
1172                         // FIXME: this should be shaded horizontally too
1173                         UI_ThemeColorShade(TH_SHADE1, dark);
1174                         glRecti(vert.xmin+2, vs->vert_min,  
1175                                         vert.xmax-2, vs->vert_max);
1176                         
1177                         /* handles draw darker */
1178                         // XXX handles are drawn with the two last args set to same values, otherwise, max appears darker than min
1179                         UI_ThemeColorShade(TH_SHADE1, darker);
1180                         
1181                         /* 'minimum' handle */
1182                         uiSetRoundBox(12);
1183                         gl_round_box_shade(GL_POLYGON, 
1184                                         vert.xmin+2, vs->vert_min-V2D_SCROLLER_HANDLE_SIZE, 
1185                                         vert.xmax-2, vs->vert_min+V2D_SCROLLER_HANDLE_SIZE, 
1186                                         V2D_SCROLLCAP_RAD, 0, 0);
1187                         
1188                         /* maximum handle */
1189                         uiSetRoundBox(3);
1190                         gl_round_box_shade(GL_POLYGON, 
1191                                         vert.xmin+2, vs->vert_max-V2D_SCROLLER_HANDLE_SIZE, 
1192                                         vert.xmax-2, vs->vert_max+V2D_SCROLLER_HANDLE_SIZE, 
1193                                         V2D_SCROLLCAP_RAD, 0, 0);
1194                 }
1195                 
1196                 /* scale indiators */
1197                 // XXX will need to update the font drawing when the new stuff comes in
1198                 if (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) {
1199                         View2DGrid *grid= vs->grid;
1200                         float fac, dfac, val;
1201                         
1202                         /* the numbers: convert grid->starty and dy to scroll coordinates 
1203                          *      - fac is y-coordinate to draw to
1204                          *      - dfac is gap between scale markings
1205                          *      - these involve a correction for horizontal scrollbar
1206                          *        NOTE: it's assumed that that scrollbar is there if this is involved!
1207                          */
1208                         fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1209                         fac= (vert.ymin + V2D_SCROLL_HEIGHT) + fac*(vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1210                         
1211                         dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
1212                         dfac= dfac * (vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
1213                         
1214                         /* set starting value, and text color */
1215                         UI_ThemeColor(TH_TEXT);
1216                         val= grid->starty;
1217                         
1218                         /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
1219                         // XXX only relevant to Sequencer, so need to review this when we port that code
1220                         if (vs->yclamp == V2D_GRID_CLAMP)
1221                                 fac += 0.5f * dfac;
1222                                 
1223                         /* draw vertical steps */
1224                         for (; fac < vert.ymax; fac+= dfac, val += grid->dy) {
1225                                 scroll_printstr(vs, (float)(vert.xmax)-14.0, fac, val, grid->powery, vs->yunits, 'v');
1226                         }                       
1227                 }       
1228                 
1229                 /* decoration outer bevel line */
1230                 UI_ThemeColorShade(TH_SHADE1, lighter);
1231                 if (v2d->scroll & V2D_SCROLL_RIGHT)
1232                         sdrawline(vert.xmin, vert.ymin, vert.xmin, vert.ymax);
1233                 else if (v2d->scroll & V2D_SCROLL_LEFT)
1234                         sdrawline(vert.xmax, vert.ymin, vert.xmax, vert.ymax);
1235         }
1236 }
1237
1238 /* free temporary memory used for drawing scrollers */
1239 void UI_view2d_scrollers_free(View2DScrollers *scrollers)
1240 {
1241         /* need to free grid as well... */
1242         if (scrollers->grid) MEM_freeN(scrollers->grid);
1243         MEM_freeN(scrollers);
1244 }
1245
1246 /* *********************************************************************** */
1247 /* Coordinate Conversions */
1248
1249 /* Convert from screen/region space to 2d-View space 
1250  *      
1251  *      - x,y                   = coordinates to convert
1252  *      - viewx,viewy           = resultant coordinates
1253  */
1254 void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
1255 {
1256         float div, ofs;
1257
1258         if (viewx) {
1259                 div= v2d->mask.xmax - v2d->mask.xmin;
1260                 ofs= v2d->mask.xmin;
1261                 
1262                 *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
1263         }
1264
1265         if (viewy) {
1266                 div= v2d->mask.ymax - v2d->mask.ymin;
1267                 ofs= v2d->mask.ymin;
1268                 
1269                 *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
1270         }
1271 }
1272
1273 /* Convert from 2d-View space to screen/region space
1274  *      - Coordinates are clamped to lie within bounds of region
1275  *
1276  *      - x,y                           = coordinates to convert
1277  *      - regionx,regiony       = resultant coordinates 
1278  */
1279 void UI_view2d_view_to_region(View2D *v2d, float x, float y, short *regionx, short *regiony)
1280 {
1281         /* set initial value in case coordinate lies outside of bounds */
1282         if (regionx)
1283                 *regionx= V2D_IS_CLIPPED;
1284         if (regiony)
1285                 *regiony= V2D_IS_CLIPPED;
1286         
1287         /* express given coordinates as proportional values */
1288         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1289         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1290         
1291         /* check if values are within bounds */
1292         if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
1293                 if (regionx)
1294                         *regionx= v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin);
1295                 if (regiony)
1296                         *regiony= v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin);
1297         }
1298 }
1299
1300 /* Convert from 2d-view space to screen/region space
1301  *      - Coordinates are NOT clamped to lie within bounds of region
1302  *
1303  *      - x,y                           = coordinates to convert
1304  *      - regionx,regiony       = resultant coordinates 
1305  */
1306 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, short *regionx, short *regiony)
1307 {
1308         /* step 1: express given coordinates as proportional values */
1309         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1310         y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1311         
1312         /* step 2: convert proportional distances to screen coordinates  */
1313         x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
1314         y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
1315         
1316         /* although we don't clamp to lie within region bounds, we must avoid exceeding size of shorts */
1317         if (regionx) {
1318                 if (x < -32760) *regionx= -32760;
1319                 else if(x > 32760) *regionx= 32760;
1320                 else *regionx= x;
1321         }
1322         if (regiony) {
1323                 if (y < -32760) *regiony= -32760;
1324                 else if(y > 32760) *regiony= 32760;
1325                 else *regiony= y;
1326         }
1327 }
1328
1329 /* *********************************************************************** */
1330 /* Utilities */
1331
1332 /* View2D data by default resides in region, so get from region stored in context */
1333 View2D *UI_view2d_fromcontext(const bContext *C)
1334 {
1335         if (C->area == NULL) return NULL;
1336         if (C->region == NULL) return NULL;
1337         return &(C->region->v2d);
1338 }
1339
1340 /* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
1341 View2D *UI_view2d_fromcontext_rwin(const bContext *C)
1342 {
1343         if (C->area == NULL) return NULL;
1344         if (C->region == NULL) return NULL;
1345         if (C->region->regiontype!=RGN_TYPE_WINDOW) {
1346                 ARegion *ar= C->area->regionbase.first;
1347                 for(; ar; ar= ar->next)
1348                         if(ar->regiontype==RGN_TYPE_WINDOW)
1349                                 return &(ar->v2d);
1350                 return NULL;
1351         }
1352         return &(C->region->v2d);
1353 }
1354
1355
1356 /* Calculate the scale per-axis of the drawing-area
1357  *      - Is used to inverse correct drawing of icons, etc. that need to follow view 
1358  *        but not be affected by scale
1359  *
1360  *      - x,y   = scale on each axis
1361  */
1362 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
1363 {
1364         if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
1365         if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
1366 }