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