1 /*
2 Copyright (c) 1991, 1992, 1995 Simon Marshall
3
4 If you still end up late, don't blame me!
5
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation.
11
12 This software is provided AS IS with no warranties of any kind. The author
13 shall have no liability with respect to the infringement of copyrights,
14 trade secrets or any patents by this file or any part thereof. In no
15 event will the author be liable for any lost revenue or profits or
16 other special, indirect and consequential damages.
17 */
18
19 /*
20 * Stuff for getting the time the alarm is due to go off, and
21 * confirmation if needed.
22 */
23
24
25
26 #include "xalarm.h"
27 #include "dates.h"
28
29 #include <X11/Xaw/Form.h>
30 #include <X11/Xaw/Dialog.h>
31
32
33 #define TIMEINSTRUCTIONS "Enter time (as [+]time[am/pm]):"
34 #define DATEINSTRUCTIONS "Enter date (as +days|date):"
35 #define WARNINGINSTRUCTIONS "Enter warnings (as time[,time...]):"
36 #define CONFIRMFORMAT "%s %s %d, %02d:%02d (in %d+%d:%02d), "
37 #define WHENFORMAT "When? (%d:%02d)"
38 #define EDITTIME (0)
39 #define EDITDATE (1)
40 #define EDITWARNINGS (2)
41 #define EDITED(str, widget) (strcmp (str, XawDialogGetValueString (widget)))
42 #define DIALOGVALUE(str, widget) (str = \
43 XawDialogGetValueString (widget), \
44 str = XtNewString (str))
45 #define UPCASE(ch) (((ch) >= 'a') and ((ch) <= 'z') ? \
46 ((ch) - 'a' + 'A') : (ch))
47
48
49
50 void PopupAndAsk(), EnteredTime(), EnteredDate(), EnteredWarnings(),
51 Confirmed(), MakeConfirmMessage(), EditedText();
52 static void PopupAndConfirm(), Popup(), PopupNext(), Edit(), Cancel(), TimeTracker();
53 extern XtIntervalId ResetTracker();
54 extern String HomeDirectory();
55 extern void Quit(), AddTimeOuts(), SetWarningTimes(), SetGeometry(),
56 SaveAlarmSettings(), SetWMName(), EnsureNotGrownOffScreen();
57 extern long TimeToMilliSeconds();
58 extern unsigned long DateToMilliSeconds();
59 extern time_t time();
60 extern struct tm *localtime();
61
62
63
64 extern AlarmData xalarm;
65
66
67
68 /*
69 * just pop up a dialog widget to get an at/in time. won't pop down
70 * until a valid time is given. EnteredTime() does this.
71 */
72
PopupAndAsk()73 void PopupAndAsk ()
74 {
75 static Widget popup = NONWIDGET;
76 Widget when, gettime, getdate, getwtime, confirm;
77
78 if (ISNONWIDGET (popup)) {
79 popup = XtVaCreatePopupShell ("When?", transientShellWidgetClass,
80 xalarm.toplevel, NULL);
81 when = XtVaCreateManagedWidget ("when", formWidgetClass, popup, NULL);
82
83 /*
84 * Widget for time input.
85 */
86 gettime = XtVaCreateManagedWidget ("time", dialogWidgetClass, when,
87 XtNvalue, (XtArgVal) xalarm.timestr, NULL);
88 XawDialogAddButton (gettime, "ok", EnteredTime, (XtPointer) NULL);
89 XawDialogAddButton (gettime, "editdate", Edit, (XtPointer) EDITDATE);
90 XawDialogAddButton (gettime, "editwarnings", Edit, (XtPointer) EDITWARNINGS);
91 XawDialogAddButton (gettime, "quit", Quit, (XtPointer) NULL);
92
93 /*
94 * Widget for date input.
95 */
96 getdate = XtVaCreateWidget ("date", dialogWidgetClass, when,
97 XtNvalue, (XtArgVal) xalarm.datestr, NULL);
98 XawDialogAddButton (getdate, "ok", EnteredDate, (XtPointer) NULL);
99 XawDialogAddButton (getdate, "edittime", Edit, (XtPointer) EDITTIME);
100 XawDialogAddButton (getdate, "editwarnings", Edit, (XtPointer) EDITWARNINGS);
101 XawDialogAddButton (getdate, "quit", Quit, (XtPointer) NULL);
102
103 /*
104 * Widget for warning times input.
105 */
106 getwtime = XtVaCreateWidget ("warnings", dialogWidgetClass, when,
107 XtNvalue, (XtArgVal) xalarm.warningsstr, NULL);
108 XawDialogAddButton (getwtime, "ok", EnteredWarnings, (XtPointer) NULL);
109 XawDialogAddButton (getwtime, "edittime", Edit, (XtPointer) EDITTIME);
110 XawDialogAddButton (getwtime, "editdate", Edit, (XtPointer) EDITDATE);
111 XawDialogAddButton (getwtime, "quit", Quit, (XtPointer) NULL);
112
113 /*
114 * Widget for confirmation.
115 */
116 confirm = XtVaCreateWidget ("confirm", dialogWidgetClass, when,
117 XtNvalue, (XtArgVal) xalarm.messagestr, NULL);
118 XawDialogAddButton (confirm, "ok", Confirmed, (XtPointer) popup);
119 XawDialogAddButton (confirm, "cancel", Cancel, (XtPointer) NULL);
120 XawDialogAddButton (confirm, "save", SaveAlarmSettings, (XtPointer) NULL);
121 XawDialogAddButton (confirm, "quit", Quit, (XtPointer) NULL);
122
123 /*
124 * Set the callback for the value widget in each Dialog.
125 */
126 AddValueCallback (xalarm.gettimewidget = gettime, EditedText, True);
127 AddValueCallback (xalarm.getdatewidget = getdate, EditedText, True);
128 AddValueCallback (xalarm.getwtimewidget = getwtime, EditedText, True);
129 AddValueCallback (xalarm.confirmwidget = confirm, EditedText, True);
130 xalarm.dialog = gettime;
131 xalarm.savebutton = XtNameToWidget (confirm, "save");
132 }
133
134 if (ISNONID (xalarm.whenid))
135 TimeTracker ((XtPointer) popup, (XtIntervalId) NULL);
136 SetGeometry (popup);
137 XtPopup (popup, XtGrabExclusive);
138 XRaiseWindow (XtDisplay (popup), XtWindow (popup));
139 PopupNext ();
140 XFlush (XtDisplay (popup));
141 }
142
143
144
145 /*
146 * Track the time for this widget. Puts a clock in the WM name, and
147 * keeps the confirmation time message upto date.
148 */
149
TimeTracker(clientdata,id)150 static void TimeTracker (clientdata, id)
151 XtPointer clientdata;
152 XtIntervalId id;
153 {
154 char wmname[TEXT], buf[TEXT];
155 time_t now;
156 struct tm *clock;
157
158 (void) time (&now);
159 clock = localtime (&now);
160
161 (void) sprintf (wmname, WHENFORMAT, clock->tm_hour, clock->tm_min);
162 SetWMName ((Widget) clientdata, wmname);
163
164 if (xalarm.dialog == xalarm.confirmwidget) {
165 /*
166 * Make sure that the alarm is not now out of date.
167 */
168 xalarm.dateout = DateToMilliSeconds (xalarm.datestr);
169 xalarm.timeout = TimeToMilliSeconds (xalarm.timestr);
170 if ((xalarm.timeout == 0) and (xalarm.dateout == 0) and
171 (xalarm.timestr[0] != '+'))
172 Popup (xalarm.gettimewidget, TIMEINSTRUCTIONS);
173 else {
174 MakeConfirmMessage (buf);
175 if (not XtIsSensitive (xalarm.savebutton))
176 (void) sprintf (ENDOF (buf), "\nSaved in %s/%s",
177 HomeDirectory (), XALARMFILE);
178 XtVaSetValues (xalarm.confirmwidget, XtNlabel, (XtArgVal) buf, NULL);
179 }
180 }
181
182 xalarm.whenid = ResetTracker (TimeTracker, clientdata, 0);
183 }
184
185
186
187 /*
188 * Popup the next dialog, if more needs to be entered.
189 */
190
PopupNext()191 static void PopupNext ()
192 {
193 if (ISINVALID (xalarm.timeout) or
194 EDITED (xalarm.timestr, xalarm.gettimewidget))
195 Popup (xalarm.gettimewidget, TIMEINSTRUCTIONS);
196 else if (ISINVALID (xalarm.dateout) or
197 EDITED (xalarm.datestr, xalarm.getdatewidget))
198 Popup (xalarm.getdatewidget, DATEINSTRUCTIONS);
199 else if (ISINVALID (xalarm.numwarnings) or
200 EDITED (xalarm.warningsstr, xalarm.getwtimewidget))
201 Popup (xalarm.getwtimewidget, WARNINGINSTRUCTIONS);
202 else if (xalarm.confirm)
203 PopupAndConfirm ();
204 else
205 Confirmed ((Widget) NULL, (XtPointer) NULL, (XtPointer) NULL);
206 }
207
208
209
210 /*
211 * If invalid, stay. Otherwise, get the rest, if any.
212 */
213
EnteredTime(widget,clientdata,calldata)214 void EnteredTime (widget, clientdata, calldata)
215 Widget widget;
216 XtPointer clientdata, calldata;
217 {
218 char message[TEXT];
219
220 xalarm.timeout = TimeToMilliSeconds
221 (DIALOGVALUE (xalarm.timestr, xalarm.gettimewidget));
222
223 if (ISVALID (xalarm.timeout))
224 PopupNext ();
225 else {
226 (void) sprintf (message, "%s%s", xalarm.errormessage, TIMEINSTRUCTIONS);
227 XtVaSetValues (xalarm.gettimewidget, XtNlabel, (XtArgVal) message, NULL);
228 }
229 }
230
231
232
233 /*
234 * If invalid, stay. Otherwise, get the rest, if any.
235 */
236
EnteredDate(widget,clientdata,calldata)237 void EnteredDate (widget, clientdata, calldata)
238 Widget widget;
239 XtPointer clientdata, calldata;
240 {
241 char message[TEXT];
242
243 xalarm.dateout = DateToMilliSeconds
244 (DIALOGVALUE (xalarm.datestr, xalarm.getdatewidget));
245
246 if (ISVALID (xalarm.dateout))
247 PopupNext ();
248 else {
249 (void) sprintf (message, "%s%s", xalarm.errormessage, DATEINSTRUCTIONS);
250 XtVaSetValues (xalarm.getdatewidget, XtNlabel, (XtArgVal) message, NULL);
251 }
252 }
253
254
255
256 /*
257 * If invalid, stay. Otherwise, get the rest, if any.
258 */
259
EnteredWarnings(widget,clientdata,calldata)260 void EnteredWarnings (widget, clientdata, calldata)
261 Widget widget;
262 XtPointer clientdata, calldata;
263 {
264 char message[TEXT];
265
266 SetWarningTimes (DIALOGVALUE (xalarm.warningsstr, xalarm.getwtimewidget));
267
268 if (ISVALID (xalarm.numwarnings))
269 PopupNext ();
270 else {
271 (void) sprintf (message, "%s%s", xalarm.errormessage, WARNINGINSTRUCTIONS);
272 XtVaSetValues (xalarm.getwtimewidget, XtNlabel, (XtArgVal) message, NULL);
273 }
274 }
275
276
277
278 /*
279 * Just toggle whether i/t concerns alarm time/date/warning time(s).
280 */
281
Edit(widget,clientdata,calldata)282 static void Edit (widget, clientdata, calldata)
283 Widget widget;
284 XtPointer clientdata, calldata;
285 {
286 switch ((int) clientdata) {
287 case EDITTIME:
288 Popup (xalarm.gettimewidget, TIMEINSTRUCTIONS);
289 break;
290 case EDITDATE:
291 Popup (xalarm.getdatewidget, DATEINSTRUCTIONS);
292 break;
293 case EDITWARNINGS:
294 Popup (xalarm.getwtimewidget, WARNINGINSTRUCTIONS);
295 break;
296 }
297 }
298
299
300
301 /*
302 * Set the sensitivity of the save button.
303 */
304
EditedText(widget,clientdata,calldata)305 void EditedText (widget, clientdata, calldata)
306 Widget widget;
307 XtPointer clientdata, calldata;
308 {
309 static Boolean edited = True;
310
311 if (not clientdata)
312 XtSetSensitive (xalarm.savebutton, edited = False);
313 else if (not edited)
314 XtSetSensitive (xalarm.savebutton, edited = True);
315 }
316
317
318
319 /*
320 * Popup to confirm, showing the time which the alarm will trigger.
321 */
322
PopupAndConfirm()323 static void PopupAndConfirm ()
324 {
325 char message[TEXT];
326
327 MakeConfirmMessage (message);
328 Popup (xalarm.confirmwidget, message);
329 }
330
331
332
333 /*
334 * Make the message that should be displayed as the label in the
335 * confirmation window.
336 */
337
MakeConfirmMessage(message)338 void MakeConfirmMessage (message)
339 String message;
340 {
341 static char strings[][4] = {WEEKDAYS, MONTHS};
342 time_t now;
343 struct tm *alarmtime;
344 unsigned long msecsout;
345 int count = 0, i;
346
347 msecsout = SUMTIMEOUTS (DateToMilliSeconds (xalarm.datestr),
348 TimeToMilliSeconds (xalarm.timestr));
349 (void) time (&now);
350 now += (time_t) (msecsout / MSECSIN1SEC);
351 alarmtime = localtime (&now);
352
353 strings[alarmtime->tm_wday][0] = UPCASE (strings[alarmtime->tm_wday][0]);
354 strings[alarmtime->tm_mon+7][0] = UPCASE (strings[alarmtime->tm_mon+7][0]);
355 msecsout += (59 * MSECSIN1SEC);
356 (void) sprintf (message, CONFIRMFORMAT,
357 strings[alarmtime->tm_wday], strings[alarmtime->tm_mon+7],
358 alarmtime->tm_mday, alarmtime->tm_hour, alarmtime->tm_min,
359 msecsout / MSECSIN1DAY, (msecsout / MSECSIN1HR) % 24,
360 (msecsout / MSECSIN1MIN) % 60);
361 msecsout -= (59 * MSECSIN1SEC);
362
363 for (i=0; i<xalarm.numwarnings; i++)
364 if (msecsout > xalarm.warnings[i])
365 count++;
366
367 if (count == 0)
368 (void) sprintf (ENDOF (message), "no warnings:");
369 else
370 (void) sprintf (ENDOF (message), "warning%s: ", PLURAL (count));
371
372 for (i=0; i<xalarm.numwarnings; i++)
373 if (xalarm.timeout > xalarm.warnings[i]) {
374 MAKETIME (ENDOF (message), xalarm.warnings[i] / MSECSIN1MIN);
375 if (--count > 0)
376 (void) strcat (message, ",");
377 }
378 }
379
380
381
382 /*
383 * Yow! Let's go!
384 */
385
Confirmed(widget,clientdata,calldata)386 void Confirmed (widget, clientdata, calldata)
387 Widget widget;
388 XtPointer clientdata, calldata;
389 {
390 String message;
391
392 DIALOGVALUE (message, xalarm.confirmwidget);
393
394 if (*message != '\0')
395 XtVaSetValues (xalarm.messagewidget,
396 XtNlabel, (XtArgVal) (xalarm.messagestr = message), NULL);
397
398 /*
399 * Reset the timeout, since the gap between entering
400 * and confirming may be significant.
401 */
402
403 xalarm.timeout = TimeToMilliSeconds (xalarm.timestr);
404 XtRemoveTimeOut (xalarm.whenid);
405 xalarm.whenid = NONID;
406 AddTimeOuts ();
407
408 XtPopdown (XtParent (XtParent (xalarm.confirmwidget)));
409 }
410
411
412
413 /*
414 * Swap back to gettime widget & remove kbd focus.
415 */
416
Cancel(widget,clientdata,calldata)417 static void Cancel (widget, clientdata, calldata)
418 Widget widget;
419 XtPointer clientdata, calldata;
420 {
421 Popup (xalarm.gettimewidget, TIMEINSTRUCTIONS);
422 }
423
424
425
426 /*
427 * Show this widget, and set its label.
428 * If there's not enough room for the new widget, move it.
429 */
430
Popup(widget,label)431 static void Popup (widget, label)
432 Widget widget;
433 String label;
434 {
435 Widget form = XtParent (widget);
436
437 XtVaSetValues (widget, XtNlabel, (XtArgVal) label, NULL);
438
439 XawFormDoLayout (form, False);
440 if (xalarm.dialog != widget) {
441 XtManageChild (widget);
442 XtUnmanageChild (xalarm.dialog);
443 xalarm.dialog = widget;
444 }
445 XawFormDoLayout (form, True);
446
447 EnsureNotGrownOffScreen (XtParent (form));
448 }
449