chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / dialog.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 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 "E.h"
25 #if ENABLE_DIALOGS
26 #include "dialog.h"
27 #include "e16-ecore_list.h"
28 #include "eimage.h"
29 #include "ewins.h"
30 #include "hints.h"
31 #include "iclass.h"
32 #include "tclass.h"
33 #include "timers.h"
34 #include "xwin.h"
35
36 #define DEBUG_DIALOGS 0
37
38 typedef struct {
39    char                horizontal;
40
41    char                numeric;
42    char                numeric_side;
43
44    int                 upper;
45    int                 lower;
46    int                 unit;
47    int                 jump;
48    int                 val;
49    int                *val_ptr;
50
51    int                 min_length;
52
53    int                 base_orig_w, base_orig_h;
54    int                 knob_orig_w, knob_orig_h;
55
56    int                 base_x, base_y, base_w, base_h;
57    int                 knob_x, knob_y, knob_w, knob_h;
58    int                 numeric_x, numeric_y, numeric_w, numeric_h;
59
60    ImageClass         *ic_base;
61    ImageClass         *ic_knob;
62
63    char                in_drag;
64
65    Win                 base_win;
66    Win                 knob_win;
67 } DItemSlider;
68
69 typedef struct {
70    Win                 area_win;
71    int                 w, h;
72    DialogItemCallbackFunc *init_func;
73    DialogItemCallbackFunc *event_func;
74 } DItemArea;
75
76 typedef struct {
77    Win                 check_win;
78    int                 check_orig_w, check_orig_h;
79    char                onoff;
80    char               *onoff_ptr;
81 } DItemCheckButton;
82
83 typedef struct {
84    char               *image;
85 } DItemImage;
86
87 typedef struct {
88    char                horizontal;
89 } DItemSeparator;
90
91 typedef struct {
92    int                 num_columns;
93    char                border;
94    char                homogenous_h;
95    char                homogenous_v;
96    int                 num_items;
97    DItem             **items;
98 } DItemTable;
99
100 typedef struct {
101    Win                 radio_win;
102    int                 radio_orig_w, radio_orig_h;
103    char                onoff;
104    int                 val;
105    int                *val_ptr;
106    DItem              *next;
107    DItem              *first;
108    DialogItemCallbackFunc *event_func;
109 } DItemRadioButton;
110
111 struct _ditem {
112    int                 type;
113    Dialog             *dlg;
114    DialogCallbackFunc *func;
115    int                 val;
116    void               *data;
117    ImageClass         *iclass;
118    TextClass          *tclass;
119    EImageBorder        padding;
120    char                fill_h;
121    char                fill_v;
122    char                do_close;
123    int                 align_h;
124    int                 align_v;
125    int                 row_span;
126    int                 col_span;
127
128    int                 x, y, w, h;
129    Win                 win;
130    char               *text;
131    union {
132       DItemCheckButton    check_button;
133       DItemTable          table;
134       DItemImage          image;
135       DItemSeparator      separator;
136       DItemRadioButton    radio_button;
137       DItemSlider         slider;
138       DItemArea           area;
139    } item;
140
141    char                realized;
142    char                update;
143
144    char                state;
145    char                hilited;
146    char                clicked;
147 };
148
149 typedef struct {
150    KeyCode             key;
151    DialogCallbackFunc *func;
152    int                 val;
153    void               *data;
154 } DKeyBind;
155
156 struct _dialog {
157    EWin               *ewin;
158    Win                 win;
159    int                 w, h;
160    char               *name;
161    char               *title;
162    PmapMask            pmm_bg;
163    TextClass          *tclass;
164    ImageClass         *iclass;
165    DItem              *item;
166    DialogCallbackFunc *exit_func;
167    int                 exit_val;
168    int                 num_bindings;
169    DKeyBind           *keybindings;
170    void               *data;
171
172    char                redraw;
173    char                update;
174    char                resize;
175    char                close;
176    char                set_title;
177    int                 xu1, yu1, xu2, yu2;
178 };
179
180 static EWin        *FindEwinByDialog(Dialog * d);
181 static int          FindADialog(void);
182
183 static void         DialogHandleEvents(Win win, XEvent * ev, void *prm);
184 static void         DItemHandleEvents(Win win, XEvent * ev, void *prm);
185
186 static void         MoveTableBy(Dialog * d, DItem * di, int dx, int dy);
187 static void         DialogItemsRealize(Dialog * d);
188 static void         DialogItemDestroy(DItem * di, int clean);
189
190 static int          DialogItemCheckButtonGetState(DItem * di);
191
192 static void         DialogUpdate(Dialog * d);
193
194 static void         DialogAddFooter(Dialog * d, DItem * parent,
195                                     int flags, DialogCallbackFunc * cb);
196 static void         DialogAddHeader(Dialog * d, DItem * parent,
197                                     const char *img, const char *txt);
198
199 static Ecore_List  *dialog_list = NULL;
200
201 static char         dialog_update_pending = 0;
202
203 void
204 DialogBindKey(Dialog * d, const char *key, DialogCallbackFunc * func, int val,
205               void *data)
206 {
207    d->num_bindings++;
208    d->keybindings = EREALLOC(DKeyBind, d->keybindings, d->num_bindings);
209    d->keybindings[d->num_bindings - 1].val = val;
210    d->keybindings[d->num_bindings - 1].func = func;
211    d->keybindings[d->num_bindings - 1].data = data;
212    d->keybindings[d->num_bindings - 1].key = EKeynameToKeycode(key);
213 }
214
215 void
216 DialogKeybindingsDestroy(Dialog * d)
217 {
218    Efree(d->keybindings);
219    d->keybindings = NULL;
220    d->num_bindings = 0;
221 }
222
223 Dialog             *
224 DialogCreate(const char *name)
225 {
226    Dialog             *d;
227
228    d = ECALLOC(Dialog, 1);
229    if (!d)
230       return NULL;
231
232    if (!dialog_list)
233       dialog_list = ecore_list_new();
234    ecore_list_append(dialog_list, d);
235
236    d->name = Estrdup(name);
237    d->win = ECreateClientWindow(VROOT, -20, -20, 2, 2);
238    EventCallbackRegister(d->win, 0, DialogHandleEvents, d);
239
240    d->iclass = ImageclassAlloc("DIALOG", 1);
241    d->tclass = TextclassAlloc("DIALOG", 1);
242
243    d->xu1 = d->yu1 = 99999;
244    d->xu2 = d->yu2 = 0;
245
246    return d;
247 }
248
249 static void
250 DialogDestroy(Dialog * d)
251 {
252    ecore_list_node_remove(dialog_list, d);
253
254    Efree(d->name);
255    Efree(d->title);
256    DialogKeybindingsDestroy(d);
257    if (d->item)
258       DialogItemDestroy(d->item, 0);
259    ImageclassFree(d->iclass);
260    TextclassFree(d->tclass);
261
262    FreePmapMask(&(d->pmm_bg));
263
264    Efree(d);
265 }
266
267 static int
268 _DialogMatchName(const void *data, const void *match)
269 {
270    return strcmp(((const Dialog *)data)->name, (const char *)match);
271 }
272
273 Dialog             *
274 DialogFind(const char *name)
275 {
276    return (Dialog *) ecore_list_find(dialog_list, _DialogMatchName, name);
277 }
278
279 void
280 DialogSetTitle(Dialog * d, const char *title)
281 {
282    Efree(d->title);
283    d->title = Estrdup(title);
284    d->set_title = 1;
285 }
286
287 void
288 DialogSetExitFunction(Dialog * d, DialogCallbackFunc * func, int val)
289 {
290    d->exit_func = func;
291    d->exit_val = val;
292 }
293
294 void
295 DialogCallExitFunction(Dialog * d)
296 {
297    if (d->exit_func)
298       d->exit_func(d, d->exit_val, NULL);
299 }
300
301 void
302 DialogSetData(Dialog * d, void *data)
303 {
304    d->data = data;
305 }
306
307 void               *
308 DialogGetData(Dialog * d)
309 {
310    return d->data;
311 }
312
313 #if 0                           /* FIXME - Merge/remove */
314 void
315 DialogAddButton(Dialog * d, const char *text, DialogCallbackFunc * func,
316                 char doclose, int image)
317 {
318    DButton            *db;
319    int                 w, h;
320    EImageBorder       *pad;
321
322    db = EMALLOC(DButton, 1);
323
324    d->num_buttons++;
325    d->button = EREALLOC(DButton *, d->button, d->num_buttons);
326    d->button[d->num_buttons - 1] = db;
327
328    db->parent = d;
329    db->text = Estrdup(text);
330    db->func = func;
331    db->image = image;
332    db->win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
333    EventCallbackRegister(db->win, 0, DButtonHandleEvents, db);
334    EMapWindow(db->win);
335    db->x = -1;
336    db->y = -1;
337    db->w = -1;
338    db->h = -1;
339    db->hilited = 0;
340    db->clicked = 0;
341    db->close = doclose;
342
343    db->tclass = TextclassAlloc("DIALOG_BUTTON", 1);
344    db->iclass = ImageclassAlloc("DIALOG_BUTTON", 1);
345
346    TextSize(db->tclass, 0, 0, STATE_NORMAL, text, &w, &h, 17);
347    pad = ImageclassGetPadding(db->iclass);
348    db->h = h + pad->top + pad->bottom;
349    db->w = w + pad->left + pad->right;
350    if (Conf.dialogs.button_image && db->image)
351       db->w += h + 2;
352
353    ESelectInput(db->win,
354                 EnterWindowMask | LeaveWindowMask | ButtonPressMask |
355                 ButtonReleaseMask);
356 }
357
358 static void
359 DialogDrawButton(Dialog * d __UNUSED__, DButton * db)
360 {
361    int                 state;
362    EImage             *im;
363
364    state = STATE_NORMAL;
365    if ((db->hilited) && (db->clicked))
366      {
367         state = STATE_CLICKED;
368      }
369    else if ((db->hilited) && (!db->clicked))
370      {
371         state = STATE_HILITED;
372      }
373    else if (!(db->hilited) && (db->clicked))
374      {
375         state = STATE_CLICKED;
376      }
377
378    im = NULL;
379    if (Conf.dialogs.button_image)
380      {
381         switch (db->image)
382           {
383           case DLG_BUTTON_OK:
384              im = ThemeImageLoad("pix/ok.png");
385              break;
386           case DLG_BUTTON_CANCEL:
387              im = ThemeImageLoad("pix/cancel.png");
388              break;
389           case DLG_BUTTON_APPLY:
390              im = ThemeImageLoad("pix/apply.png");
391              break;
392           case DLG_BUTTON_CLOSE:
393              im = ThemeImageLoad("pix/close.png");
394              break;
395           default:
396              break;
397           }
398      }
399
400    if (im)
401      {
402         ImageClass         *ic = db->iclass;
403         EImageBorder       *pad;
404         int                 h;
405
406         ImageclassApply(db->iclass, db->win, 0, 0, state, ST_WIDGET);
407
408         pad = ImageclassGetPadding(ic);
409         h = db->h - (pad->top + pad->bottom);
410         TextDraw(db->tclass, db->win, None, 0, 0, state, db->text,
411                  h + 2 + pad->left, pad->top,
412                  db->w - (h + 2 + pad->left + pad->right),
413                  h, h, TextclassGetJustification(db->tclass));
414
415         EImageRenderOnDrawable(im, db->win, None,
416                                EIMAGE_BLEND | EIMAGE_ANTI_ALIAS,
417                                pad->left, pad->top, h, h);
418         EImageFree(im);
419      }
420    else
421      {
422         ITApply(db->win, db->iclass, NULL, state, 0, 0,
423                 ST_WIDGET, db->tclass, NULL, db->text, 0);
424      }
425 }
426 #endif
427
428 DItem              *
429 DialogItemAddButton(DItem * parent, const char *text, DialogCallbackFunc * func,
430                     int val, char doclose, int image __UNUSED__)
431 {
432    DItem              *di;
433
434    di = DialogAddItem(parent, DITEM_BUTTON);
435    DialogItemSetText(di, text);
436    DialogItemSetCallback(di, func, 0, NULL);
437    di->val = val;
438    di->do_close = doclose;
439
440    return di;
441 }
442
443 void
444 DialogRedraw(Dialog * d)
445 {
446    if ((!d->tclass) || (!d->iclass))
447       return;
448
449 #if DEBUG_DIALOGS
450    Eprintf("DialogRedraw win=%#lx pmap=%#lx\n",
451            WinGetXwin(d->win), WinGetPmap(d->win));
452 #endif
453
454    FreePmapMask(&(d->pmm_bg));
455    ImageclassApplyCopy(d->iclass, d->win, d->w, d->h, 0, 0, STATE_NORMAL,
456                        &(d->pmm_bg), IC_FLAG_FULL_SIZE, ST_DIALOG);
457    if (d->pmm_bg.pmap == None)
458       return;
459
460    EGetWindowBackgroundPixmap(d->win);
461    EXCopyArea(d->pmm_bg.pmap, WinGetPmap(d->win), 0, 0, d->w, d->h, 0, 0);
462
463    d->redraw = 1;
464
465    DialogDrawItems(d, d->item, 0, 0, 99999, 99999);
466 }
467
468 static void
469 _DialogEwinInit(EWin * ewin)
470 {
471    Dialog             *d = (Dialog *) ewin->data;
472
473    EwinSetTitle(ewin, d->title);
474    EwinSetClass(ewin, d->name, "Enlightenment_Dialog");
475    d->set_title = 0;
476
477    ewin->props.focus_when_mapped = 1;
478
479    EoSetLayer(ewin, 10);
480 }
481
482 static void
483 _DialogEwinMoveResize(EWin * ewin, int resize __UNUSED__)
484 {
485    Dialog             *d = (Dialog *) ewin->data;
486
487    if (!d || Mode.mode != MODE_NONE || !EoIsShown(ewin))
488       return;
489
490    if (TransparencyUpdateNeeded() || ImageclassIsTransparent(d->iclass))
491       DialogRedraw(d);
492 }
493
494 static void
495 _DialogEwinClose(EWin * ewin)
496 {
497    DialogDestroy((Dialog *) ewin->data);
498    ewin->data = NULL;
499 }
500
501 static const EWinOps _DialogEwinOps = {
502    _DialogEwinInit,
503    NULL,
504    _DialogEwinMoveResize,
505    _DialogEwinClose,
506 };
507
508 void
509 DialogArrange(Dialog * d, int resize)
510 {
511    if (resize)
512       DialogItemsRealize(d);
513
514    if (d->set_title)
515      {
516         EwinSetTitle(d->ewin, d->title);
517         d->set_title = 0;
518      }
519
520    ICCCM_SetSizeConstraints(d->ewin, d->w, d->h, d->w, d->h, 0, 0, 1, 1,
521                             0.0, 65535.0);
522
523    if (resize)
524      {
525         EwinResize(d->ewin, d->w, d->h);
526         d->resize = 1;
527         DialogRedraw(d);
528         DialogUpdate(d);
529         d->resize = 0;
530         ArrangeEwinCentered(d->ewin);
531      }
532 }
533
534 static void
535 DialogShowArranged(Dialog * d, int center)
536 {
537    EWin               *ewin;
538
539    ewin = FindEwinByDialog(d);
540    if (ewin)
541      {
542 #if 0                           /* Make dialogs sticky? */
543         if (EoGetDesk(ewin) != DesksGetCurrent())
544            EwinMoveToDesktop(ewin, DesksGetCurrent());
545 #endif
546         EwinRaise(ewin);
547         EwinShow(ewin);
548         return;
549      }
550
551    DialogItemsRealize(d);
552
553    ewin = AddInternalToFamily(d->win, "DIALOG", EWIN_TYPE_DIALOG,
554                               &_DialogEwinOps, d);
555    d->ewin = ewin;
556    if (!ewin)
557       return;
558
559    DialogArrange(d, 0);
560
561    ewin->client.event_mask |= KeyPressMask;
562    ESelectInput(d->win, ewin->client.event_mask);
563
564    EwinMoveToDesktop(ewin, EoGetDesk(ewin));
565
566    if (ewin->state.placed)
567      {
568         EwinMoveResize(ewin, EoGetX(ewin), EoGetY(ewin), d->w, d->h);
569      }
570    else
571      {
572         EwinResize(ewin, d->w, d->h);
573         if (center || FindADialog() == 1)
574            ArrangeEwinCentered(ewin);
575         else
576            ArrangeEwin(ewin);
577      }
578
579    DialogRedraw(d);
580    DialogUpdate(d);
581    EwinShow(ewin);
582 }
583
584 void
585 DialogShow(Dialog * d)
586 {
587    DialogShowArranged(d, 0);
588 }
589
590 void
591 DialogShowCentered(Dialog * d)
592 {
593    DialogShowArranged(d, 1);
594 }
595
596 void
597 DialogClose(Dialog * d)
598 {
599    d->close = 1;
600 }
601
602 static void
603 _DialogClose(Dialog * d)
604 {
605    if (!d)
606       return;
607
608    DialogCallExitFunction(d);
609
610    EwinHide(d->ewin);
611 }
612
613 void
614 DialogShowSimple(const DialogDef * dd, void *data)
615 {
616    DialogShowSimpleWithName(dd, dd->name, data);
617 }
618
619 void
620 DialogFill(Dialog * d, DItem * parent, const DialogDef * dd, void *data)
621 {
622    DItem              *content;
623
624    if (Conf.dialogs.headers && (dd->header_image || dd->header_text))
625       DialogAddHeader(d, parent, dd->header_image, _(dd->header_text));
626
627    content = DialogAddItem(parent, DITEM_TABLE);
628    if (!content)
629       return;
630
631    dd->fill(d, content, data);
632
633    if (dd->func)
634       DialogAddFooter(d, parent, dd->flags, dd->func);
635 }
636
637 void
638 DialogShowSimpleWithName(const DialogDef * dd, const char *name, void *data)
639 {
640    Dialog             *d;
641    DItem              *table;
642
643    d = DialogFind(name);
644    if (d)
645      {
646         SoundPlay(SOUND_SETTINGS_ACTIVE);
647         DialogShow(d);
648         return;
649      }
650    SoundPlay(dd->sound);
651
652    d = DialogCreate(name);
653    if (!d)
654       return;
655
656    DialogSetTitle(d, _(dd->title));
657
658    table = DialogInitItem(d);
659    if (!table)
660       return;
661
662    DialogFill(d, table, dd, data);
663
664    DialogShow(d);
665 }
666
667 static DItem       *
668 DialogItemCreate(int type)
669 {
670    DItem              *di;
671
672    di = ECALLOC(DItem, 1);
673
674    di->type = type;
675    di->align_h = 512;
676    di->align_v = 512;
677    di->row_span = 1;
678    di->col_span = 1;
679    di->item.table.num_columns = 1;
680    di->item.table.border = 0;
681    di->item.table.homogenous_h = 0;
682    di->item.table.homogenous_v = 0;
683    di->item.table.num_items = 0;
684    di->item.table.items = NULL;
685
686    DialogItemSetPadding(di, 2, 2, 2, 2);
687    DialogItemSetFill(di, 1, 0);
688
689    return di;
690 }
691
692 DItem              *
693 DialogInitItem(Dialog * d)
694 {
695    DItem              *di;
696
697    if (d->item)
698       return NULL;
699
700    di = DialogItemCreate(DITEM_TABLE);
701    d->item = di;
702    if (!di)
703       return di;
704
705    di->dlg = d;
706    di->item.table.num_columns = 1;
707
708    return di;
709 }
710
711 DItem              *
712 DialogAddItem(DItem * dii, int type)
713 {
714    DItem              *di;
715
716    di = DialogItemCreate(type);
717    if (!di)
718       return di;
719
720    switch (di->type)
721      {
722      default:
723         break;
724      case DITEM_AREA:
725         di->item.area.w = 32;
726         di->item.area.h = 32;
727         break;
728      case DITEM_CHECKBUTTON:
729         di->item.check_button.check_win = 0;
730         di->item.check_button.onoff = 0;
731         di->item.check_button.onoff_ptr = &(di->item.check_button.onoff);
732         di->item.check_button.check_orig_w = 10;
733         di->item.check_button.check_orig_h = 10;
734         break;
735      case DITEM_TABLE:
736         di->item.table.num_columns = 1;
737         di->item.table.border = 0;
738         di->item.table.homogenous_h = 0;
739         di->item.table.homogenous_v = 0;
740         di->item.table.num_items = 0;
741         di->item.table.items = NULL;
742         break;
743      case DITEM_IMAGE:
744         di->item.image.image = NULL;
745         break;
746      case DITEM_SEPARATOR:
747         di->item.separator.horizontal = 0;
748         break;
749      case DITEM_RADIOBUTTON:
750         di->item.radio_button.radio_win = 0;
751         di->item.radio_button.onoff = 0;
752         di->item.radio_button.val = 0;
753         di->item.radio_button.val_ptr = 0;
754         di->item.radio_button.next = NULL;
755         di->item.radio_button.first = NULL;
756         di->item.radio_button.radio_orig_w = 10;
757         di->item.radio_button.radio_orig_h = 10;
758         di->item.radio_button.event_func = NULL;
759         break;
760      case DITEM_SLIDER:
761         di->item.slider.horizontal = 1;
762         di->item.slider.numeric = 0;
763         di->item.slider.numeric_side = 0;
764         di->item.slider.upper = 100;
765         di->item.slider.lower = 0;
766         di->item.slider.unit = 10;
767         di->item.slider.jump = 20;
768         di->item.slider.val = 0;
769         di->item.slider.val_ptr = NULL;
770         di->item.slider.min_length = 64;
771         di->item.slider.ic_base = NULL;
772         di->item.slider.ic_knob = NULL;
773         di->item.slider.base_win = 0;
774         di->item.slider.knob_win = 0;
775         di->item.slider.base_orig_w = 10;
776         di->item.slider.base_orig_h = 10;
777         di->item.slider.knob_orig_w = 6;
778         di->item.slider.knob_orig_h = 6;
779         di->item.slider.base_x = 0;
780         di->item.slider.base_y = 0;
781         di->item.slider.base_w = 0;
782         di->item.slider.base_h = 0;
783         di->item.slider.knob_x = 0;
784         di->item.slider.knob_y = 0;
785         di->item.slider.knob_w = 0;
786         di->item.slider.knob_h = 0;
787         di->item.slider.numeric_x = 0;
788         di->item.slider.numeric_y = 0;
789         di->item.slider.numeric_w = 0;
790         di->item.slider.numeric_h = 0;
791         di->item.slider.in_drag = 0;
792         break;
793      }
794
795    if (dii)
796      {
797         dii->item.table.num_items++;
798         dii->item.table.items =
799            EREALLOC(DItem *, dii->item.table.items, dii->item.table.num_items);
800         dii->item.table.items[dii->item.table.num_items - 1] = di;
801         di->dlg = dii->dlg;
802      }
803
804    return di;
805 }
806
807 static void
808 DialogAddHeader(Dialog * d __UNUSED__, DItem * parent, const char *img,
809                 const char *txt)
810 {
811    DItem              *table, *di;
812
813    table = DialogAddItem(parent, DITEM_TABLE);
814    DialogItemTableSetOptions(table, 2, 0, 0, 0);
815    DialogItemSetAlign(table, 512, 0);
816    DialogItemSetFill(table, 0, 0);
817
818    di = DialogAddItem(table, DITEM_IMAGE);
819    DialogItemImageSetFile(di, img);
820
821    di = DialogAddItem(table, DITEM_TEXT);
822    DialogItemSetText(di, txt);
823
824    di = DialogAddItem(parent, DITEM_SEPARATOR);
825 }
826
827 static void
828 DialogAddFooter(Dialog * d, DItem * parent, int flags, DialogCallbackFunc * cb)
829 {
830    DItem              *table, *di;
831    int                 n_buttons;
832
833    if (!(flags & DLG_NO_SEPARATOR))
834       di = DialogAddItem(parent, DITEM_SEPARATOR);
835
836    table = DialogAddItem(parent, DITEM_TABLE);
837    DialogItemSetAlign(table, 512, 0);
838    DialogItemSetFill(table, 0, 0);
839
840    /* FIXME - The "real" dialog buttons are slightly different */
841    n_buttons = 0;
842    if (flags & 1)
843      {
844         di = DialogItemAddButton(table, _("OK"), cb, 0, 1, DLG_BUTTON_OK);
845         n_buttons++;
846      }
847    if (flags & 2)
848      {
849         di = DialogItemAddButton(table, _("Apply"), cb, 1, 0, DLG_BUTTON_APPLY);
850         DialogBindKey(d, "Return", cb, 1, NULL);
851         n_buttons++;
852      }
853    if (flags & 4)
854      {
855         di =
856            DialogItemAddButton(table, _("Close"), NULL, 0, 1, DLG_BUTTON_CLOSE);
857         DialogBindKey(d, "Escape", DialogCallbackClose, 0, NULL);
858         n_buttons++;
859      }
860
861    DialogItemTableSetOptions(table, n_buttons, 0, 1, 0);
862
863    DialogSetExitFunction(d, cb, 2);
864 }
865
866 Dialog             *
867 DialogItemGetDialog(DItem * di)
868 {
869    return di->dlg;
870 }
871
872 void
873 DialogItemSetCallback(DItem * di, DialogCallbackFunc * func, int val,
874                       void *data)
875 {
876    di->func = func;
877    di->val = val;
878    di->data = data;
879 }
880
881 void
882 DialogItemSetPadding(DItem * di, int left, int right, int top, int bottom)
883 {
884    di->padding.left = left;
885    di->padding.right = right;
886    di->padding.top = top;
887    di->padding.bottom = bottom;
888 }
889
890 void
891 DialogItemSetFill(DItem * di, char fill_h, char fill_v)
892 {
893    di->fill_h = fill_h;
894    di->fill_v = fill_v;
895 }
896
897 void
898 DialogItemSetAlign(DItem * di, int align_h, int align_v)
899 {
900    di->align_h = align_h;
901    di->align_v = align_v;
902 }
903
904 void
905 DialogItemSetRowSpan(DItem * di, int row_span)
906 {
907    di->row_span = row_span;
908 }
909
910 void
911 DialogItemSetColSpan(DItem * di, int col_span)
912 {
913    di->col_span = col_span;
914 }
915
916 void
917 DialogItemCallCallback(Dialog * d, DItem * di)
918 {
919    if (di->func)
920       di->func(d, di->val, di->data);
921 }
922
923 static void
924 DialogRealizeItem(Dialog * d, DItem * di)
925 {
926    const char         *iclass, *tclass;
927    int                 iw = 0, ih = 0;
928    int                 register_win_callback;
929    EImage             *im;
930    EImageBorder       *pad;
931
932    if (di->realized && di->type != DITEM_TABLE)
933       return;
934    di->realized = 1;
935
936    iclass = tclass = NULL;
937    if (di->type == DITEM_BUTTON)
938      {
939         iclass = "DIALOG_WIDGET_BUTTON";
940         tclass = iclass;
941      }
942    else if (di->type == DITEM_CHECKBUTTON)
943      {
944         iclass = "DIALOG_WIDGET_CHECK_BUTTON";
945         tclass = iclass;
946      }
947    else if (di->type == DITEM_TEXT)
948      {
949         tclass = "DIALOG_WIDGET_TEXT";
950      }
951    else if (di->type == DITEM_SEPARATOR)
952      {
953         iclass = "DIALOG_WIDGET_SEPARATOR";
954      }
955    else if (di->type == DITEM_RADIOBUTTON)
956      {
957         iclass = "DIALOG_WIDGET_RADIO_BUTTON";
958         tclass = iclass;
959      }
960 #if 0
961    else if (di->type == DITEM_SLIDER)
962      {
963         iclass = NULL;
964      }
965 #endif
966    else if (di->type == DITEM_AREA)
967      {
968         iclass = "DIALOG_WIDGET_AREA";
969      }
970
971    if (!di->iclass && iclass)
972      {
973         di->iclass = ImageclassAlloc(iclass, 1);
974         if (!di->iclass)
975           {
976              di->type = DITEM_NONE;
977              return;
978           }
979      }
980
981    if (!di->tclass && tclass)
982      {
983         di->tclass = TextclassAlloc(tclass, 1);
984         if (!di->tclass)
985           {
986              di->type = DITEM_NONE;
987              return;
988           }
989      }
990
991    if (di->type == DITEM_TABLE)
992      {
993         int                 i;
994
995         for (i = 0; i < di->item.table.num_items; i++)
996            DialogRealizeItem(d, di->item.table.items[i]);
997      }
998
999    register_win_callback = 1;
1000
1001    switch (di->type)
1002      {
1003      case DITEM_SLIDER:
1004         if (di->item.slider.numeric)
1005           {
1006              di->win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1007              EMapWindow(di->win);
1008              ESelectInput(di->win,
1009                           EnterWindowMask | LeaveWindowMask |
1010                           ButtonPressMask | ButtonReleaseMask);
1011           }
1012         di->item.slider.base_win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1013         EMapWindow(di->item.slider.base_win);
1014         di->item.slider.knob_win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1015         EMapWindow(di->item.slider.knob_win);
1016         ESelectInput(di->item.slider.base_win,
1017                      EnterWindowMask | LeaveWindowMask |
1018                      ButtonPressMask | ButtonReleaseMask);
1019         EventCallbackRegister(di->item.slider.base_win, 0, DItemHandleEvents,
1020                               di);
1021         ESelectInput(di->item.slider.knob_win,
1022                      EnterWindowMask | LeaveWindowMask |
1023                      ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
1024         EventCallbackRegister(di->item.slider.knob_win, 0, DItemHandleEvents,
1025                               di);
1026
1027         if (!di->item.slider.ic_base)
1028           {
1029              if (di->item.slider.horizontal)
1030                 di->item.slider.ic_base =
1031                    ImageclassAlloc("DIALOG_WIDGET_SLIDER_BASE_HORIZONTAL", 1);
1032              else
1033                 di->item.slider.ic_base =
1034                    ImageclassAlloc("DIALOG_WIDGET_SLIDER_BASE_VERTICAL", 1);
1035           }
1036         im = ImageclassGetImage(di->item.slider.ic_base, 0, 0, 0);
1037         if (im)
1038           {
1039              EImageGetSize(im, &di->item.slider.base_orig_w,
1040                            &di->item.slider.base_orig_h);
1041              EImageFree(im);
1042           }
1043
1044         if (!di->item.slider.ic_knob)
1045           {
1046              if (di->item.slider.horizontal)
1047                 di->item.slider.ic_knob =
1048                    ImageclassAlloc("DIALOG_WIDGET_SLIDER_KNOB_HORIZONTAL", 1);
1049              else
1050                 di->item.slider.ic_knob =
1051                    ImageclassAlloc("DIALOG_WIDGET_SLIDER_KNOB_VERTICAL", 1);
1052           }
1053         im = ImageclassGetImage(di->item.slider.ic_knob, 0, 0, 0);
1054         if (im)
1055           {
1056              EImageGetSize(im, &di->item.slider.knob_orig_w,
1057                            &di->item.slider.knob_orig_h);
1058              EImageFree(im);
1059           }
1060
1061         pad = ImageclassGetPadding(di->item.slider.ic_base);
1062         if (di->item.slider.horizontal)
1063           {
1064              iw = di->item.slider.min_length + pad->left + pad->right;
1065              ih = di->item.slider.base_orig_h;
1066           }
1067         else
1068           {
1069              iw = di->item.slider.base_orig_w;
1070              ih = di->item.slider.min_length + pad->top + pad->bottom;
1071           }
1072         di->w = iw;
1073         di->h = ih;
1074         break;
1075      case DITEM_BUTTON:
1076         pad = ImageclassGetPadding(di->iclass);
1077         TextSize(di->tclass, 0, 0, STATE_NORMAL, di->text, &iw, &ih, 17);
1078         iw += pad->left + pad->right;
1079         ih += pad->top + pad->bottom;
1080         di->win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1081         EMapWindow(di->win);
1082         ESelectInput(di->win,
1083                      EnterWindowMask | LeaveWindowMask | ButtonPressMask |
1084                      ButtonReleaseMask);
1085         di->w = iw;
1086         di->h = ih;
1087         break;
1088      case DITEM_AREA:
1089         pad = ImageclassGetPadding(di->iclass);
1090         iw = di->item.area.w;
1091         ih = di->item.area.h;
1092         iw += pad->left + pad->right;
1093         ih += pad->top + pad->bottom;
1094         di->win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1095         EMapWindow(di->win);
1096         di->item.area.area_win = ECreateWindow(di->win, -20, -20, 2, 2, 0);
1097         EMapWindow(di->item.area.area_win);
1098         ESelectInput(di->item.area.area_win,
1099                      EnterWindowMask | LeaveWindowMask | ButtonPressMask |
1100                      ButtonReleaseMask | PointerMotionMask);
1101         EventCallbackRegister(di->item.area.area_win, 0, DItemHandleEvents, di);
1102         di->w = iw;
1103         di->h = ih;
1104         break;
1105      case DITEM_CHECKBUTTON:
1106         pad = ImageclassGetPadding(di->iclass);
1107         im = ImageclassGetImage(di->iclass, 0, 0, 0);
1108         if (im)
1109           {
1110              EImageGetSize(im, &di->item.check_button.check_orig_w,
1111                            &di->item.check_button.check_orig_h);
1112              EImageFree(im);
1113           }
1114         TextSize(di->tclass, 0, 0, STATE_NORMAL, di->text, &iw, &ih, 17);
1115         if (ih < di->item.check_button.check_orig_h)
1116            ih = di->item.check_button.check_orig_h;
1117         iw += di->item.check_button.check_orig_w + pad->left;
1118         di->item.check_button.check_win =
1119            ECreateWindow(d->win, -20, -20, 2, 2, 0);
1120         di->win = ECreateEventWindow(d->win, -20, -20, 2, 2);
1121         EMapWindow(di->item.check_button.check_win);
1122         EMapWindow(di->win);
1123         ESelectInput(di->win,
1124                      EnterWindowMask | LeaveWindowMask | ButtonPressMask |
1125                      ButtonReleaseMask);
1126         di->w = iw;
1127         di->h = ih;
1128         break;
1129      case DITEM_TEXT:
1130         TextSize(di->tclass, 0, 0, STATE_NORMAL, di->text, &iw, &ih, 17);
1131         di->w = iw;
1132         di->h = ih;
1133         break;
1134      case DITEM_IMAGE:
1135         im = ThemeImageLoad(di->item.image.image);
1136         if (im)
1137           {
1138              EImageGetSize(im, &iw, &ih);
1139              di->win = ECreateWindow(d->win, 0, 0, iw, ih, 0);
1140              EMapWindow(di->win);
1141              EImageApplyToWin(im, di->win, 0, 0, 0);
1142              EImageFree(im);
1143           }
1144         di->w = iw;
1145         di->h = ih;
1146         register_win_callback = 0;
1147         break;
1148      case DITEM_SEPARATOR:
1149         pad = ImageclassGetPadding(di->iclass);
1150         iw = pad->left + pad->right;
1151         ih = pad->top + pad->bottom;
1152         di->win = ECreateWindow(d->win, -20, -20, 2, 2, 0);
1153         EMapWindow(di->win);
1154         di->w = iw;
1155         di->h = ih;
1156         register_win_callback = 0;
1157         break;
1158      case DITEM_RADIOBUTTON:
1159         pad = ImageclassGetPadding(di->iclass);
1160         im = ImageclassGetImage(di->iclass, 0, 0, 0);
1161         if (im)
1162           {
1163              EImageGetSize(im, &di->item.radio_button.radio_orig_w,
1164                            &di->item.radio_button.radio_orig_h);
1165              EImageFree(im);
1166           }
1167         TextSize(di->tclass, 0, 0, STATE_NORMAL, di->text, &iw, &ih, 17);
1168         if (ih < di->item.radio_button.radio_orig_h)
1169            ih = di->item.radio_button.radio_orig_h;
1170         iw += di->item.radio_button.radio_orig_w + pad->left;
1171         di->item.radio_button.radio_win =
1172            ECreateWindow(d->win, -20, -20, 2, 2, 0);
1173         di->win = ECreateEventWindow(d->win, -20, -20, 2, 2);
1174         EMapWindow(di->item.radio_button.radio_win);
1175         EMapWindow(di->win);
1176         ESelectInput(di->win,
1177                      EnterWindowMask | LeaveWindowMask | ButtonPressMask |
1178                      ButtonReleaseMask);
1179         di->w = iw;
1180         di->h = ih;
1181         break;
1182      case DITEM_TABLE:
1183         {
1184            int                 cols, rows;
1185
1186            pad = ImageclassGetPadding(d->iclass);
1187
1188            cols = di->item.table.num_columns;
1189            rows = 1;
1190            if (cols > 0)
1191              {
1192                 int                 i, r, c, x, y;
1193                 int                *col_size, *row_size;
1194
1195                 col_size = EMALLOC(int, cols);
1196                 row_size = EMALLOC(int, rows);
1197
1198                 row_size[0] = 0;
1199                 for (i = 0; i < cols; i++)
1200                    col_size[i] = 0;
1201
1202                 r = c = 0;
1203                 for (i = 0; i < di->item.table.num_items; i++)
1204                   {
1205                      DItem              *dii;
1206                      int                 w, h, j, ww;
1207
1208                      dii = di->item.table.items[i];
1209                      w = dii->w + (dii->padding.left + dii->padding.right);
1210                      h = dii->h + (dii->padding.top + dii->padding.bottom);
1211                      ww = 0;
1212                      for (j = 0; j < dii->col_span; j++)
1213                         ww += col_size[c + j];
1214                      if (w > ww)
1215                        {
1216                           ww = (w + dii->col_span - 1) / dii->col_span;
1217                           for (j = 0; j < dii->col_span; j++)
1218                              if (col_size[c + j] < ww)
1219                                 col_size[c + j] = ww;
1220                        }
1221                      if (h > row_size[r])
1222                         row_size[r] = h;
1223                      c += dii->col_span;
1224                      if (c >= cols)
1225                        {
1226                           c = 0;
1227                           r++;
1228                           rows++;
1229                           row_size = EREALLOC(int, row_size, rows);
1230
1231                           row_size[rows - 1] = 0;
1232                        }
1233                   }
1234                 if (di->item.table.homogenous_h)
1235                   {
1236                      int                 max = 0;
1237
1238                      for (i = 0; i < cols; i++)
1239                        {
1240                           if (col_size[i] > max)
1241                              max = col_size[i];
1242                        }
1243                      for (i = 0; i < cols; i++)
1244                         col_size[i] = max;
1245                   }
1246                 if (di->item.table.homogenous_v)
1247                   {
1248                      int                 max = 0;
1249
1250                      for (i = 0; i < rows; i++)
1251                        {
1252                           if (row_size[i] > max)
1253                              max = row_size[i];
1254                        }
1255                      for (i = 0; i < rows; i++)
1256                         row_size[i] = max;
1257                   }
1258
1259                 iw = ih = 0;
1260                 for (i = 0; i < cols; i++)
1261                    iw += col_size[i];
1262                 for (i = 0; i < rows; i++)
1263                    ih += row_size[i];
1264                 di->w = iw;
1265                 di->h = ih;
1266
1267                 x = y = 0;
1268                 r = c = 0;
1269                 for (i = 0; i < di->item.table.num_items; i++)
1270                   {
1271                      DItem              *dii;
1272                      int                 j, sw = 0, sh = 0;
1273
1274                      dii = di->item.table.items[i];
1275
1276                      for (j = 0; j < dii->col_span; j++)
1277                         sw += col_size[c + j];
1278                      for (j = 0; j < dii->row_span; j++)
1279                         sh += row_size[r + j];
1280                      if (dii->fill_h)
1281                         dii->w = sw - (dii->padding.left + dii->padding.right);
1282                      if (dii->fill_v)
1283                         dii->h = sh - (dii->padding.top + dii->padding.bottom);
1284                      if (dii->w <= 0 || dii->h <= 0)
1285                         goto skip;
1286                      if (dii->type == DITEM_TABLE)
1287                        {
1288                           int                 dx, dy, newx, newy;
1289
1290                           newx =
1291                              di->x + x + pad->left +
1292                              dii->padding.left +
1293                              (((sw
1294                                 - (dii->padding.left + dii->padding.right) -
1295                                 dii->w) * dii->align_h) >> 10);
1296                           newy =
1297                              di->y + y + pad->top +
1298                              dii->padding.top +
1299                              (((sh
1300                                 - (dii->padding.top + dii->padding.bottom) -
1301                                 dii->h) * dii->align_v) >> 10);
1302                           dx = newx - dii->x - pad->left;
1303                           dy = newy - dii->y - pad->top;
1304                           MoveTableBy(d, dii, dx, dy);
1305                        }
1306                      else
1307                        {
1308                           dii->x =
1309                              di->x + x + pad->left +
1310                              dii->padding.left +
1311                              (((sw
1312                                 - (dii->padding.left + dii->padding.right) -
1313                                 dii->w) * dii->align_h) >> 10);
1314                           dii->y =
1315                              di->y + y + pad->top +
1316                              dii->padding.top +
1317                              (((sh
1318                                 - (dii->padding.top + dii->padding.bottom) -
1319                                 dii->h) * dii->align_v) >> 10);
1320                           if (dii->win)
1321                              EMoveResizeWindow(dii->win, dii->x, dii->y,
1322                                                dii->w, dii->h);
1323                           if (dii->type == DITEM_CHECKBUTTON)
1324                              EMoveResizeWindow(dii->item.check_button.
1325                                                check_win, dii->x,
1326                                                dii->y +
1327                                                ((dii->h -
1328                                                  dii->item.check_button.
1329                                                  check_orig_h) / 2),
1330                                                dii->item.check_button.
1331                                                check_orig_w,
1332                                                dii->item.check_button.
1333                                                check_orig_h);
1334                           if (dii->type == DITEM_RADIOBUTTON)
1335                              EMoveResizeWindow(dii->item.radio_button.
1336                                                radio_win, dii->x,
1337                                                dii->y +
1338                                                ((dii->h -
1339                                                  dii->item.radio_button.
1340                                                  radio_orig_h) / 2),
1341                                                dii->item.radio_button.
1342                                                radio_orig_w,
1343                                                dii->item.radio_button.
1344                                                radio_orig_h);
1345                           if (dii->type == DITEM_AREA)
1346                             {
1347                                pad = ImageclassGetPadding(dii->iclass);
1348                                dii->item.area.w =
1349                                   dii->w - (pad->left + pad->right);
1350                                dii->item.area.h =
1351                                   dii->h - (pad->top + pad->bottom);
1352                                EMoveResizeWindow(dii->item.area.area_win,
1353                                                  pad->left, pad->top,
1354                                                  dii->item.area.w,
1355                                                  dii->item.area.h);
1356                             }
1357                           if (dii->type == DITEM_SLIDER)
1358                             {
1359                                dii->item.slider.base_x = 0;
1360                                dii->item.slider.base_y = 0;
1361                                dii->item.slider.base_w = dii->w;
1362                                dii->item.slider.base_h = dii->h;
1363                                dii->item.slider.knob_w =
1364                                   dii->item.slider.knob_orig_w;
1365                                dii->item.slider.knob_h =
1366                                   dii->item.slider.knob_orig_h;
1367                                if (dii->item.slider.base_win)
1368                                   EMoveResizeWindow(dii->item.slider.base_win,
1369                                                     dii->x +
1370                                                     dii->item.slider.base_x,
1371                                                     dii->y +
1372                                                     dii->item.slider.base_y,
1373                                                     dii->item.slider.base_w,
1374                                                     dii->item.slider.base_h);
1375                                if (dii->win)
1376                                   EMoveResizeWindow(dii->win,
1377                                                     dii->x +
1378                                                     dii->item.slider.
1379                                                     numeric_x,
1380                                                     dii->y +
1381                                                     dii->item.slider.
1382                                                     numeric_y,
1383                                                     dii->item.slider.
1384                                                     numeric_w,
1385                                                     dii->item.slider.numeric_h);
1386                             }
1387                        }
1388                    skip:
1389                      x += sw;
1390                      c += dii->col_span;
1391                      if (c >= cols)
1392                        {
1393                           x = 0;
1394                           y += row_size[r];
1395                           c = 0;
1396                           r++;
1397                        }
1398                   }
1399                 Efree(col_size);
1400                 Efree(row_size);
1401              }
1402         }
1403         break;
1404      case DITEM_NONE:
1405      default:
1406         di->w = 0;
1407         di->h = 0;
1408         break;
1409      }
1410
1411    if (di->win && register_win_callback)
1412       EventCallbackRegister(di->win, 0, DItemHandleEvents, di);
1413 }
1414
1415 static void
1416 MoveTableBy(Dialog * d, DItem * di, int dx, int dy)
1417 {
1418    int                 i;
1419
1420    di->x += dx;
1421    di->y += dy;
1422    for (i = 0; i < di->item.table.num_items; i++)
1423      {
1424         DItem              *dii;
1425
1426         dii = di->item.table.items[i];
1427
1428         if (dii->type == DITEM_TABLE)
1429           {
1430              MoveTableBy(d, dii, dx, dy);
1431              continue;
1432           }
1433
1434         dii->x += dx;
1435         dii->y += dy;
1436
1437         if (dii->win)
1438            EMoveWindow(dii->win, dii->x, dii->y);
1439
1440         switch (dii->type)
1441           {
1442           case DITEM_CHECKBUTTON:
1443              EMoveWindow(dii->item.check_button.check_win, dii->x,
1444                          dii->y +
1445                          ((dii->h - dii->item.check_button.check_orig_h) / 2));
1446              break;
1447           case DITEM_RADIOBUTTON:
1448              EMoveWindow(dii->item.radio_button.radio_win, dii->x,
1449                          dii->y +
1450                          ((dii->h - dii->item.radio_button.radio_orig_h) / 2));
1451              break;
1452           case DITEM_SLIDER:
1453              {
1454                 if (dii->item.slider.base_win)
1455                    EMoveResizeWindow(dii->item.slider.base_win,
1456                                      dii->x + dii->item.slider.base_x,
1457                                      dii->y + dii->item.slider.base_y,
1458                                      dii->item.slider.base_w,
1459                                      dii->item.slider.base_h);
1460                 if (dii->item.slider.knob_win)
1461                    EMoveResizeWindow(dii->item.slider.knob_win,
1462                                      dii->x + dii->item.slider.knob_x,
1463                                      dii->y + dii->item.slider.knob_y,
1464                                      dii->item.slider.knob_w,
1465                                      dii->item.slider.knob_h);
1466                 if (dii->win)
1467                    EMoveResizeWindow(dii->win,
1468                                      dii->x + dii->item.slider.numeric_x,
1469                                      dii->y + dii->item.slider.numeric_y,
1470                                      dii->item.slider.numeric_w,
1471                                      dii->item.slider.numeric_h);
1472                 break;
1473              }
1474           }
1475      }
1476 }
1477
1478 void
1479 DialogDrawItems(Dialog * d, DItem * di, int x, int y, int w, int h)
1480 {
1481    d->update = 1;
1482    di->update = 1;
1483
1484    if (d->xu1 > x)
1485       d->xu1 = x;
1486    if (d->yu1 > y)
1487       d->yu1 = y;
1488    x += w;
1489    y += h;
1490    if (d->xu2 < x)
1491       d->xu2 = x;
1492    if (d->yu2 < y)
1493       d->yu2 = y;
1494
1495    dialog_update_pending = 1;
1496
1497 #if DEBUG_DIALOGS
1498    Eprintf("DialogDrawItems t=%d u=%d - %d,%d -> %d,%d\n", di->type, di->update,
1499            d->xu1, d->yu1, d->xu2, d->yu2);
1500 #endif
1501 }
1502
1503 static void
1504 DialogDrawItem(Dialog * d, DItem * di)
1505 {
1506    int                 state, x, w;
1507    EImageBorder       *pad;
1508
1509    if (!di->update && di->type != DITEM_TABLE)
1510       return;
1511
1512    if (di->x > d->xu2 || di->y > d->yu2 ||
1513        di->x + di->w <= d->xu1 || di->y + di->h <= d->yu1)
1514       goto done;
1515
1516 #if DEBUG_DIALOGS
1517    Eprintf("DialogDrawItem t=%d u=%d - %d,%d -> %d,%d\n", di->type, di->update,
1518            d->xu1, d->yu1, d->xu2, d->yu2);
1519 #endif
1520
1521    switch (di->type)
1522      {
1523      case DITEM_TABLE:
1524         {
1525            int                 i;
1526            DItem              *dii;
1527
1528            for (i = 0; i < di->item.table.num_items; i++)
1529              {
1530                 dii = di->item.table.items[i];
1531                 if (di->update)
1532                    dii->update = 1;
1533                 DialogDrawItem(d, dii);
1534              }
1535
1536 #if 0                           /* Debug */
1537            {
1538               XGCValues           gcv;
1539               GC                  gc;
1540
1541               pad = ImageclassGetPadding(d->iclass);
1542               gcv.subwindow_mode = IncludeInferiors;
1543               gc = EXCreateGC(WinGetPmap(d->win), GCSubwindowMode, &gcv);
1544               XSetForeground(disp, gc, Dpy.pixel_black);
1545               XDrawRectangle(disp, WinGetPmap(d->win), gc,
1546                              pad->left + di->x, pad->top + di->y, di->w, di->h);
1547               EXFreeGC(gc);
1548            }
1549 #endif
1550         }
1551         break;
1552
1553      case DITEM_SLIDER:
1554         if (di->item.slider.horizontal)
1555           {
1556              di->item.slider.knob_x = di->item.slider.base_x +
1557                 (((di->item.slider.base_w - di->item.slider.knob_w) *
1558                   (di->item.slider.val - di->item.slider.lower)) /
1559                  (di->item.slider.upper - di->item.slider.lower));
1560              di->item.slider.knob_y = di->item.slider.base_y +
1561                 ((di->item.slider.base_h - di->item.slider.knob_h) / 2);
1562           }
1563         else
1564           {
1565              di->item.slider.knob_y = di->item.slider.base_y +
1566                 (((di->item.slider.base_h - di->item.slider.knob_h) *
1567                   (di->item.slider.val - di->item.slider.lower)) /
1568                  (di->item.slider.upper - di->item.slider.lower));
1569              di->item.slider.knob_x = di->item.slider.base_x +
1570                 ((di->item.slider.base_w - di->item.slider.knob_w) / 2);
1571           }
1572         if (di->item.slider.knob_win)
1573            EMoveResizeWindow(di->item.slider.knob_win,
1574                              di->x + di->item.slider.knob_x,
1575                              di->y + di->item.slider.knob_y,
1576                              di->item.slider.knob_w, di->item.slider.knob_h);
1577         if (di->item.slider.base_win)
1578            ImageclassApply(di->item.slider.ic_base,
1579                            di->item.slider.base_win,
1580                            0, 0, STATE_NORMAL, ST_WIDGET);
1581         state = STATE_NORMAL;
1582         if ((di->hilited) && (di->clicked))
1583            state = STATE_CLICKED;
1584         else if ((di->hilited) && (!di->clicked))
1585            state = STATE_HILITED;
1586         else if (!(di->hilited) && (di->clicked))
1587            state = STATE_CLICKED;
1588         if (di->item.slider.knob_win)
1589            ImageclassApply(di->item.slider.ic_knob,
1590                            di->item.slider.knob_win, 0, 0, state, ST_WIDGET);
1591         break;
1592
1593      case DITEM_BUTTON:
1594         state = STATE_NORMAL;
1595         if ((di->hilited) && (di->clicked))
1596            state = STATE_CLICKED;
1597         else if ((di->hilited) && (!di->clicked))
1598            state = STATE_HILITED;
1599         else if (!(di->hilited) && (di->clicked))
1600            state = STATE_CLICKED;
1601         ITApply(di->win, di->iclass, NULL, state, 0, 0,
1602                 ST_WIDGET, di->tclass, NULL, di->text, 0);
1603         break;
1604
1605      case DITEM_AREA:
1606         if (!d->redraw)
1607            break;
1608         ImageclassApply(di->iclass, di->win, 0, 0, STATE_NORMAL, ST_DIALOG);
1609         if (di->item.area.init_func)
1610            di->item.area.init_func(di, 0, NULL);
1611         break;
1612
1613      case DITEM_SEPARATOR:
1614         if (!d->redraw)
1615            break;
1616         if (di->item.separator.horizontal)
1617            ImageclassApply(di->iclass, di->win, 0, 0, STATE_NORMAL, ST_WIDGET);
1618         else
1619            ImageclassApply(di->iclass, di->win, 0, 0, STATE_CLICKED, ST_WIDGET);
1620         break;
1621
1622      case DITEM_TEXT:
1623         state = STATE_NORMAL;
1624         x = di->x;
1625         w = di->w;
1626         goto draw_text;
1627
1628      case DITEM_CHECKBUTTON:
1629         state = STATE_NORMAL;
1630         if ((di->hilited) && (di->clicked))
1631            state = STATE_CLICKED;
1632         else if ((di->hilited) && (!di->clicked))
1633            state = STATE_HILITED;
1634         else if (!(di->hilited) && (di->clicked))
1635            state = STATE_CLICKED;
1636         ImageclassApply(di->iclass, di->item.check_button.check_win,
1637                         DialogItemCheckButtonGetState(di), 0, state, ST_WIDGET);
1638         if (!d->redraw &&
1639             (TextclassGetTextState(di->tclass, di->state, 0, 0) ==
1640              TextclassGetTextState(di->tclass, state, 0, 0)))
1641            break;
1642         pad = ImageclassGetPadding(di->iclass);
1643         x = di->x + di->item.check_button.check_orig_w + pad->left;
1644         w = di->w - di->item.check_button.check_orig_w - pad->left;
1645         goto draw_text;
1646
1647      case DITEM_RADIOBUTTON:
1648         state = STATE_NORMAL;
1649         if ((di->hilited) && (di->clicked))
1650            state = STATE_CLICKED;
1651         else if ((di->hilited) && (!di->clicked))
1652            state = STATE_HILITED;
1653         else if (!(di->hilited) && (di->clicked))
1654            state = STATE_CLICKED;
1655         ImageclassApply(di->iclass, di->item.radio_button.radio_win,
1656                         di->item.radio_button.onoff, 0, state, ST_WIDGET);
1657         if (!d->redraw &&
1658             (TextclassGetTextState(di->tclass, di->state, 0, 0) ==
1659              TextclassGetTextState(di->tclass, state, 0, 0)))
1660            break;
1661         pad = ImageclassGetPadding(di->iclass);
1662         x = di->x + di->item.radio_button.radio_orig_w + pad->left;
1663         w = di->w - di->item.radio_button.radio_orig_w - pad->left;
1664         goto draw_text;
1665
1666      default:
1667         break;
1668
1669       draw_text:
1670         di->state = state;
1671         if (!di->text || !di->tclass)
1672            break;
1673         if (!d->redraw || di->update)
1674            EXCopyArea(d->pmm_bg.pmap, WinGetPmap(d->win),
1675                       di->x, di->y, di->w, di->h, di->x, di->y);
1676         TextDraw(di->tclass, d->win, WinGetPmap(d->win), 0, 0, state, di->text,
1677                  x, di->y, w, 99999, 17, TextclassGetJustification(di->tclass));
1678         break;
1679      }
1680
1681  done:
1682    di->update = 0;
1683 }
1684
1685 static void
1686 DialogUpdate(Dialog * d)
1687 {
1688    do
1689      {
1690         d->update = 0;
1691         if (d->item)
1692            DialogDrawItem(d, d->item);
1693      }
1694    while (d->update);
1695    if (d->xu1 < d->xu2 && d->yu1 < d->yu2)
1696       EClearArea(d->win, d->xu1, d->yu1, d->xu2 - d->xu1, d->yu2 - d->yu1);
1697    d->xu1 = d->yu1 = 99999;
1698    d->xu2 = d->yu2 = 0;
1699 }
1700
1701 static void
1702 _DialogsCheckUpdate(void *data __UNUSED__)
1703 {
1704    Dialog             *d;
1705
1706    if (!dialog_update_pending)
1707       return;
1708    dialog_update_pending = 0;
1709
1710    ECORE_LIST_FOR_EACH(dialog_list, d)
1711    {
1712       if (d->update)
1713          DialogUpdate(d);
1714       d->redraw = 0;
1715    }
1716 }
1717
1718 void
1719 DialogsInit(void)
1720 {
1721    IdlerAdd(_DialogsCheckUpdate, NULL);
1722 }
1723
1724 static void
1725 DialogItemsRealize(Dialog * d)
1726 {
1727    EImageBorder       *pad;
1728
1729    if (!d->item)
1730       return;
1731
1732    DialogRealizeItem(d, d->item);
1733    DialogDrawItems(d, d->item, 0, 0, 99999, 99999);
1734    pad = ImageclassGetPadding(d->iclass);
1735    d->w = d->item->w + pad->left + pad->right;
1736    d->h = d->item->h + pad->top + pad->bottom;
1737    EResizeWindow(d->win, d->w, d->h);
1738 }
1739
1740 void
1741 DialogItemSetText(DItem * di, const char *text)
1742 {
1743    Efree(di->text);
1744    di->text = Estrdup(text);
1745 }
1746
1747 void
1748 DialogItemRadioButtonSetEventFunc(DItem * di, DialogItemCallbackFunc * func)
1749 {
1750    di->item.radio_button.event_func = func;
1751 }
1752
1753 void
1754 DialogItemRadioButtonSetFirst(DItem * di, DItem * first)
1755 {
1756    di->item.radio_button.first = first;
1757    if (di == first)
1758       return;
1759    while (first->item.radio_button.next)
1760       first = first->item.radio_button.next;
1761    first->item.radio_button.next = di;
1762 }
1763
1764 void
1765 DialogItemRadioButtonGroupSetValPtr(DItem * di, int *val_ptr)
1766 {
1767    while (di)
1768      {
1769         di->item.radio_button.val_ptr = val_ptr;
1770         if (*val_ptr == di->item.radio_button.val)
1771            di->item.radio_button.onoff = 1;
1772         di = di->item.radio_button.next;
1773      }
1774 }
1775
1776 void
1777 DialogItemRadioButtonGroupSetVal(DItem * di, int val)
1778 {
1779    di->item.radio_button.val = val;
1780 }
1781
1782 void
1783 DialogItemCheckButtonSetState(DItem * di, char onoff)
1784 {
1785    *(di->item.check_button.onoff_ptr) = onoff;
1786 }
1787
1788 void
1789 DialogItemCheckButtonSetPtr(DItem * di, char *onoff_ptr)
1790 {
1791    di->item.check_button.onoff_ptr = onoff_ptr;
1792 }
1793
1794 static int
1795 DialogItemCheckButtonGetState(DItem * di)
1796 {
1797    return *(di->item.check_button.onoff_ptr) ? 1 : 0;
1798 }
1799
1800 void
1801 DialogItemTableSetOptions(DItem * di, int num_columns, char border,
1802                           char homogenous_h, char homogenous_v)
1803 {
1804    di->item.table.num_columns = num_columns;
1805    di->item.table.border = border;
1806    di->item.table.homogenous_h = homogenous_h;
1807    di->item.table.homogenous_v = homogenous_v;
1808 }
1809
1810 void
1811 DialogItemSeparatorSetOrientation(DItem * di, char horizontal)
1812 {
1813    di->item.separator.horizontal = horizontal;
1814 }
1815
1816 void
1817 DialogItemImageSetFile(DItem * di, const char *image)
1818 {
1819    Efree(di->item.image.image);
1820    di->item.image.image = Estrdup(image);
1821    di->fill_h = 0;
1822    di->fill_v = 0;
1823 }
1824
1825 void
1826 DialogItemSliderSetVal(DItem * di, int val)
1827 {
1828    if (val < di->item.slider.lower)
1829       val = di->item.slider.lower;
1830    else if (val > di->item.slider.upper)
1831       val = di->item.slider.upper;
1832    di->item.slider.val = val;
1833    if (di->item.slider.val_ptr)
1834       *di->item.slider.val_ptr = val;
1835 }
1836
1837 void
1838 DialogItemSliderSetValPtr(DItem * di, int *val_ptr)
1839 {
1840    di->item.slider.val_ptr = val_ptr;
1841    DialogItemSliderSetVal(di, *val_ptr);
1842 }
1843
1844 void
1845 DialogItemSliderSetBounds(DItem * di, int lower, int upper)
1846 {
1847    if (lower < upper)
1848      {
1849         di->item.slider.lower = lower;
1850         di->item.slider.upper = upper;
1851      }
1852    else
1853      {
1854         di->item.slider.lower = upper;
1855         di->item.slider.upper = lower;
1856      }
1857    if (di->item.slider.upper <= di->item.slider.lower)
1858       di->item.slider.upper = di->item.slider.lower + 1;
1859 }
1860
1861 void
1862 DialogItemSliderSetUnits(DItem * di, int units)
1863 {
1864    di->item.slider.unit = units;
1865 }
1866
1867 void
1868 DialogItemSliderSetJump(DItem * di, int jump)
1869 {
1870    di->item.slider.jump = jump;
1871 }
1872
1873 void
1874 DialogItemSliderSetMinLength(DItem * di, int min)
1875 {
1876    di->item.slider.min_length = min;
1877 }
1878
1879 void
1880 DialogItemSliderSetOrientation(DItem * di, char horizontal)
1881 {
1882    di->item.slider.horizontal = horizontal;
1883 }
1884
1885 int
1886 DialogItemSliderGetVal(DItem * di)
1887 {
1888    return di->item.slider.val;
1889 }
1890
1891 void
1892 DialogItemSliderGetBounds(DItem * di, int *lower, int *upper)
1893 {
1894    if (lower)
1895       *lower = di->item.slider.lower;
1896    if (upper)
1897       *upper = di->item.slider.upper;
1898 }
1899
1900 void
1901 DialogItemAreaSetSize(DItem * di, int w, int h)
1902 {
1903    di->item.area.w = w;
1904    di->item.area.h = h;
1905 }
1906
1907 Win
1908 DialogItemAreaGetWindow(DItem * di)
1909 {
1910    return di->item.area.area_win;
1911 }
1912
1913 void
1914 DialogItemAreaGetSize(DItem * di, int *w, int *h)
1915 {
1916    *w = di->item.area.w;
1917    *h = di->item.area.h;
1918 }
1919
1920 void
1921 DialogItemAreaSetInitFunc(DItem * di, DialogItemCallbackFunc * func)
1922 {
1923    di->item.area.init_func = func;
1924 }
1925
1926 void
1927 DialogItemAreaSetEventFunc(DItem * di, DialogItemCallbackFunc * func)
1928 {
1929    di->item.area.event_func = func;
1930 }
1931
1932 void
1933 DialogItemTableEmpty(DItem * di)
1934 {
1935    int                 i;
1936
1937    if (di->type != DITEM_TABLE)
1938       return;
1939
1940    for (i = 0; i < di->item.table.num_items; i++)
1941       DialogItemDestroy(di->item.table.items[i], 1);
1942
1943    Efree(di->item.table.items);
1944    di->item.table.items = NULL;
1945    di->item.table.num_items = 0;
1946 }
1947
1948 static void
1949 DialogItemDestroy(DItem * di, int clean)
1950 {
1951    if (di->type == DITEM_TABLE)
1952       DialogItemTableEmpty(di);
1953
1954    Efree(di->text);
1955
1956    switch (di->type)
1957      {
1958      default:
1959         break;
1960      case DITEM_CHECKBUTTON:
1961         if (!clean)
1962            break;
1963         EDestroyWindow(di->item.check_button.check_win);
1964         break;
1965      case DITEM_IMAGE:
1966         Efree(di->item.image.image);
1967         break;
1968      case DITEM_RADIOBUTTON:
1969         if (!clean)
1970            break;
1971         EDestroyWindow(di->item.radio_button.radio_win);
1972         break;
1973      case DITEM_SLIDER:
1974         ImageclassFree(di->item.slider.ic_base);
1975         ImageclassFree(di->item.slider.ic_knob);
1976         if (!clean)
1977            break;
1978         EDestroyWindow(di->item.slider.base_win);
1979         EDestroyWindow(di->item.slider.knob_win);
1980         break;
1981      case DITEM_AREA:
1982         if (!clean)
1983            break;
1984         EDestroyWindow(di->item.area.area_win);
1985         break;
1986      }
1987
1988    if (clean && di->win)
1989       EDestroyWindow(di->win);
1990    ImageclassFree(di->iclass);
1991    TextclassFree(di->tclass);
1992
1993    Efree(di);
1994 }
1995
1996 /* Convenience callback to close dialog */
1997 void
1998 DialogCallbackClose(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1999 {
2000    DialogClose(d);
2001 }
2002
2003 /*
2004  * Predefined dialogs
2005  */
2006
2007 void
2008 DialogOK(const char *title, const char *fmt, ...)
2009 {
2010    char                text[10240];
2011    va_list             args;
2012
2013    va_start(args, fmt);
2014    Evsnprintf(text, sizeof(text), fmt, args);
2015    va_end(args);
2016
2017    DialogOKstr(title, text);
2018 }
2019
2020 void
2021 DialogOKstr(const char *title, const char *txt)
2022 {
2023    Dialog             *d;
2024    DItem              *table, *di;
2025
2026    d = DialogCreate("DIALOG");
2027
2028    table = DialogInitItem(d);
2029    DialogSetTitle(d, title);
2030
2031    di = DialogAddItem(table, DITEM_TEXT);
2032    DialogItemSetText(di, txt);
2033
2034    di = DialogItemAddButton(table, _("OK"), DialogCallbackClose, 0, 1,
2035                             DLG_BUTTON_OK);
2036    DialogItemSetFill(di, 0, 0);
2037
2038    DialogBindKey(d, "Return", DialogCallbackClose, 0, NULL);
2039    DialogBindKey(d, "Escape", DialogCallbackClose, 0, NULL);
2040
2041    DialogShow(d);
2042 }
2043
2044 /*
2045  * Dialog event handlers
2046  */
2047 static int
2048 _DlgPixToVal(const DItem * di, int dx, int sr)
2049 {
2050    int                 vr, value;
2051
2052    vr = di->item.slider.upper - di->item.slider.lower;
2053    dx = (int)(((float)dx / (sr * di->item.slider.unit)) * vr + .5);
2054    dx *= di->item.slider.unit;
2055    value = di->item.slider.lower + dx;
2056
2057    if (value < di->item.slider.lower)
2058       value = di->item.slider.lower;
2059    if (value > di->item.slider.upper)
2060       value = di->item.slider.upper;
2061
2062    return value;
2063 }
2064
2065 static void
2066 DialogEventKeyPress(Dialog * d, XEvent * ev)
2067 {
2068    int                 i;
2069
2070    for (i = 0; i < d->num_bindings; i++)
2071      {
2072         if (ev->xkey.keycode != d->keybindings[i].key)
2073            continue;
2074         d->keybindings[i].func(d, d->keybindings[i].val,
2075                                d->keybindings[i].data);
2076         break;
2077      }
2078 }
2079
2080 static void
2081 DialogHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
2082 {
2083    Dialog             *d = (Dialog *) prm;
2084
2085    switch (ev->type)
2086      {
2087      case KeyPress:
2088         DialogEventKeyPress(d, ev);
2089         break;
2090      }
2091
2092    if (d->close)
2093       _DialogClose(d);
2094 }
2095
2096 static void
2097 DItemEventMotion(Win win __UNUSED__, DItem * di, XEvent * ev)
2098 {
2099    switch (di->type)
2100      {
2101      case DITEM_AREA:
2102         if (di->item.area.event_func)
2103            di->item.area.event_func(di, 0, ev);
2104         break;
2105
2106      case DITEM_SLIDER:
2107         if (!di->item.slider.in_drag)
2108            break;
2109         if (ev->xmotion.window == WinGetXwin(di->item.slider.knob_win))
2110           {
2111              if (di->item.slider.horizontal)
2112                {
2113                   di->item.slider.val =
2114                      _DlgPixToVal(di,
2115                                   ev->xbutton.x + di->item.slider.knob_x -
2116                                   di->item.slider.knob_w / 2,
2117                                   di->item.slider.base_w -
2118                                   di->item.slider.knob_w);
2119                }
2120              else
2121                {
2122                   di->item.slider.val =
2123                      _DlgPixToVal(di,
2124                                   ev->xbutton.y + di->item.slider.knob_y -
2125                                   di->item.slider.knob_h / 2,
2126                                   di->item.slider.base_h -
2127                                   di->item.slider.knob_h);
2128                }
2129              if (di->item.slider.val_ptr)
2130                 *di->item.slider.val_ptr = di->item.slider.val;
2131              if (di->func)
2132                 (di->func) (di->dlg, di->val, di->data);
2133           }
2134
2135         DialogDrawItems(di->dlg, di, di->x, di->y, di->w, di->h);
2136         break;
2137      }
2138 }
2139
2140 static void
2141 DItemEventMouseDown(Win win, DItem * di, XEvent * ev)
2142 {
2143    int                 x, y, wheel_jump;
2144
2145    switch (di->type)
2146      {
2147      case DITEM_AREA:
2148         if (di->item.area.event_func)
2149            di->item.area.event_func(di, 0, ev);
2150         break;
2151
2152      case DITEM_SLIDER:
2153         if (ev->xbutton.window == WinGetXwin(di->item.slider.knob_win))
2154           {
2155              if (ev->xbutton.button >= 1 && ev->xbutton.button <= 3)
2156                {
2157                   di->item.slider.in_drag = 1;
2158                   break;
2159                }
2160           }
2161
2162         /* Coords -> item.slider.base_win */
2163         ETranslateCoordinates(win, di->item.slider.base_win,
2164                               ev->xbutton.x, ev->xbutton.y, &x, &y, NULL);
2165
2166         switch (ev->xbutton.button)
2167           {
2168           case 1:
2169           case 3:
2170              if (di->item.slider.horizontal)
2171                {
2172                   if (ev->xbutton.x >
2173                       (di->item.slider.knob_x + (di->item.slider.knob_w / 2)))
2174                      di->item.slider.val += di->item.slider.jump;
2175                   else
2176                      di->item.slider.val -= di->item.slider.jump;
2177                }
2178              else
2179                {
2180                   if (ev->xbutton.y >
2181                       (di->item.slider.knob_y + (di->item.slider.knob_h / 2)))
2182                      di->item.slider.val += di->item.slider.jump;
2183                   else
2184                      di->item.slider.val -= di->item.slider.jump;
2185                }
2186              break;
2187
2188           case 2:
2189              if (di->item.slider.horizontal)
2190                {
2191                   di->item.slider.val =
2192                      _DlgPixToVal(di,
2193                                   ev->xbutton.x - di->item.slider.knob_w / 2,
2194                                   di->item.slider.base_w -
2195                                   di->item.slider.knob_w);
2196                }
2197              else
2198                {
2199                   di->item.slider.val =
2200                      _DlgPixToVal(di,
2201                                   ev->xbutton.y - di->item.slider.knob_h / 2,
2202                                   di->item.slider.base_h -
2203                                   di->item.slider.knob_h);
2204                }
2205              break;
2206
2207           case 4:
2208           case 5:
2209              wheel_jump = di->item.slider.jump / 2;
2210              if (!wheel_jump)
2211                 wheel_jump++;
2212
2213              if (ev->xbutton.button == 5)
2214                {
2215                   di->item.slider.val -= wheel_jump;
2216                }
2217              else if (ev->xbutton.button == 4)
2218                {
2219                   di->item.slider.val += wheel_jump;
2220                }
2221              break;
2222           }
2223         if (di->item.slider.val < di->item.slider.lower)
2224            di->item.slider.val = di->item.slider.lower;
2225         if (di->item.slider.val > di->item.slider.upper)
2226            di->item.slider.val = di->item.slider.upper;
2227         if (di->item.slider.val_ptr)
2228            *di->item.slider.val_ptr = di->item.slider.val;
2229 #if 0                           /* Remove? */
2230         if (di->func)
2231            (di->func) (d, di->val, di->data);
2232 #endif
2233         break;
2234      }
2235
2236    di->clicked = 1;
2237
2238    DialogDrawItems(di->dlg, di, di->x, di->y, di->w, di->h);
2239 }
2240
2241 static void
2242 DItemEventMouseUp(Win win, DItem * di, XEvent * ev)
2243 {
2244    DItem              *dii;
2245
2246    if (ev->xbutton.window != Mode.events.last_bpress)
2247       return;
2248
2249    switch (di->type)
2250      {
2251      case DITEM_AREA:
2252         if (di->item.area.event_func)
2253            di->item.area.event_func(di, 0, ev);
2254         break;
2255
2256      case DITEM_CHECKBUTTON:
2257         DialogItemCheckButtonSetState(di, !DialogItemCheckButtonGetState(di));
2258         break;
2259
2260      case DITEM_RADIOBUTTON:
2261         dii = di->item.radio_button.first;
2262         while (dii)
2263           {
2264              if (dii->item.radio_button.onoff)
2265                {
2266                   dii->item.radio_button.onoff = 0;
2267                   DialogDrawItems(di->dlg, dii, dii->x, dii->y, dii->w, dii->h);
2268                }
2269              dii = dii->item.radio_button.next;
2270           }
2271         di->item.radio_button.onoff = 1;
2272         if (di->item.radio_button.val_ptr)
2273            *di->item.radio_button.val_ptr = di->item.radio_button.val;
2274         break;
2275
2276      case DITEM_SLIDER:
2277         if (win == di->item.slider.knob_win)
2278            di->item.slider.in_drag = 0;
2279         break;
2280      }
2281
2282    if (di->hilited && di->clicked)
2283      {
2284         if (di->func)
2285            di->func(di->dlg, di->val, di->data);
2286
2287         if (di->do_close)
2288            di->dlg->close = 1;
2289      }
2290
2291    di->clicked = 0;
2292
2293    DialogDrawItems(di->dlg, di, di->x, di->y, di->w, di->h);
2294 }
2295
2296 static void
2297 DItemEventMouseIn(Win win __UNUSED__, DItem * di, XEvent * ev)
2298 {
2299    switch (di->type)
2300      {
2301      case DITEM_AREA:
2302         if (di->item.area.event_func)
2303            di->item.area.event_func(di, 0, ev);
2304         break;
2305
2306      case DITEM_RADIOBUTTON:
2307         if (di->item.radio_button.event_func)
2308            di->item.radio_button.event_func(di, di->item.radio_button.val, ev);
2309         break;
2310      }
2311
2312    di->hilited = 1;
2313
2314    DialogDrawItems(di->dlg, di, di->x, di->y, di->w, di->h);
2315 }
2316
2317 static void
2318 DItemEventMouseOut(Win win __UNUSED__, DItem * di, XEvent * ev)
2319 {
2320    switch (di->type)
2321      {
2322      case DITEM_AREA:
2323         if (di->item.area.event_func)
2324            di->item.area.event_func(di, 0, ev);
2325         break;
2326
2327      case DITEM_RADIOBUTTON:
2328         if (di->item.radio_button.event_func)
2329            di->item.radio_button.event_func(di, di->item.radio_button.val,
2330                                             NULL);
2331         break;
2332      }
2333
2334    if (!di->clicked)
2335       di->hilited = 0;
2336
2337    DialogDrawItems(di->dlg, di, di->x, di->y, di->w, di->h);
2338 }
2339
2340 static void
2341 DItemHandleEvents(Win win, XEvent * ev, void *prm)
2342 {
2343    DItem              *di = (DItem *) prm;
2344
2345    switch (ev->type)
2346      {
2347      case ButtonPress:
2348         DItemEventMouseDown(win, di, ev);
2349         break;
2350      case ButtonRelease:
2351         DItemEventMouseUp(win, di, ev);
2352         break;
2353      case MotionNotify:
2354         DItemEventMotion(win, di, ev);
2355         break;
2356      case EnterNotify:
2357         DItemEventMouseIn(win, di, ev);
2358         break;
2359      case LeaveNotify:
2360         DItemEventMouseOut(win, di, ev);
2361         break;
2362      }
2363
2364    if (di->dlg->close)
2365       _DialogClose(di->dlg);
2366 }
2367
2368 /*
2369  * Finders
2370  */
2371
2372 static EWin        *
2373 FindEwinByDialog(Dialog * d)
2374 {
2375    EWin               *const *ewins;
2376    int                 i, num;
2377
2378    ewins = EwinListGetAll(&num);
2379    for (i = 0; i < num; i++)
2380      {
2381         if ((Dialog *) (ewins[i]->data) == d)
2382            return ewins[i];
2383      }
2384
2385    return NULL;
2386 }
2387
2388 static int
2389 FindADialog(void)
2390 {
2391    EWin               *const *ewins;
2392    int                 i, num, n;
2393
2394    ewins = EwinListGetAll(&num);
2395    for (i = n = 0; i < num; i++)
2396      {
2397         if (ewins[i]->type == EWIN_TYPE_DIALOG)
2398            n++;
2399      }
2400
2401    return n;
2402 }
2403
2404 #endif /* ENABLE_DIALOGS */