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 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tkUnixScrlbr.c,v 1.3 2000/11/22 01:49:38 ericm Exp $ 13 */ 14 15#include "tkScrollbar.h" 16 17/* 18 * Minimum slider length, in pixels (designed to make sure that the slider 19 * is always easy to grab with the mouse). 20 */ 21 22#define MIN_SLIDER_LENGTH 5 23 24/* 25 * Declaration of Unix specific scrollbar structure. 26 */ 27 28typedef struct UnixScrollbar { 29 TkScrollbar info; /* Generic scrollbar info. */ 30 GC troughGC; /* For drawing trough. */ 31 GC copyGC; /* Used for copying from pixmap onto screen. */ 32} UnixScrollbar; 33 34/* 35 * The class procedure table for the scrollbar widget. All fields except 36 * size are left initialized to NULL, which should happen automatically 37 * since the variable is declared at this scope. 38 */ 39 40Tk_ClassProcs tkpScrollbarProcs = { 41 sizeof(Tk_ClassProcs) /* size */ 42}; 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(tkwin) 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. 82 * It is invoked as a do-when-idle handler, so it only runs 83 * when there's 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(clientData) 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 121 * the scrollbar in a pixmap, then copies the pixmap to the 122 * screen in a single operation. This means that there's no 123 * point in time where the on-sreen 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 152 * points probably seem odd, but they were carefully chosen with 153 * respect to X's rules for filling polygons. These point choices 154 * cause the arrows to just fill the narrow dimension of the 155 * scrollbar and be properly 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, 246 * then 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 264 * procedure recomputes various geometry information used in 265 * displaying the scrollbar. 266 * 267 * Results: 268 * None. 269 * 270 * Side effects: 271 * The scrollbar will be displayed differently. 272 * 273 *---------------------------------------------------------------------- 274 */ 275 276extern void 277TkpComputeScrollbarGeometry(scrollPtr) 278 register TkScrollbar *scrollPtr; /* Scrollbar whose geometry may 279 * have changed. */ 280{ 281 int width, fieldLength; 282 283 if (scrollPtr->highlightWidth < 0) { 284 scrollPtr->highlightWidth = 0; 285 } 286 scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth; 287 width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin) 288 : Tk_Height(scrollPtr->tkwin); 289 scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1; 290 fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin) 291 : Tk_Width(scrollPtr->tkwin)) 292 - 2*(scrollPtr->arrowLength + scrollPtr->inset); 293 if (fieldLength < 0) { 294 fieldLength = 0; 295 } 296 scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction; 297 scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction; 298 299 /* 300 * Adjust the slider so that some piece of it is always 301 * displayed in the scrollbar and so that it has at least 302 * a minimal width (so it can be grabbed with the mouse). 303 */ 304 305 if (scrollPtr->sliderFirst > (fieldLength - MIN_SLIDER_LENGTH)) { 306 scrollPtr->sliderFirst = fieldLength - MIN_SLIDER_LENGTH; 307 } 308 if (scrollPtr->sliderFirst < 0) { 309 scrollPtr->sliderFirst = 0; 310 } 311 if (scrollPtr->sliderLast < (scrollPtr->sliderFirst 312 + MIN_SLIDER_LENGTH)) { 313 scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH; 314 } 315 if (scrollPtr->sliderLast > fieldLength) { 316 scrollPtr->sliderLast = fieldLength; 317 } 318 scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset; 319 scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset; 320 321 /* 322 * Register the desired geometry for the window (leave enough space 323 * for the two arrows plus a minimum-size slider, plus border around 324 * the whole window, if any). Then arrange for the window to be 325 * 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(scrollPtr) 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 378 * platform specific options. 379 * 380 * Results: 381 * None. 382 * 383 * Side effects: 384 * Configuration info may get changed. 385 * 386 *---------------------------------------------------------------------- 387 */ 388 389void 390TkpConfigureScrollbar(scrollPtr) 391 register TkScrollbar *scrollPtr; /* Information about widget; may or 392 * may not already have values for 393 * 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 420 * given position. 421 * 422 * Results: 423 * One of TOP_ARROW, TOP_GAP, etc., indicating which element 424 * of the scrollbar covers the position given by (x, y). If 425 * (x,y) is outside the scrollbar entirely, then OUTSIDE is 426 * returned. 427 * 428 * Side effects: 429 * None. 430 * 431 *-------------------------------------------------------------- 432 */ 433 434int 435TkpScrollbarPosition(scrollPtr, x, y) 436 register TkScrollbar *scrollPtr; /* Scrollbar widget record. */ 437 int x, y; /* Coordinates within scrollPtr's 438 * window. */ 439{ 440 int length, width, tmp; 441 442 if (scrollPtr->vertical) { 443 length = Tk_Height(scrollPtr->tkwin); 444 width = Tk_Width(scrollPtr->tkwin); 445 } else { 446 tmp = x; 447 x = y; 448 y = tmp; 449 length = Tk_Width(scrollPtr->tkwin); 450 width = Tk_Height(scrollPtr->tkwin); 451 } 452 453 if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset)) 454 || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) { 455 return OUTSIDE; 456 } 457 458 /* 459 * All of the calculations in this procedure mirror those in 460 * TkpDisplayScrollbar. Be sure to keep the two consistent. 461 */ 462 463 if (y < (scrollPtr->inset + scrollPtr->arrowLength)) { 464 return TOP_ARROW; 465 } 466 if (y < scrollPtr->sliderFirst) { 467 return TOP_GAP; 468 } 469 if (y < scrollPtr->sliderLast) { 470 return SLIDER; 471 } 472 if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) { 473 return BOTTOM_ARROW; 474 } 475 return BOTTOM_GAP; 476} 477