1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 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 "config.h"
25 
26 #include <X11/Xlib.h>
27 
28 #include "E.h"
29 #include "aclass.h"
30 #include "buttons.h"
31 #include "cursors.h"
32 #include "desktops.h"
33 #include "eimage.h"
34 #include "emodule.h"
35 #include "file.h"
36 #include "grabs.h"
37 #include "iclass.h"
38 #include "list.h"
39 #include "tclass.h"
40 #include "tooltips.h"
41 #include "xwin.h"
42 
43 #define BUTTON_EVENT_MASK \
44   (KeyPressMask | KeyReleaseMask | \
45    ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | \
46    PointerMotionMask)
47 
48 typedef struct {
49    int                 width_min, width_max;
50    int                 height_min, height_max;
51    int                 xorigin, yorigin;
52    int                 xabs, xrel;
53    int                 yabs, yrel;
54    int                 xsizerel, xsizeabs;
55    int                 ysizerel, ysizeabs;
56    char                size_from_image;
57 } BGeometry;
58 
59 struct _button {
60    dlist_t             list;
61    EObj                o;
62    BGeometry           geom;
63    ImageClass         *iclass;
64    ActionClass        *aclass;
65    TextClass          *tclass;
66    char               *label;
67    int                 id;
68    int                 flags;
69    char                internal;
70    char                default_show;
71    char                state;
72    char                left;
73    ButtonCbFunc       *cb_func;
74    void               *cb_prm;
75 
76 #if 0				/* Unused */
77    EX_Window           inside_win;
78    EX_Window           event_win;
79 #endif
80    unsigned int        ref_count;
81 };
82 
83 static              LIST_HEAD(button_list);
84 
85 static struct {
86    Button             *button;
87    char                loading_user;
88    char                move_pending;
89    char                action_inhibit;
90    int                 start_x, start_y;
91 } Mode_buttons;
92 
93 static void         ButtonHandleEvents(Win win, XEvent * ev, void *btn);
94 
95 #if 0				/* Unused */
96 void
97 ButtonIncRefcount(Button * b)
98 {
99    b->ref_count++;
100 }
101 
102 void
103 ButtonDecRefcount(Button * b)
104 {
105    b->ref_count--;
106 }
107 #endif
108 
109 static int
ButtonIsFixed(const Button * b)110 ButtonIsFixed(const Button * b)
111 {
112    return b->flags & FLAG_FIXED;
113 }
114 
115 static int
ButtonIsInternal(const Button * b)116 ButtonIsInternal(const Button * b)
117 {
118    return b->internal;
119 }
120 
121 Button             *
ButtonCreate(const char * name,int id,const char * iclass,const char * aclass,const char * tclass,const char * label,int ontop,int flags,int minw,int maxw,int minh,int maxh,int xo,int yo,int xa,int xr,int ya,int yr,int xsr,int xsa,int ysr,int ysa,char simg,int desk,char sticky)122 ButtonCreate(const char *name, int id, const char *iclass,
123 	     const char *aclass, const char *tclass, const char *label,
124 	     int ontop, int flags, int minw, int maxw, int minh, int maxh,
125 	     int xo, int yo, int xa, int xr, int ya, int yr, int xsr, int xsa,
126 	     int ysr, int ysa, char simg, int desk, char sticky)
127 {
128    Button             *b;
129 
130    if (desk < 0 || desk >= (int)DesksGetNumber())
131       return NULL;
132 
133    if (sticky && ontop == 1)
134       desk = 0;
135 
136    b = ECALLOC(Button, 1);
137    if (!b)
138       return b;
139 
140    LIST_APPEND(Button, &button_list, b);
141 
142    b->id = id;
143 
144    if (label && *label)
145       b->label = Estrdup(label);
146 
147    b->iclass = ImageclassAlloc(iclass, 1);
148    b->aclass = ActionclassAlloc(aclass);
149    if (b->label)
150       b->tclass = TextclassAlloc(tclass, 1);
151 
152    b->flags = flags;
153    b->geom.width_min = minw;
154    b->geom.width_max = maxw;
155    b->geom.height_min = minh;
156    b->geom.height_max = maxh;
157    b->geom.xorigin = xo;
158    b->geom.yorigin = yo;
159    b->geom.xabs = xa;
160    b->geom.xrel = xr;
161    b->geom.yabs = ya;
162    b->geom.yrel = yr;
163    b->geom.xsizeabs = xsa;
164    b->geom.xsizerel = xsr;
165    b->geom.ysizeabs = ysa;
166    b->geom.ysizerel = ysr;
167    b->geom.size_from_image = simg;
168    b->default_show = 1;
169 
170    EoSetSticky(b, sticky);
171    EoSetDesk(b, DeskGet(desk));
172    EoInit(b, EOBJ_TYPE_BUTTON, NoXID, -100, -100, 50, 50, 0, name);
173    EoSetLayer(b, ontop);
174    EoSetFade(b, 1);
175 
176    ESelectInput(EoGetWin(b), BUTTON_EVENT_MASK);
177    EventCallbackRegister(EoGetWin(b), ButtonHandleEvents, b);
178 
179    return b;
180 }
181 
182 void
ButtonDestroy(Button * b)183 ButtonDestroy(Button * b)
184 {
185    if (!b)
186       return;
187 
188    if (b->ref_count > 0)
189      {
190 	DialogOK("Button Error!", _("%u references remain"), b->ref_count);
191 	return;
192      }
193 
194    LIST_REMOVE(Button, &button_list, b);
195 
196    EoFini(b);
197 
198    ImageclassFree(b->iclass);
199    ActionclassFree(b->aclass);
200    TextclassFree(b->tclass);
201    Efree(b->label);
202 
203    Efree(b);
204 }
205 
206 static int
_ButtonMatchName(const void * data,const void * match)207 _ButtonMatchName(const void *data, const void *match)
208 {
209    return strcmp(EoGetName((const Button *)data), (const char *)match);
210 }
211 
212 Button             *
ButtonFind(const char * name)213 ButtonFind(const char *name)
214 {
215    return LIST_FIND(Button, &button_list, _ButtonMatchName, name);
216 }
217 
218 static void
ButtonCalc(Button * b)219 ButtonCalc(Button * b)
220 {
221    int                 w, h, x, y, xo, yo;
222    EImage             *im;
223 
224    w = 32;
225    h = 32;
226    if (b->geom.size_from_image)
227      {
228 	im = ImageclassGetImage(b->iclass, 0, 0, 0);
229 	if (im)
230 	  {
231 	     EImageGetSize(im, &w, &h);
232 	     EImageFree(im);
233 	  }
234 	else
235 	  {
236 	     if (!b->iclass)
237 		b->iclass = ImageclassAlloc(NULL, 1);
238 	     w = 32;
239 	     h = 32;
240 	  }
241      }
242    else
243      {
244 	w = ((b->geom.xsizerel * WinGetW(VROOT)) >> 10) + b->geom.xsizeabs;
245 	h = ((b->geom.ysizerel * WinGetH(VROOT)) >> 10) + b->geom.ysizeabs;
246      }
247    if (w > b->geom.width_max)
248       w = b->geom.width_max;
249    else if (w < b->geom.width_min)
250       w = b->geom.width_min;
251    if (h > b->geom.height_max)
252       h = b->geom.height_max;
253    else if (h < b->geom.height_min)
254       h = b->geom.height_min;
255    xo = (w * b->geom.xorigin) >> 10;
256    yo = (h * b->geom.yorigin) >> 10;
257    x = ((b->geom.xrel * WinGetW(VROOT)) >> 10) + b->geom.xabs - xo;
258    y = ((b->geom.yrel * WinGetH(VROOT)) >> 10) + b->geom.yabs - yo;
259 
260    EoMoveResize(b, x, y, w, h);
261 }
262 
263 static void
ButtonDraw(Button * b)264 ButtonDraw(Button * b)
265 {
266    ITApply(EoGetWin(b), b->iclass, NULL,
267 	   b->state, 0, 0, b->tclass, NULL, b->label, 0);
268    EoShapeUpdate(b, 0);
269 }
270 
271 #if 0				/* Unused */
272 void
273 ButtonDrawWithState(Button * b, int state)
274 {
275    b->state = state;
276    ButtonDraw(b);
277 }
278 #endif
279 
280 void
ButtonShow(Button * b)281 ButtonShow(Button * b)
282 {
283    ButtonCalc(b);
284    ButtonDraw(b);
285    EoMap(b, 0);
286 }
287 
288 EObj               *
ButtonSwallowInto(Button * b,EObj * eo)289 ButtonSwallowInto(Button * b, EObj * eo)
290 {
291    b->internal = 1;
292    b->default_show = 0;
293    b->flags |= FLAG_FIXED;
294    b->ref_count++;
295    EobjReparent(EoObj(b), eo, 0, 0);
296    ButtonCalc(b);
297    ButtonDraw(b);
298    EMapWindow(EoGetWin(b));
299    return EoObj(b);
300 }
301 
302 void
ButtonSetCallback(Button * b,ButtonCbFunc * func,void * prm)303 ButtonSetCallback(Button * b, ButtonCbFunc * func, void *prm)
304 {
305    b->cb_prm = prm;
306    b->cb_func = func;
307 }
308 
309 static void
ButtonMoveToDesktop(Button * b,Desk * dsk)310 ButtonMoveToDesktop(Button * b, Desk * dsk)
311 {
312    if (EoIsSticky(b) && EoGetLayer(b) == 1)
313       dsk = DeskGet(0);
314 
315    if (!dsk)
316       return;
317 
318    if (EoGetDesk(b) != dsk)
319       EoReparent(b, EoObj(dsk), EoGetX(b), EoGetY(b));
320 }
321 
322 void
ButtonHide(Button * b)323 ButtonHide(Button * b)
324 {
325    EoUnmap(b);
326 }
327 
328 static void
ButtonToggle(Button * b)329 ButtonToggle(Button * b)
330 {
331    if (b->internal)
332       return;
333 
334    if (EoIsShown(b))
335       ButtonHide(b);
336    else
337       ButtonShow(b);
338 }
339 
340 void
ButtonMoveToCoord(Button * b,int x,int y)341 ButtonMoveToCoord(Button * b, int x, int y)
342 {
343    int                 rx, ry, relx, rely, absx, absy;
344 
345    if (ButtonIsFixed(b))
346       return;
347 
348    if ((x + (EoGetW(b) >> 1)) < (WinGetW(VROOT) / 3))
349       relx = 0;
350    else if ((x + (EoGetW(b) >> 1)) > ((WinGetW(VROOT) * 2) / 3))
351       relx = 1024;
352    else
353       relx = 512;
354    rx = (relx * WinGetW(VROOT)) >> 10;
355    absx = x - rx;
356    if ((y + (EoGetH(b) >> 1)) < (WinGetH(VROOT) / 3))
357       rely = 0;
358    else if ((y + (EoGetH(b) >> 1)) > ((WinGetH(VROOT) * 2) / 3))
359       rely = 1024;
360    else
361       rely = 512;
362    ry = (rely * WinGetH(VROOT)) >> 10;
363    absy = y - ry;
364    if (!(b->flags & FLAG_FIXED_HORIZ))
365      {
366 	b->geom.xorigin = 0;
367 	b->geom.xabs = absx;
368 	b->geom.xrel = relx;
369      }
370    if (!(b->flags & FLAG_FIXED_VERT))
371      {
372 	b->geom.yorigin = 0;
373 	b->geom.yabs = absy;
374 	b->geom.yrel = rely;
375      }
376 
377    ButtonCalc(b);
378 }
379 
380 void
ButtonMoveRelative(Button * b,int dx,int dy)381 ButtonMoveRelative(Button * b, int dx, int dy)
382 {
383    ButtonMoveToCoord(b, EoGetX(b) + dx, EoGetY(b) + dy);
384 }
385 
386 int
ButtonDoShowDefault(const Button * b)387 ButtonDoShowDefault(const Button * b)
388 {
389    return !b->internal && b->default_show;
390 }
391 
392 #if 0				/* Unused */
393 int
394 ButtonEmbedWindow(Button * b, EX_Window WindowToEmbed)
395 {
396 
397    int                 w, h;
398 
399    EReparentWindow(WindowToEmbed, EoGetWin(b), 0, 0);
400    b->inside_win = WindowToEmbed;
401    EGetGeometry(WindowToEmbed, NULL, NULL, NULL, &w, &h, NULL, NULL);
402    EMoveWindow(b->inside_win, (EoGetW(b) - w) >> 1, (EoGetH(b) - h) >> 1);
403    b->event_win = ECreateEventWindow(EoGetWin(b), 0, 0, w, h);
404    EventCallbackRegister(b->event_win, ButtonHandleEvents, b);
405 
406    ESelectInput(b->event_win,
407 		ButtonPressMask | ButtonReleaseMask | EnterWindowMask |
408 		LeaveWindowMask | ButtonMotionMask);
409 
410    EMoveWindow(b->event_win, (EoGetW(b) - w) >> 1, (EoGetH(b) - h) >> 1);
411    EMapRaised(b->event_win);
412 
413    return 0;
414 }
415 #endif
416 
417 static void
ButtonDragStart(Button * b)418 ButtonDragStart(Button * b)
419 {
420    if (ButtonIsFixed(b))
421       return;
422 
423    GrabPointerSet(EoGetWin(b), ECSR_GRAB, 0);
424    Mode.mode = MODE_BUTTONDRAG;
425    Mode_buttons.move_pending = 1;
426    Mode_buttons.start_x = Mode.events.cx;
427    Mode_buttons.start_y = Mode.events.cy;
428 }
429 
430 static void
ButtonDragEnd(Button * b)431 ButtonDragEnd(Button * b)
432 {
433    Desk               *dsk;
434 
435    Mode.mode = MODE_NONE;
436 
437    if (!Mode_buttons.move_pending)
438      {
439 	dsk = DesktopAt(Mode.events.mx, Mode.events.my);
440 	ButtonMoveToDesktop(b, dsk);
441 	dsk = EoGetDesk(b);
442 	ButtonMoveRelative(b, -EoGetX(dsk), -EoGetY(dsk));
443      }
444    else
445       Mode_buttons.move_pending = 0;
446 
447    autosave();
448 }
449 
450 void
ButtonsForeach(int id,Desk * dsk,void (* func)(Button * b))451 ButtonsForeach(int id, Desk * dsk, void (*func)(Button * b))
452 {
453    Button             *b, *tmp;
454 
455    LIST_FOR_EACH_SAFE(Button, &button_list, b, tmp)
456    {
457       if (id >= 0 && id != b->id)
458 	 continue;
459       if (dsk && dsk != EoGetDesk(b))
460 	 continue;
461       func(b);
462    }
463 }
464 
465 void
ButtonsMoveStickyToDesk(Desk * dsk)466 ButtonsMoveStickyToDesk(Desk * dsk)
467 {
468    Button             *b;
469 
470    LIST_FOR_EACH(Button, &button_list, b)
471    {
472       if (!EoIsSticky(b) || ButtonIsInternal(b))
473 	 continue;
474 
475       ButtonMoveToDesktop(b, dsk);
476    }
477 }
478 
479 /*
480  * Button event handlers
481  */
482 
483 static void
ButtonDoAction(Button * b,XEvent * ev)484 ButtonDoAction(Button * b, XEvent * ev)
485 {
486    if (b->cb_func)
487       b->cb_func(b->cb_prm, ev, b->aclass);
488    else
489       ActionclassEvent(b->aclass, ev, NULL);
490 }
491 
492 static void
ButtonEventMouseDown(Button * b,XEvent * ev)493 ButtonEventMouseDown(Button * b, XEvent * ev)
494 {
495    Mode_buttons.button = b;
496 
497    GrabPointerSet(EoGetWin(b), ECSR_GRAB, 0);
498 
499 #if 0				/* Unused */
500    if (b->inside_win)
501      {
502 	EX_Window           win = ev->xbutton.window;
503 
504 	ev->xbutton.window = b->inside_win;
505 	EXSendEvent(b->inside_win, ButtonPressMask, ev);
506 	ev->xbutton.window = win;
507      }
508 #endif
509 
510    b->state = STATE_CLICKED;
511    ButtonDraw(b);
512 
513    if (!ButtonIsInternal(b))
514      {
515 	ActionClass        *ac;
516 
517 	ac = ActionclassFind("ACTION_BUTTON_DRAG");
518 	if (ac && !Mode_buttons.action_inhibit)
519 	   ActionclassEvent(ac, ev, NULL);
520      }
521 
522    if (b->aclass && !Mode_buttons.action_inhibit)
523       ButtonDoAction(b, ev);
524 }
525 
526 static void
ButtonEventMouseUp(Button * b,XEvent * ev)527 ButtonEventMouseUp(Button * b, XEvent * ev)
528 {
529 #if 0				/* Unused */
530    if (b->inside_win && !Mode_buttons.action_inhibit)
531      {
532 	EX_Window           win = ev->xbutton.window;
533 
534 	ev->xbutton.window = b->inside_win;
535 	EXSendEvent(b->inside_win, ButtonReleaseMask, ev);
536 	ev->xbutton.window = win;
537      }
538 #endif
539 
540    if ((b->state == STATE_CLICKED) && (!b->left))
541       b->state = STATE_HILITED;
542    else
543       b->state = STATE_NORMAL;
544    ButtonDraw(b);
545 
546    GrabPointerRelease();
547 
548    b->left = 0;
549 
550    if (Mode.mode == MODE_BUTTONDRAG)
551       ButtonDragEnd(Mode_buttons.button);
552    Mode_buttons.button = NULL;
553 
554    if (b->aclass && !b->left && !Mode_buttons.action_inhibit)
555       ButtonDoAction(b, ev);
556    Mode_buttons.action_inhibit = 0;
557 }
558 
559 static void
ButtonEventMotion(Button * b,XEvent * ev __UNUSED__)560 ButtonEventMotion(Button * b, XEvent * ev __UNUSED__)
561 {
562    int                 dx, dy;
563 
564    if (Mode.mode != MODE_BUTTONDRAG)
565       return;
566 
567    dx = Mode.events.mx - Mode.events.px;
568    dy = Mode.events.my - Mode.events.py;
569 
570    if (Mode_buttons.move_pending)
571      {
572 	int                 x, y;
573 
574 	x = Mode.events.mx - Mode_buttons.start_x;
575 	y = Mode.events.my - Mode_buttons.start_y;
576 	if (x < 0)
577 	   x = -x;
578 	if (y < 0)
579 	   y = -y;
580 	if ((x > Conf.buttons.move_resistance) ||
581 	    (y > Conf.buttons.move_resistance))
582 	   Mode_buttons.move_pending = 0;
583 	Mode_buttons.action_inhibit = 1;
584      }
585    if (!Mode_buttons.move_pending)
586       ButtonMoveRelative(b, dx, dy);
587 }
588 
589 static void
ButtonEventMouseIn(Button * b,XEvent * ev)590 ButtonEventMouseIn(Button * b, XEvent * ev)
591 {
592    if (b->state == STATE_CLICKED)
593       b->left = 0;
594    else
595      {
596 	b->state = STATE_HILITED;
597 	ButtonDraw(b);
598 	if (b->aclass && !Mode_buttons.action_inhibit)
599 	   ActionclassEvent(b->aclass, ev, NULL);
600      }
601 }
602 
603 static void
ButtonEventMouseOut(Button * b,XEvent * ev)604 ButtonEventMouseOut(Button * b, XEvent * ev)
605 {
606    if (b->state == STATE_CLICKED)
607       b->left = 1;
608    else
609      {
610 	b->state = STATE_NORMAL;
611 	ButtonDraw(b);
612 	if (b->aclass && !Mode_buttons.action_inhibit)
613 	   ActionclassEvent(b->aclass, ev, NULL);
614      }
615 }
616 
617 static ActionClass *
ButtonGetAclass(void * data)618 ButtonGetAclass(void *data)
619 {
620    Button             *b = (Button *) data;
621 
622    /* Validate button */
623    if (!LIST_CHECK(Button, &button_list, b))
624       return NULL;
625 
626    return b->aclass;
627 }
628 
629 static void
ButtonHandleEvents(Win win __UNUSED__,XEvent * ev,void * prm)630 ButtonHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
631 {
632    Button             *b = (Button *) prm;
633 
634    switch (ev->type)
635      {
636      case ButtonPress:
637 	ButtonEventMouseDown(b, ev);
638 	break;
639      case ButtonRelease:
640 	ButtonEventMouseUp(b, ev);
641 	break;
642      case MotionNotify:
643 	ButtonEventMotion(b, ev);
644 	if (b->aclass)
645 	   TooltipsSetPending(0, ButtonGetAclass, b);
646 	break;
647      case EnterNotify:
648 	ButtonEventMouseIn(b, ev);
649 	break;
650      case LeaveNotify:
651 	ButtonEventMouseOut(b, ev);
652 	break;
653      }
654 
655    if (b->cb_func)
656       b->cb_func(b->cb_prm, ev, NULL);
657 }
658 
659 /*
660  * Configuration load/save
661  */
662 #include "conf.h"
663 
664 int
ButtonsConfigLoad(FILE * fs)665 ButtonsConfigLoad(FILE * fs)
666 {
667    int                 err = 0;
668    char                s[FILEPATH_LEN_MAX];
669    char                s2[FILEPATH_LEN_MAX];
670    char               *p2;
671    int                 i1, i2;
672    char                name[64], label[64];
673    char                iclass[64], aclass[64], tclass[64];
674    Button             *bt = NULL;
675    Button             *pbt = NULL;
676    int                 ontop = 0;
677    int                 flags = 0, minw = 1, maxw = 99999, minh = 1;
678    int                 maxh = 99999, xo = 0, yo = 0, xa = 0;
679    int                 xr = 0, ya = 0, yr = 0;
680    int                 xsr = 0, xsa = 0, ysr = 0, ysa = 0;
681    char                simg = 0;
682    int                 desk = 0;
683    char                sticky = 0;
684    char                show = 1;
685    char                internal = 0;
686 
687    name[0] = label[0] = '\0';
688    iclass[0] = aclass[0] = tclass[0] = '\0';
689 
690    while (GetLine(s, sizeof(s), fs))
691      {
692 	i1 = ConfigParseline1(s, s2, &p2, NULL);
693 	i2 = atoi(s2);
694 	switch (i1)
695 	  {
696 	  case CONFIG_CLOSE:
697 	     if (!pbt && !Mode_buttons.loading_user)
698 	       {
699 		  bt = ButtonCreate(name, 0, iclass, aclass, tclass, label,
700 				    ontop, flags, minw, maxw, minh, maxh,
701 				    xo, yo, xa, xr, ya, yr, xsr, xsa, ysr, ysa,
702 				    simg, desk, sticky);
703 		  if (bt)
704 		    {
705 		       bt->default_show = show;
706 		       bt->internal = internal;
707 		    }
708 	       }
709 	     else if (pbt)
710 	       {
711 		  if (label[0])
712 		     EFREE_DUP(pbt->label, label);
713 		  EoSetLayer(pbt, ontop);
714 		  EoSetSticky(pbt, sticky);
715 		  ButtonMoveToDesktop(pbt, DeskGet(desk));
716 		  if (iclass[0])
717 		     pbt->iclass = ImageclassFind(iclass, 1);
718 		  if (aclass[0])
719 		     pbt->aclass = ActionclassFind(aclass);
720 		  if (tclass[0])
721 		     pbt->tclass = TextclassFind(tclass, 1);
722 		  pbt->flags = flags;
723 		  pbt->internal = internal;
724 		  pbt->default_show = show;
725 		  pbt->geom.width_min = minw;
726 		  pbt->geom.width_max = maxw;
727 		  pbt->geom.height_min = minh;
728 		  pbt->geom.height_max = maxh;
729 		  pbt->geom.xorigin = xo;
730 		  pbt->geom.yorigin = yo;
731 		  pbt->geom.xabs = xa;
732 		  pbt->geom.xrel = xr;
733 		  pbt->geom.yabs = ya;
734 		  pbt->geom.yrel = yr;
735 		  pbt->geom.xsizerel = xsr;
736 		  pbt->geom.xsizeabs = xsa;
737 		  pbt->geom.ysizerel = ysr;
738 		  pbt->geom.ysizeabs = ysa;
739 		  pbt->geom.size_from_image = simg;
740 	       }
741 	     goto done;
742 	  case CONFIG_CLASSNAME:
743 	     STRCPY(name, s2);
744 	     pbt = ButtonFind(name);
745 	     break;
746 	  case BUTTON_LABEL:
747 	     STRCPY(label, s2);
748 	     break;
749 	  case CONFIG_IMAGECLASS:
750 	     STRCPY(iclass, s2);
751 	     break;
752 	  case CONFIG_ACTIONCLASS:
753 	     STRCPY(aclass, s2);
754 	     break;
755 	  case CONFIG_TEXT:
756 	     STRCPY(tclass, s2);
757 	     break;
758 	  case BORDERPART_ONTOP:
759 	     ontop = i2;
760 	     break;
761 	  case BORDERPART_WMIN:
762 	     minw = i2;
763 	     break;
764 	  case BORDERPART_WMAX:
765 	     maxw = i2;
766 	     break;
767 	  case BORDERPART_HMIN:
768 	     minh = i2;
769 	     break;
770 	  case BORDERPART_FLAGS:
771 	     flags = i2;
772 	     break;
773 	  case BORDERPART_HMAX:
774 	     maxh = i2;
775 	     break;
776 	  case BUTTON_XO:
777 	     xo = i2;
778 	     break;
779 	  case BUTTON_YO:
780 	     yo = i2;
781 	     break;
782 	  case BUTTON_XA:
783 	     xa = i2;
784 	     break;
785 	  case BUTTON_XR:
786 	     xr = i2;
787 	     break;
788 	  case BUTTON_YA:
789 	     ya = i2;
790 	     break;
791 	  case BUTTON_YR:
792 	     yr = i2;
793 	     break;
794 	  case BUTTON_XSR:
795 	     xsr = i2;
796 	     break;
797 	  case BUTTON_XSA:
798 	     xsa = i2;
799 	     break;
800 	  case BUTTON_YSR:
801 	     ysr = i2;
802 	     break;
803 	  case BUTTON_YSA:
804 	     ysa = i2;
805 	     break;
806 	  case BUTTON_SIMG:
807 	     simg = i2;
808 	     break;
809 	  case BUTTON_DESK:
810 	     desk = i2;
811 	     break;
812 	  case BUTTON_STICKY:
813 	     sticky = i2;
814 	     break;
815 	  case BUTTON_INTERNAL:
816 	     internal = i2;
817 	     break;
818 	  case BUTTON_SHOW:
819 	     show = i2;
820 	     break;
821 	  default:
822 	     break;
823 	  }
824      }
825    err = -1;
826 
827  done:
828    return err;
829 }
830 
831 static void
ButtonsConfigLoadUser(void)832 ButtonsConfigLoadUser(void)
833 {
834    char                s[4096];
835 
836    Esnprintf(s, sizeof(s), "%s.buttons", EGetSavePrefix());
837 
838    Mode_buttons.loading_user = 1;
839    ConfigFileLoad(s, NULL, ConfigFileRead, 0);
840    Mode_buttons.loading_user = 0;
841 }
842 
843 static void
ButtonsConfigSave(void)844 ButtonsConfigSave(void)
845 {
846    char                s[FILEPATH_LEN_MAX], st[FILEPATH_LEN_MAX];
847    FILE               *fs;
848    Button             *b;
849    int                 flags;
850 
851    if (LIST_IS_EMPTY(&button_list))
852       return;
853 
854    Etmp(st);
855    fs = fopen(st, "w");
856    if (!fs)
857       return;
858 
859    LIST_FOR_EACH(Button, &button_list, b)
860    {
861       if (b->id != 0 || b->internal)
862 	 continue;
863 
864       fprintf(fs, "4 999\n");
865       fprintf(fs, "100 %s\n", EoGetName(b));
866 #if 0				/* Remove? */
867       if (b->iclass)
868 	 fprintf(fs, "12 %s\n", ImageclassGetName(b->iclass));
869       if (b->aclass)
870 	 fprintf(fs, "11 %s\n", ActionclassGetName(b->aclass));
871       if (EoGetLayer(b) >= 0)
872 	 fprintf(fs, "453 %i\n", EoGetLayer(b));
873 #endif
874       fprintf(fs, "456 %i\n", b->geom.width_min);
875       fprintf(fs, "457 %i\n", b->geom.width_max);
876       fprintf(fs, "468 %i\n", b->geom.height_min);
877       fprintf(fs, "469 %i\n", b->geom.height_max);
878       fprintf(fs, "528 %i\n", b->geom.xorigin);
879       fprintf(fs, "529 %i\n", b->geom.yorigin);
880       fprintf(fs, "530 %i\n", b->geom.xabs);
881       fprintf(fs, "531 %i\n", b->geom.xrel);
882       fprintf(fs, "532 %i\n", b->geom.yabs);
883       fprintf(fs, "533 %i\n", b->geom.yrel);
884       fprintf(fs, "534 %i\n", b->geom.xsizerel);
885       fprintf(fs, "535 %i\n", b->geom.xsizeabs);
886       fprintf(fs, "536 %i\n", b->geom.ysizerel);
887       fprintf(fs, "537 %i\n", b->geom.ysizeabs);
888       fprintf(fs, "538 %i\n", b->geom.size_from_image);
889       fprintf(fs, "539 %u\n", EoGetDeskNum(b));
890       fprintf(fs, "540 %i\n", EoIsSticky(b));
891       fprintf(fs, "542 %u\n", EoIsShown(b));
892 
893       if (b->flags)
894 	{
895 	   flags = 0;
896 	   if (((b->flags & FLAG_FIXED_HORIZ) &&
897 		(b->flags & FLAG_FIXED_VERT)) || (b->flags & FLAG_FIXED))
898 	      flags = 2;
899 	   else if (b->flags & FLAG_FIXED_HORIZ)
900 	      flags = 3;
901 	   else if (b->flags & FLAG_FIXED_VERT)
902 	      flags = 4;
903 	   else if (b->flags & FLAG_TITLE)
904 	      flags = 0;
905 	   else if (b->flags & FLAG_MINIICON)
906 	      flags = 1;
907 	   fprintf(fs, "454 %i\n", flags);
908 	}
909       fprintf(fs, "1000\n");
910    }
911 
912    fclose(fs);
913 
914    Esnprintf(s, sizeof(s), "%s.buttons", EGetSavePrefix());
915    E_mv(st, s);
916 }
917 
918 /*
919  * Buttons Module
920  */
921 
922 static void
ButtonsSighan(int sig,void * prm __UNUSED__)923 ButtonsSighan(int sig, void *prm __UNUSED__)
924 {
925    switch (sig)
926      {
927      case ESIGNAL_INIT:
928 	memset(&Mode_buttons, 0, sizeof(Mode_buttons));
929 	break;
930 
931      case ESIGNAL_CONFIGURE:
932 	ButtonsConfigLoadUser();
933 	break;
934 
935      case ESIGNAL_EXIT:
936 	if (Mode.wm.save_ok)
937 	   ButtonsConfigSave();
938 	break;
939      }
940 }
941 
942 typedef struct {
943    int                 id;
944    int                 match;
945    const char         *regex;
946 } button_match_data;
947 
948 static void
_ButtonHideShow(void * data,void * prm)949 _ButtonHideShow(void *data, void *prm)
950 {
951    Button             *b = (Button *) data;
952    button_match_data  *bmd = (button_match_data *) prm;
953    int                 match;
954 
955    if (bmd->id >= 0 && bmd->id != b->id)
956       return;
957 
958    if (bmd->regex)
959      {
960 	match = matchregexp(bmd->regex, EoGetName(b));
961 	if ((match && !bmd->match) || (!match && bmd->match))
962 	   return;
963 #if ENABLE_DESKRAY
964 	if (!strcmp(EoGetName(b), "_DESKTOP_DESKRAY_DRAG_CONTROL"))
965 	   return;
966 #endif
967      }
968 
969    ButtonToggle(b);
970 }
971 
972 static void
doHideShowButton(const char * params)973 doHideShowButton(const char *params)
974 {
975    char                s[1024];
976    const char         *ss;
977    int                 len;
978    button_match_data   bmd = { -1, 1, NULL };
979    Button             *b;
980 
981    if (!params)
982      {
983 	bmd.id = 0;
984 	LIST_FOR_EACH(Button, &button_list, b) _ButtonHideShow(b, &bmd);
985 	goto done;
986      }
987 
988    s[0] = '\0';
989    len = 0;
990    sscanf(params, "%1000s %n", s, &len);
991    ss = (len > 0) ? params + len : NULL;
992 
993    if (!strcmp(s, "button"))
994      {
995 	sscanf(params, "%*s %1000s", s);
996 	b = ButtonFind(s);
997 	if (b)
998 	   ButtonToggle(b);
999      }
1000    else if (!strcmp(s, "buttons"))
1001      {
1002 	if (!ss)
1003 	   return;
1004 
1005 	bmd.regex = ss;
1006 	LIST_FOR_EACH(Button, &button_list, b) _ButtonHideShow(b, &bmd);
1007      }
1008    else if (!strcmp(s, "all_buttons_except"))
1009      {
1010 	if (!ss)
1011 	   return;
1012 
1013 	bmd.id = 0;
1014 	bmd.match = 0;
1015 	bmd.regex = ss;
1016 	LIST_FOR_EACH(Button, &button_list, b) _ButtonHideShow(b, &bmd);
1017      }
1018    else if (!strcmp(s, "all"))
1019      {
1020 	LIST_FOR_EACH(Button, &button_list, b) _ButtonHideShow(b, &bmd);
1021      }
1022 
1023  done:
1024    autosave();
1025 }
1026 
1027 static void
ButtonsIpc(const char * params)1028 ButtonsIpc(const char *params)
1029 {
1030    const char         *p;
1031    char                cmd[128], prm[4096];
1032    int                 len;
1033    Button             *b;
1034 
1035    cmd[0] = prm[0] = '\0';
1036    p = params;
1037    if (p)
1038      {
1039 	len = 0;
1040 	sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1041 	p += len;
1042      }
1043 
1044    if (!p || cmd[0] == '?')
1045      {
1046      }
1047    else if (!strncmp(cmd, "list", 2))
1048      {
1049 	IpcPrintf("Win       d  s  l     x     y     w     h name\n");
1050 	LIST_FOR_EACH(Button, &button_list, b)
1051 	   IpcPrintf("%#x %2d %2d %2d %5d+%5d %5dx%5d %s\n",
1052 		     EoGetXwin(b), EoGetDeskNum(b), EoIsSticky(b),
1053 		     EoGetLayer(b), EoGetX(b), EoGetY(b), EoGetW(b), EoGetH(b),
1054 		     EoGetName(b));
1055      }
1056    else if (!strncmp(cmd, "move", 2))
1057      {
1058 	if (Mode_buttons.button)
1059 	   ButtonDragStart(Mode_buttons.button);
1060      }
1061 }
1062 
1063 static void
IPC_ButtonShow(const char * params)1064 IPC_ButtonShow(const char *params)
1065 {
1066    doHideShowButton(params);
1067 }
1068 
1069 static const IpcItem ButtonsIpcArray[] = {
1070    {
1071     ButtonsIpc,
1072     "button", "btn",
1073     "Button functions",
1074     "  button list               List buttons\n"},
1075    {
1076     IPC_ButtonShow,
1077     "button_show", NULL,
1078     "Show or Hide buttons on desktop",
1079     "use \"button_show <button/buttons/all_buttons_except/all> "
1080     "<BUTTON_STRING>\"\nexamples: \"button_show buttons all\" "
1081     "(removes all buttons and the dragbar)\n\"button_show\" "
1082     "(removes all buttons)\n \"button_show buttons CONFIG*\" "
1083     "(removes all buttons with CONFIG in the start)\n"},
1084 };
1085 
1086 #if 0
1087 static const CfgItem ButtonsCfgItems[] = {
1088    CFG_ITEM_BOOL(Conf.buttons, enable, 1),
1089 };
1090 #endif
1091 
1092 /*
1093  * Module descriptor
1094  */
1095 extern const EModule ModButtons;
1096 
1097 const EModule       ModButtons = {
1098    "buttons", "btn",
1099    ButtonsSighan,
1100    MOD_ITEMS(ButtonsIpcArray),
1101    {0, NULL}
1102 };
1103