1 /*
2  * Various popups, such as the About... dialog.
3  *
4  *	create_about_popup()
5  *					Create About info popup
6  *	create_error_popup(widget, error, fmt, ...)
7  *					Create error popup with Unix error
8  *	create_nodaemon_popup()
9  *					Create no-daemon warning popup
10  *	create_multiple_popup()
11  *					Create multiple-plan's warning popup
12  *	multiple_writer_warning_popup()
13  *					Create popup with write warning
14  */
15 
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <time.h>
20 #include <string.h>
21 #include <stdarg.h>
22 #include <sys/types.h>
23 #if defined(__EMX__) || defined(LINUX) || defined(MACOSX)
24 #include <sys/wait.h>
25 #endif
26 #include <Xm/Xm.h>
27 #include <Xm/MessageB.h>
28 #include <Xm/Protocols.h>
29 #include "cal.h"
30 #include "version.h"
31 
32 #define NODAEMON_ONCE		/* if defined, the error popup that offers */
33 				/* to start pland comes up only once. */
34 static Boolean suppress_daemon_warning = False;
35 
36 #ifndef __DATE__
37 #define __DATE__ ""
38 #endif
39 
40 extern char		*progname;	/* argv[0] */
41 extern Display		*display;	/* everybody uses the same server */
42 extern Widget		mainwindow;	/* popup menus hang off main window */
43 extern XtAppContext	app;		/* application handle */
44 extern BOOL		interactive;	/* interactive or fast appt entry? */
45 extern BOOL		startup_lock(BOOL, BOOL);
46 
47 
48 /*---------------------------------------------------------- about ----------*/
49 static char about_message[] = "\
50 \n\
51 Day Planner version %s\n\
52 Compiled %s\n\n\
53 Author: Thomas Driemeyer\n\
54 <thomas@bitrot.de>\n\
55 http://www.bitrot.de\n\n\
56 The sources are available at\n\
57 ftp://ftp.bitrot.de and\n\
58 ftp://plan.ftp.fu-berlin.de\
59 \n";
60 
61 
create_about_popup(void)62 void create_about_popup(void)
63 {
64 	char		msg[512];
65 	Widget		dialog;
66 	XmString	s;
67 	Arg		args[10];
68 	int		n;
69 
70 	sprintf(msg, _(about_message), VERSION JAPANVERSION, __DATE__);
71 	s = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
72 	n = 0;
73 	XtSetArg(args[n], XmNmessageString,	s);			n++;
74 	dialog = XmCreateInformationDialog(mainwindow, _("About"), args, n);
75 	XmStringFree(s);
76 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
77 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
78 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
79 	XtManageChild(dialog);
80 }
81 
82 
83 /*---------------------------------------------------------- errors ---------*/
create_error_popup(Widget widget,UNUSED int error,char * fmt,...)84 void create_error_popup(
85 	Widget		widget,
86 	UNUSED int	error,
87 	char		*fmt, ...)
88 {
89 	va_list		parm;
90 	char		msg[10240];
91 	Widget		dialog;
92 	XmString	string;
93 	Arg		args;
94 
95 	if (!widget)
96 		widget = mainwindow;
97 	strcpy(msg, _("ERROR:\n\n"));
98 
99 	va_start(parm, fmt);
100 	vsprintf(msg + strlen(msg), fmt, parm);
101 	va_end(parm);
102 #	if defined(sgi) || defined(_sgi)
103 	if (error) {
104 		strcat(msg, "\n");
105 		strcat(msg, sys_errlist[error]);
106 	}
107 #	endif
108 	if (!interactive || !widget) {
109 		fputs(msg, stderr);
110 		return;
111 	}
112 	string = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
113 	XtSetArg(args, XmNmessageString, string);
114 	dialog = XmCreateWarningDialog(widget, _("Error"), &args, 1);
115 	XmStringFree(string);
116 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
117 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
118 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
119 	XtManageChild(dialog);
120 }
121 
122 
123 /*---------------------------------------------------------- no daemon ------*/
124 static char nodaemon_message[] = "\
125 Warning:\n\
126 There is no Scheduler daemon. Without a daemon,\n\
127 no action will be taken when an alarm triggers.\n\
128 Please start \"%s\" in your ~/.xsession or\n\
129 ~/.sgisession file, or run plan with the -S option.";
130 
131 void run_daemon(Widget dialog);
132 static void cancel_callback(Widget dialog);
133 static BOOL running;
134 
create_nodaemon_popup(void)135 void create_nodaemon_popup(void)
136 {
137 	char		msg[512];
138 	Widget		dialog;
139 	XmString	s1, s2, s3;
140 	Arg		args[10];
141 	int		n;
142 
143 	if (running)
144 		return;
145 	running = TRUE;
146 #ifdef NODAEMON_ONCE
147 	if (suppress_daemon_warning)
148 		return;
149 #endif
150 	sprintf(msg, _(nodaemon_message), DAEMON_FN);
151 	s1 = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
152 	s2 = XmStringCreateSimple(_("Start daemon now"));
153 	s3 = XmStringCreateSimple(_("Continue without daemon"));
154 	n = 0;
155 	XtSetArg(args[n], XmNmessageString,	s1);		n++;
156 	XtSetArg(args[n], XmNokLabelString,	s2);		n++;
157 	XtSetArg(args[n], XmNcancelLabelString,	s3);		n++;
158 	dialog = XmCreateWarningDialog(mainwindow, _("Warning"), args, n);
159 	XmStringFree(s1);
160 	XmStringFree(s2);
161 	XmStringFree(s3);
162 	XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)
163 			run_daemon, (XtPointer)NULL);
164 	XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)
165 			cancel_callback, (XtPointer)NULL);
166 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
167 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
168 	XtManageChild(dialog);
169 }
170 
171 
172 /*
173  * start the daemon. Nohup the daemon process so that it survives the death
174  * of its parent. Also used by the -S autostart option. pland immediately
175  * backgrounds itself, so the main process must call wait() to avoid zombies.
176  */
177 
178 /*ARGSUSED*/
run_daemon(UNUSED Widget dialog)179 void run_daemon(
180 	UNUSED Widget	dialog)		/* not used */
181 {
182 	char		path[1024];
183 	PID_T		pid;
184 
185 	running = FALSE;
186 	if (!find_file(path, DAEMON_FN, TRUE)) {
187 		fprintf(stderr, _("%s: WARNING: can't find %s\n"),
188 							progname, DAEMON_FN);
189 		create_nodaemon_popup();
190 		return;
191 	}
192 	pid = fork();
193 	if (pid > 0) {
194 		wait(0);
195 		return;
196 	}
197 	if (pid == 0) {
198 		fprintf(stderr, _("%s: starting %s\n"), progname, DAEMON_FN);
199 #if defined(BSD) || defined(MIPS)
200 		setpgrp(0, 0);
201 #else
202 #ifdef __EMX__
203 
204 #else
205 		setsid();
206 #endif
207 #endif
208 		detach_from_network();
209 		execl(path, DAEMON_FN, (char *)0);
210 	}
211 	fprintf(stderr, _("%s: WARNING: can't start %s: "), progname,
212 								DAEMON_FN);
213 	perror("");
214 	create_nodaemon_popup();
215 }
216 
217 
218 /*
219  * don't start the daemon, and set the flag that prevents the popup in the
220  * future.
221  */
222 
223 /*ARGSUSED*/
cancel_callback(UNUSED Widget dialog)224 static void cancel_callback(
225 	UNUSED Widget	dialog)
226 {
227 	running = FALSE;
228 	suppress_daemon_warning = True;
229 }
230 
231 
232 /*---------------------------------------------------------- multiple plan's */
233 static char multiple_message[] = "\
234 Warning:\n\
235 Another %s program is running.\n\
236 %s\n\
237 Press Continue to start up anyway, or Kill to\n\
238 attempt to kill the other program. Continuing\n\
239 may cause loss of new appointment entries, and\n\
240 command-line entry of appointments will not work.";
241 
242 static void kill_callback(Widget, XtPointer, XtPointer);
243 static void cont_callback(Widget, XtPointer, XtPointer);
244 static BOOL cont;
245 
create_multiple_popup(BOOL nolock)246 void create_multiple_popup(
247 	BOOL		nolock)		/* tried to kill competitor */
248 {
249 	char		msg[512];
250 	Widget		dialog;
251 	XmString	s1, s2, s3;
252 	Arg		args[10];
253 	int		n;
254 
255 	cont = FALSE;
256 	sprintf(msg, _(multiple_message), progname,
257 		nolock ? _("The other program could not be killed.\n") : "");
258 	s1 = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
259 	s2 = XmStringCreateSimple(_("Kill"));
260 	s3 = XmStringCreateSimple(_("Continue"));
261 	n = 0;
262 	XtSetArg(args[n], XmNmessageString,	s1);		n++;
263 	XtSetArg(args[n], XmNokLabelString,	s2);		n++;
264 	XtSetArg(args[n], XmNcancelLabelString,	s3);		n++;
265 	XtSetArg(args[n], XmNdialogStyle,
266 			  XmDIALOG_FULL_APPLICATION_MODAL);	n++;
267 	dialog = XmCreateWarningDialog(mainwindow, _("Warning"), args, n);
268 	XmStringFree(s1);
269 	XmStringFree(s2);
270 	XmStringFree(s3);
271 	XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)
272 			kill_callback, (XtPointer)NULL);
273 	XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)
274 			cont_callback, (XtPointer)NULL);
275 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
276 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
277 	XtManageChild(dialog);
278 	while (!cont) {
279 		XEvent event;
280 		XtAppNextEvent(app, &event);
281 		XtDispatchEvent(&event);
282 	}
283 }
284 
285 
286 /*
287  * The user pressed Kill. Try to acquire a lock, then let
288  * create_multiple_popup continue so that the main plan window can come up.
289  */
290 
291 /*ARGSUSED*/
kill_callback(UNUSED Widget dialog,UNUSED XtPointer a,UNUSED XtPointer b)292 static void kill_callback(
293 	UNUSED Widget	 dialog,
294 	UNUSED XtPointer a,
295 	UNUSED XtPointer b)
296 {
297 	if (!startup_lock(FALSE, TRUE))
298 		create_multiple_popup(TRUE);
299 	cont = TRUE;
300 }
301 
302 
303 /*
304  * The user pressed Continue. Let create_multiple_popup continue so that the
305  * main plan window can come up.
306  */
307 
308 /*ARGSUSED*/
cont_callback(UNUSED Widget dialog,UNUSED XtPointer a,UNUSED XtPointer b)309 static void cont_callback(
310 	UNUSED Widget	 dialog,
311 	UNUSED XtPointer a,
312 	UNUSED XtPointer b)
313 {
314 	cont = TRUE;
315 }
316 
317 
318 /*---------------------------------------------------------- multiple wr ----*/
multiple_writer_warning_popup(Widget widget)319 void multiple_writer_warning_popup(
320 	Widget		widget)		/* install near this widget */
321 {
322 	Widget		dialog;
323 	XmString	string;
324 	Arg		args[2];
325 
326 	string = XmStringCreateLtoR(
327 _("WARNING:\n\n\
328 You can now edit other users' appointments.\n\
329 plan will normally notice multiple writes at the same\n\
330 time (except over NFS), but will not notice if another\n\
331 user has modified the same appointment file while you\n\
332 were changing it. The older changes will get lost in\n\
333 this case. Watch out!)"), XmSTRING_DEFAULT_CHARSET);
334 	XtSetArg(args[0], XmNmessageString, string);
335 	XtSetArg(args[1], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
336 	dialog = XmCreateWarningDialog(widget, _("Error"), args, 2);
337 	XmStringFree(string);
338 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
339 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
340 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
341 	XtManageChild(dialog);
342 }
343 
344 
345 /*---------------------------------------------------------- multiple wr ----*/
new_language_popup(Widget widget)346 void new_language_popup(
347 	Widget		widget)		/* install near this widget */
348 {
349 	Widget		dialog;
350 	XmString	string;
351 	Arg		args[2];
352 
353 	string = XmStringCreateLtoR(_("The new language will be effective\n"
354 			"after plan is restarted."), XmSTRING_DEFAULT_CHARSET);
355 	XtSetArg(args[0], XmNmessageString, string);
356 	XtSetArg(args[1], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
357 	dialog = XmCreateWarningDialog(widget, _("Warning"), args, 2);
358 	XmStringFree(string);
359 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
360 	XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
361 	(void)XmInternAtom(display, "WM_DELETE_WINDOW", False);
362 	XtManageChild(dialog);
363 }
364