Blender V2.61 - r43446

view2d.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  * 
00021  * Contributor(s): Blender Foundation, Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <float.h>
00032 #include <limits.h>
00033 #include <math.h>
00034 #include <string.h>
00035 
00036 #include "MEM_guardedalloc.h"
00037 
00038 #include "DNA_scene_types.h"
00039 #include "DNA_userdef_types.h"
00040 
00041 #include "BLI_blenlib.h"
00042 #include "BLI_utildefines.h"
00043 
00044 #include "BKE_context.h"
00045 #include "BKE_screen.h"
00046 #include "BKE_global.h"
00047 
00048 
00049 #include "WM_api.h"
00050 
00051 #include "BIF_gl.h"
00052 
00053 #include "BLF_api.h"
00054 
00055 #include "ED_anim_api.h"
00056 #include "ED_screen.h"
00057 
00058 #include "UI_interface.h"
00059 #include "UI_view2d.h"
00060 
00061 #include "interface_intern.h"
00062 
00063 /* *********************************************************************** */
00064 
00065 /* XXX still unresolved: scrolls hide/unhide vs region mask handling */
00066 /* XXX there's V2D_SCROLL_HORIZONTAL_HIDE and V2D_SCROLL_HORIZONTAL_FULLR ... */
00067 
00068 /* helper to allow scrollbars to dynamically hide
00069  *  - returns a copy of the scrollbar settings with the flags to display 
00070  *    horizontal/vertical scrollbars removed
00071  *  - input scroll value is the v2d->scroll var
00072  *  - hide flags are set per region at drawtime
00073  */
00074 static int view2d_scroll_mapped(int scroll)
00075 {
00076     if(scroll & V2D_SCROLL_HORIZONTAL_HIDE)
00077         scroll &= ~(V2D_SCROLL_HORIZONTAL);
00078     if(scroll & V2D_SCROLL_VERTICAL_HIDE)
00079         scroll &= ~(V2D_SCROLL_VERTICAL);
00080     return scroll;
00081 }
00082 
00083 /* called each time cur changes, to dynamically update masks */
00084 static void view2d_masks(View2D *v2d)
00085 {
00086     int scroll;
00087     
00088     /* mask - view frame */
00089     v2d->mask.xmin= v2d->mask.ymin= 0;
00090     v2d->mask.xmax= v2d->winx - 1;  /* -1 yes! masks are pixels */
00091     v2d->mask.ymax= v2d->winy - 1;
00092 
00093 #if 0
00094     // XXX see above
00095     v2d->scroll &= ~(V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_VERTICAL_HIDE);
00096     /* check size if: */
00097     if (v2d->scroll & V2D_SCROLL_HORIZONTAL)
00098         if(!(v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL))
00099             if (v2d->tot.xmax-v2d->tot.xmin <= v2d->cur.xmax-v2d->cur.xmin)
00100                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
00101     if (v2d->scroll & V2D_SCROLL_VERTICAL)
00102         if(!(v2d->scroll & V2D_SCROLL_SCALE_VERTICAL))
00103             if (v2d->tot.ymax-v2d->tot.ymin <= v2d->cur.ymax-v2d->cur.ymin)
00104                 v2d->scroll |= V2D_SCROLL_VERTICAL_HIDE;
00105 #endif
00106     scroll= view2d_scroll_mapped(v2d->scroll);
00107     
00108     /* scrollers shrink mask area, but should be based off regionsize 
00109      *  - they can only be on one to two edges of the region they define
00110      *  - if they overlap, they must not occupy the corners (which are reserved for other widgets)
00111      */
00112     if (scroll) {
00113         /* vertical scroller */
00114         if (scroll & V2D_SCROLL_LEFT) {
00115             /* on left-hand edge of region */
00116             v2d->vert= v2d->mask;
00117             v2d->vert.xmax= V2D_SCROLL_WIDTH;
00118             v2d->mask.xmin= v2d->vert.xmax + 1;
00119         }
00120         else if (scroll & V2D_SCROLL_RIGHT) {
00121             /* on right-hand edge of region */
00122             v2d->vert= v2d->mask;
00123             v2d->vert.xmax++; /* one pixel extra... was leaving a minor gap... */
00124             v2d->vert.xmin= v2d->vert.xmax - V2D_SCROLL_WIDTH;
00125             v2d->mask.xmax= v2d->vert.xmin - 1;
00126         }
00127         
00128         /* horizontal scroller */
00129         if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
00130             /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
00131             v2d->hor= v2d->mask;
00132             v2d->hor.ymax= V2D_SCROLL_HEIGHT;
00133             v2d->mask.ymin= v2d->hor.ymax + 1;
00134         }
00135         else if (scroll & V2D_SCROLL_TOP) {
00136             /* on upper edge of region */
00137             v2d->hor= v2d->mask;
00138             v2d->hor.ymin= v2d->hor.ymax - V2D_SCROLL_HEIGHT;
00139             v2d->mask.ymax= v2d->hor.ymin - 1;
00140         }
00141         
00142         /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */
00143         if (scroll & V2D_SCROLL_VERTICAL) {
00144             /* just set y min/max for vertical scroller to y min/max of mask as appropriate */
00145             if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
00146                 /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
00147                 v2d->vert.ymin= v2d->mask.ymin;
00148             }
00149             else if (scroll & V2D_SCROLL_TOP) {
00150                 /* on upper edge of region */
00151                 v2d->vert.ymax= v2d->mask.ymax;
00152             }
00153         }
00154     }
00155     
00156 }
00157 
00158 /* Refresh and Validation */
00159 
00160 /* Initialise all relevant View2D data (including view rects if first time) and/or refresh mask sizes after view resize
00161  *  - for some of these presets, it is expected that the region will have defined some
00162  *    additional settings necessary for the customisation of the 2D viewport to its requirements
00163  *  - this function should only be called from region init() callbacks, where it is expected that
00164  *    this is called before UI_view2d_size_update(), as this one checks that the rects are properly initialised. 
00165  */
00166 void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
00167 {
00168     short tot_changed= 0, init= 0;
00169     uiStyle *style= UI_GetStyle();
00170 
00171     /* initialise data if there is a need for such */
00172     if ((v2d->flag & V2D_IS_INITIALISED) == 0) {
00173         /* set initialised flag so that View2D doesn't get reinitialised next time again */
00174         v2d->flag |= V2D_IS_INITIALISED;
00175 
00176         init= 1;
00177         
00178         /* see eView2D_CommonViewTypes in UI_view2d.h for available view presets */
00179         switch (type) {
00180             /* 'standard view' - optimum setup for 'standard' view behaviour,
00181              *  that should be used new views as basis for their
00182              *  own unique View2D settings, which should be used instead of this in most cases...
00183              */
00184             case V2D_COMMONVIEW_STANDARD:
00185             {
00186                 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
00187                 v2d->keepzoom= (V2D_KEEPASPECT|V2D_LIMITZOOM);
00188                 v2d->minzoom= 0.01f;
00189                 v2d->maxzoom= 1000.0f;
00190                 
00191                 /* tot rect and cur should be same size, and aligned using 'standard' OpenGL coordinates for now 
00192                  *  - region can resize 'tot' later to fit other data
00193                  *  - keeptot is only within bounds, as strict locking is not that critical
00194                  *  - view is aligned for (0,0) -> (winx-1, winy-1) setup
00195                  */
00196                 v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
00197                 v2d->keeptot= V2D_KEEPTOT_BOUNDS;
00198                 
00199                 v2d->tot.xmin= v2d->tot.ymin= 0.0f;
00200                 v2d->tot.xmax= (float)(winx - 1);
00201                 v2d->tot.ymax= (float)(winy - 1);
00202                 
00203                 v2d->cur= v2d->tot;
00204                 
00205                 /* scrollers - should we have these by default? */
00206                 // XXX for now, we don't override this, or set it either!
00207             }
00208                 break;
00209             
00210             /* 'list/channel view' - zoom, aspect ratio, and alignment restrictions are set here */
00211             case V2D_COMMONVIEW_LIST:
00212             {
00213                 /* zoom + aspect ratio are locked */
00214                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
00215                 v2d->minzoom= v2d->maxzoom= 1.0f;
00216                 
00217                 /* tot rect has strictly regulated placement, and must only occur in +/- quadrant */
00218                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
00219                 v2d->keeptot = V2D_KEEPTOT_STRICT;
00220                 tot_changed= 1;
00221                 
00222                 /* scroller settings are currently not set here... that is left for regions... */
00223             }
00224                 break;
00225                 
00226             /* 'stack view' - practically the same as list/channel view, except is located in the pos y half instead. 
00227              *  zoom, aspect ratio, and alignment restrictions are set here */
00228             case V2D_COMMONVIEW_STACK:
00229             {
00230                 /* zoom + aspect ratio are locked */
00231                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
00232                 v2d->minzoom= v2d->maxzoom= 1.0f;
00233                 
00234                 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
00235                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
00236                 v2d->keeptot = V2D_KEEPTOT_STRICT;
00237                 tot_changed= 1;
00238                 
00239                 /* scroller settings are currently not set here... that is left for regions... */
00240             }
00241                 break;
00242                 
00243             /* 'header' regions - zoom, aspect ratio, alignment, and panning restrictions are set here */
00244             case V2D_COMMONVIEW_HEADER:
00245             {
00246                 /* zoom + aspect ratio are locked */
00247                 v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
00248                 v2d->minzoom= v2d->maxzoom= 1.0f;
00249                 v2d->min[0]= v2d->max[0]= (float)(winx-1);
00250                 v2d->min[1]= v2d->max[1]= (float)(winy-1);
00251                 
00252                 /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
00253                 v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
00254                 v2d->keeptot = V2D_KEEPTOT_STRICT;
00255                 tot_changed= 1;
00256                 
00257                 /* panning in y-axis is prohibited */
00258                 v2d->keepofs= V2D_LOCKOFS_Y;
00259                 
00260                 /* absolutely no scrollers allowed */
00261                 v2d->scroll= 0;
00262                 
00263             }
00264                 break;
00265             
00266             /* panels view, with horizontal/vertical align */
00267             case V2D_COMMONVIEW_PANELS_UI:
00268             {
00269                 float panelzoom= (style) ? style->panelzoom : 1.0f;
00270                 
00271                 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
00272                 v2d->keepzoom= (V2D_KEEPASPECT|V2D_LIMITZOOM|V2D_KEEPZOOM);
00273                 v2d->minzoom= 0.5f;
00274                 v2d->maxzoom= 2.0f;
00275                 //tot_changed= 1;
00276                 
00277                 v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
00278                 v2d->keeptot= V2D_KEEPTOT_BOUNDS;
00279                 
00280                 v2d->scroll |= (V2D_SCROLL_RIGHT|V2D_SCROLL_BOTTOM);
00281                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
00282                 v2d->scroll &= ~V2D_SCROLL_VERTICAL_HIDE;
00283 
00284                 v2d->tot.xmin= 0.0f;
00285                 v2d->tot.xmax= winx;
00286                 
00287                 v2d->tot.ymax= 0.0f;
00288                 v2d->tot.ymin= -winy;
00289                 
00290                 v2d->cur.xmin= 0.0f;
00291                 /* bad workaround for keeping zoom level with scrollers */
00292                 v2d->cur.xmax= (winx - V2D_SCROLL_WIDTH)*panelzoom;
00293                 
00294                 v2d->cur.ymax= 0.0f;
00295                 v2d->cur.ymin= (-winy)*panelzoom;
00296             }
00297                 break;
00298                 
00299                 /* other view types are completely defined using their own settings already */
00300             default:
00301                 /* we don't do anything here, as settings should be fine, but just make sure that rect */
00302                 break;  
00303         }
00304     }
00305     
00306     /* store view size */
00307     v2d->winx= winx;
00308     v2d->winy= winy;
00309     
00310     /* set masks */
00311     view2d_masks(v2d);
00312     
00313     /* set 'tot' rect before setting cur? */
00314     if (tot_changed) 
00315         UI_view2d_totRect_set_resize(v2d, winx, winy, !init);
00316     else
00317         UI_view2d_curRect_validate_resize(v2d, !init);
00318 }
00319 
00320 /* Ensure View2D rects remain in a viable configuration 
00321  *  - cur is not allowed to be: larger than max, smaller than min, or outside of tot
00322  */
00323 // XXX pre2.5 -> this used to be called  test_view2d()
00324 void UI_view2d_curRect_validate_resize(View2D *v2d, int resize)
00325 {
00326     float totwidth, totheight, curwidth, curheight, width, height;
00327     float winx, winy;
00328     rctf *cur, *tot;
00329     
00330     /* use mask as size of region that View2D resides in, as it takes into account scrollbars already  */
00331     winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
00332     winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
00333     
00334     /* get pointers to rcts for less typing */
00335     cur= &v2d->cur;
00336     tot= &v2d->tot;
00337     
00338     /* we must satisfy the following constraints (in decreasing order of importance):
00339      *  - alignment restrictions are respected
00340      *  - cur must not fall outside of tot
00341      *  - axis locks (zoom and offset) must be maintained
00342      *  - zoom must not be excessive (check either sizes or zoom values)
00343      *  - aspect ratio should be respected (NOTE: this is quite closely realted to zoom too)
00344      */
00345     
00346     /* Step 1: if keepzoom, adjust the sizes of the rects only
00347      *  - firstly, we calculate the sizes of the rects
00348      *  - curwidth and curheight are saved as reference... modify width and height values here
00349      */
00350     totwidth= tot->xmax - tot->xmin;
00351     totheight= tot->ymax - tot->ymin;
00352     curwidth= width= cur->xmax - cur->xmin;
00353     curheight= height= cur->ymax - cur->ymin;
00354     
00355     /* if zoom is locked, size on the appropriate axis is reset to mask size */
00356     if (v2d->keepzoom & V2D_LOCKZOOM_X)
00357         width= winx;
00358     if (v2d->keepzoom & V2D_LOCKZOOM_Y)
00359         height= winy;
00360         
00361     /* values used to divide, so make it safe 
00362      * NOTE: width and height must use FLT_MIN instead of 1, otherwise it is impossible to
00363      *      get enough resolution in Graph Editor for editing some curves
00364      */
00365     if(width < FLT_MIN) width= 1;
00366     if(height < FLT_MIN) height= 1;
00367     if(winx < 1) winx= 1;
00368     if(winy < 1) winy= 1;
00369     
00370     /* V2D_LIMITZOOM indicates that zoom level should be preserved when the window size changes */
00371     if (resize && (v2d->keepzoom & V2D_KEEPZOOM)) {
00372         float zoom, oldzoom;
00373 
00374         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
00375             zoom= winx / width;
00376             oldzoom= v2d->oldwinx / curwidth;
00377 
00378             if(oldzoom != zoom)
00379                 width *= zoom/oldzoom;
00380         }
00381 
00382         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
00383             zoom= winy / height;
00384             oldzoom= v2d->oldwiny / curheight;
00385 
00386             if(oldzoom != zoom)
00387                 height *= zoom/oldzoom;
00388         }
00389     }
00390     /* keepzoom (V2D_LIMITZOOM set), indicates that zoom level on each axis must not exceed limits 
00391      * NOTE: in general, it is not expected that the lock-zoom will be used in conjunction with this
00392      */
00393     else if (v2d->keepzoom & V2D_LIMITZOOM) {
00394         float zoom, fac;
00395         
00396         /* check if excessive zoom on x-axis */
00397         if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
00398             zoom= winx / width;
00399             if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
00400                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
00401                 width *= fac;
00402             }
00403         }
00404         
00405         /* check if excessive zoom on y-axis */
00406         if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
00407             zoom= winy / height;
00408             if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
00409                 fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
00410                 height *= fac;
00411             }
00412         }
00413     }
00414     else {
00415         /* make sure sizes don't exceed that of the min/max sizes (even though we're not doing zoom clamping) */
00416         CLAMP(width, v2d->min[0], v2d->max[0]);
00417         CLAMP(height, v2d->min[1], v2d->max[1]);
00418     }
00419     
00420     /* check if we should restore aspect ratio (if view size changed) */
00421     if (v2d->keepzoom & V2D_KEEPASPECT) {
00422         short do_x=0, do_y=0, do_cur /* , do_win */ /* UNUSED */;
00423         float /* curRatio, */ /* UNUSED */ winRatio;
00424         
00425         /* when a window edge changes, the aspect ratio can't be used to
00426          * find which is the best new 'cur' rect. thats why it stores 'old' 
00427          */
00428         if (winx != v2d->oldwinx) do_x= 1;
00429         if (winy != v2d->oldwiny) do_y= 1;
00430         
00431         /* curRatio= height / width; */ /* UNUSED */
00432         winRatio= winy / winx;
00433         
00434         /* both sizes change (area/region maximised)  */
00435         if (do_x == do_y) {
00436             if (do_x && do_y) {
00437                 /* here is 1,1 case, so all others must be 0,0 */
00438                 if (ABS(winx - v2d->oldwinx) > ABS(winy - v2d->oldwiny)) do_y= 0;
00439                 else do_x= 0;
00440             }
00441             else if (winRatio > 1.0f) do_x= 0; 
00442             else do_x= 1;
00443         }
00444         do_cur= do_x;
00445         /* do_win= do_y; */ /* UNUSED */
00446         
00447         if (do_cur) {
00448             if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winx != v2d->oldwinx)) {
00449                 /* special exception for Outliner (and later channel-lists):
00450                  *  - The view may be moved left to avoid contents being pushed out of view when view shrinks. 
00451                  *  - The keeptot code will make sure cur->xmin will not be less than tot->xmin (which cannot be allowed)
00452                  *  - width is not adjusted for changed ratios here...
00453                  */
00454                 if (winx < v2d->oldwinx) {
00455                     float temp = v2d->oldwinx - winx;
00456                     
00457                     cur->xmin -= temp;
00458                     cur->xmax -= temp;
00459                     
00460                     /* width does not get modified, as keepaspect here is just set to make 
00461                      * sure visible area adjusts to changing view shape! 
00462                      */
00463                 }
00464             }
00465             else {
00466                 /* portrait window: correct for x */
00467                 width= height / winRatio;
00468             }
00469         }
00470         else {
00471             if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winy != v2d->oldwiny)) {
00472                 /* special exception for Outliner (and later channel-lists):
00473                  *  - Currently, no actions need to be taken here...
00474                  */
00475 
00476                 if (winy < v2d->oldwiny) {
00477                     float temp = v2d->oldwiny - winy;
00478                     
00479                     cur->ymin += temp;
00480                     cur->ymax += temp;
00481                 }
00482 
00483             }
00484             else {
00485                 /* landscape window: correct for y */
00486                 height = width * winRatio;
00487             }
00488         }
00489         
00490         /* store region size for next time */
00491         v2d->oldwinx= (short)winx; 
00492         v2d->oldwiny= (short)winy;
00493     }
00494     
00495     /* Step 2: apply new sizes to cur rect, but need to take into account alignment settings here... */
00496     if ((width != curwidth) || (height != curheight)) {
00497         float temp, dh;
00498         
00499         /* resize from centerpoint, unless otherwise specified */
00500         if (width != curwidth) {
00501             if (v2d->keepofs & V2D_LOCKOFS_X) {
00502                 cur->xmax += width - (cur->xmax - cur->xmin);
00503             }
00504             else if (v2d->keepofs & V2D_KEEPOFS_X) {
00505                 if(v2d->align & V2D_ALIGN_NO_POS_X)
00506                     cur->xmin -= width - (cur->xmax - cur->xmin);
00507                 else
00508                     cur->xmax += width - (cur->xmax - cur->xmin);
00509             }
00510             else {
00511                 temp= (cur->xmax + cur->xmin) * 0.5f;
00512                 dh= width * 0.5f;
00513                 
00514                 cur->xmin = temp - dh;
00515                 cur->xmax = temp + dh;
00516             }
00517         }
00518         if (height != curheight) {
00519             if (v2d->keepofs & V2D_LOCKOFS_Y) {
00520                 cur->ymax += height - (cur->ymax - cur->ymin);
00521             }
00522             else if (v2d->keepofs & V2D_KEEPOFS_Y) {
00523                 if(v2d->align & V2D_ALIGN_NO_POS_Y)
00524                     cur->ymin -= height - (cur->ymax - cur->ymin);
00525                 else
00526                     cur->ymax += height - (cur->ymax - cur->ymin);
00527             }
00528             else {
00529                 temp= (cur->ymax + cur->ymin) * 0.5f;
00530                 dh= height * 0.5f;
00531                 
00532                 cur->ymin = temp - dh;
00533                 cur->ymax = temp + dh;
00534             }
00535         }
00536     }
00537     
00538     /* Step 3: adjust so that it doesn't fall outside of bounds of 'tot' */
00539     if (v2d->keeptot) {
00540         float temp, diff;
00541         
00542         /* recalculate extents of cur */
00543         curwidth= cur->xmax - cur->xmin;
00544         curheight= cur->ymax - cur->ymin;
00545         
00546         /* width */
00547         if ( (curwidth > totwidth) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_X|V2D_LIMITZOOM)) ) {
00548             /* if zoom doesn't have to be maintained, just clamp edges */
00549             if (cur->xmin < tot->xmin) cur->xmin= tot->xmin;
00550             if (cur->xmax > tot->xmax) cur->xmax= tot->xmax;
00551         }
00552         else if (v2d->keeptot == V2D_KEEPTOT_STRICT) {
00553             /* This is an exception for the outliner (and later channel-lists, headers) 
00554              *  - must clamp within tot rect (absolutely no excuses)
00555              *  --> therefore, cur->xmin must not be less than tot->xmin
00556              */
00557             if (cur->xmin < tot->xmin) {
00558                 /* move cur across so that it sits at minimum of tot */
00559                 temp= tot->xmin - cur->xmin;
00560                 
00561                 cur->xmin += temp;
00562                 cur->xmax += temp;
00563             }
00564             else if (cur->xmax > tot->xmax) {
00565                 /* - only offset by difference of cur-xmax and tot-xmax if that would not move 
00566                  *  cur-xmin to lie past tot-xmin
00567                  * - otherwise, simply shift to tot-xmin???
00568                  */
00569                 temp= cur->xmax - tot->xmax;
00570                 
00571                 if ((cur->xmin - temp) < tot->xmin) {
00572                     /* only offset by difference from cur-min and tot-min */
00573                     temp= cur->xmin - tot->xmin;
00574                     
00575                     cur->xmin -= temp;
00576                     cur->xmax -= temp;
00577                 }
00578                 else {
00579                     cur->xmin -= temp;
00580                     cur->xmax -= temp;
00581                 }
00582             }
00583         }
00584         else {
00585             /* This here occurs when:
00586              *  - width too big, but maintaining zoom (i.e. widths cannot be changed)
00587              *  - width is OK, but need to check if outside of boundaries
00588              * 
00589              * So, resolution is to just shift view by the gap between the extremities.
00590              * We favour moving the 'minimum' across, as that's origin for most things
00591              * (XXX - in the past, max was favoured... if there are bugs, swap!)
00592              */
00593             if ((cur->xmin < tot->xmin) && (cur->xmax > tot->xmax)) {
00594                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
00595                 temp= (tot->xmax + tot->xmin) * 0.5f;
00596                 diff= curheight * 0.5f;
00597                 
00598                 cur->xmin= temp - diff;
00599                 cur->xmax= temp + diff;
00600             }
00601             else if (cur->xmin < tot->xmin) {
00602                 /* move cur across so that it sits at minimum of tot */
00603                 temp= tot->xmin - cur->xmin;
00604                 
00605                 cur->xmin += temp;
00606                 cur->xmax += temp;
00607             }
00608             else if (cur->xmax > tot->xmax) {
00609                 /* - only offset by difference of cur-xmax and tot-xmax if that would not move 
00610                  *  cur-xmin to lie past tot-xmin
00611                  * - otherwise, simply shift to tot-xmin???
00612                  */
00613                 temp= cur->xmax - tot->xmax;
00614                 
00615                 if ((cur->xmin - temp) < tot->xmin) {
00616                     /* only offset by difference from cur-min and tot-min */
00617                     temp= cur->xmin - tot->xmin;
00618                     
00619                     cur->xmin -= temp;
00620                     cur->xmax -= temp;
00621                 }
00622                 else {
00623                     cur->xmin -= temp;
00624                     cur->xmax -= temp;
00625                 }
00626             }
00627         }
00628         
00629         /* height */
00630         if ( (curheight > totheight) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_Y|V2D_LIMITZOOM)) ) {
00631             /* if zoom doesn't have to be maintained, just clamp edges */
00632             if (cur->ymin < tot->ymin) cur->ymin= tot->ymin;
00633             if (cur->ymax > tot->ymax) cur->ymax= tot->ymax;
00634         }
00635         else {
00636             /* This here occurs when:
00637              *  - height too big, but maintaining zoom (i.e. heights cannot be changed)
00638              *  - height is OK, but need to check if outside of boundaries
00639              * 
00640              * So, resolution is to just shift view by the gap between the extremities.
00641              * We favour moving the 'minimum' across, as that's origin for most things
00642              */
00643             if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
00644                 /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
00645                 temp= (tot->ymax + tot->ymin) * 0.5f;
00646                 diff= curheight * 0.5f;
00647                 
00648                 cur->ymin= temp - diff;
00649                 cur->ymax= temp + diff;
00650             }
00651             else if (cur->ymin < tot->ymin) {
00652                 /* there's still space remaining, so shift up */
00653                 temp= tot->ymin - cur->ymin;
00654                 
00655                 cur->ymin += temp;
00656                 cur->ymax += temp;
00657             }
00658             else if (cur->ymax > tot->ymax) {
00659                 /* there's still space remaining, so shift down */
00660                 temp= cur->ymax - tot->ymax;
00661                 
00662                 cur->ymin -= temp;
00663                 cur->ymax -= temp;
00664             }
00665         }
00666     }
00667     
00668     /* Step 4: Make sure alignment restrictions are respected */
00669     if (v2d->align) {
00670         /* If alignment flags are set (but keeptot is not), they must still be respected, as although
00671          * they don't specify any particular bounds to stay within, they do define ranges which are 
00672          * invalid.
00673          *
00674          * Here, we only check to make sure that on each axis, the 'cur' rect doesn't stray into these 
00675          * invalid zones, otherwise we offset.
00676          */
00677         
00678         /* handle width - posx and negx flags are mutually exclusive, so watch out */
00679         if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
00680             /* width is in negative-x half */
00681             if (v2d->cur.xmax > 0) {
00682                 v2d->cur.xmin -= v2d->cur.xmax;
00683                 v2d->cur.xmax= 0.0f;
00684             }
00685         }
00686         else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
00687             /* width is in positive-x half */
00688             if (v2d->cur.xmin < 0) {
00689                 v2d->cur.xmax -= v2d->cur.xmin;
00690                 v2d->cur.xmin = 0.0f;
00691             }
00692         }
00693         
00694         /* handle height - posx and negx flags are mutually exclusive, so watch out */
00695         if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
00696             /* height is in negative-y half */
00697             if (v2d->cur.ymax > 0) {
00698                 v2d->cur.ymin -= v2d->cur.ymax;
00699                 v2d->cur.ymax = 0.0f;
00700             }
00701         }
00702         else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
00703             /* height is in positive-y half */
00704             if (v2d->cur.ymin < 0) {
00705                 v2d->cur.ymax -= v2d->cur.ymin;
00706                 v2d->cur.ymin = 0.0f;
00707             }
00708         }
00709     }
00710     
00711     /* set masks */
00712     view2d_masks(v2d);
00713 }
00714 
00715 void UI_view2d_curRect_validate(View2D *v2d)
00716 {
00717     UI_view2d_curRect_validate_resize(v2d, 0);
00718 }
00719 
00720 /* ------------------ */
00721 
00722 /* Called by menus to activate it, or by view2d operators to make sure 'related' views stay in synchrony */
00723 void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
00724 {
00725     ScrArea *sa;
00726     ARegion *ar;
00727     
00728     /* don't continue if no view syncing to be done */
00729     if ((v2dcur->flag & (V2D_VIEWSYNC_SCREEN_TIME|V2D_VIEWSYNC_AREA_VERTICAL)) == 0)
00730         return;
00731         
00732     /* check if doing within area syncing (i.e. channels/vertical) */
00733     if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) {
00734         for (ar= area->regionbase.first; ar; ar= ar->next) {
00735             /* don't operate on self */
00736             if (v2dcur != &ar->v2d) {
00737                 /* only if view has vertical locks enabled */
00738                 if (ar->v2d.flag & V2D_VIEWSYNC_AREA_VERTICAL) {
00739                     if (flag == V2D_LOCK_COPY) {
00740                         /* other views with locks on must copy active */
00741                         ar->v2d.cur.ymin= v2dcur->cur.ymin;
00742                         ar->v2d.cur.ymax= v2dcur->cur.ymax;
00743                     }
00744                     else { /* V2D_LOCK_SET */
00745                         /* active must copy others */
00746                         v2dcur->cur.ymin= ar->v2d.cur.ymin;
00747                         v2dcur->cur.ymax= ar->v2d.cur.ymax;
00748                     }
00749                     
00750                     /* region possibly changed, so refresh */
00751                     ED_region_tag_redraw(ar);
00752                 }
00753             }
00754         }
00755     }
00756     
00757     /* check if doing whole screen syncing (i.e. time/horizontal) */
00758     if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
00759         for (sa= screen->areabase.first; sa; sa= sa->next) {
00760             for (ar= sa->regionbase.first; ar; ar= ar->next) {
00761                 /* don't operate on self */
00762                 if (v2dcur != &ar->v2d) {
00763                     /* only if view has horizontal locks enabled */
00764                     if (ar->v2d.flag & V2D_VIEWSYNC_SCREEN_TIME) {
00765                         if (flag == V2D_LOCK_COPY) {
00766                             /* other views with locks on must copy active */
00767                             ar->v2d.cur.xmin= v2dcur->cur.xmin;
00768                             ar->v2d.cur.xmax= v2dcur->cur.xmax;
00769                         }
00770                         else { /* V2D_LOCK_SET */
00771                             /* active must copy others */
00772                             v2dcur->cur.xmin= ar->v2d.cur.xmin;
00773                             v2dcur->cur.xmax= ar->v2d.cur.xmax;
00774                         }
00775                         
00776                         /* region possibly changed, so refresh */
00777                         ED_region_tag_redraw(ar);
00778                     }
00779                 }
00780             }
00781         }
00782     }
00783 }
00784 
00785 
00786 /* Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot) 
00787  * This does not take into account if zooming the view on an axis will improve the view (if allowed)
00788  */
00789 void UI_view2d_curRect_reset (View2D *v2d)
00790 {
00791     float width, height;
00792     
00793     /* assume width and height of 'cur' rect by default, should be same size as mask */
00794     width= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
00795     height= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
00796     
00797     /* handle width - posx and negx flags are mutually exclusive, so watch out */
00798     if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
00799         /* width is in negative-x half */
00800         v2d->cur.xmin= (float)-width;
00801         v2d->cur.xmax= 0.0f;
00802     }
00803     else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
00804         /* width is in positive-x half */
00805         v2d->cur.xmin= 0.0f;
00806         v2d->cur.xmax= (float)width;
00807     }
00808     else {
00809         /* width is centered around x==0 */
00810         const float dx= (float)width / 2.0f;
00811         
00812         v2d->cur.xmin= -dx;
00813         v2d->cur.xmax= dx;
00814     }
00815     
00816     /* handle height - posx and negx flags are mutually exclusive, so watch out */
00817     if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
00818         /* height is in negative-y half */
00819         v2d->cur.ymin= (float)-height;
00820         v2d->cur.ymax= 0.0f;
00821     }
00822     else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
00823         /* height is in positive-y half */
00824         v2d->cur.ymin= 0.0f;
00825         v2d->cur.ymax= (float)height;
00826     }
00827     else {
00828         /* height is centered around y==0 */
00829         const float dy= (float)height / 2.0f;
00830         
00831         v2d->cur.ymin= -dy;
00832         v2d->cur.ymax= dy;
00833     }
00834 }
00835 
00836 /* ------------------ */
00837 
00838 /* Change the size of the maximum viewable area (i.e. 'tot' rect) */
00839 void UI_view2d_totRect_set_resize (View2D *v2d, int width, int height, int resize)
00840 {
00841     int scroll= view2d_scroll_mapped(v2d->scroll);
00842     
00843     /* don't do anything if either value is 0 */
00844     width= abs(width);
00845     height= abs(height);
00846     
00847     /* hrumf! */
00848     /* XXX: there are work arounds for this in the panel and file browse code. */
00849     if(scroll & V2D_SCROLL_HORIZONTAL) 
00850         width -= V2D_SCROLL_WIDTH;
00851     if(scroll & V2D_SCROLL_VERTICAL) 
00852         height -= V2D_SCROLL_HEIGHT;
00853     
00854     if (ELEM3(0, v2d, width, height)) {
00855         if (G.f & G_DEBUG)
00856             printf("Error: View2D totRect set exiting: v2d=%p width=%d height=%d \n", (void *)v2d, width, height); // XXX temp debug info
00857         return;
00858     }
00859     
00860     /* handle width - posx and negx flags are mutually exclusive, so watch out */
00861     if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
00862         /* width is in negative-x half */
00863         v2d->tot.xmin= (float)-width;
00864         v2d->tot.xmax= 0.0f;
00865     }
00866     else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
00867         /* width is in positive-x half */
00868         v2d->tot.xmin= 0.0f;
00869         v2d->tot.xmax= (float)width;
00870     }
00871     else {
00872         /* width is centered around x==0 */
00873         const float dx= (float)width / 2.0f;
00874         
00875         v2d->tot.xmin= -dx;
00876         v2d->tot.xmax= dx;
00877     }
00878     
00879     /* handle height - posx and negx flags are mutually exclusive, so watch out */
00880     if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
00881         /* height is in negative-y half */
00882         v2d->tot.ymin= (float)-height;
00883         v2d->tot.ymax= 0.0f;
00884     }
00885     else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
00886         /* height is in positive-y half */
00887         v2d->tot.ymin= 0.0f;
00888         v2d->tot.ymax= (float)height;
00889     }
00890     else {
00891         /* height is centered around y==0 */
00892         const float dy= (float)height / 2.0f;
00893         
00894         v2d->tot.ymin= -dy;
00895         v2d->tot.ymax= dy;
00896     }
00897     
00898     /* make sure that 'cur' rect is in a valid state as a result of these changes */
00899     UI_view2d_curRect_validate_resize(v2d, resize);
00900 }
00901 
00902 void UI_view2d_totRect_set(View2D *v2d, int width, int height)
00903 {
00904     UI_view2d_totRect_set_resize(v2d, width, height, 0);
00905 }
00906 
00907 int UI_view2d_tab_set(View2D *v2d, int tab)
00908 {
00909     float default_offset[2]= {0.0f, 0.0f};
00910     float *offset, *new_offset;
00911     int changed= 0;
00912 
00913     /* if tab changed, change offset */
00914     if(tab != v2d->tab_cur && v2d->tab_offset) {
00915         if(tab < v2d->tab_num)
00916             offset= &v2d->tab_offset[tab*2];
00917         else
00918             offset= default_offset;
00919 
00920         v2d->cur.xmax += offset[0] - v2d->cur.xmin;
00921         v2d->cur.xmin= offset[0];
00922 
00923         v2d->cur.ymin += offset[1] - v2d->cur.ymax;
00924         v2d->cur.ymax= offset[1];
00925 
00926         /* validation should happen in subsequent totRect_set */
00927 
00928         changed= 1;
00929     }
00930 
00931     /* resize array if needed */
00932     if(tab >= v2d->tab_num) {
00933         new_offset= MEM_callocN(sizeof(float)*(tab+1)*2, "view2d tab offset");
00934 
00935         if(v2d->tab_offset) {
00936             memcpy(new_offset, v2d->tab_offset, sizeof(float)*v2d->tab_num*2);
00937             MEM_freeN(v2d->tab_offset);
00938         }
00939 
00940         v2d->tab_offset= new_offset;
00941         v2d->tab_num= tab+1;
00942     }
00943 
00944     /* set current tab and offset */
00945     v2d->tab_cur= tab;
00946     v2d->tab_offset[2*tab+0]= v2d->cur.xmin;
00947     v2d->tab_offset[2*tab+1]= v2d->cur.ymax;
00948 
00949     return changed;
00950 }
00951 
00952 /* *********************************************************************** */
00953 /* View Matrix Setup */
00954 
00955 /* mapping function to ensure 'cur' draws extended over the area where sliders are */
00956 static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
00957 {
00958     *curmasked= v2d->cur;
00959     
00960     if (view2d_scroll_mapped(v2d->scroll)) {
00961         float dx= (v2d->cur.xmax-v2d->cur.xmin)/((float)(v2d->mask.xmax-v2d->mask.xmin+1));
00962         float dy= (v2d->cur.ymax-v2d->cur.ymin)/((float)(v2d->mask.ymax-v2d->mask.ymin+1));
00963         
00964         if (v2d->mask.xmin != 0)
00965             curmasked->xmin -= dx*(float)v2d->mask.xmin;
00966         if (v2d->mask.xmax+1 != v2d->winx)
00967             curmasked->xmax += dx*(float)(v2d->winx - v2d->mask.xmax-1);
00968         
00969         if (v2d->mask.ymin != 0)
00970             curmasked->ymin -= dy*(float)v2d->mask.ymin;
00971         if (v2d->mask.ymax+1 != v2d->winy)
00972             curmasked->ymax += dy*(float)(v2d->winy - v2d->mask.ymax-1);
00973         
00974     }
00975 }
00976 
00977 /* Set view matrices to use 'cur' rect as viewing frame for View2D drawing */
00978 void UI_view2d_view_ortho(View2D *v2d)
00979 {
00980     rctf curmasked;
00981     float xofs, yofs;
00982     
00983     /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing, 
00984      * but only applied where requsted
00985      */
00986     /* XXX brecht: instead of zero at least use a tiny offset, otherwise
00987      * pixel rounding is effectively random due to float inaccuracy */
00988     xofs= 0.001f*(v2d->cur.xmax - v2d->cur.xmin)/(v2d->mask.xmax - v2d->mask.xmin);
00989     yofs= 0.001f*(v2d->cur.ymax - v2d->cur.ymin)/(v2d->mask.ymax - v2d->mask.ymin);
00990     
00991     /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
00992     view2d_map_cur_using_mask(v2d, &curmasked);
00993     
00994     curmasked.xmin-= xofs; curmasked.xmax-=xofs;
00995     curmasked.ymin-= yofs; curmasked.ymax-=yofs;
00996     
00997     /* XXX ton: this flag set by outliner, for icons */
00998     if(v2d->flag & V2D_PIXELOFS_X) {
00999         curmasked.xmin= floorf(curmasked.xmin) - 0.001f;
01000         curmasked.xmax= floorf(curmasked.xmax) - 0.001f;
01001     }
01002     if(v2d->flag & V2D_PIXELOFS_Y) {
01003         curmasked.ymin= floorf(curmasked.ymin) - 0.001f;
01004         curmasked.ymax= floorf(curmasked.ymax) - 0.001f;
01005     }
01006     
01007     /* set matrix on all appropriate axes */
01008     wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
01009 
01010     /* XXX is this necessary? */
01011     glLoadIdentity();
01012 }
01013 
01014 /* Set view matrices to only use one axis of 'cur' only
01015  *  - xaxis     = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x)
01016  */
01017 void UI_view2d_view_orthoSpecial(ARegion *ar, View2D *v2d, short xaxis)
01018 {
01019     rctf curmasked;
01020     float xofs, yofs;
01021     
01022     /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing, 
01023      * but only applied where requsted
01024      */
01025     /* XXX temp (ton) */
01026     xofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_X) ? 0.375f : 0.0f;
01027     yofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_Y) ? 0.375f : 0.0f;
01028     
01029     /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
01030     view2d_map_cur_using_mask(v2d, &curmasked);
01031     
01032     /* only set matrix with 'cur' coordinates on relevant axes */
01033     if (xaxis)
01034         wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, -yofs, ar->winy-yofs);
01035     else
01036         wmOrtho2(-xofs, ar->winx-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
01037         
01038     /* XXX is this necessary? */
01039     glLoadIdentity();
01040 } 
01041 
01042 
01043 /* Restore view matrices after drawing */
01044 void UI_view2d_view_restore(const bContext *C)
01045 {
01046     ARegion *ar= CTX_wm_region(C);
01047     int width= ar->winrct.xmax-ar->winrct.xmin+1;
01048     int height= ar->winrct.ymax-ar->winrct.ymin+1;
01049     
01050     wmOrtho2(0.0f, (float)width, 0.0f, (float)height);
01051     glLoadIdentity();
01052     
01053     //  ED_region_pixelspace(CTX_wm_region(C));
01054 }
01055 
01056 /* *********************************************************************** */
01057 /* Gridlines */
01058 
01059 /* View2DGrid is typedef'd in UI_view2d.h */
01060 struct View2DGrid {
01061     float dx, dy;           /* stepsize (in pixels) between gridlines */
01062     float startx, starty;   /* initial coordinates to start drawing grid from */
01063     int powerx, powery;     /* step as power of 10 */
01064 };
01065 
01066 /* --------------- */
01067 
01068 /* try to write step as a power of 10 */
01069 static void step_to_grid(float *step, int *power, int unit)
01070 {
01071     const float loga= (float)log10(*step);
01072     float rem;
01073     
01074     *power= (int)(loga);
01075     
01076     rem= loga - (*power);
01077     rem= (float)pow(10.0, rem);
01078     
01079     if (loga < 0.0f) {
01080         if (rem < 0.2f) rem= 0.2f;
01081         else if(rem < 0.5f) rem= 0.5f;
01082         else rem= 1.0f;
01083         
01084         *step= rem * (float)pow(10.0, (*power));
01085         
01086         /* for frames, we want 1.0 frame intervals only */
01087         if (unit == V2D_UNIT_FRAMES) {
01088             rem = 1.0f;
01089             *step = 2.0f; /* use 2 since there are grid lines drawn in between, this way to get 1 line per frane */
01090         }
01091         
01092         /* prevents printing 1.0 2.0 3.0 etc */
01093         if (rem == 1.0f) (*power)++;    
01094     }
01095     else {
01096         if (rem < 2.0f) rem= 2.0f;
01097         else if(rem < 5.0f) rem= 5.0f;
01098         else rem= 10.0f;
01099         
01100         *step= rem * (float)pow(10.0, (*power));
01101         
01102         (*power)++;
01103         /* prevents printing 1.0, 2.0, 3.0, etc. */
01104         if (rem == 10.0f) (*power)++;   
01105     }
01106 }
01107 
01108 /* Intialise settings necessary for drawing gridlines in a 2d-view 
01109  *  - Currently, will return pointer to View2DGrid struct that needs to 
01110  *    be freed with UI_view2d_grid_free()
01111  *  - Is used for scrollbar drawing too (for units drawing)
01112  *  - Units + clamping args will be checked, to make sure they are valid values that can be used
01113  *    so it is very possible that we won't return grid at all!
01114  *  
01115  *  - xunits,yunits = V2D_UNIT_*  grid steps in seconds or frames 
01116  *  - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals
01117  *  - winx          = width of region we're drawing to, note: not used but keeping for completeness.
01118  *  - winy          = height of region we're drawing into
01119  */
01120 View2DGrid *UI_view2d_grid_calc(Scene *scene, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp, int UNUSED(winx), int winy)
01121 {
01122 
01123     View2DGrid *grid;
01124     float space, pixels, seconddiv;
01125     
01126     /* check that there are at least some workable args */
01127     if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) && ELEM(V2D_ARG_DUMMY, yunits, yclamp))
01128         return NULL;
01129     
01130     /* grid here is allocated... */
01131     grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
01132     
01133     /* rule: gridstep is minimal GRIDSTEP pixels */
01134     if (xunits == V2D_UNIT_SECONDS) {
01135         seconddiv= (float)(0.01 * FPS);
01136     }
01137     else {
01138         seconddiv= 1.0f;
01139     }
01140     
01141     /* calculate x-axis grid scale (only if both args are valid) */
01142     if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
01143         space= v2d->cur.xmax - v2d->cur.xmin;
01144         pixels= (float)(v2d->mask.xmax - v2d->mask.xmin);
01145         
01146         if(pixels!=0.0f) {
01147             grid->dx= (U.v2d_min_gridsize * space) / (seconddiv * pixels);
01148             step_to_grid(&grid->dx, &grid->powerx, xunits);
01149             grid->dx *= seconddiv;
01150         }
01151         
01152         if (xclamp == V2D_GRID_CLAMP) {
01153             if (grid->dx < 0.1f) grid->dx= 0.1f;
01154             grid->powerx-= 2;
01155             if (grid->powerx < -2) grid->powerx= -2;
01156         }
01157     }
01158     
01159     /* calculate y-axis grid scale (only if both args are valid) */
01160     if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
01161         space= v2d->cur.ymax - v2d->cur.ymin;
01162         pixels= (float)winy;
01163         
01164         grid->dy= U.v2d_min_gridsize * space / pixels;
01165         step_to_grid(&grid->dy, &grid->powery, yunits);
01166         
01167         if (yclamp == V2D_GRID_CLAMP) {
01168             if (grid->dy < 1.0f) grid->dy= 1.0f;
01169             if (grid->powery < 1) grid->powery= 1;
01170         }
01171     }
01172     
01173     /* calculate start position */
01174     if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
01175         grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - (float)fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
01176         if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
01177     }
01178     else
01179         grid->startx= v2d->cur.xmin;
01180         
01181     if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
01182         grid->starty= (v2d->cur.ymin - (float)fmod(v2d->cur.ymin, grid->dy));
01183         if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
01184     }
01185     else
01186         grid->starty= v2d->cur.ymin;
01187     
01188     return grid;
01189 }
01190 
01191 /* Draw gridlines in the given 2d-region */
01192 void UI_view2d_grid_draw(View2D *v2d, View2DGrid *grid, int flag)
01193 {
01194     float vec1[2], vec2[2];
01195     int a, step;
01196     
01197     /* check for grid first, as it may not exist */
01198     if (grid == NULL)
01199         return;
01200     
01201     /* vertical lines */
01202     if (flag & V2D_VERTICAL_LINES) {
01203         /* initialise initial settings */
01204         vec1[0]= vec2[0]= grid->startx;
01205         vec1[1]= grid->starty;
01206         vec2[1]= v2d->cur.ymax;
01207         
01208         /* minor gridlines */
01209         step= (v2d->mask.xmax - v2d->mask.xmin + 1) / U.v2d_min_gridsize;
01210         UI_ThemeColor(TH_GRID);
01211         
01212         for (a=0; a<step; a++) {
01213             glBegin(GL_LINE_STRIP);
01214                 glVertex2fv(vec1); 
01215                 glVertex2fv(vec2);
01216             glEnd();
01217             
01218             vec2[0]= vec1[0]+= grid->dx;
01219         }
01220         
01221         /* major gridlines */
01222         vec2[0]= vec1[0]-= 0.5f*grid->dx;
01223         UI_ThemeColorShade(TH_GRID, 16);
01224         
01225         step++;
01226         for (a=0; a<=step; a++) {
01227             glBegin(GL_LINE_STRIP);
01228                 glVertex2fv(vec1); 
01229                 glVertex2fv(vec2);
01230             glEnd();
01231             
01232             vec2[0]= vec1[0]-= grid->dx;
01233         }
01234     }
01235     
01236     /* horizontal lines */
01237     if (flag & V2D_HORIZONTAL_LINES) {
01238         /* only major gridlines */
01239         vec1[1]= vec2[1]= grid->starty;
01240         vec1[0]= grid->startx;
01241         vec2[0]= v2d->cur.xmax;
01242         
01243         step= (v2d->mask.ymax - v2d->mask.ymin + 1) / U.v2d_min_gridsize;
01244         
01245         UI_ThemeColor(TH_GRID);
01246         for (a=0; a<=step; a++) {
01247             glBegin(GL_LINE_STRIP);
01248                 glVertex2fv(vec1); 
01249                 glVertex2fv(vec2);
01250             glEnd();
01251             
01252             vec2[1]= vec1[1]+= grid->dy;
01253         }
01254         
01255         /* fine grid lines */
01256         vec2[1]= vec1[1]-= 0.5f*grid->dy;
01257         step++;
01258         
01259         if (flag & V2D_HORIZONTAL_FINELINES) { 
01260             UI_ThemeColorShade(TH_GRID, 16);
01261             for (a=0; a<step; a++) {
01262                 glBegin(GL_LINE_STRIP);
01263                     glVertex2fv(vec1); 
01264                     glVertex2fv(vec2);
01265                 glEnd();
01266                 
01267                 vec2[1]= vec1[1]-= grid->dy;
01268             }
01269         }
01270     }
01271     
01272     /* Axes are drawn as darker lines */
01273     UI_ThemeColorShade(TH_GRID, -50);
01274     
01275     /* horizontal axis */
01276     if (flag & V2D_HORIZONTAL_AXIS) {
01277         vec1[0]= v2d->cur.xmin;
01278         vec2[0]= v2d->cur.xmax;
01279         vec1[1]= vec2[1]= 0.0f;
01280         
01281         glBegin(GL_LINE_STRIP);
01282             glVertex2fv(vec1);
01283             glVertex2fv(vec2);
01284         glEnd();
01285     }
01286     
01287     /* vertical axis */
01288     if (flag & V2D_VERTICAL_AXIS) {
01289         vec1[1]= v2d->cur.ymin;
01290         vec2[1]= v2d->cur.ymax;
01291         vec1[0]= vec2[0]= 0.0f;
01292         
01293         glBegin(GL_LINE_STRIP);
01294             glVertex2fv(vec1); 
01295             glVertex2fv(vec2);
01296         glEnd();
01297     }
01298 }
01299 
01300 /* Draw a constant grid in given 2d-region */
01301 void UI_view2d_constant_grid_draw(View2D *v2d)
01302 {
01303     float start, step= 25.0f;
01304 
01305     UI_ThemeColorShade(TH_BACK, -10);
01306     
01307     start= v2d->cur.xmin - (float)fmod(v2d->cur.xmin, step);
01308     
01309     glBegin(GL_LINES);
01310     for(; start<v2d->cur.xmax; start+=step) {
01311         glVertex2f(start, v2d->cur.ymin);
01312         glVertex2f(start, v2d->cur.ymax);
01313     }
01314 
01315     start= v2d->cur.ymin - (float)fmod(v2d->cur.ymin, step);
01316     for(; start<v2d->cur.ymax; start+=step) {
01317         glVertex2f(v2d->cur.xmin, start);
01318         glVertex2f(v2d->cur.xmax, start);
01319     }
01320     
01321     /* X and Y axis */
01322     UI_ThemeColorShade(TH_BACK, -18);
01323     glVertex2f(0.0f, v2d->cur.ymin);
01324     glVertex2f(0.0f, v2d->cur.ymax);
01325     glVertex2f(v2d->cur.xmin, 0.0f);
01326     glVertex2f(v2d->cur.xmax, 0.0f);
01327     
01328     glEnd();
01329 }
01330 
01331 /* the price we pay for not exposting structs :( */
01332 void UI_view2d_grid_size(View2DGrid *grid, float *r_dx, float *r_dy)
01333 {
01334     *r_dx= grid->dx;
01335     *r_dy= grid->dy;
01336 }
01337 
01338 /* free temporary memory used for drawing grid */
01339 void UI_view2d_grid_free(View2DGrid *grid)
01340 {
01341     /* only free if there's a grid */
01342     if (grid)
01343         MEM_freeN(grid);
01344 }
01345 
01346 /* *********************************************************************** */
01347 /* Scrollers */
01348 
01349 /* View2DScrollers is typedef'd in UI_view2d.h 
01350  * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. 
01351  *         For now, we don't need to have a separate (internal) header for structs like this...
01352  */
01353 struct View2DScrollers {
01354         /* focus bubbles */
01355     int vert_min, vert_max; /* vertical scrollbar */
01356     int hor_min, hor_max;   /* horizontal scrollbar */
01357     
01358     rcti hor, vert;         /* exact size of slider backdrop */
01359     int horfull, vertfull;  /* set if sliders are full, we don't draw them */
01360     
01361     /* scales */
01362     View2DGrid *grid;       /* grid for coordinate drawing */
01363     short xunits, xclamp;   /* units and clamping options for x-axis */
01364     short yunits, yclamp;   /* units and clamping options for y-axis */
01365 };
01366 
01367 /* Calculate relevant scroller properties */
01368 View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
01369 {
01370     View2DScrollers *scrollers;
01371     rcti vert, hor;
01372     float fac1, fac2, totsize, scrollsize;
01373     int scroll= view2d_scroll_mapped(v2d->scroll);
01374     
01375     /* scrollers is allocated here... */
01376     scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
01377     
01378     vert= v2d->vert;
01379     hor= v2d->hor;
01380     
01381     /* slider rects need to be smaller than region */
01382     hor.xmin+=4;
01383     hor.xmax-=4;
01384     if (scroll & V2D_SCROLL_BOTTOM)
01385         hor.ymin+=4;
01386     else
01387         hor.ymax-=4;
01388     
01389     if (scroll & V2D_SCROLL_LEFT)
01390         vert.xmin+=4;
01391     else
01392         vert.xmax-=4;
01393     vert.ymin+=4;
01394     vert.ymax-=4;
01395     
01396     CLAMP(vert.ymin, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
01397     CLAMP(hor.xmin, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
01398     
01399     /* store in scrollers, used for drawing */
01400     scrollers->vert= vert;
01401     scrollers->hor= hor;
01402     
01403     /* scroller 'buttons':
01404      *  - These should always remain within the visible region of the scrollbar
01405      *  - They represent the region of 'tot' that is visible in 'cur'
01406      */
01407     
01408     /* horizontal scrollers */
01409     if (scroll & V2D_SCROLL_HORIZONTAL) {
01410         /* scroller 'button' extents */
01411         totsize= v2d->tot.xmax - v2d->tot.xmin;
01412         scrollsize= (float)(hor.xmax - hor.xmin);
01413         if(totsize==0.0f) totsize = 1.0f; /* avoid divide by zero */
01414         
01415         fac1= (v2d->cur.xmin - v2d->tot.xmin) / totsize;
01416         if(fac1<=0.0f)
01417             scrollers->hor_min= hor.xmin;
01418         else
01419             scrollers->hor_min= (int)(hor.xmin + (fac1 * scrollsize));
01420         
01421         fac2= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
01422         if(fac2>=1.0f)
01423             scrollers->hor_max= hor.xmax;
01424         else
01425             scrollers->hor_max= (int)(hor.xmin + (fac2 * scrollsize));
01426         
01427         /* prevent inverted sliders */
01428         if (scrollers->hor_min > scrollers->hor_max) 
01429             scrollers->hor_min= scrollers->hor_max;
01430         /* prevent sliders from being too small, and disappearing */
01431         if ((scrollers->hor_max - scrollers->hor_min) < V2D_SCROLLER_HANDLE_SIZE) {
01432             scrollers->hor_max= scrollers->hor_min + V2D_SCROLLER_HANDLE_SIZE;
01433 
01434             CLAMP(scrollers->hor_max, hor.xmin+V2D_SCROLLER_HANDLE_SIZE, hor.xmax);
01435             CLAMP(scrollers->hor_min, hor.xmin, hor.xmax-V2D_SCROLLER_HANDLE_SIZE);
01436         }
01437         
01438         /* check whether sliders can disappear due to the full-range being used */
01439         if(v2d->keeptot) {
01440             if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
01441                 v2d->scroll |= V2D_SCROLL_HORIZONTAL_FULLR;
01442                 scrollers->horfull= 1;
01443             }
01444             else    
01445                 v2d->scroll &= ~V2D_SCROLL_HORIZONTAL_FULLR;
01446         }
01447     }
01448     
01449     /* vertical scrollers */
01450     if (scroll & V2D_SCROLL_VERTICAL) {
01451         /* scroller 'button' extents */
01452         totsize= v2d->tot.ymax - v2d->tot.ymin;
01453         scrollsize= (float)(vert.ymax - vert.ymin);
01454         if(totsize==0.0f) totsize = 1.0f; /* avoid divide by zero */
01455         
01456         fac1= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
01457         if(fac1<=0.0f)
01458             scrollers->vert_min= vert.ymin;
01459         else
01460             scrollers->vert_min= (int)(vert.ymin + (fac1 * scrollsize));
01461         
01462         fac2= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
01463         if(fac2>=1.0f)
01464             scrollers->vert_max= vert.ymax;
01465         else
01466             scrollers->vert_max= (int)(vert.ymin + (fac2 * scrollsize));
01467         
01468         /* prevent inverted sliders */
01469         if (scrollers->vert_min > scrollers->vert_max) 
01470             scrollers->vert_min= scrollers->vert_max;
01471         /* prevent sliders from being too small, and disappearing */
01472         if ((scrollers->vert_max - scrollers->vert_min) < V2D_SCROLLER_HANDLE_SIZE) {
01473             
01474             scrollers->vert_max= scrollers->vert_min + V2D_SCROLLER_HANDLE_SIZE;
01475             
01476             CLAMP(scrollers->vert_max, vert.ymin+V2D_SCROLLER_HANDLE_SIZE, vert.ymax);
01477             CLAMP(scrollers->vert_min, vert.ymin, vert.ymax-V2D_SCROLLER_HANDLE_SIZE);
01478         }
01479 
01480         /* check whether sliders can disappear due to the full-range being used */
01481         if(v2d->keeptot) {
01482             if ((fac1 <= 0.0f) && (fac2 >= 1.0f)) { 
01483                 v2d->scroll |= V2D_SCROLL_VERTICAL_FULLR;
01484                 scrollers->vertfull= 1;
01485             }
01486             else    
01487                 v2d->scroll &= ~V2D_SCROLL_VERTICAL_FULLR;
01488         }
01489     }
01490     
01491     /* grid markings on scrollbars */
01492     if (scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
01493         /* store clamping */
01494         scrollers->xclamp= xclamp;
01495         scrollers->xunits= xunits;
01496         scrollers->yclamp= yclamp;
01497         scrollers->yunits= yunits;
01498         
01499         scrollers->grid= UI_view2d_grid_calc(CTX_data_scene(C), v2d, xunits, xclamp, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
01500     }
01501     
01502     /* return scrollers */
01503     return scrollers;
01504 }
01505 
01506 /* Print scale marking along a time scrollbar */
01507 static void scroll_printstr(Scene *scene, float x, float y, float val, int power, short unit, char dir)
01508 {
01509     int len;
01510     char timecode_str[32];
01511     
01512     /* adjust the scale unit to work ok */
01513     if (dir == 'v') {
01514         /* here we bump up the power by factor of 10, as 
01515          * rotation values (hence 'degrees') are divided by 10 to 
01516          * be able to show the curves at the same time
01517          */
01518         if (ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME)) {
01519             power += 1;
01520             val *= 10;
01521         }
01522     }
01523     
01524     /* get string to print */
01525     ANIM_timecode_string_from_frame(timecode_str, scene, power, (unit == V2D_UNIT_SECONDS), val);
01526     
01527     /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
01528     len= strlen(timecode_str);
01529     if (dir == 'h') {
01530         /* seconds/timecode display has slightly longer strings... */
01531         if (unit == V2D_UNIT_SECONDS)
01532             x-= 3*len;
01533         else
01534             x-= 4*len;
01535     }
01536     
01537     /* Add degree sympbol to end of string for vertical scrollbar? */
01538     if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
01539         timecode_str[len]= 186;
01540         timecode_str[len+1]= 0;
01541     }
01542     
01543     /* draw it */
01544     BLF_draw_default_ascii(x, y, 0.0f, timecode_str, sizeof(timecode_str));
01545 }
01546 
01547 /* Draw scrollbars in the given 2d-region */
01548 void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
01549 {
01550     Scene *scene= CTX_data_scene(C);
01551     rcti vert, hor;
01552     int scroll= view2d_scroll_mapped(v2d->scroll);
01553     
01554     /* make copies of rects for less typing */
01555     vert= vs->vert;
01556     hor= vs->hor;
01557     
01558     /* horizontal scrollbar */
01559     if (scroll & V2D_SCROLL_HORIZONTAL) {
01560         /* only draw scrollbar when it doesn't fill the entire space */
01561         if(vs->horfull==0) {
01562             bTheme *btheme= UI_GetTheme();
01563             uiWidgetColors wcol= btheme->tui.wcol_scroll;
01564             rcti slider;
01565             int state;
01566             
01567             slider.xmin= vs->hor_min;
01568             slider.xmax= vs->hor_max;
01569             slider.ymin= hor.ymin;
01570             slider.ymax= hor.ymax;
01571             
01572             state= (v2d->scroll_ui & V2D_SCROLL_H_ACTIVE)?UI_SCROLL_PRESSED:0;
01573             
01574             /* show zoom handles if:
01575              *  - zooming on x-axis is allowed (no scroll otherwise)
01576              *  - slider bubble is large enough (no overdraw confusion)
01577              *  - scale is shown on the scroller 
01578              *    (workaround to make sure that button windows don't show these, 
01579              *      and only the time-grids with their zoomability can do so)
01580              */
01581             if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0 && 
01582                 (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) &&
01583                 (slider.xmax - slider.xmin > V2D_SCROLLER_HANDLE_SIZE))
01584             {
01585                 state |= UI_SCROLL_ARROWS;
01586             }
01587             
01588             UI_ThemeColor(TH_BACK);
01589             glRecti(v2d->hor.xmin, v2d->hor.ymin, v2d->hor.xmax, v2d->hor.ymax);
01590             
01591             uiWidgetScrollDraw(&wcol, &hor, &slider, state);
01592         }
01593         
01594         /* scale indicators */
01595         if ((scroll & V2D_SCROLL_SCALE_HORIZONTAL) && (vs->grid)) {
01596             View2DGrid *grid= vs->grid;
01597             float fac, dfac, fac2, val;
01598             
01599             /* the numbers: convert grid->startx and -dx to scroll coordinates 
01600              *  - fac is x-coordinate to draw to
01601              *  - dfac is gap between scale markings
01602              */
01603             fac= (grid->startx - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
01604             fac= (float)hor.xmin + fac*(hor.xmax - hor.xmin);
01605             
01606             dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
01607             dfac= dfac * (hor.xmax - hor.xmin);
01608             
01609             /* set starting value, and text color */
01610             UI_ThemeColor(TH_TEXT);
01611             val= grid->startx;
01612             
01613             /* if we're clamping to whole numbers only, make sure entries won't be repeated */
01614             if (vs->xclamp == V2D_GRID_CLAMP) {
01615                 while (grid->dx < 0.9999f) {
01616                     grid->dx *= 2.0f;
01617                     dfac *= 2.0f;
01618                 }
01619             }
01620             if (vs->xunits == V2D_UNIT_FRAMES)
01621                 grid->powerx= 1;
01622             
01623             /* draw numbers in the appropriate range */
01624             if (dfac > 0.0f) {
01625                 float h= 2.0f+(float)(hor.ymin);
01626                 
01627                 for (; fac < hor.xmax-10; fac+=dfac, val+=grid->dx) {
01628                     
01629                     /* make prints look nicer for scrollers */
01630                     if(fac < hor.xmin+10)
01631                         continue;
01632                     
01633                     switch (vs->xunits) {                           
01634                         case V2D_UNIT_FRAMES:       /* frames (as whole numbers)*/
01635                             scroll_printstr(scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMES, 'h');
01636                             break;
01637                             
01638                         case V2D_UNIT_FRAMESCALE:   /* frames (not always as whole numbers) */
01639                             scroll_printstr(scene, fac, h, val, grid->powerx, V2D_UNIT_FRAMESCALE, 'h');
01640                             break;
01641                         
01642                         case V2D_UNIT_SECONDS:      /* seconds */
01643                             fac2= val/(float)FPS;
01644                             scroll_printstr(scene, fac, h, fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
01645                             break;
01646                             
01647                         case V2D_UNIT_SECONDSSEQ:   /* seconds with special calculations (only used for sequencer only) */
01648                         {
01649                             float time;
01650                             
01651                             fac2= val/(float)FPS;
01652                             time= (float)floor(fac2);
01653                             fac2= fac2-time;
01654                             
01655                             scroll_printstr(scene, fac, h, time+(float)FPS*fac2/100.0f, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
01656                         }
01657                             break;
01658                             
01659                         case V2D_UNIT_DEGREES:      /* Graph Editor for rotation Drivers */
01660                             /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
01661                             scroll_printstr(scene, fac, h, val, grid->powerx, V2D_UNIT_DEGREES, 'v');
01662                             break;
01663                     }
01664                 }
01665             }
01666         }
01667     }
01668     
01669     /* vertical scrollbar */
01670     if (scroll & V2D_SCROLL_VERTICAL) {
01671         /* only draw scrollbar when it doesn't fill the entire space */
01672         if(vs->vertfull==0) {
01673             bTheme *btheme= UI_GetTheme();
01674             uiWidgetColors wcol= btheme->tui.wcol_scroll;
01675             rcti slider;
01676             int state;
01677             
01678             slider.xmin= vert.xmin;
01679             slider.xmax= vert.xmax;
01680             slider.ymin= vs->vert_min;
01681             slider.ymax= vs->vert_max;
01682             
01683             state= (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE)?UI_SCROLL_PRESSED:0;
01684             
01685             /* show zoom handles if:
01686              *  - zooming on y-axis is allowed (no scroll otherwise)
01687              *  - slider bubble is large enough (no overdraw confusion)
01688              *  - scale is shown on the scroller 
01689              *    (workaround to make sure that button windows don't show these, 
01690              *      and only the time-grids with their zoomability can do so)
01691              */
01692             if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0 && 
01693                 (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) &&
01694                 (slider.ymax - slider.ymin > V2D_SCROLLER_HANDLE_SIZE))
01695             {
01696                 state |= UI_SCROLL_ARROWS;
01697             }
01698                 
01699             UI_ThemeColor(TH_BACK);
01700             glRecti(v2d->vert.xmin, v2d->vert.ymin, v2d->vert.xmax, v2d->vert.ymax);
01701             
01702             uiWidgetScrollDraw(&wcol, &vert, &slider, state);
01703         }
01704         
01705         
01706         /* scale indiators */
01707         if ((scroll & V2D_SCROLL_SCALE_VERTICAL) && (vs->grid)) {
01708             View2DGrid *grid= vs->grid;
01709             float fac, dfac, val;
01710             
01711             /* the numbers: convert grid->starty and dy to scroll coordinates 
01712              *  - fac is y-coordinate to draw to
01713              *  - dfac is gap between scale markings
01714              *  - these involve a correction for horizontal scrollbar
01715              *    NOTE: it's assumed that that scrollbar is there if this is involved!
01716              */
01717             fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
01718             fac= vert.ymin + fac*(vert.ymax - vert.ymin);
01719             
01720             dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
01721             dfac= dfac * (vert.ymax - vert.ymin);
01722             
01723             /* set starting value, and text color */
01724             UI_ThemeColor(TH_TEXT);
01725             val= grid->starty;
01726             
01727             /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
01728             if (vs->yclamp == V2D_GRID_CLAMP)
01729                 fac += 0.5f * dfac;
01730                 
01731             /* draw vertical steps */
01732             if (dfac > 0.0f) {
01733                 
01734                 BLF_rotation_default(90.0f);
01735                 BLF_enable_default(BLF_ROTATION);
01736 
01737                 for (; fac < vert.ymax-10; fac+= dfac, val += grid->dy) {
01738                     
01739                     /* make prints look nicer for scrollers */
01740                     if(fac < vert.ymin+10)
01741                         continue;
01742                     
01743                     scroll_printstr(scene, (float)(vert.xmax)-2.0f, fac, val, grid->powery, vs->yunits, 'v');
01744                 }
01745                 
01746                 BLF_disable_default(BLF_ROTATION);
01747             }
01748         }   
01749     }
01750     
01751 }
01752 
01753 /* free temporary memory used for drawing scrollers */
01754 void UI_view2d_scrollers_free(View2DScrollers *scrollers)
01755 {
01756     /* need to free grid as well... */
01757     if (scrollers->grid) MEM_freeN(scrollers->grid);
01758     MEM_freeN(scrollers);
01759 }
01760 
01761 /* *********************************************************************** */
01762 /* List View Utilities */
01763 
01764 /* Get the view-coordinates of the nominated cell 
01765  *  - columnwidth, rowheight    = size of each 'cell'
01766  *  - startx, starty            = coordinates (in 'tot' rect space) that the list starts from
01767  *                            This should be (0,0) for most views. However, for those where the starting row was offsetted
01768  *                            (like for Animation Editor channel lists, to make the first entry more visible), these will be 
01769  *                            the min-coordinates of the first item.
01770  *  - column, row               = the 2d-corodinates (in 2D-view / 'tot' rect space) the cell exists at
01771  *  - rect                  = coordinates of the cell (passed as single var instead of 4 separate, as it's more useful this way)
01772  */
01773 void UI_view2d_listview_cell_to_view(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, int column, int row, rctf *rect)
01774 {
01775     /* sanity checks */
01776     if ELEM(NULL, v2d, rect)
01777         return;
01778     if ((columnwidth <= 0) && (rowheight <= 0)) {
01779         rect->xmin= rect->xmax= 0.0f;
01780         rect->ymin= rect->ymax= 0.0f;
01781         return;
01782     }
01783     
01784     /* x-coordinates */
01785     rect->xmin= startx + (float)(columnwidth * column);
01786     rect->xmax= startx + (float)(columnwidth * (column + 1));
01787     
01788     if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
01789         /* simply negate the values for the coordinates if in negative half */
01790         rect->xmin = -rect->xmin;
01791         rect->xmax = -rect->xmax;
01792     }
01793     
01794     /* y-coordinates */
01795     rect->ymin= starty + (float)(rowheight * row);
01796     rect->ymax= starty + (float)(rowheight * (row + 1));
01797     
01798     if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
01799         /* simply negate the values for the coordinates if in negative half */
01800         rect->ymin = -rect->ymin;
01801         rect->ymax = -rect->ymax;
01802     }
01803 }
01804 
01805 /* Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in.
01806  *  - columnwidth, rowheight    = size of each 'cell'
01807  *  - startx, starty            = coordinates (in 'tot' rect space) that the list starts from
01808  *                            This should be (0,0) for most views. However, for those where the starting row was offsetted
01809  *                            (like for Animation Editor channel lists, to make the first entry more visible), these will be 
01810  *                            the min-coordinates of the first item.
01811  *  - viewx, viewy          = 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for
01812  *  - column, row               = the 'coordinates' of the relevant 'cell'
01813  */
01814 void UI_view2d_listview_view_to_cell(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
01815                         float viewx, float viewy, int *column, int *row)
01816 {
01817     /* adjust view coordinates to be all positive ints, corrected for the start offset */
01818     const int x= (int)(floorf(fabsf(viewx) + 0.5f) - startx);
01819     const int y= (int)(floorf(fabsf(viewy) + 0.5f) - starty);
01820     
01821     /* sizes must not be negative */
01822     if ( (v2d == NULL) || ((columnwidth <= 0) && (rowheight <= 0)) ) {
01823         if (column) *column= 0;
01824         if (row) *row= 0;
01825         
01826         return;
01827     }
01828     
01829     /* get column */
01830     if ((column) && (columnwidth > 0))
01831         *column= x / columnwidth;
01832     else if (column)
01833         *column= 0;
01834     
01835     /* get row */
01836     if ((row) && (rowheight > 0))
01837         *row= y / rowheight;
01838     else if (row)
01839         *row= 0;
01840 }
01841 
01842 /* Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect 
01843  *  - columnwidth, rowheight    = size of each 'cell'
01844  *  - startx, starty            = coordinates that the list starts from, which should be (0,0) for most views
01845  *  - column/row_min/max        = the starting and ending column/row indices
01846  */
01847 void UI_view2d_listview_visible_cells(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, 
01848                         int *column_min, int *column_max, int *row_min, int *row_max)
01849 {
01850     /* using 'cur' rect coordinates, call the cell-getting function to get the cells for this */
01851     if (v2d) {
01852         /* min */
01853         UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
01854                     v2d->cur.xmin, v2d->cur.ymin, column_min, row_min);
01855                     
01856         /* max*/
01857         UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty, 
01858                     v2d->cur.xmax, v2d->cur.ymax, column_max, row_max);
01859     }
01860 }
01861 
01862 /* *********************************************************************** */
01863 /* Coordinate Conversions */
01864 
01865 /* Convert from screen/region space to 2d-View space 
01866  *  
01867  *  - x,y           = coordinates to convert
01868  *  - viewx,viewy       = resultant coordinates
01869  */
01870 void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
01871 {
01872     float div, ofs;
01873 
01874     if (viewx) {
01875         div= (float)(v2d->mask.xmax - v2d->mask.xmin);
01876         ofs= (float)v2d->mask.xmin;
01877         
01878         *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
01879     }
01880 
01881     if (viewy) {
01882         div= (float)(v2d->mask.ymax - v2d->mask.ymin);
01883         ofs= (float)v2d->mask.ymin;
01884         
01885         *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
01886     }
01887 }
01888 
01889 /* Convert from 2d-View space to screen/region space
01890  *  - Coordinates are clamped to lie within bounds of region
01891  *
01892  *  - x,y               = coordinates to convert
01893  *  - regionx,regiony   = resultant coordinates 
01894  */
01895 void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *regionx, int *regiony)
01896 {
01897     /* set initial value in case coordinate lies outside of bounds */
01898     if (regionx)
01899         *regionx= V2D_IS_CLIPPED;
01900     if (regiony)
01901         *regiony= V2D_IS_CLIPPED;
01902     
01903     /* express given coordinates as proportional values */
01904     x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
01905     y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
01906     
01907     /* check if values are within bounds */
01908     if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
01909         if (regionx)
01910             *regionx= (int)(v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin));
01911         if (regiony)
01912             *regiony= (int)(v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin));
01913     }
01914 }
01915 
01916 /* Convert from 2d-view space to screen/region space
01917  *  - Coordinates are NOT clamped to lie within bounds of region
01918  *
01919  *  - x,y               = coordinates to convert
01920  *  - regionx,regiony   = resultant coordinates 
01921  */
01922 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, int *regionx, int *regiony)
01923 {
01924     /* step 1: express given coordinates as proportional values */
01925     x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
01926     y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
01927     
01928     /* step 2: convert proportional distances to screen coordinates  */
01929     x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
01930     y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
01931     
01932     /* although we don't clamp to lie within region bounds, we must avoid exceeding size of ints */
01933     if (regionx) {
01934         if (x < INT_MIN) *regionx= INT_MIN;
01935         else if(x > INT_MAX) *regionx= INT_MAX;
01936         else *regionx= (int)x;
01937     }
01938     if (regiony) {
01939         if (y < INT_MIN) *regiony= INT_MIN;
01940         else if(y > INT_MAX) *regiony= INT_MAX;
01941         else *regiony= (int)y;
01942     }
01943 }
01944 
01945 /* *********************************************************************** */
01946 /* Utilities */
01947 
01948 /* View2D data by default resides in region, so get from region stored in context */
01949 View2D *UI_view2d_fromcontext(const bContext *C)
01950 {
01951     ScrArea *area= CTX_wm_area(C);
01952     ARegion *region= CTX_wm_region(C);
01953 
01954     if (area == NULL) return NULL;
01955     if (region == NULL) return NULL;
01956     return &(region->v2d);
01957 }
01958 
01959 /* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
01960 View2D *UI_view2d_fromcontext_rwin(const bContext *C)
01961 {
01962     ScrArea *sa= CTX_wm_area(C);
01963     ARegion *region= CTX_wm_region(C);
01964 
01965     if (sa == NULL) return NULL;
01966     if (region == NULL) return NULL;
01967     if (region->regiontype!=RGN_TYPE_WINDOW) {
01968         ARegion *ar= BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
01969         return ar ? &(ar->v2d) : NULL;
01970     }
01971     return &(region->v2d);
01972 }
01973 
01974 
01975 /* Calculate the scale per-axis of the drawing-area
01976  *  - Is used to inverse correct drawing of icons, etc. that need to follow view 
01977  *    but not be affected by scale
01978  *
01979  *  - x,y   = scale on each axis
01980  */
01981 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
01982 {
01983     if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
01984     if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
01985 }
01986 
01987 /* Check if mouse is within scrollers
01988  *  - Returns appropriate code for match
01989  *      'h' = in horizontal scroller
01990  *      'v' = in vertical scroller
01991  *      0 = not in scroller
01992  *  
01993  *  - x,y   = mouse coordinates in screen (not region) space
01994  */
01995 short UI_view2d_mouse_in_scrollers (const bContext *C, View2D *v2d, int x, int y)
01996 {
01997     ARegion *ar= CTX_wm_region(C);
01998     int co[2];
01999     int scroll= view2d_scroll_mapped(v2d->scroll);
02000     
02001     /* clamp x,y to region-coordinates first */
02002     co[0]= x - ar->winrct.xmin;
02003     co[1]= y - ar->winrct.ymin;
02004     
02005     /* check if within scrollbars */
02006     if (scroll & V2D_SCROLL_HORIZONTAL) {
02007         if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h';
02008     }
02009     if (scroll & V2D_SCROLL_VERTICAL) {
02010         if (IN_2D_VERT_SCROLL(v2d, co)) return 'v';
02011     }   
02012     
02013     /* not found */
02014     return 0;
02015 }
02016 
02017 /* ******************* view2d text drawing cache ******************** */
02018 
02019 /* assumes caches are used correctly, so for time being no local storage in v2d */
02020 static ListBase strings= {NULL, NULL};
02021 
02022 typedef struct View2DString {
02023     struct View2DString *next, *prev;
02024     union {
02025         unsigned char ub[4];
02026         int pack;
02027     } col;
02028     int mval[2];
02029     rcti rect;
02030 } View2DString;
02031 
02032 
02033 void UI_view2d_text_cache_add(View2D *v2d, float x, float y, const char *str, const char col[4])
02034 {
02035     int mval[2];
02036     
02037     UI_view2d_view_to_region(v2d, x, y, mval, mval+1);
02038     
02039     if(mval[0]!=V2D_IS_CLIPPED && mval[1]!=V2D_IS_CLIPPED) {
02040         int len= strlen(str)+1;
02041         /* use calloc, rect has to be zeroe'd */
02042         View2DString *v2s= MEM_callocN(sizeof(View2DString)+len, "View2DString");
02043         char *v2s_str= (char *)(v2s+1);
02044         memcpy(v2s_str, str, len);
02045 
02046         BLI_addtail(&strings, v2s);
02047         v2s->col.pack= *((int *)col);
02048         v2s->mval[0]= mval[0];
02049         v2s->mval[1]= mval[1];
02050     }
02051 }
02052 
02053 /* no clip (yet) */
02054 void UI_view2d_text_cache_rectf(View2D *v2d, rctf *rect, const char *str, const char col[4])
02055 {
02056     int len= strlen(str)+1;
02057     View2DString *v2s= MEM_callocN(sizeof(View2DString)+len, "View2DString");
02058     char *v2s_str= (char *)(v2s+1);
02059     memcpy(v2s_str, str, len);
02060 
02061     UI_view2d_to_region_no_clip(v2d, rect->xmin, rect->ymin, &v2s->rect.xmin, &v2s->rect.ymin);
02062     UI_view2d_to_region_no_clip(v2d, rect->xmax, rect->ymax, &v2s->rect.xmax, &v2s->rect.ymax);
02063 
02064     v2s->col.pack= *((int *)col);
02065     v2s->mval[0]= v2s->rect.xmin;
02066     v2s->mval[1]= v2s->rect.ymin;
02067 
02068     BLI_addtail(&strings, v2s);
02069 }
02070 
02071 
02072 void UI_view2d_text_cache_draw(ARegion *ar)
02073 {
02074     View2DString *v2s;
02075     int col_pack_prev= 0;
02076     
02077     // glMatrixMode(GL_PROJECTION);
02078     // glPushMatrix();
02079     // glMatrixMode(GL_MODELVIEW);
02080     // glPushMatrix();
02081     ED_region_pixelspace(ar);
02082     
02083     for(v2s= strings.first; v2s; v2s= v2s->next) {
02084         const char *str= (const char *)(v2s+1);
02085         int xofs=0, yofs;
02086 
02087         yofs= ceil( 0.5f*(v2s->rect.ymax - v2s->rect.ymin - BLF_height_default("28")));
02088         if(yofs<1) yofs= 1;
02089 
02090         if(col_pack_prev != v2s->col.pack) {
02091             glColor3ubv(v2s->col.ub);
02092             col_pack_prev= v2s->col.pack;
02093         }
02094 
02095         if(v2s->rect.xmin >= v2s->rect.xmax)
02096             BLF_draw_default((float)v2s->mval[0]+xofs, (float)v2s->mval[1]+yofs, 0.0, str, BLF_DRAW_STR_DUMMY_MAX);
02097         else {
02098             BLF_clipping_default(v2s->rect.xmin-4, v2s->rect.ymin-4, v2s->rect.xmax+4, v2s->rect.ymax+4);
02099             BLF_enable_default(BLF_CLIPPING);
02100             BLF_draw_default(v2s->rect.xmin+xofs, v2s->rect.ymin+yofs, 0.0f, str, BLF_DRAW_STR_DUMMY_MAX);
02101             BLF_disable_default(BLF_CLIPPING);
02102         }
02103     }
02104     
02105     // glMatrixMode(GL_PROJECTION);
02106     // glPopMatrix();
02107     // glMatrixMode(GL_MODELVIEW);
02108     // glPopMatrix();
02109     
02110     if(strings.first) 
02111         BLI_freelistN(&strings);
02112 }
02113 
02114 
02115 /* ******************************************************** */
02116 
02117