diff options
Diffstat (limited to 'unix/tkUnixScrlbr.c')
-rw-r--r-- | unix/tkUnixScrlbr.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/unix/tkUnixScrlbr.c b/unix/tkUnixScrlbr.c new file mode 100644 index 0000000..74b46e8 --- /dev/null +++ b/unix/tkUnixScrlbr.c @@ -0,0 +1,476 @@ +/* + * tkUnixScrollbar.c -- + * + * This file implements the Unix specific portion of the scrollbar + * widget. + * + * Copyright (c) 1996 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tkUnixScrlbr.c 1.8 96/12/10 20:05:07 + */ + +#include "tkScrollbar.h" + +/* + * Minimum slider length, in pixels (designed to make sure that the slider + * is always easy to grab with the mouse). + */ + +#define MIN_SLIDER_LENGTH 5 + +/* + * Declaration of Unix specific scrollbar structure. + */ + +typedef struct UnixScrollbar { + TkScrollbar info; /* Generic scrollbar info. */ + GC troughGC; /* For drawing trough. */ + GC copyGC; /* Used for copying from pixmap onto screen. */ +} UnixScrollbar; + +/* + * The class procedure table for the scrollbar widget. + */ + +TkClassProcs tkpScrollbarProcs = { + NULL, /* createProc. */ + NULL, /* geometryProc. */ + NULL /* modalProc. */ +}; + + +/* + *---------------------------------------------------------------------- + * + * TkpCreateScrollbar -- + * + * Allocate a new TkScrollbar structure. + * + * Results: + * Returns a newly allocated TkScrollbar structure. + * + * Side effects: + * Registers an event handler for the widget. + * + *---------------------------------------------------------------------- + */ + +TkScrollbar * +TkpCreateScrollbar(tkwin) + Tk_Window tkwin; +{ + UnixScrollbar *scrollPtr = (UnixScrollbar *)ckalloc(sizeof(UnixScrollbar)); + scrollPtr->troughGC = None; + scrollPtr->copyGC = None; + + Tk_CreateEventHandler(tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + TkScrollbarEventProc, (ClientData) scrollPtr); + + return (TkScrollbar *) scrollPtr; +} + +/* + *-------------------------------------------------------------- + * + * TkpDisplayScrollbar -- + * + * This procedure redraws the contents of a scrollbar window. + * It is invoked as a do-when-idle handler, so it only runs + * when there's nothing else for the application to do. + * + * Results: + * None. + * + * Side effects: + * Information appears on the screen. + * + *-------------------------------------------------------------- + */ + +void +TkpDisplayScrollbar(clientData) + ClientData clientData; /* Information about window. */ +{ + register TkScrollbar *scrollPtr = (TkScrollbar *) clientData; + register Tk_Window tkwin = scrollPtr->tkwin; + XPoint points[7]; + Tk_3DBorder border; + int relief, width, elementBorderWidth; + Pixmap pixmap; + + if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { + goto done; + } + + if (scrollPtr->vertical) { + width = Tk_Width(tkwin) - 2*scrollPtr->inset; + } else { + width = Tk_Height(tkwin) - 2*scrollPtr->inset; + } + elementBorderWidth = scrollPtr->elementBorderWidth; + if (elementBorderWidth < 0) { + elementBorderWidth = scrollPtr->borderWidth; + } + + /* + * In order to avoid screen flashes, this procedure redraws + * the scrollbar in a pixmap, then copies the pixmap to the + * screen in a single operation. This means that there's no + * point in time where the on-sreen image has been cleared. + */ + + pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin), + Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); + + if (scrollPtr->highlightWidth != 0) { + GC gc; + + if (scrollPtr->flags & GOT_FOCUS) { + gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap); + } else { + gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap); + } + Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap); + } + Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder, + scrollPtr->highlightWidth, scrollPtr->highlightWidth, + Tk_Width(tkwin) - 2*scrollPtr->highlightWidth, + Tk_Height(tkwin) - 2*scrollPtr->highlightWidth, + scrollPtr->borderWidth, scrollPtr->relief); + XFillRectangle(scrollPtr->display, pixmap, + ((UnixScrollbar*)scrollPtr)->troughGC, + scrollPtr->inset, scrollPtr->inset, + (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset), + (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset)); + + /* + * Draw the top or left arrow. The coordinates of the polygon + * points probably seem odd, but they were carefully chosen with + * respect to X's rules for filling polygons. These point choices + * cause the arrows to just fill the narrow dimension of the + * scrollbar and be properly centered. + */ + + if (scrollPtr->activeField == TOP_ARROW) { + border = scrollPtr->activeBorder; + relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief + : TK_RELIEF_RAISED; + } else { + border = scrollPtr->bgBorder; + relief = TK_RELIEF_RAISED; + } + if (scrollPtr->vertical) { + points[0].x = scrollPtr->inset - 1; + points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1; + points[1].x = width + scrollPtr->inset; + points[1].y = points[0].y; + points[2].x = width/2 + scrollPtr->inset; + points[2].y = scrollPtr->inset - 1; + Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3, + elementBorderWidth, relief); + } else { + points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1; + points[0].y = scrollPtr->inset - 1; + points[1].x = scrollPtr->inset; + points[1].y = width/2 + scrollPtr->inset; + points[2].x = points[0].x; + points[2].y = width + scrollPtr->inset; + Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3, + elementBorderWidth, relief); + } + + /* + * Display the bottom or right arrow. + */ + + if (scrollPtr->activeField == BOTTOM_ARROW) { + border = scrollPtr->activeBorder; + relief = scrollPtr->activeField == BOTTOM_ARROW + ? scrollPtr->activeRelief : TK_RELIEF_RAISED; + } else { + border = scrollPtr->bgBorder; + relief = TK_RELIEF_RAISED; + } + if (scrollPtr->vertical) { + points[0].x = scrollPtr->inset; + points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength + - scrollPtr->inset + 1; + points[1].x = width/2 + scrollPtr->inset; + points[1].y = Tk_Height(tkwin) - scrollPtr->inset; + points[2].x = width + scrollPtr->inset; + points[2].y = points[0].y; + Tk_Fill3DPolygon(tkwin, pixmap, border, + points, 3, elementBorderWidth, relief); + } else { + points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength + - scrollPtr->inset + 1; + points[0].y = scrollPtr->inset - 1; + points[1].x = points[0].x; + points[1].y = width + scrollPtr->inset; + points[2].x = Tk_Width(tkwin) - scrollPtr->inset; + points[2].y = width/2 + scrollPtr->inset; + Tk_Fill3DPolygon(tkwin, pixmap, border, + points, 3, elementBorderWidth, relief); + } + + /* + * Display the slider. + */ + + if (scrollPtr->activeField == SLIDER) { + border = scrollPtr->activeBorder; + relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief + : TK_RELIEF_RAISED; + } else { + border = scrollPtr->bgBorder; + relief = TK_RELIEF_RAISED; + } + if (scrollPtr->vertical) { + Tk_Fill3DRectangle(tkwin, pixmap, border, + scrollPtr->inset, scrollPtr->sliderFirst, + width, scrollPtr->sliderLast - scrollPtr->sliderFirst, + elementBorderWidth, relief); + } else { + Tk_Fill3DRectangle(tkwin, pixmap, border, + scrollPtr->sliderFirst, scrollPtr->inset, + scrollPtr->sliderLast - scrollPtr->sliderFirst, width, + elementBorderWidth, relief); + } + + /* + * Copy the information from the off-screen pixmap onto the screen, + * then delete the pixmap. + */ + + XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin), + ((UnixScrollbar*)scrollPtr)->copyGC, 0, 0, + (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0); + Tk_FreePixmap(scrollPtr->display, pixmap); + + done: + scrollPtr->flags &= ~REDRAW_PENDING; +} + +/* + *---------------------------------------------------------------------- + * + * TkpComputeScrollbarGeometry -- + * + * After changes in a scrollbar's size or configuration, this + * procedure recomputes various geometry information used in + * displaying the scrollbar. + * + * Results: + * None. + * + * Side effects: + * The scrollbar will be displayed differently. + * + *---------------------------------------------------------------------- + */ + +extern void +TkpComputeScrollbarGeometry(scrollPtr) + register TkScrollbar *scrollPtr; /* Scrollbar whose geometry may + * have changed. */ +{ + int width, fieldLength; + + if (scrollPtr->highlightWidth < 0) { + scrollPtr->highlightWidth = 0; + } + scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth; + width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin) + : Tk_Height(scrollPtr->tkwin); + scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1; + fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin) + : Tk_Width(scrollPtr->tkwin)) + - 2*(scrollPtr->arrowLength + scrollPtr->inset); + if (fieldLength < 0) { + fieldLength = 0; + } + scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction; + scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction; + + /* + * Adjust the slider so that some piece of it is always + * displayed in the scrollbar and so that it has at least + * a minimal width (so it can be grabbed with the mouse). + */ + + if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) { + scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth; + } + if (scrollPtr->sliderFirst < 0) { + scrollPtr->sliderFirst = 0; + } + if (scrollPtr->sliderLast < (scrollPtr->sliderFirst + + MIN_SLIDER_LENGTH)) { + scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH; + } + if (scrollPtr->sliderLast > fieldLength) { + scrollPtr->sliderLast = fieldLength; + } + scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset; + scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset; + + /* + * Register the desired geometry for the window (leave enough space + * for the two arrows plus a minimum-size slider, plus border around + * the whole window, if any). Then arrange for the window to be + * redisplayed. + */ + + if (scrollPtr->vertical) { + Tk_GeometryRequest(scrollPtr->tkwin, + scrollPtr->width + 2*scrollPtr->inset, + 2*(scrollPtr->arrowLength + scrollPtr->borderWidth + + scrollPtr->inset)); + } else { + Tk_GeometryRequest(scrollPtr->tkwin, + 2*(scrollPtr->arrowLength + scrollPtr->borderWidth + + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset); + } + Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset); +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyScrollbar -- + * + * Free data structures associated with the scrollbar control. + * + * Results: + * None. + * + * Side effects: + * Frees the GCs associated with the scrollbar. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyScrollbar(scrollPtr) + TkScrollbar *scrollPtr; +{ + UnixScrollbar *unixScrollPtr = (UnixScrollbar *)scrollPtr; + + if (unixScrollPtr->troughGC != None) { + Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC); + } + if (unixScrollPtr->copyGC != None) { + Tk_FreeGC(scrollPtr->display, unixScrollPtr->copyGC); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpConfigureScrollbar -- + * + * This procedure is called after the generic code has finished + * processing configuration options, in order to configure + * platform specific options. + * + * Results: + * None. + * + * Side effects: + * Configuration info may get changed. + * + *---------------------------------------------------------------------- + */ + +void +TkpConfigureScrollbar(scrollPtr) + register TkScrollbar *scrollPtr; /* Information about widget; may or + * may not already have values for + * some fields. */ +{ + XGCValues gcValues; + GC new; + UnixScrollbar *unixScrollPtr = (UnixScrollbar *) scrollPtr; + + Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder); + + gcValues.foreground = scrollPtr->troughColorPtr->pixel; + new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues); + if (unixScrollPtr->troughGC != None) { + Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC); + } + unixScrollPtr->troughGC = new; + if (unixScrollPtr->copyGC == None) { + gcValues.graphics_exposures = False; + unixScrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures, + &gcValues); + } +} + +/* + *-------------------------------------------------------------- + * + * TkpScrollbarPosition -- + * + * Determine the scrollbar element corresponding to a + * given position. + * + * Results: + * One of TOP_ARROW, TOP_GAP, etc., indicating which element + * of the scrollbar covers the position given by (x, y). If + * (x,y) is outside the scrollbar entirely, then OUTSIDE is + * returned. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +int +TkpScrollbarPosition(scrollPtr, x, y) + register TkScrollbar *scrollPtr; /* Scrollbar widget record. */ + int x, y; /* Coordinates within scrollPtr's + * window. */ +{ + int length, width, tmp; + + if (scrollPtr->vertical) { + length = Tk_Height(scrollPtr->tkwin); + width = Tk_Width(scrollPtr->tkwin); + } else { + tmp = x; + x = y; + y = tmp; + length = Tk_Width(scrollPtr->tkwin); + width = Tk_Height(scrollPtr->tkwin); + } + + if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset)) + || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) { + return OUTSIDE; + } + + /* + * All of the calculations in this procedure mirror those in + * TkpDisplayScrollbar. Be sure to keep the two consistent. + */ + + if (y < (scrollPtr->inset + scrollPtr->arrowLength)) { + return TOP_ARROW; + } + if (y < scrollPtr->sliderFirst) { + return TOP_GAP; + } + if (y < scrollPtr->sliderLast) { + return SLIDER; + } + if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) { + return BOTTOM_ARROW; + } + return BOTTOM_GAP; +} |