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