diff options
Diffstat (limited to 'win/tkWinScrlbr.c')
-rw-r--r-- | win/tkWinScrlbr.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/win/tkWinScrlbr.c b/win/tkWinScrlbr.c new file mode 100644 index 0000000..6c1a664 --- /dev/null +++ b/win/tkWinScrlbr.c @@ -0,0 +1,745 @@ +/* + * tkWinScrollbar.c -- + * + * This file implements the Windows 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: @(#) tkWinScrlbr.c 1.19 97/08/13 17:37:49 + */ + +#include "tkWinInt.h" +#include "tkScrollbar.h" + + +/* + * The following constant is used to specify the maximum scroll position. + * This value is limited by the Win32 API to either 16-bits or 32-bits, + * depending on the context. For now we'll just use a value small + * enough to fit in 16-bits, but which gives us 4-digits of precision. + */ + +#define MAX_SCROLL 10000 + +/* + * Declaration of Windows specific scrollbar structure. + */ + +typedef struct WinScrollbar { + TkScrollbar info; /* Generic scrollbar info. */ + WNDPROC oldProc; /* Old window procedure. */ + int lastVertical; /* 1 if was vertical at last refresh. */ + HWND hwnd; /* Current window handle. */ + int winFlags; /* Various flags; see below. */ +} WinScrollbar; + +/* + * Flag bits for native scrollbars: + * + * IN_MODAL_LOOP: Non-zero means this scrollbar is in the middle + * of a modal loop. + * ALREADY_DEAD: Non-zero means this scrollbar has been + * destroyed, but has not been cleaned up. + */ + +#define IN_MODAL_LOOP 1 +#define ALREADY_DEAD 2 + +/* + * Cached system metrics used to determine scrollbar geometry. + */ + +static int initialized = 0; +static int hArrowWidth, hThumb; /* Horizontal control metrics. */ +static int vArrowWidth, vArrowHeight, vThumb; /* Vertical control metrics. */ + +/* + * This variable holds the default width for a scrollbar in string + * form for use in a Tk_ConfigSpec. + */ + +static char defWidth[8]; + +/* + * Declarations for functions defined in this file. + */ + +static Window CreateProc _ANSI_ARGS_((Tk_Window tkwin, + Window parent, ClientData instanceData)); +static void ModalLoopProc _ANSI_ARGS_((Tk_Window tkwin, + XEvent *eventPtr)); +static int ScrollbarBindProc _ANSI_ARGS_((ClientData clientData, + Tcl_Interp *interp, XEvent *eventPtr, + Tk_Window tkwin, KeySym keySym)); +static LRESULT CALLBACK ScrollbarProc _ANSI_ARGS_((HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam)); +static void UpdateScrollbar _ANSI_ARGS_(( + WinScrollbar *scrollPtr)); +static void UpdateScrollbarMetrics _ANSI_ARGS_((void)); + +/* + * The class procedure table for the scrollbar widget. + */ + +TkClassProcs tkpScrollbarProcs = { + CreateProc, /* createProc */ + NULL, /* geometryProc */ + ModalLoopProc, /* 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; +{ + WinScrollbar *scrollPtr; + TkWindow *winPtr = (TkWindow *)tkwin; + + if (!initialized) { + UpdateScrollbarMetrics(); + initialized = 1; + } + + scrollPtr = (WinScrollbar *) ckalloc(sizeof(WinScrollbar)); + scrollPtr->winFlags = 0; + scrollPtr->hwnd = NULL; + + Tk_CreateEventHandler(tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + TkScrollbarEventProc, (ClientData) scrollPtr); + + if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) { + Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL, + (ClientData)1); + TkCreateBindingProcedure(winPtr->mainPtr->interp, + winPtr->mainPtr->bindingTable, + (ClientData)Tk_GetUid("Scrollbar"), "<ButtonPress>", + ScrollbarBindProc, NULL, NULL); + } + + return (TkScrollbar*) scrollPtr; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateScrollbar -- + * + * This function updates the position and size of the scrollbar + * thumb based on the current settings. + * + * Results: + * None. + * + * Side effects: + * Moves the thumb. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateScrollbar(scrollPtr) + WinScrollbar *scrollPtr; +{ + SCROLLINFO scrollInfo; + double thumbSize; + + /* + * Update the current scrollbar position and shape. + */ + + scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.nMin = 0; + scrollInfo.nMax = MAX_SCROLL; + thumbSize = (scrollPtr->info.lastFraction - scrollPtr->info.firstFraction); + if (tkpIsWin32s) { + scrollInfo.nPage = 0; + } else { + scrollInfo.nPage = ((UINT) (thumbSize * (double) MAX_SCROLL)) + 1; + } + if (thumbSize < 1.0) { + scrollInfo.nPos = (int) + ((scrollPtr->info.firstFraction / (1.0-thumbSize)) + * (MAX_SCROLL - (scrollInfo.nPage - 1))); + } else { + scrollInfo.nPos = 0; + } + SetScrollInfo(scrollPtr->hwnd, SB_CTL, &scrollInfo, TRUE); +} + +/* + *---------------------------------------------------------------------- + * + * CreateProc -- + * + * This function creates a new Scrollbar control, subclasses + * the instance, and generates a new Window object. + * + * Results: + * Returns the newly allocated Window object, or None on failure. + * + * Side effects: + * Causes a new Scrollbar control to come into existence. + * + *---------------------------------------------------------------------- + */ + +static Window +CreateProc(tkwin, parentWin, instanceData) + Tk_Window tkwin; /* Token for window. */ + Window parentWin; /* Parent of new window. */ + ClientData instanceData; /* Scrollbar instance data. */ +{ + DWORD style; + Window window; + HWND parent; + TkWindow *winPtr; + WinScrollbar *scrollPtr = (WinScrollbar *)instanceData; + + parent = Tk_GetHWND(parentWin); + + if (scrollPtr->info.vertical) { + style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + | SBS_VERT | SBS_RIGHTALIGN; + } else { + style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + | SBS_HORZ | SBS_BOTTOMALIGN; + } + + scrollPtr->hwnd = CreateWindow("SCROLLBAR", NULL, style, + Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), + parent, NULL, Tk_GetHINSTANCE(), NULL); + + /* + * Ensure new window is inserted into the stacking order at the correct + * place. + */ + + SetWindowPos(scrollPtr->hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + for (winPtr = ((TkWindow*)tkwin)->nextPtr; winPtr != NULL; + winPtr = winPtr->nextPtr) { + if ((winPtr->window != None) && !(winPtr->flags & TK_TOP_LEVEL)) { + TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window), + Below); + break; + } + } + + scrollPtr->lastVertical = scrollPtr->info.vertical; + scrollPtr->oldProc = (WNDPROC)SetWindowLong(scrollPtr->hwnd, GWL_WNDPROC, + (DWORD) ScrollbarProc); + window = Tk_AttachHWND(tkwin, scrollPtr->hwnd); + + UpdateScrollbar(scrollPtr); + return window; +} + +/* + *-------------------------------------------------------------- + * + * 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. */ +{ + WinScrollbar *scrollPtr = (WinScrollbar *) clientData; + Tk_Window tkwin = scrollPtr->info.tkwin; + + scrollPtr->info.flags &= ~REDRAW_PENDING; + if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { + return; + } + + /* + * Destroy and recreate the scrollbar control if the orientation + * has changed. + */ + + if (scrollPtr->lastVertical != scrollPtr->info.vertical) { + HWND hwnd = Tk_GetHWND(Tk_WindowId(tkwin)); + + SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) scrollPtr->oldProc); + DestroyWindow(hwnd); + + CreateProc(tkwin, Tk_WindowId(Tk_Parent(tkwin)), + (ClientData) scrollPtr); + } else { + UpdateScrollbar(scrollPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyScrollbar -- + * + * Free data structures associated with the scrollbar control. + * + * Results: + * None. + * + * Side effects: + * Restores the default control state. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyScrollbar(scrollPtr) + TkScrollbar *scrollPtr; +{ + WinScrollbar *winScrollPtr = (WinScrollbar *)scrollPtr; + HWND hwnd = winScrollPtr->hwnd; + if (hwnd) { + SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winScrollPtr->oldProc); + if (winScrollPtr->winFlags & IN_MODAL_LOOP) { + ((TkWindow *)scrollPtr->tkwin)->flags |= TK_DONT_DESTROY_WINDOW; + SetParent(hwnd, NULL); + } + } + winScrollPtr->winFlags |= ALREADY_DEAD; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateScrollbarMetrics -- + * + * This function retrieves the current system metrics for a + * scrollbar. + * + * Results: + * None. + * + * Side effects: + * Updates the geometry cache info for all scrollbars. + * + *---------------------------------------------------------------------- + */ + +void +UpdateScrollbarMetrics() +{ + Tk_ConfigSpec *specPtr; + + hArrowWidth = GetSystemMetrics(SM_CXHSCROLL); + hThumb = GetSystemMetrics(SM_CXHTHUMB); + vArrowWidth = GetSystemMetrics(SM_CXVSCROLL); + vArrowHeight = GetSystemMetrics(SM_CYVSCROLL); + vThumb = GetSystemMetrics(SM_CYVTHUMB); + + sprintf(defWidth, "%d", vArrowWidth); + for (specPtr = tkpScrollbarConfigSpecs; specPtr->type != TK_CONFIG_END; + specPtr++) { + if (specPtr->offset == Tk_Offset(TkScrollbar, width)) { + specPtr->defValue = defWidth; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ + +void +TkpComputeScrollbarGeometry(scrollPtr) + register TkScrollbar *scrollPtr; /* Scrollbar whose geometry may + * have changed. */ +{ + int fieldLength, minThumbSize; + + /* + * Windows doesn't use focus rings on scrollbars, but we still + * perform basic sanity checks to appease backwards compatibility. + */ + + if (scrollPtr->highlightWidth < 0) { + scrollPtr->highlightWidth = 0; + } + + if (scrollPtr->vertical) { + scrollPtr->arrowLength = vArrowHeight; + fieldLength = Tk_Height(scrollPtr->tkwin); + minThumbSize = vThumb; + } else { + scrollPtr->arrowLength = hArrowWidth; + fieldLength = Tk_Width(scrollPtr->tkwin); + minThumbSize = hThumb; + } + fieldLength -= 2*scrollPtr->arrowLength; + if (fieldLength < 0) { + fieldLength = 0; + } + scrollPtr->sliderFirst = (int) ((double)fieldLength + * scrollPtr->firstFraction); + scrollPtr->sliderLast = (int) ((double)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) { + scrollPtr->sliderFirst = fieldLength; + } + if (scrollPtr->sliderFirst < 0) { + scrollPtr->sliderFirst = 0; + } + if (scrollPtr->sliderLast < (scrollPtr->sliderFirst + + minThumbSize)) { + scrollPtr->sliderLast = scrollPtr->sliderFirst + minThumbSize; + } + if (scrollPtr->sliderLast > fieldLength) { + scrollPtr->sliderLast = fieldLength; + } + scrollPtr->sliderFirst += scrollPtr->arrowLength; + scrollPtr->sliderLast += scrollPtr->arrowLength; + + /* + * 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->arrowLength + minThumbSize); + } else { + Tk_GeometryRequest(scrollPtr->tkwin, + 2*scrollPtr->arrowLength + minThumbSize, scrollPtr->width); + } + Tk_SetInternalBorder(scrollPtr->tkwin, 0); +} + +/* + *---------------------------------------------------------------------- + * + * ScrollbarProc -- + * + * This function is call by Windows whenever an event occurs on + * a scrollbar control created by Tk. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * May generate events. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +ScrollbarProc(hwnd, message, wParam, lParam) + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; +{ + LRESULT result; + POINT point; + WinScrollbar *scrollPtr; + Tk_Window tkwin = Tk_HWNDToWindow(hwnd); + + if (tkwin == NULL) { + panic("ScrollbarProc called on an invalid HWND"); + } + scrollPtr = (WinScrollbar *)((TkWindow*)tkwin)->instanceData; + + switch(message) { + case WM_HSCROLL: + case WM_VSCROLL: { + Tcl_Interp *interp; + Tcl_DString cmdString; + int command = LOWORD(wParam); + int code; + + GetCursorPos(&point); + Tk_TranslateWinEvent(NULL, WM_MOUSEMOVE, 0, + MAKELPARAM(point.x, point.y), &result); + + if (command == SB_ENDSCROLL) { + return 0; + } + + /* + * Bail out immediately if there isn't a command to invoke. + */ + + if (scrollPtr->info.commandSize == 0) { + Tcl_ServiceAll(); + return 0; + } + + Tcl_DStringInit(&cmdString); + Tcl_DStringAppend(&cmdString, scrollPtr->info.command, + scrollPtr->info.commandSize); + + if (command == SB_LINELEFT || command == SB_LINERIGHT) { + Tcl_DStringAppendElement(&cmdString, "scroll"); + Tcl_DStringAppendElement(&cmdString, + (command == SB_LINELEFT ) ? "-1" : "1"); + Tcl_DStringAppendElement(&cmdString, "units"); + } else if (command == SB_PAGELEFT || command == SB_PAGERIGHT) { + Tcl_DStringAppendElement(&cmdString, "scroll"); + Tcl_DStringAppendElement(&cmdString, + (command == SB_PAGELEFT ) ? "-1" : "1"); + Tcl_DStringAppendElement(&cmdString, "pages"); + } else { + char valueString[TCL_DOUBLE_SPACE]; + double pos = 0.0; + switch (command) { + case SB_THUMBPOSITION: + pos = ((double)HIWORD(wParam)) / MAX_SCROLL; + break; + + case SB_THUMBTRACK: + pos = ((double)HIWORD(wParam)) / MAX_SCROLL; + break; + + case SB_TOP: + pos = 0.0; + break; + + case SB_BOTTOM: + pos = 1.0; + break; + } + sprintf(valueString, "%g", pos); + Tcl_DStringAppendElement(&cmdString, "moveto"); + Tcl_DStringAppendElement(&cmdString, valueString); + } + + interp = scrollPtr->info.interp; + code = Tcl_GlobalEval(interp, cmdString.string); + if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (scrollbar command)"); + Tcl_BackgroundError(interp); + } + Tcl_DStringFree(&cmdString); + + Tcl_ServiceAll(); + return 0; + } + + default: + if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { + return result; + } + } + return CallWindowProc(scrollPtr->oldProc, hwnd, message, wParam, lParam); +} + +/* + *---------------------------------------------------------------------- + * + * 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: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpConfigureScrollbar(scrollPtr) + register TkScrollbar *scrollPtr; /* Information about widget; may or + * may not already have values for + * some fields. */ +{ +} + +/* + *-------------------------------------------------------------- + * + * ScrollbarBindProc -- + * + * This procedure is invoked when the default <ButtonPress> + * binding on the Scrollbar bind tag fires. + * + * Results: + * None. + * + * Side effects: + * The event enters a modal loop. + * + *-------------------------------------------------------------- + */ + +static int +ScrollbarBindProc(clientData, interp, eventPtr, tkwin, keySym) + ClientData clientData; + Tcl_Interp *interp; + XEvent *eventPtr; + Tk_Window tkwin; + KeySym keySym; +{ + TkWindow *winPtr = (TkWindow*)tkwin; + if (eventPtr->type == ButtonPress) { + winPtr->flags |= TK_DEFER_MODAL; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ModalLoopProc -- + * + * This function is invoked at the end of the event processing + * whenever the ScrollbarBindProc has been invoked for a ButtonPress + * event. + * + * Results: + * None. + * + * Side effects: + * Enters a modal loop. + * + *---------------------------------------------------------------------- + */ + +static void +ModalLoopProc(tkwin, eventPtr) + Tk_Window tkwin; + XEvent *eventPtr; +{ + TkWindow *winPtr = (TkWindow*)tkwin; + WinScrollbar *scrollPtr = (WinScrollbar *) winPtr->instanceData; + int oldMode; + + Tcl_Preserve((ClientData)scrollPtr); + scrollPtr->winFlags |= IN_MODAL_LOOP; + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + TkWinResendEvent(scrollPtr->oldProc, scrollPtr->hwnd, eventPtr); + (void) Tcl_SetServiceMode(oldMode); + scrollPtr->winFlags &= ~IN_MODAL_LOOP; + if (scrollPtr->hwnd && scrollPtr->winFlags & ALREADY_DEAD) { + DestroyWindow(scrollPtr->hwnd); + } + Tcl_Release((ClientData)scrollPtr); +} + +/* + *-------------------------------------------------------------- + * + * 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; +} |