chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / alert.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2008 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <X11/Xlib.h>
30 #include <X11/keysym.h>
31 #include "alert.h"
32 #include "lang.h"
33 #include "session.h"
34 #include "sounds.h"
35 #include "util.h"
36
37 #define ExTextExtents XmbTextExtents
38 #define ExDrawString XmbDrawString
39
40 #define ExSetColor(pxc, r, g, b) \
41   do { \
42     (pxc)->red = (r << 8) | r; (pxc)->green = (g << 8) | g; (pxc)->blue = (b << 8) | b; \
43   } while (0)
44
45 static XFontSet     xfs = NULL;
46
47 #define DRAW_BOX_OUT(mdd, mgc, mwin, mx, my, mw, mh) \
48         AlertDrawBox(mdd, mgc, mwin, mx, my, mw, mh, \
49         colorful, cols[0], cols[2], cols[3])
50 #define DRAW_BOX_IN(mdd, mgc, mwin, mx, my, mw, mh) \
51         AlertDrawBox(mdd, mgc, mwin, mx, my, mw, mh, \
52         colorful, cols[2], cols[0], cols[3])
53 static void
54 AlertDrawBox(Display * mdd, GC mgc, Window mwin, int mx, int my, int mw, int mh,
55              int colorful, unsigned long c1, unsigned long c2, unsigned long cb)
56 {
57    if (colorful)
58      {
59         XSetForeground(mdd, mgc, cb);
60         XDrawRectangle(mdd, mwin, mgc, mx, my, mw - 1, mh - 1);
61         XSetForeground(mdd, mgc, c1);
62         XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + mw - 3, my + 1);
63         XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + 1, my + mh - 3);
64         XSetForeground(mdd, mgc, c2);
65         XDrawLine(mdd, mwin, mgc, mx + 2, my + mh - 2, mx + mw - 2,
66                   my + mh - 2);
67         XDrawLine(mdd, mwin, mgc, mx + mw - 2, my + 2, mx + mw - 2,
68                   my + mh - 2);
69      }
70    else
71      {
72         XDrawRectangle(mdd, mwin, mgc, mx, my, mw - 1, mh - 1);
73      }
74 }
75
76 #define DRAW_THIN_BOX_IN(mdd, mgc, mwin, mx, my, mw, mh) \
77         AlertDrawThinBoxIn(mdd, mgc, mwin, mx, my, mw, mh, \
78         colorful, cols[2], cols[0])
79 static void
80 AlertDrawThinBoxIn(Display * mdd, GC mgc, Window mwin, int mx, int my, int mw,
81                    int mh, int colorful, unsigned long c1, unsigned long c2)
82 {
83    if (colorful)
84      {
85         XSetForeground(mdd, mgc, c1);
86         XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + mw - 3, my + 1);
87         XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + 1, my + mh - 3);
88         XSetForeground(mdd, mgc, c2);
89         XDrawLine(mdd, mwin, mgc, mx + 2, my + mh - 2, mx + mw - 2,
90                   my + mh - 2);
91         XDrawLine(mdd, mwin, mgc, mx + mw - 2, my + 2, mx + mw - 2,
92                   my + mh - 2);
93      }
94 }
95
96 #define DRAW_HEADER(mdd, mgc, mwin, mx, my, mstr) \
97         AlertDrawHeader(mdd, mgc, mwin, mx, my, mstr, \
98         colorful, cols[2], cols[3], cols[4])
99 static void
100 AlertDrawHeader(Display * mdd, GC mgc, Window mwin, int mx, int my,
101                 const char *mstr, int colorful, unsigned long cb,
102                 unsigned long ct1, unsigned long ct2)
103 {
104    int                 len = strlen(mstr);
105
106    if (colorful)
107      {
108         XSetForeground(mdd, mgc, cb);
109         ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my + 1, mstr, len);
110         ExDrawString(mdd, mwin, xfs, mgc, mx + 2, my + 1, mstr, len);
111         ExDrawString(mdd, mwin, xfs, mgc, mx + 2, my + 2, mstr, len);
112         ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my + 2, mstr, len);
113         XSetForeground(mdd, mgc, ct1);
114         ExDrawString(mdd, mwin, xfs, mgc, mx - 1, my, mstr, len);
115         ExDrawString(mdd, mwin, xfs, mgc, mx, my - 1, mstr, len);
116         ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my, mstr, len);
117         ExDrawString(mdd, mwin, xfs, mgc, mx, my + 1, mstr, len);
118         XSetForeground(mdd, mgc, ct2);
119         ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
120      }
121    else
122      {
123         ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
124      }
125 }
126
127 #define DRAW_STRING(mdd, mgc, mwin, mx, my, mstr) \
128         AlertDrawString(mdd, mgc, mwin, mx, my, mstr, \
129         colorful, cols[3])
130 static void
131 AlertDrawString(Display * mdd, GC mgc, Window mwin, int mx, int my,
132                 const char *mstr, int colorful, unsigned long ct1)
133 {
134    int                 len = strlen(mstr);
135
136    if (colorful)
137      {
138         XSetForeground(mdd, mgc, ct1);
139         ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
140      }
141    else
142      {
143         ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
144      }
145 }
146
147 static char        *
148 AlertButtonText(int btn, const char *text)
149 {
150    char               *s;
151
152    if (!text)
153       return NULL;
154
155    s = EMALLOC(char, strlen(text) + 6);
156    if (!s)
157       return NULL;
158
159    sprintf(s, "(F%d) %s", btn, text);
160
161    return s;
162 }
163
164 static void
165 ShowAlert(const char *title,
166           const char *ignore, const char *restart, const char *quit, char *text)
167 {
168    Window              win = 0, b1 = 0, b2 = 0, b3 = 0;
169    Display            *dd;
170    int                 wid, hih, w, h, i, j, k, mask;
171    XGCValues           gcv;
172    GC                  gc;
173    char                line[1024];
174    XEvent              ev;
175    XSetWindowAttributes att;
176    XRectangle          rect1, rect2;
177    char                colorful;
178    unsigned long       cols[5];
179    XColor              xcl;
180    Colormap            cmap;
181    int                 cnum, fh, x, y, ww, hh, mh;
182    char               *str1, *str2, *str3;
183    KeyCode             key;
184    int                 button;
185    char              **missing_charset_list_return, *def_string_return;
186    int                 missing_charset_count_return;
187    XFontStruct       **font_struct_list_return;
188    char              **font_name_list_return;
189
190    SoundPlay(SOUND_ALERT);
191
192    if (!text)
193       return;
194
195    /*
196     * We may get here from obscure places like an X-error or signal handler
197     * and things seem to work properly only if we do a new XOpenDisplay().
198     */
199    dd = XOpenDisplay(NULL);
200    if (!dd)
201      {
202         fprintf(stderr, "%s", text);
203         fflush(stderr);
204         return;
205      }
206
207    cmap = DefaultColormap(dd, DefaultScreen(dd));
208
209    if (!title)
210       title = _("Enlightenment Error");
211    str1 = AlertButtonText(1, ignore);
212    str2 = AlertButtonText(2, restart);
213    str3 = AlertButtonText(3, quit);
214
215    cnum = 0;
216    colorful = 0;
217    cols[0] = cols[1] = cols[2] = cols[3] = cols[4] = 0;
218    if (DefaultDepth(dd, DefaultScreen(dd)) > 4)
219      {
220         ExSetColor(&xcl, 220, 220, 220);
221         if (!XAllocColor(dd, cmap, &xcl))
222            goto CN;
223         cols[cnum++] = xcl.pixel;
224         ExSetColor(&xcl, 160, 160, 160);
225         if (!XAllocColor(dd, cmap, &xcl))
226            goto CN;
227         cols[cnum++] = xcl.pixel;
228         ExSetColor(&xcl, 100, 100, 100);
229         if (!XAllocColor(dd, cmap, &xcl))
230            goto CN;
231         cols[cnum++] = xcl.pixel;
232         ExSetColor(&xcl, 0, 0, 0);
233         if (!XAllocColor(dd, cmap, &xcl))
234            goto CN;
235         cols[cnum++] = xcl.pixel;
236         ExSetColor(&xcl, 255, 255, 255);
237         if (!XAllocColor(dd, cmap, &xcl))
238            goto CN;
239         cols[cnum++] = xcl.pixel;
240         colorful = 1;
241      }
242  CN:
243
244    if (colorful)
245       att.background_pixel = cols[1];
246    else
247       att.background_pixel = BlackPixel(dd, DefaultScreen(dd));
248    if (colorful)
249       att.border_pixel = cols[3];
250    else
251       att.border_pixel = WhitePixel(dd, DefaultScreen(dd));
252    att.backing_store = Always;
253    att.save_under = True;
254    att.override_redirect = True;
255    mask = CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWSaveUnder |
256       CWBackingStore;
257    win = XCreateWindow(dd, DefaultRootWindow(dd), -100, -100, 1, 1, 0,
258                        CopyFromParent, InputOutput, CopyFromParent, mask, &att);
259
260    if (str1 && sscanf(str1, "%s", line) > 0)
261      {
262         b1 = XCreateWindow(dd, win, -100, -100, 1, 1, 0, CopyFromParent,
263                            InputOutput, CopyFromParent, mask, &att);
264         XMapWindow(dd, b1);
265      }
266    if (str2 && sscanf(str2, "%s", line) > 0)
267      {
268         b2 = XCreateWindow(dd, win, -100, -100, 1, 1, 0, CopyFromParent,
269                            InputOutput, CopyFromParent, mask, &att);
270         XMapWindow(dd, b2);
271      }
272    if (str3 && sscanf(str3, "%s", line) > 0)
273      {
274         b3 = XCreateWindow(dd, win, -100, -100, 1, 1, 0, CopyFromParent,
275                            InputOutput, CopyFromParent, mask, &att);
276         XMapWindow(dd, b3);
277      }
278
279    gc = XCreateGC(dd, win, 0, &gcv);
280    if (colorful)
281       XSetForeground(dd, gc, cols[3]);
282    else
283       XSetForeground(dd, gc, att.border_pixel);
284
285    xfs = XCreateFontSet(dd, "-*-sans unicode-*-r-*-*-12-*-*-*-*-*-*-*,"
286                         "-*-helvetica-*-r-*-*-12-*-*-*-*-*-*-*,fixed",
287                         &missing_charset_list_return,
288                         &missing_charset_count_return, &def_string_return);
289    if (!xfs)
290       goto done;
291
292    if (missing_charset_list_return)
293       XFreeStringList(missing_charset_list_return);
294
295    k = XFontsOfFontSet(xfs, &font_struct_list_return, &font_name_list_return);
296    fh = 0;
297    for (i = 0; i < k; i++)
298      {
299         h = font_struct_list_return[i]->ascent +
300            font_struct_list_return[i]->descent;
301         if (fh < h)
302            fh = h;
303      }
304
305    XSelectInput(dd, win, KeyPressMask | KeyReleaseMask | ExposureMask);
306    XMapWindow(dd, win);
307    XGrabPointer(dd, win, True, ButtonPressMask | ButtonReleaseMask,
308                 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
309    XGrabKeyboard(dd, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
310    XSetInputFocus(dd, win, RevertToPointerRoot, CurrentTime);
311
312    XGrabServer(dd);
313    XSync(dd, False);
314
315    wid = DisplayWidth(dd, DefaultScreen(dd));
316    hih = DisplayHeight(dd, DefaultScreen(dd));
317    ww = (wid >= 600) ? 600 : (wid / 40) * 40;
318    hh = (hih >= 440) ? 440 : (hih / 40) * 40;
319
320    for (i = 40; i < ww; i += 40)
321      {
322         w = i;
323         h = (i * hh) / ww;
324         x = (wid - w) >> 1;
325         y = (hih - h) >> 1;
326         XMoveResizeWindow(dd, win, x, y, w, h);
327         DRAW_BOX_OUT(dd, gc, win, 0, 0, w, h);
328         XSync(dd, False);
329      }
330    x = (wid - ww) >> 1;
331    y = (hih - hh) >> 1;
332    XMoveResizeWindow(dd, win, x, y, ww, hh);
333    XUngrabServer(dd);
334    XSync(dd, False);
335
336    mh = 0;
337    if (str1)
338      {
339         ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
340         mh = (rect2.width > mh) ? rect2.width : mh;
341      }
342    if (str2)
343      {
344         ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
345         mh = (rect2.width > mh) ? rect2.width : mh;
346      }
347    if (str3)
348      {
349         ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
350         mh = (rect2.width > mh) ? rect2.width : mh;
351      }
352    mh += 10;
353
354    w = (wid - ww) / 2;
355    h = (hih - hh) / 2;
356    if (str1 && sscanf(str1, "%s", line) > 0)
357      {
358         w = 5 + (((ww - 20 - mh) * 0) / 4);
359         XMoveResizeWindow(dd, b1, w, hh - 15 - fh, mh + 10, fh + 10);
360         XSelectInput(dd, b1,
361                      ButtonPressMask | ButtonReleaseMask | ExposureMask);
362      }
363    if (str2 && sscanf(str2, "%s", line) > 0)
364      {
365         w = 5 + (((ww - 20 - mh) * 1) / 2);
366         XMoveResizeWindow(dd, b2, w, hh - 15 - fh, mh + 10, fh + 10);
367         XSelectInput(dd, b2,
368                      ButtonPressMask | ButtonReleaseMask | ExposureMask);
369      }
370    if (str3 && sscanf(str3, "%s", line) > 0)
371      {
372         w = 5 + (((ww - 20 - mh) * 2) / 2);
373         XMoveResizeWindow(dd, b3, w, hh - 15 - fh, mh + 10, fh + 10);
374         XSelectInput(dd, b3,
375                      ButtonPressMask | ButtonReleaseMask | ExposureMask);
376      }
377    XSync(dd, False);
378
379    button = 0;
380    for (; button == 0;)
381      {
382         XNextEvent(dd, &ev);
383         switch (ev.type)
384           {
385           case KeyPress:
386              key = XKeysymToKeycode(dd, XK_F1);
387              if (key == ev.xkey.keycode)
388                {
389                   DRAW_BOX_IN(dd, gc, b1, 0, 0, mh + 10, fh + 10);
390                   XSync(dd, False);
391                   sleep(1);
392                   DRAW_BOX_OUT(dd, gc, b1, 0, 0, mh + 10, fh + 10);
393                   button = 1;
394                   break;
395                }
396              key = XKeysymToKeycode(dd, XK_F2);
397              if (key == ev.xkey.keycode)
398                {
399                   DRAW_BOX_IN(dd, gc, b2, 0, 0, mh + 10, fh + 10);
400                   XSync(dd, False);
401                   sleep(1);
402                   DRAW_BOX_OUT(dd, gc, b2, 0, 0, mh + 10, fh + 10);
403                   button = 2;
404                   break;
405                }
406              key = XKeysymToKeycode(dd, XK_F3);
407              if (key == ev.xkey.keycode)
408                {
409                   DRAW_BOX_IN(dd, gc, b3, 0, 0, mh + 10, fh + 10);
410                   XSync(dd, False);
411                   sleep(1);
412                   DRAW_BOX_OUT(dd, gc, b3, 0, 0, mh + 10, fh + 10);
413                   button = 3;
414                   break;
415                }
416              XSync(dd, False);
417              break;
418
419           case ButtonPress:
420              if (ev.xbutton.window == b1)
421                {
422                   DRAW_BOX_IN(dd, gc, b1, 0, 0, mh + 10, fh + 10);
423                }
424              else if (ev.xbutton.window == b2)
425                {
426                   DRAW_BOX_IN(dd, gc, b2, 0, 0, mh + 10, fh + 10);
427                }
428              else if (ev.xbutton.window == b3)
429                {
430                   DRAW_BOX_IN(dd, gc, b3, 0, 0, mh + 10, fh + 10);
431                }
432              XSync(dd, False);
433              break;
434
435           case ButtonRelease:
436              if (ev.xbutton.window == b1)
437                {
438                   DRAW_BOX_OUT(dd, gc, b1, 0, 0, mh + 10, fh + 10);
439                   button = 1;
440                }
441              else if (ev.xbutton.window == b2)
442                {
443                   DRAW_BOX_OUT(dd, gc, b2, 0, 0, mh + 10, fh + 10);
444                   button = 2;
445                }
446              else if (ev.xbutton.window == b3)
447                {
448                   DRAW_BOX_OUT(dd, gc, b3, 0, 0, mh + 10, fh + 10);
449                   button = 3;
450                }
451              XSync(dd, False);
452              break;
453
454           case Expose:
455              /* Flush all other Expose events */
456              while (XCheckTypedWindowEvent(dd, ev.xexpose.window, Expose, &ev))
457                 ;
458
459              ExTextExtents(xfs, title, strlen(title), &rect1, &rect2);
460              w = rect2.width;
461
462              DRAW_HEADER(dd, gc, win, (ww - w) / 2, 5 - rect2.y, title);
463              DRAW_BOX_OUT(dd, gc, win, 0, 0, ww, fh + 10);
464              DRAW_BOX_OUT(dd, gc, win, 0, fh + 10 - 1, ww,
465                           hh - fh - fh - 30 + 2);
466              DRAW_BOX_OUT(dd, gc, win, 0, hh - fh - 20, ww, fh + 20);
467              i = 0;
468              j = 0;
469              k = fh + 10;
470              while (text[i])
471                {
472                   line[j++] = text[i++];
473                   if (line[j - 1] == '\n')
474                     {
475                        line[j - 1] = 0;
476                        j = 0;
477                        DRAW_STRING(dd, gc, win, 6, 6 + k + fh, line);
478                        k += fh + 2;
479                     }
480                }
481              if (str1 && sscanf(str1, "%s", line) > 0)
482                {
483                   ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
484                   h = rect2.width;
485                   w = 3 + (((ww - 20 - mh) * 0) / 4);
486                   DRAW_HEADER(dd, gc, b1, 5 + (mh - h) / 2, 5 - rect2.y, str1);
487                   DRAW_BOX_OUT(dd, gc, b1, 0, 0, mh + 10, fh + 10);
488                   DRAW_THIN_BOX_IN(dd, gc, win, w, hh - 17 - fh, mh + 14,
489                                    fh + 14);
490                }
491              if (str2 && sscanf(str2, "%s", line) > 0)
492                {
493                   ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
494                   h = rect2.width;
495                   w = 3 + (((ww - 20 - mh) * 1) / 2);
496                   DRAW_HEADER(dd, gc, b2, 5 + (mh - h) / 2, 5 - rect2.y, str2);
497                   DRAW_BOX_OUT(dd, gc, b2, 0, 0, mh + 10, fh + 10);
498                   DRAW_THIN_BOX_IN(dd, gc, win, w, hh - 17 - fh, mh + 14,
499                                    fh + 14);
500                }
501              if (str3 && sscanf(str3, "%s", line) > 0)
502                {
503                   ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
504                   h = rect2.width;
505                   w = 3 + (((ww - 20 - mh) * 2) / 2);
506                   DRAW_HEADER(dd, gc, b3, 5 + (mh - h) / 2, 5 - rect2.y, str3);
507                   DRAW_BOX_OUT(dd, gc, b3, 0, 0, mh + 10, fh + 10);
508                   DRAW_THIN_BOX_IN(dd, gc, win, w, hh - 17 - fh, mh + 14,
509                                    fh + 14);
510                }
511              XSync(dd, False);
512              break;
513
514           default:
515              break;
516           }
517      }
518
519    XDestroyWindow(dd, win);
520    XFreeGC(dd, gc);
521    XFreeFontSet(dd, xfs);
522    if (cnum > 0)
523       XFreeColors(dd, cmap, cols, cnum, 0);
524    XCloseDisplay(dd);
525
526    switch (button)
527      {
528      default:
529      case 1:
530         break;
531      case 2:
532         SessionExit(EEXIT_RESTART, NULL);
533         break;
534      case 3:
535         SessionExit(EEXIT_EXIT, NULL);
536         break;
537      }
538
539  done:
540    Efree(str1);
541    Efree(str2);
542    Efree(str3);
543 }
544
545 void
546 AlertX(const char *title, const char *ignore,
547        const char *restart, const char *quit, const char *fmt, ...)
548 {
549    char                text[10240];
550    va_list             args;
551
552    va_start(args, fmt);
553    Evsnprintf(text, sizeof(text), fmt, args);
554    va_end(args);
555
556    ShowAlert(title, ignore, restart, quit, text);
557 }
558
559 void
560 Alert(const char *fmt, ...)
561 {
562    char                text[10240];
563    va_list             args;
564
565    va_start(args, fmt);
566    Evsnprintf(text, sizeof(text), fmt, args);
567    va_end(args);
568
569    ShowAlert(_("Enlightenment Message Dialog"), _("Ignore this"),
570              _("Restart Enlightenment"), _("Quit Enlightenment"), text);
571 }
572
573 void
574 AlertOK(const char *fmt, ...)
575 {
576    char                text[10240];
577    va_list             args;
578
579    va_start(args, fmt);
580    Evsnprintf(text, 10240, fmt, args);
581    va_end(args);
582
583    ShowAlert(_("Attention !!!"), _("OK"), NULL, NULL, text);
584 }