diff options
Diffstat (limited to 'tk8.6/win/tkWinScrlbr.c')
-rw-r--r-- | tk8.6/win/tkWinScrlbr.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/tk8.6/win/tkWinScrlbr.c b/tk8.6/win/tkWinScrlbr.c new file mode 100644 index 0000000..1b3717e --- /dev/null +++ b/tk8.6/win/tkWinScrlbr.c @@ -0,0 +1,701 @@ +/* + * 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. + */ + +#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 vArrowHeight, vThumb; /* Vertical control metrics. */ + +TCL_DECLARE_MUTEX(winScrlbrMutex) + +/* + * Declarations for functions defined in this file. + */ + +static Window CreateProc(Tk_Window tkwin, Window parent, + ClientData instanceData); +static void ModalLoop(WinScrollbar *, XEvent *eventPtr); +static LRESULT CALLBACK ScrollbarProc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam); +static void UpdateScrollbar(WinScrollbar *scrollPtr); +static void UpdateScrollbarMetrics(void); + +/* + * The class procedure table for the scrollbar widget. + */ + +const Tk_ClassProcs tkpScrollbarProcs = { + sizeof(Tk_ClassProcs), /* size */ + NULL, /* worldChangedProc */ + CreateProc, /* createProc */ + NULL /* modalProc */ +}; + +static void +WinScrollbarEventProc(ClientData clientData, XEvent *eventPtr) +{ + WinScrollbar *scrollPtr = clientData; + + if (eventPtr->type == ButtonPress) { + ModalLoop(scrollPtr, eventPtr); + } else { + TkScrollbarEventProc(clientData, eventPtr); + } +} + + +/* + *---------------------------------------------------------------------- + * + * TkpCreateScrollbar -- + * + * Allocate a new TkScrollbar structure. + * + * Results: + * Returns a newly allocated TkScrollbar structure. + * + * Side effects: + * Registers an event handler for the widget. + * + *---------------------------------------------------------------------- + */ + +TkScrollbar * +TkpCreateScrollbar( + Tk_Window tkwin) +{ + WinScrollbar *scrollPtr; + + if (!initialized) { + Tcl_MutexLock(&winScrlbrMutex); + UpdateScrollbarMetrics(); + initialized = 1; + Tcl_MutexUnlock(&winScrlbrMutex); + } + + scrollPtr = ckalloc(sizeof(WinScrollbar)); + scrollPtr->winFlags = 0; + scrollPtr->hwnd = NULL; + + Tk_CreateEventHandler(tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask|ButtonPressMask, + WinScrollbarEventProc, scrollPtr); + + 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( + 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); + 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; + + /* + * Disable the scrollbar when there is nothing to scroll. This is + * standard Windows style (see eg Notepad). Also prevents possible + * crash on XP+ systems [Bug #624116]. + */ + + scrollInfo.fMask |= SIF_DISABLENOSCROLL; + } + 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( + 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; + } else { + style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + | SBS_HORZ; + } + + scrollPtr->hwnd = CreateWindow(TEXT("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_HIERARCHY)) { + TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window), + Below); + break; + } + } + + scrollPtr->lastVertical = scrollPtr->info.vertical; + scrollPtr->oldProc = (WNDPROC)SetWindowLongPtr(scrollPtr->hwnd, + GWLP_WNDPROC, (LONG_PTR) 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) /* 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)); + + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) 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( + TkScrollbar *scrollPtr) +{ + WinScrollbar *winScrollPtr = (WinScrollbar *)scrollPtr; + HWND hwnd = winScrollPtr->hwnd; + + if (hwnd) { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (INT_PTR) 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(void) +{ + int arrowWidth = GetSystemMetrics(SM_CXVSCROLL); + + hArrowWidth = GetSystemMetrics(SM_CXHSCROLL); + hThumb = GetSystemMetrics(SM_CXHTHUMB); + vArrowHeight = GetSystemMetrics(SM_CYVSCROLL); + vThumb = GetSystemMetrics(SM_CYVTHUMB); + + sprintf(tkDefScrollbarWidth, "%d", arrowWidth); +} + +/* + *---------------------------------------------------------------------- + * + * 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( + 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 hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + POINT point; + WinScrollbar *scrollPtr; + Tk_Window tkwin = Tk_HWNDToWindow(hwnd); + + if (tkwin == NULL) { + Tcl_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; + } + + Tcl_PrintDouble(NULL, pos, valueString); + Tcl_DStringAppendElement(&cmdString, "moveto"); + Tcl_DStringAppendElement(&cmdString, valueString); + } + + interp = scrollPtr->info.interp; + code = Tcl_EvalEx(interp, cmdString.string, -1, TCL_EVAL_GLOBAL); + if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (scrollbar command)"); + Tcl_BackgroundException(interp, code); + } + 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( + register TkScrollbar *scrollPtr) + /* Information about widget; may or may not + * already have values for some fields. */ +{ +} + +/* + *---------------------------------------------------------------------- + * + * ModalLoop -- + * + * This function is invoked in response to a ButtonPress event. + * It resends the event to the Scrollbar window procedure, + * which in turn enters a modal loop. + * + *---------------------------------------------------------------------- + */ + +static void +ModalLoop( + WinScrollbar *scrollPtr, + XEvent *eventPtr) +{ + int oldMode; + + if (scrollPtr->hwnd) { + 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( + register TkScrollbar *scrollPtr, + /* Scrollbar widget record. */ + int x, int 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; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |