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