Blender V2.61 - r43446
|
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