OSDN Git Service

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