1 /*
2 * Copyright (c) 1993-2009, 2014-2016, 2019 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of Paul Mattes nor the names of his contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * idle_gui.c
30 * This module handles the idle command GUI.
31 */
32
33 #include "globals.h"
34 #include "xglobals.h"
35
36 #include <X11/StringDefs.h>
37 #include <X11/Xaw/Toggle.h>
38 #include <X11/Xaw/Command.h>
39 #include <X11/Xaw/Form.h>
40 #include <X11/Shell.h>
41 #include <X11/Xaw/AsciiText.h>
42 #include <X11/Xaw/TextSrc.h>
43 #include <X11/Xaw/TextSink.h>
44 #include <X11/Xaw/AsciiSrc.h>
45 #include <X11/Xaw/AsciiSink.h>
46
47 #include <errno.h>
48
49 #include "appres.h"
50 #include "dialog.h"
51 #include "idle.h"
52 #include "idle_gui.h"
53 #include "objects.h"
54 #include "popups.h"
55 #include "xmenubar.h"
56 #include "xpopups.h"
57
58 /* Macros. */
59 #define FILE_WIDTH 300 /* width of file name widgets */
60 #define MARGIN 3 /* distance from margins to widgets */
61 #define CLOSE_VGAP 0 /* distance between paired toggles */
62 #define FAR_VGAP 10 /* distance between single toggles and groups */
63 #define BUTTON_GAP 5 /* horizontal distance between buttons */
64
65 /* Globals. */
66
67 /* Statics. */
68 static enum idle_enum s_disabled = IDLE_DISABLED;
69 static enum idle_enum s_session = IDLE_SESSION;
70 static enum idle_enum s_perm = IDLE_PERM;
71 static char hms = 'm';
72 static bool fuzz = false;
73 static char s_hours = 'h';
74 static char s_minutes = 'm';
75 static char s_seconds = 's';
76 static Widget idle_dialog, idle_shell, command_value, timeout_value;
77 static Widget enable_toggle, enable_perm_toggle, disable_toggle;
78 static Widget hours_toggle, minutes_toggle, seconds_toggle, fuzz_toggle;
79 static sr_t *idle_sr = NULL;
80
81 static void idle_cancel(Widget w, XtPointer client_data, XtPointer call_data);
82 static void idle_popup_callback(Widget w, XtPointer client_data,
83 XtPointer call_data);
84 static void idle_popup_init(void);
85 static bool idle_start(void);
86 static void okay_callback(Widget w, XtPointer call_parms,
87 XtPointer call_data);
88 static void toggle_enable(Widget w, XtPointer client_data,
89 XtPointer call_data);
90 static void mark_toggle(Widget w, Pixmap p);
91 static void toggle_hms(Widget w, XtPointer client_data,
92 XtPointer call_data);
93 static void toggle_fuzz(Widget w, XtPointer client_data,
94 XtPointer call_data);
95
96 /* "Idle Command" dialog. */
97
98 /*
99 * Pop up the "Idle" menu.
100 * Called back from the "Configure Idle Command" option on the Options menu.
101 */
102 void
popup_idle(void)103 popup_idle(void)
104 {
105 char *its;
106 char *s;
107
108 /* Initialize it. */
109 if (idle_shell == NULL) {
110 idle_popup_init();
111 }
112
113 /*
114 * Split the idle_timeout_string (the raw resource value) into fuzz,
115 * a number, and h/m/s.
116 */
117 its = NewString(idle_timeout_string);
118 if (its != NULL) {
119 if (*its == '~') {
120 fuzz = true;
121 its++;
122 } else {
123 fuzz = false;
124 }
125 s = its;
126 while (isdigit((unsigned char)*s)) {
127 s++;
128 }
129 switch (*s) {
130 case 'h':
131 case 'H':
132 hms = 'h';
133 break;
134 case 'm':
135 case 'M':
136 hms = 'm';
137 break;
138 case 's':
139 case 'S':
140 hms = 's';
141 break;
142 default:
143 break;
144 }
145 *s = '\0';
146 }
147
148 /* Set the resource values. */
149 dialog_set(&idle_sr, idle_dialog);
150 XtVaSetValues(command_value,
151 XtNstring, idle_command,
152 NULL);
153 XtVaSetValues(timeout_value, XtNstring, its, NULL);
154 mark_toggle(enable_toggle, (idle_user_enabled == IDLE_SESSION)?
155 diamond : no_diamond);
156 mark_toggle(enable_perm_toggle, (idle_user_enabled == IDLE_PERM)?
157 diamond : no_diamond);
158 mark_toggle(disable_toggle, (idle_user_enabled == IDLE_DISABLED)?
159 diamond : no_diamond);
160 mark_toggle(hours_toggle, (hms == 'h') ? diamond : no_diamond);
161 mark_toggle(minutes_toggle, (hms == 'm') ? diamond : no_diamond);
162 mark_toggle(seconds_toggle, (hms == 's') ? diamond : no_diamond);
163 mark_toggle(fuzz_toggle, fuzz ? dot : no_dot);
164
165 /* Pop it up. */
166 popup_popup(idle_shell, XtGrabNone);
167 }
168
169 /* Initialize the idle pop-up. */
170 static void
idle_popup_init(void)171 idle_popup_init(void)
172 {
173 Widget w;
174 Widget cancel_button;
175 Widget command_label, timeout_label;
176 Widget okay_button;
177
178 /* Prime the dialog functions. */
179 dialog_set(&idle_sr, idle_dialog);
180
181 /* Create the menu shell. */
182 idle_shell = XtVaCreatePopupShell(
183 "idlePopup", transientShellWidgetClass, toplevel,
184 NULL);
185 XtAddCallback(idle_shell, XtNpopupCallback, place_popup,
186 (XtPointer)CenterP);
187 XtAddCallback(idle_shell, XtNpopupCallback, idle_popup_callback, NULL);
188
189 /* Create the form within the shell. */
190 idle_dialog = XtVaCreateManagedWidget(
191 ObjDialog, formWidgetClass, idle_shell,
192 NULL);
193
194 /* Create the file name widgets. */
195 command_label = XtVaCreateManagedWidget(
196 "command", labelWidgetClass, idle_dialog,
197 XtNvertDistance, FAR_VGAP,
198 XtNhorizDistance, MARGIN,
199 XtNborderWidth, 0,
200 NULL);
201 command_value = XtVaCreateManagedWidget(
202 "value", asciiTextWidgetClass, idle_dialog,
203 XtNeditType, XawtextEdit,
204 XtNwidth, FILE_WIDTH,
205 XtNvertDistance, FAR_VGAP,
206 XtNfromHoriz, command_label,
207 XtNhorizDistance, 0,
208 NULL);
209 dialog_match_dimension(command_label, command_value, XtNheight);
210 w = XawTextGetSource(command_value);
211 if (w == NULL) {
212 XtWarning("Cannot find text source in dialog");
213 } else {
214 XtAddCallback(w, XtNcallback, dialog_text_callback,
215 (XtPointer)&t_command);
216 }
217 dialog_register_sensitivity(command_value,
218 NULL, False,
219 NULL, False,
220 NULL, False);
221
222 timeout_label = XtVaCreateManagedWidget(
223 "timeout", labelWidgetClass, idle_dialog,
224 XtNfromVert, command_label,
225 XtNvertDistance, 3,
226 XtNhorizDistance, MARGIN,
227 XtNborderWidth, 0,
228 NULL);
229 timeout_value = XtVaCreateManagedWidget(
230 "value", asciiTextWidgetClass, idle_dialog,
231 XtNeditType, XawtextEdit,
232 XtNwidth, FILE_WIDTH,
233 XtNdisplayCaret, False,
234 XtNfromVert, command_label,
235 XtNvertDistance, 3,
236 XtNfromHoriz, timeout_label,
237 XtNhorizDistance, 0,
238 NULL);
239 dialog_match_dimension(timeout_label, timeout_value, XtNheight);
240 dialog_match_dimension(command_label, timeout_label, XtNwidth);
241 w = XawTextGetSource(timeout_value);
242 if (w == NULL) {
243 XtWarning("Cannot find text source in dialog");
244 } else {
245 XtAddCallback(w, XtNcallback, dialog_text_callback,
246 (XtPointer)&t_numeric);
247 }
248 dialog_register_sensitivity(timeout_value,
249 NULL, False,
250 NULL, False,
251 NULL, False);
252
253 /* Create the hour/minute/seconds radio buttons. */
254 hours_toggle = XtVaCreateManagedWidget(
255 "hours", commandWidgetClass, idle_dialog,
256 XtNfromVert, timeout_value,
257 XtNvertDistance, CLOSE_VGAP,
258 XtNhorizDistance, MARGIN,
259 XtNborderWidth, 0,
260 XtNsensitive, True,
261 NULL);
262 dialog_apply_bitmap(hours_toggle, no_diamond);
263 XtAddCallback(hours_toggle, XtNcallback, toggle_hms,
264 (XtPointer)&s_hours);
265 minutes_toggle = XtVaCreateManagedWidget(
266 "minutes", commandWidgetClass, idle_dialog,
267 XtNfromVert, timeout_value,
268 XtNvertDistance, CLOSE_VGAP,
269 XtNfromHoriz, hours_toggle,
270 XtNhorizDistance, MARGIN,
271 XtNborderWidth, 0,
272 XtNsensitive, True,
273 NULL);
274 dialog_apply_bitmap(minutes_toggle, diamond);
275 XtAddCallback(minutes_toggle, XtNcallback, toggle_hms,
276 (XtPointer)&s_minutes);
277 seconds_toggle = XtVaCreateManagedWidget(
278 "seconds", commandWidgetClass, idle_dialog,
279 XtNfromVert, timeout_value,
280 XtNvertDistance, CLOSE_VGAP,
281 XtNfromHoriz, minutes_toggle,
282 XtNhorizDistance, MARGIN,
283 XtNborderWidth, 0,
284 XtNsensitive, True,
285 NULL);
286 dialog_apply_bitmap(seconds_toggle, no_diamond);
287 XtAddCallback(seconds_toggle, XtNcallback, toggle_hms,
288 (XtPointer)&s_seconds);
289
290 /* Create the fuzz toggle. */
291 fuzz_toggle = XtVaCreateManagedWidget(
292 "fuzz", commandWidgetClass, idle_dialog,
293 XtNfromVert, hours_toggle,
294 XtNvertDistance, CLOSE_VGAP,
295 XtNhorizDistance, MARGIN,
296 XtNborderWidth, 0,
297 XtNsensitive, True,
298 NULL);
299 dialog_apply_bitmap(fuzz_toggle, no_dot);
300 XtAddCallback(fuzz_toggle, XtNcallback, toggle_fuzz, NULL);
301
302 /* Create enable/disable toggles. */
303 enable_toggle = XtVaCreateManagedWidget(
304 "enable", commandWidgetClass, idle_dialog,
305 XtNfromVert, fuzz_toggle,
306 XtNvertDistance, FAR_VGAP,
307 XtNhorizDistance, MARGIN,
308 XtNborderWidth, 0,
309 NULL);
310 dialog_apply_bitmap(enable_toggle,
311 (idle_user_enabled == IDLE_SESSION)? diamond: no_diamond);
312 XtAddCallback(enable_toggle, XtNcallback, toggle_enable,
313 (XtPointer)&s_session);
314 enable_perm_toggle = XtVaCreateManagedWidget(
315 "enablePerm", commandWidgetClass, idle_dialog,
316 XtNfromVert, enable_toggle,
317 XtNvertDistance, CLOSE_VGAP,
318 XtNhorizDistance, MARGIN,
319 XtNborderWidth, 0,
320 NULL);
321 dialog_apply_bitmap(enable_perm_toggle,
322 (idle_user_enabled == IDLE_PERM)? diamond: no_diamond);
323 XtAddCallback(enable_perm_toggle, XtNcallback, toggle_enable,
324 (XtPointer)&s_perm);
325 disable_toggle = XtVaCreateManagedWidget(
326 "disable", commandWidgetClass, idle_dialog,
327 XtNfromVert, enable_perm_toggle,
328 XtNvertDistance, CLOSE_VGAP,
329 XtNhorizDistance, MARGIN,
330 XtNborderWidth, 0,
331 NULL);
332 dialog_apply_bitmap(disable_toggle,
333 (idle_user_enabled == IDLE_DISABLED)? diamond: no_diamond);
334 XtAddCallback(disable_toggle, XtNcallback, toggle_enable,
335 (XtPointer)&s_disabled);
336
337 /* Set up the buttons at the bottom. */
338 okay_button = XtVaCreateManagedWidget(
339 ObjConfirmButton, commandWidgetClass, idle_dialog,
340 XtNfromVert, disable_toggle,
341 XtNvertDistance, FAR_VGAP,
342 XtNhorizDistance, MARGIN,
343 NULL);
344 XtAddCallback(okay_button, XtNcallback, okay_callback, NULL);
345
346 cancel_button = XtVaCreateManagedWidget(
347 ObjCancelButton, commandWidgetClass, idle_dialog,
348 XtNfromVert, disable_toggle,
349 XtNvertDistance, FAR_VGAP,
350 XtNfromHoriz, okay_button,
351 XtNhorizDistance, BUTTON_GAP,
352 NULL);
353 XtAddCallback(cancel_button, XtNcallback, idle_cancel, 0);
354 }
355
356 /* Callbacks for all the idle widgets. */
357
358 /* Idle pop-up popping up. */
359 static void
idle_popup_callback(Widget w _is_unused,XtPointer client_data _is_unused,XtPointer call_data _is_unused)360 idle_popup_callback(Widget w _is_unused, XtPointer client_data _is_unused,
361 XtPointer call_data _is_unused)
362 {
363 /* Set the focus to the command widget. */
364 PA_dialog_focus_xaction(command_value, NULL, NULL, NULL);
365 }
366
367 /* Cancel button pushed. */
368 static void
idle_cancel(Widget w _is_unused,XtPointer client_data _is_unused,XtPointer call_data _is_unused)369 idle_cancel(Widget w _is_unused, XtPointer client_data _is_unused,
370 XtPointer call_data _is_unused)
371 {
372 XtPopdown(idle_shell);
373 }
374
375 /* OK button pushed. */
376 static void
okay_callback(Widget w _is_unused,XtPointer call_parms _is_unused,XtPointer call_data _is_unused)377 okay_callback(Widget w _is_unused, XtPointer call_parms _is_unused,
378 XtPointer call_data _is_unused)
379 {
380 if (idle_start()) {
381 idle_changed = true;
382 XtPopdown(idle_shell);
383 }
384 }
385
386 /* Mark a toggle. */
387 static void
mark_toggle(Widget w,Pixmap p)388 mark_toggle(Widget w, Pixmap p)
389 {
390 XtVaSetValues(w, XtNleftBitmap, p, NULL);
391 }
392
393 /* Hour/minute/second options. */
394 static void
toggle_hms(Widget w _is_unused,XtPointer client_data,XtPointer call_data _is_unused)395 toggle_hms(Widget w _is_unused, XtPointer client_data,
396 XtPointer call_data _is_unused)
397 {
398 /* Toggle the flag */
399 hms = *(char *)client_data;
400
401 /* Change the widget states. */
402 mark_toggle(hours_toggle, (hms == 'h') ? diamond : no_diamond);
403 mark_toggle(minutes_toggle, (hms == 'm') ? diamond : no_diamond);
404 mark_toggle(seconds_toggle, (hms == 's') ? diamond : no_diamond);
405 }
406
407 /* Fuzz option. */
408 static void
toggle_fuzz(Widget w _is_unused,XtPointer client_data,XtPointer call_data _is_unused)409 toggle_fuzz(Widget w _is_unused, XtPointer client_data,
410 XtPointer call_data _is_unused)
411 {
412 /* Toggle the flag */
413 fuzz = !fuzz;
414
415 /* Change the widget state. */
416 mark_toggle(fuzz_toggle, fuzz ? dot : no_dot);
417 }
418
419 /* Enable/disable options. */
420 static void
toggle_enable(Widget w _is_unused,XtPointer client_data,XtPointer call_data _is_unused)421 toggle_enable(Widget w _is_unused, XtPointer client_data,
422 XtPointer call_data _is_unused)
423 {
424 /* Toggle the flag */
425 idle_user_enabled = *(enum idle_enum *)client_data;
426
427 /* Change the widget states. */
428 mark_toggle(enable_toggle, (idle_user_enabled == IDLE_SESSION)?
429 diamond: no_diamond);
430 mark_toggle(enable_perm_toggle, (idle_user_enabled == IDLE_PERM)?
431 diamond: no_diamond);
432 mark_toggle(disable_toggle, (idle_user_enabled == IDLE_DISABLED)?
433 diamond: no_diamond);
434 }
435
436 /*
437 * Called when the user presses the OK button on the idle command dialog.
438 * Returns true for success, false otherwise.
439 */
440 static bool
idle_start(void)441 idle_start(void)
442 {
443 char *cmd, *tmo, *its;
444
445 /* Update the globals, so the dialog has the same values next time. */
446 XtVaGetValues(command_value, XtNstring, &cmd, NULL);
447 Replace(idle_command, NewString(cmd));
448 XtVaGetValues(timeout_value, XtNstring, &tmo, NULL);
449 its = Malloc(strlen(tmo) + 3);
450 sprintf(its, "%s%s%c", fuzz? "~": "", tmo, hms);
451 Replace(idle_timeout_string, its);
452
453 /* See if they've turned it off. */
454 if (!idle_user_enabled) {
455 /* If they're turned it off, cancel the timer. */
456 cancel_idle_timer();
457 return true;
458 }
459
460 /* They've turned it on, and possibly reconfigured it. */
461
462 /* Validate the timeout. It should work, yes? */
463 if (!process_idle_timeout_value(its)) {
464 return false;
465 }
466
467 /* Seems okay. Reset to the new interval and command. */
468 if (IN_3270) {
469 reset_idle_timer();
470 }
471 return true;
472 }
473