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