tkWinScrlbr.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:20k
- /*
- * 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.
- *
- * RCS: @(#) $Id: tkWinScrlbr.c,v 1.9 2003/02/21 02:07:50 hobbs Exp $
- */
- #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. */
- TCL_DECLARE_MUTEX(winScrlbrMutex)
- /*
- * This variable holds the default width for a scrollbar in string
- * form for use in a Tk_ConfigSpec.
- */
- static char defWidth[TCL_INTEGER_SPACE];
- /*
- * 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.
- */
- Tk_ClassProcs tkpScrollbarProcs = {
- sizeof(Tk_ClassProcs), /* size */
- NULL, /* worldChangedProc */
- CreateProc, /* createProc */
- 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) {
- Tcl_MutexLock(&winScrlbrMutex);
- UpdateScrollbarMetrics();
- initialized = 1;
- Tcl_MutexUnlock(&winScrlbrMutex);
- }
- 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);
- 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(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_HIERARCHY)) {
- TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window),
- Below);
- break;
- }
- }
- scrollPtr->lastVertical = scrollPtr->info.vertical;
- #ifdef _WIN64
- scrollPtr->oldProc = (WNDPROC)SetWindowLongPtr(scrollPtr->hwnd,
- GWLP_WNDPROC, (LONG_PTR) ScrollbarProc);
- #else
- scrollPtr->oldProc = (WNDPROC)SetWindowLong(scrollPtr->hwnd, GWL_WNDPROC,
- (DWORD) ScrollbarProc);
- #endif
- 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));
- #ifdef _WIN64
- SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) scrollPtr->oldProc);
- #else
- SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) scrollPtr->oldProc);
- #endif
- 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) {
- #ifdef _WIN64
- SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) winScrollPtr->oldProc);
- #else
- SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winScrollPtr->oldProc);
- #endif
- 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;
- 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(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;
- }