1/* 2 * tkUnixScrollbar.c -- 3 * 4 * This file implements the Unix specific portion of the scrollbar 5 * widget. 6 * 7 * Copyright (c) 1996 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16#include "tkScrollbar.h" 17 18/* 19 * Minimum slider length, in pixels (designed to make sure that the slider is 20 * always easy to grab with the mouse). 21 */ 22 23#define MIN_SLIDER_LENGTH 5 24 25/* 26 * Declaration of Unix specific scrollbar structure. 27 */ 28 29typedef struct UnixScrollbar { 30 TkScrollbar info; /* Generic scrollbar info. */ 31 GC troughGC; /* For drawing trough. */ 32 GC copyGC; /* Used for copying from pixmap onto screen. */ 33} UnixScrollbar; 34 35/* 36 * The class procedure table for the scrollbar widget. All fields except size 37 * are left initialized to NULL, which should happen automatically since the 38 * variable is declared at this scope. 39 */ 40 41Tk_ClassProcs tkpScrollbarProcs = { 42 sizeof(Tk_ClassProcs) /* size */ 43}; 44 45/* 46 *---------------------------------------------------------------------- 47 * 48 * TkpCreateScrollbar -- 49 * 50 * Allocate a new TkScrollbar structure. 51 * 52 * Results: 53 * Returns a newly allocated TkScrollbar structure. 54 * 55 * Side effects: 56 * Registers an event handler for the widget. 57 * 58 *---------------------------------------------------------------------- 59 */ 60 61TkScrollbar * 62TkpCreateScrollbar( 63 Tk_Window tkwin) 64{ 65 UnixScrollbar *scrollPtr = (UnixScrollbar *)ckalloc(sizeof(UnixScrollbar)); 66 scrollPtr->troughGC = None; 67 scrollPtr->copyGC = None; 68 69 Tk_CreateEventHandler(tkwin, 70 ExposureMask|StructureNotifyMask|FocusChangeMask, 71 TkScrollbarEventProc, (ClientData) scrollPtr); 72 73 return (TkScrollbar *) scrollPtr; 74} 75 76/* 77 *-------------------------------------------------------------- 78 * 79 * TkpDisplayScrollbar -- 80 * 81 * This procedure redraws the contents of a scrollbar window. It is 82 * invoked as a do-when-idle handler, so it only runs when there's 83 * nothing else for the application to do. 84 * 85 * Results: 86 * None. 87 * 88 * Side effects: 89 * Information appears on the screen. 90 * 91 *-------------------------------------------------------------- 92 */ 93 94void 95TkpDisplayScrollbar( 96 ClientData clientData) /* Information about window. */ 97{ 98 register TkScrollbar *scrollPtr = (TkScrollbar *) clientData; 99 register Tk_Window tkwin = scrollPtr->tkwin; 100 XPoint points[7]; 101 Tk_3DBorder border; 102 int relief, width, elementBorderWidth; 103 Pixmap pixmap; 104 105 if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { 106 goto done; 107 } 108 109 if (scrollPtr->vertical) { 110 width = Tk_Width(tkwin) - 2*scrollPtr->inset; 111 } else { 112 width = Tk_Height(tkwin) - 2*scrollPtr->inset; 113 } 114 elementBorderWidth = scrollPtr->elementBorderWidth; 115 if (elementBorderWidth < 0) { 116 elementBorderWidth = scrollPtr->borderWidth; 117 } 118 119 /* 120 * In order to avoid screen flashes, this procedure redraws the scrollbar 121 * in a pixmap, then copies the pixmap to the screen in a single 122 * operation. This means that there's no point in time where the on-sreen 123 * image has been cleared. 124 */ 125 126 pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin), 127 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 128 129 if (scrollPtr->highlightWidth != 0) { 130 GC gc; 131 132 if (scrollPtr->flags & GOT_FOCUS) { 133 gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap); 134 } else { 135 gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap); 136 } 137 Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap); 138 } 139 Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder, 140 scrollPtr->highlightWidth, scrollPtr->highlightWidth, 141 Tk_Width(tkwin) - 2*scrollPtr->highlightWidth, 142 Tk_Height(tkwin) - 2*scrollPtr->highlightWidth, 143 scrollPtr->borderWidth, scrollPtr->relief); 144 XFillRectangle(scrollPtr->display, pixmap, 145 ((UnixScrollbar*)scrollPtr)->troughGC, 146 scrollPtr->inset, scrollPtr->inset, 147 (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset), 148 (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset)); 149 150 /* 151 * Draw the top or left arrow. The coordinates of the polygon points 152 * probably seem odd, but they were carefully chosen with respect to X's 153 * rules for filling polygons. These point choices cause the arrows to 154 * just fill the narrow dimension of the scrollbar and be properly 155 * centered. 156 */ 157 158 if (scrollPtr->activeField == TOP_ARROW) { 159 border = scrollPtr->activeBorder; 160 relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief 161 : TK_RELIEF_RAISED; 162 } else { 163 border = scrollPtr->bgBorder; 164 relief = TK_RELIEF_RAISED; 165 } 166 if (scrollPtr->vertical) { 167 points[0].x = scrollPtr->inset - 1; 168 points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1; 169 points[1].x = width + scrollPtr->inset; 170 points[1].y = points[0].y; 171 points[2].x = width/2 + scrollPtr->inset; 172 points[2].y = scrollPtr->inset - 1; 173 Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3, 174 elementBorderWidth, relief); 175 } else { 176 points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1; 177 points[0].y = scrollPtr->inset - 1; 178 points[1].x = scrollPtr->inset; 179 points[1].y = width/2 + scrollPtr->inset; 180 points[2].x = points[0].x; 181 points[2].y = width + scrollPtr->inset; 182 Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3, 183 elementBorderWidth, relief); 184 } 185 186 /* 187 * Display the bottom or right arrow. 188 */ 189 190 if (scrollPtr->activeField == BOTTOM_ARROW) { 191 border = scrollPtr->activeBorder; 192 relief = scrollPtr->activeField == BOTTOM_ARROW 193 ? scrollPtr->activeRelief : TK_RELIEF_RAISED; 194 } else { 195 border = scrollPtr->bgBorder; 196 relief = TK_RELIEF_RAISED; 197 } 198 if (scrollPtr->vertical) { 199 points[0].x = scrollPtr->inset; 200 points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength 201 - scrollPtr->inset + 1; 202 points[1].x = width/2 + scrollPtr->inset; 203 points[1].y = Tk_Height(tkwin) - scrollPtr->inset; 204 points[2].x = width + scrollPtr->inset; 205 points[2].y = points[0].y; 206 Tk_Fill3DPolygon(tkwin, pixmap, border, 207 points, 3, elementBorderWidth, relief); 208 } else { 209 points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength 210 - scrollPtr->inset + 1; 211 points[0].y = scrollPtr->inset - 1; 212 points[1].x = points[0].x; 213 points[1].y = width + scrollPtr->inset; 214 points[2].x = Tk_Width(tkwin) - scrollPtr->inset; 215 points[2].y = width/2 + scrollPtr->inset; 216 Tk_Fill3DPolygon(tkwin, pixmap, border, 217 points, 3, elementBorderWidth, relief); 218 } 219 220 /* 221 * Display the slider. 222 */ 223 224 if (scrollPtr->activeField == SLIDER) { 225 border = scrollPtr->activeBorder; 226 relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief 227 : TK_RELIEF_RAISED; 228 } else { 229 border = scrollPtr->bgBorder; 230 relief = TK_RELIEF_RAISED; 231 } 232 if (scrollPtr->vertical) { 233 Tk_Fill3DRectangle(tkwin, pixmap, border, 234 scrollPtr->inset, scrollPtr->sliderFirst, 235 width, scrollPtr->sliderLast - scrollPtr->sliderFirst, 236 elementBorderWidth, relief); 237 } else { 238 Tk_Fill3DRectangle(tkwin, pixmap, border, 239 scrollPtr->sliderFirst, scrollPtr->inset, 240 scrollPtr->sliderLast - scrollPtr->sliderFirst, width, 241 elementBorderWidth, relief); 242 } 243 244 /* 245 * Copy the information from the off-screen pixmap onto the screen, then 246 * delete the pixmap. 247 */ 248 249 XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin), 250 ((UnixScrollbar*)scrollPtr)->copyGC, 0, 0, 251 (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); 252 Tk_FreePixmap(scrollPtr->display, pixmap); 253 254 done: 255 scrollPtr->flags &= ~REDRAW_PENDING; 256} 257 258/* 259 *---------------------------------------------------------------------- 260 * 261 * TkpComputeScrollbarGeometry -- 262 * 263 * After changes in a scrollbar's size or configuration, this procedure 264 * recomputes various geometry information used in displaying the 265 * scrollbar. 266 * 267 * Results: 268 * None. 269 * 270 * Side effects: 271 * The scrollbar will be displayed differently. 272 * 273 *---------------------------------------------------------------------- 274 */ 275 276extern void 277TkpComputeScrollbarGeometry( 278 register TkScrollbar *scrollPtr) 279 /* Scrollbar whose geometry may have 280 * changed. */ 281{ 282 int width, fieldLength; 283 284 if (scrollPtr->highlightWidth < 0) { 285 scrollPtr->highlightWidth = 0; 286 } 287 scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth; 288 width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin) 289 : Tk_Height(scrollPtr->tkwin); 290 scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1; 291 fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin) 292 : Tk_Width(scrollPtr->tkwin)) 293 - 2*(scrollPtr->arrowLength + scrollPtr->inset); 294 if (fieldLength < 0) { 295 fieldLength = 0; 296 } 297 scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction; 298 scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction; 299 300 /* 301 * Adjust the slider so that some piece of it is always displayed in the 302 * scrollbar and so that it has at least a minimal width (so it can be 303 * grabbed with the mouse). 304 */ 305 306 if (scrollPtr->sliderFirst > (fieldLength - MIN_SLIDER_LENGTH)) { 307 scrollPtr->sliderFirst = fieldLength - MIN_SLIDER_LENGTH; 308 } 309 if (scrollPtr->sliderFirst < 0) { 310 scrollPtr->sliderFirst = 0; 311 } 312 if (scrollPtr->sliderLast < (scrollPtr->sliderFirst 313 + MIN_SLIDER_LENGTH)) { 314 scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH; 315 } 316 if (scrollPtr->sliderLast > fieldLength) { 317 scrollPtr->sliderLast = fieldLength; 318 } 319 scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset; 320 scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset; 321 322 /* 323 * Register the desired geometry for the window (leave enough space for 324 * the two arrows plus a minimum-size slider, plus border around the whole 325 * window, if any). Then arrange for the window to be redisplayed. 326 */ 327 328 if (scrollPtr->vertical) { 329 Tk_GeometryRequest(scrollPtr->tkwin, 330 scrollPtr->width + 2*scrollPtr->inset, 331 2*(scrollPtr->arrowLength + scrollPtr->borderWidth 332 + scrollPtr->inset)); 333 } else { 334 Tk_GeometryRequest(scrollPtr->tkwin, 335 2*(scrollPtr->arrowLength + scrollPtr->borderWidth 336 + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset); 337 } 338 Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset); 339} 340 341/* 342 *---------------------------------------------------------------------- 343 * 344 * TkpDestroyScrollbar -- 345 * 346 * Free data structures associated with the scrollbar control. 347 * 348 * Results: 349 * None. 350 * 351 * Side effects: 352 * Frees the GCs associated with the scrollbar. 353 * 354 *---------------------------------------------------------------------- 355 */ 356 357void 358TkpDestroyScrollbar( 359 TkScrollbar *scrollPtr) 360{ 361 UnixScrollbar *unixScrollPtr = (UnixScrollbar *)scrollPtr; 362 363 if (unixScrollPtr->troughGC != None) { 364 Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC); 365 } 366 if (unixScrollPtr->copyGC != None) { 367 Tk_FreeGC(scrollPtr->display, unixScrollPtr->copyGC); 368 } 369} 370 371/* 372 *---------------------------------------------------------------------- 373 * 374 * TkpConfigureScrollbar -- 375 * 376 * This procedure is called after the generic code has finished 377 * processing configuration options, in order to configure platform 378 * specific options. 379 * 380 * Results: 381 * None. 382 * 383 * Side effects: 384 * Configuration info may get changed. 385 * 386 *---------------------------------------------------------------------- 387 */ 388 389void 390TkpConfigureScrollbar( 391 register TkScrollbar *scrollPtr) 392 /* Information about widget; may or may not 393 * already have values for some fields. */ 394{ 395 XGCValues gcValues; 396 GC new; 397 UnixScrollbar *unixScrollPtr = (UnixScrollbar *) scrollPtr; 398 399 Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder); 400 401 gcValues.foreground = scrollPtr->troughColorPtr->pixel; 402 new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues); 403 if (unixScrollPtr->troughGC != None) { 404 Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC); 405 } 406 unixScrollPtr->troughGC = new; 407 if (unixScrollPtr->copyGC == None) { 408 gcValues.graphics_exposures = False; 409 unixScrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures, 410 &gcValues); 411 } 412} 413 414/* 415 *-------------------------------------------------------------- 416 * 417 * TkpScrollbarPosition -- 418 * 419 * Determine the scrollbar element corresponding to a given position. 420 * 421 * Results: 422 * One of TOP_ARROW, TOP_GAP, etc., indicating which element of the 423 * scrollbar covers the position given by (x, y). If (x,y) is outside the 424 * scrollbar entirely, then OUTSIDE is returned. 425 * 426 * Side effects: 427 * None. 428 * 429 *-------------------------------------------------------------- 430 */ 431 432int 433TkpScrollbarPosition( 434 register TkScrollbar *scrollPtr, 435 /* Scrollbar widget record. */ 436 int x, int y) /* Coordinates within scrollPtr's window. */ 437{ 438 int length, width, tmp; 439 440 if (scrollPtr->vertical) { 441 length = Tk_Height(scrollPtr->tkwin); 442 width = Tk_Width(scrollPtr->tkwin); 443 } else { 444 tmp = x; 445 x = y; 446 y = tmp; 447 length = Tk_Width(scrollPtr->tkwin); 448 width = Tk_Height(scrollPtr->tkwin); 449 } 450 451 if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset)) 452 || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) { 453 return OUTSIDE; 454 } 455 456 /* 457 * All of the calculations in this procedure mirror those in 458 * TkpDisplayScrollbar. Be sure to keep the two consistent. 459 */ 460 461 if (y < (scrollPtr->inset + scrollPtr->arrowLength)) { 462 return TOP_ARROW; 463 } 464 if (y < scrollPtr->sliderFirst) { 465 return TOP_GAP; 466 } 467 if (y < scrollPtr->sliderLast) { 468 return SLIDER; 469 } 470 if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) { 471 return BOTTOM_ARROW; 472 } 473 return BOTTOM_GAP; 474} 475 476/* 477 * Local Variables: 478 * mode: c 479 * c-basic-offset: 4 480 * fill-column: 78 481 * End: 482 */ 483