1/*
2 *
3Copyright 1990, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Jim Fulton, MIT X Consortium
26 *
27 * This widget is used for press-and-hold style buttons.
28 */
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33#include <X11/IntrinsicP.h>
34#include <X11/StringDefs.h>
35#include <X11/Xaw/RepeaterP.h>
36#include <X11/Xaw/XawInit.h>
37
38#define DO_CALLBACK(rw) \
39XtCallCallbackList((Widget)rw, rw->command.callbacks, NULL)
40
41#define ADD_TIMEOUT(rw, delay)					\
42XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)rw),	\
43		delay, tic, (XtPointer)rw)
44
45#define CLEAR_TIMEOUT(rw) \
46if ((rw)->repeater.timer) {			\
47    XtRemoveTimeOut((rw)->repeater.timer);	\
48    (rw)->repeater.timer = 0;			\
49}
50
51/*
52 * Class Methods
53 */
54static void XawRepeaterInitialize(Widget, Widget, ArgList, Cardinal*);
55static void XawRepeaterDestroy(Widget);
56static Boolean XawRepeaterSetValues(Widget, Widget, Widget,
57				    ArgList, Cardinal*);
58
59/*
60 * Prototypes
61 */
62static void tic(XtPointer, XtIntervalId*);
63
64/*
65 * Actions
66 */
67static void ActionStart(Widget, XEvent*, String*, Cardinal*);
68static void ActionStop(Widget, XEvent*, String*, Cardinal*);
69
70/*
71 * Initialization
72 */
73static char defaultTranslations[] =
74"<Enter>:"	"highlight()\n"
75"<Leave>:"	"unhighlight()\n"
76"<Btn1Down>:"	"set() start()\n"
77"<Btn1Up>:"	"stop() unset()\n"
78;
79
80static XtActionsRec actions[] = {
81  {"start",	ActionStart},
82  {"stop",	ActionStop},
83};
84
85#define offset(field)	XtOffsetOf(RepeaterRec, repeater.field)
86static XtResource resources[] = {
87  {
88    XtNdecay,
89    XtCDecay,
90    XtRInt,
91    sizeof(int),
92    offset(decay),
93    XtRImmediate,
94    (XtPointer)REP_DEF_DECAY
95  },
96  {
97    XtNinitialDelay,
98    XtCDelay,
99    XtRInt,
100    sizeof(int),
101    offset(initial_delay),
102    XtRImmediate,
103    (XtPointer)REP_DEF_INITIAL_DELAY
104  },
105  {
106    XtNminimumDelay,
107    XtCMinimumDelay,
108    XtRInt,
109    sizeof(int),
110    offset(minimum_delay),
111    XtRImmediate,
112    (XtPointer)REP_DEF_MINIMUM_DELAY
113  },
114  {
115    XtNrepeatDelay,
116    XtCDelay,
117    XtRInt,
118    sizeof(int),
119    offset(repeat_delay),
120    XtRImmediate,
121    (XtPointer)REP_DEF_REPEAT_DELAY
122  },
123  {
124    XtNflash,
125    XtCBoolean,
126    XtRBoolean,
127    sizeof(Boolean),
128    offset(flash),
129    XtRImmediate,
130    (XtPointer)False
131  },
132  {
133    XtNstartCallback,
134    XtCStartCallback,
135    XtRCallback,
136    sizeof(XtPointer),
137    offset(start_callbacks),
138    XtRImmediate,
139    NULL
140  },
141  {
142    XtNstopCallback,
143    XtCStopCallback,
144    XtRCallback,
145    sizeof(XtPointer),
146    offset(stop_callbacks),
147    XtRImmediate,
148    NULL
149  },
150};
151#undef offset
152
153#define Superclass	(&commandClassRec)
154RepeaterClassRec repeaterClassRec = {
155  /* core */
156  {
157    (WidgetClass)Superclass,		/* superclass */
158    "Repeater",				/* class_name */
159    sizeof(RepeaterRec),		/* widget_size */
160    XawInitializeWidgetSet,		/* class_initialize */
161    NULL,				/* class_part_initialize */
162    False,				/* class_inited */
163    XawRepeaterInitialize,		/* initialize */
164    NULL,				/* initialize_hook */
165    XtInheritRealize,			/* realize */
166    actions,				/* actions */
167    XtNumber(actions),			/* num_actions */
168    resources,				/* resources */
169    XtNumber(resources),		/* num_resources */
170    NULLQUARK,				/* xrm_class */
171    True,				/* compress_motion */
172    True,				/* compress_exposure */
173    True,				/* compress_enterleave */
174    False,				/* visible_interest */
175    XawRepeaterDestroy,			/* destroy */
176    XtInheritResize,			/* resize */
177    XtInheritExpose,			/* expose */
178    XawRepeaterSetValues,		/* set_values */
179    NULL,				/* set_values_hook */
180    XtInheritSetValuesAlmost,		/* set_values_almost */
181    NULL,				/* get_values_hook */
182    NULL,				/* accept_focus */
183    XtVersion,				/* version */
184    NULL,				/* callback_private */
185    defaultTranslations,		/* tm_table */
186    XtInheritQueryGeometry,		/* query_geometry */
187    XtInheritDisplayAccelerator,	/* display_accelerator */
188    NULL,				/* extension */
189  },
190  /* simple */
191  {
192    XtInheritChangeSensitive,		/* change_sensitive */
193#ifndef OLDXAW
194    NULL,
195#endif
196  },
197  /* label */
198  {
199    NULL,				/* extension */
200  },
201  /* command */
202  {
203    NULL,				/* extension */
204  },
205  /* repeater */
206  {
207    NULL,				/* extension */
208  },
209};
210
211WidgetClass repeaterWidgetClass = (WidgetClass) &repeaterClassRec;
212
213
214/*
215 * Implementation
216 */
217/*ARGSUSED*/
218static void
219tic(XtPointer client_data, XtIntervalId *id _X_UNUSED)
220{
221    RepeaterWidget rw = (RepeaterWidget)client_data;
222
223    rw->repeater.timer = 0;		/* timer is removed */
224    if (rw->repeater.flash) {
225	Widget w = (Widget)rw;
226
227	XClearWindow(XtDisplay(w), XtWindow(w));
228	XtCallActionProc(w, "reset", NULL, NULL, 0);
229	XClearWindow(XtDisplay(w), XtWindow(w));
230	XtCallActionProc(w, "set", NULL, NULL, 0);
231    }
232    DO_CALLBACK(rw);
233
234    rw->repeater.timer = ADD_TIMEOUT(rw, (unsigned long)rw->repeater.next_delay);
235
236    if (rw->repeater.decay) {
237	rw->repeater.next_delay -= rw->repeater.decay;
238	if (rw->repeater.next_delay < rw->repeater.minimum_delay)
239	    rw->repeater.next_delay = rw->repeater.minimum_delay;
240    }
241}
242
243/*ARGSUSED*/
244static void
245XawRepeaterInitialize(Widget greq _X_UNUSED, Widget gnew,
246		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
247{
248    RepeaterWidget cnew = (RepeaterWidget)gnew;
249
250    if (cnew->repeater.minimum_delay < 0)
251	cnew->repeater.minimum_delay = 0;
252    cnew->repeater.timer = 0;
253}
254
255static void
256XawRepeaterDestroy(Widget gw)
257{
258    CLEAR_TIMEOUT((RepeaterWidget)gw);
259}
260
261/*ARGSUSED*/
262static Boolean
263XawRepeaterSetValues(Widget gcur, Widget greq _X_UNUSED, Widget gnew,
264		     ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
265{
266    RepeaterWidget cur = (RepeaterWidget)gcur;
267    RepeaterWidget cnew = (RepeaterWidget)gnew;
268
269    if (cur->repeater.minimum_delay != cnew->repeater.minimum_delay) {
270	if (cnew->repeater.next_delay < cnew->repeater.minimum_delay)
271	    cnew->repeater.next_delay = cnew->repeater.minimum_delay;
272    }
273
274    return (False);
275}
276
277/*ARGSUSED*/
278static void
279ActionStart(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
280{
281    RepeaterWidget rw = (RepeaterWidget)gw;
282
283    CLEAR_TIMEOUT(rw);
284    if (rw->repeater.start_callbacks)
285	XtCallCallbackList(gw, rw->repeater.start_callbacks, NULL);
286
287    DO_CALLBACK(rw);
288    rw->repeater.timer = ADD_TIMEOUT(rw, (unsigned long)rw->repeater.initial_delay);
289    rw->repeater.next_delay = rw->repeater.repeat_delay;
290}
291
292/*ARGSUSED*/
293static void
294ActionStop(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
295{
296    RepeaterWidget rw = (RepeaterWidget)gw;
297
298    CLEAR_TIMEOUT((RepeaterWidget)gw);
299    if (rw->repeater.stop_callbacks)
300	XtCallCallbackList(gw, rw->repeater.stop_callbacks, NULL);
301}
302