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