1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
/* track.c,v 1.9 2007/10/22 03:13:34 jenglish Exp
* Copyright (c) 2004, Joe English
*
* TtkTrackElementState() -- helper routine for widgets
* like scrollbars in which individual elements may
* be active or pressed instead of the widget as a whole.
*
* Usage:
* TtkTrackElementState(&recordPtr->core);
*
* Registers an event handler on the widget that tracks pointer
* events and updates the state of the element under the
* mouse cursor.
*
* The "active" element is the one under the mouse cursor,
* and is normally set to the ACTIVE state unless another element
* is currently being pressed.
*
* The active element becomes "pressed" on <ButtonPress> events,
* and remains "active" and "pressed" until the corresponding
* <ButtonRelease> event.
*
* TODO: Handle "chords" properly (e.g., <B1-ButtonPress-2>)
*/
#include <tk.h>
#include "tkTheme.h"
#include "widget.h"
typedef struct
{
WidgetCore *corePtr; /* Widget to track */
Ttk_LayoutNode *activeElement; /* element under the mouse cursor */
Ttk_LayoutNode *pressedElement; /* currently pressed element */
} ElementStateTracker;
/*
* ActivateElement(es, node) --
* Make 'node' the active element if non-NULL.
* Deactivates the currently active element if different.
*
* The active element has TTK_STATE_ACTIVE set _unless_
* another element is 'pressed'
*/
static void ActivateElement(ElementStateTracker *es, Ttk_LayoutNode *node)
{
if (es->activeElement == node) {
/* No change */
return;
}
if (!es->pressedElement) {
if (es->activeElement) {
/* Deactivate old element */
Ttk_ChangeElementState(es->activeElement, 0,TTK_STATE_ACTIVE);
}
if (node) {
/* Activate new element */
Ttk_ChangeElementState(node, TTK_STATE_ACTIVE,0);
}
TtkRedisplayWidget(es->corePtr);
}
es->activeElement = node;
}
/* ReleaseElement --
* Releases the currently pressed element, if any.
*/
static void ReleaseElement(ElementStateTracker *es)
{
if (!es->pressedElement)
return;
Ttk_ChangeElementState(
es->pressedElement, 0,TTK_STATE_PRESSED|TTK_STATE_ACTIVE);
es->pressedElement = 0;
/* Reactivate element under the mouse cursor:
*/
if (es->activeElement)
Ttk_ChangeElementState(es->activeElement, TTK_STATE_ACTIVE,0);
TtkRedisplayWidget(es->corePtr);
}
/* PressElement --
* Presses the specified element.
*/
static void PressElement(ElementStateTracker *es, Ttk_LayoutNode *node)
{
if (es->pressedElement) {
ReleaseElement(es);
}
if (node) {
Ttk_ChangeElementState(
node, TTK_STATE_PRESSED|TTK_STATE_ACTIVE, 0);
}
es->pressedElement = node;
TtkRedisplayWidget(es->corePtr);
}
/* ElementStateEventProc --
* Event handler for tracking element states.
*/
static const unsigned ElementStateMask =
ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
| LeaveWindowMask
| EnterWindowMask
| StructureNotifyMask
;
static void
ElementStateEventProc(ClientData clientData, XEvent *ev)
{
ElementStateTracker *es = (ElementStateTracker *)clientData;
Ttk_LayoutNode *node;
switch (ev->type)
{
case MotionNotify :
node = Ttk_LayoutIdentify(
es->corePtr->layout,ev->xmotion.x,ev->xmotion.y);
ActivateElement(es, node);
break;
case LeaveNotify:
ActivateElement(es, 0);
if (ev->xcrossing.mode == NotifyGrab)
PressElement(es, 0);
break;
case EnterNotify:
node = Ttk_LayoutIdentify(
es->corePtr->layout,ev->xcrossing.x,ev->xcrossing.y);
ActivateElement(es, node);
break;
case ButtonPress:
node = Ttk_LayoutIdentify(
es->corePtr->layout, ev->xbutton.x, ev->xbutton.y);
if (node)
PressElement(es, node);
break;
case ButtonRelease:
ReleaseElement(es);
break;
case DestroyNotify:
/* Unregister this event handler and free client data.
*/
Tk_DeleteEventHandler(es->corePtr->tkwin,
ElementStateMask, ElementStateEventProc, es);
ckfree(clientData);
break;
}
}
/*
* TtkTrackElementState --
* Register an event handler to manage the 'pressed'
* and 'active' states of individual widget elements.
*/
void TtkTrackElementState(WidgetCore *corePtr)
{
ElementStateTracker *es = (ElementStateTracker*)ckalloc(sizeof(*es));
es->corePtr = corePtr;
es->activeElement = es->pressedElement = 0;
Tk_CreateEventHandler(corePtr->tkwin,
ElementStateMask,ElementStateEventProc,es);
}
|