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 "borders.h"
31 #include "cursors.h"
32 #include "ewins.h"
33 #include "focus.h"
34 #include "grabs.h"
35 #include "hints.h"
36 #include "iclass.h"
37 #include "icons.h"
38 #include "list.h"
39 #include "snaps.h"
40 #include "tclass.h"
41 #include "timers.h"
42 #include "tooltips.h"
43 #include "windowmatch.h"
44 #include "xwin.h"
45
46 #define EWIN_BORDER_PART_EVENT_MASK \
47 (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \
48 EnterWindowMask | LeaveWindowMask | PointerMotionMask)
49 #define EWIN_BORDER_TITLE_EVENT_MASK \
50 (EWIN_BORDER_PART_EVENT_MASK)
51
52 typedef struct {
53 int min, max;
54 } WinLimit;
55
56 typedef struct {
57 int originbox;
58 struct {
59 int percent;
60 int absolute;
61 } x , y;
62 } WinPoint;
63
64 typedef struct {
65 WinLimit width, height;
66 WinPoint topleft, bottomright;
67 } Geometry;
68
69 typedef struct {
70 Geometry geom;
71 ImageClass *iclass;
72 ActionClass *aclass;
73 TextClass *tclass;
74 ECursor *ec;
75 signed char ontop;
76 int flags;
77 char keep_for_shade;
78 } WinPart;
79
80 struct _border {
81 dlist_t list;
82 char *name;
83 char *group_border_name;
84 EImageBorder border;
85 int num_winparts;
86 WinPart *part;
87 char no_extent;
88 char changes_shape;
89 char shadedir;
90 char throwaway;
91 unsigned int ref_count;
92 ActionClass *aclass;
93 };
94
95 struct _ewinbit {
96 EWin *ewin; /* Belongs to */
97 Win win;
98 int x, y, w, h;
99 int cx, cy, cw, ch;
100 char state;
101 char expose;
102 char left;
103 ImageState *is;
104 TextState *ts;
105 };
106
107 static LIST_HEAD(border_list);
108
109 static void _BorderDestroy(Border * b);
110 static void _BorderWinpartHandleEvents(Win win, XEvent * ev, void *prm);
111 static void _BorderFrameHandleEvents(Win win, XEvent * ev, void *prm);
112 static Border *_BorderGetFallback(void);
113
114 static void
_BorderWinpartRealise(EWin * ewin,int i)115 _BorderWinpartRealise(EWin * ewin, int i)
116 {
117 EWinBit *ewb = &ewin->bits[i];
118
119 if ((ewb->cx != ewb->x) || (ewb->cy != ewb->y) ||
120 (ewb->cw != ewb->w) || (ewb->ch != ewb->h))
121 {
122 if ((ewb->w <= 0) || (ewb->h <= 0))
123 {
124 EUnmapWindow(ewb->win);
125 }
126 else
127 {
128 EMapWindow(ewb->win);
129 EMoveResizeWindow(ewb->win, ewb->x, ewb->y, ewb->w, ewb->h);
130 }
131 }
132 }
133
134 static void
_BorderWinpartITclassApply(EWin * ewin,int i,int force)135 _BorderWinpartITclassApply(EWin * ewin, int i, int force)
136 {
137 EWinBit *ewb = &ewin->bits[i];
138 WinPart *ebp = &ewin->border->part[i];
139 ImageState *is;
140 TextState *ts;
141 const char *txt;
142 int flags;
143
144 if (!ewb->win)
145 return;
146
147 #if 0 /* Debug */
148 Eprintf("%s: %#x %#x %2d %d %s\n", __func__,
149 EwinGetClientXwin(ewin), EoGetXwin(ewin), i, force,
150 EwinGetTitle(ewin));
151 #endif
152
153 is = ImageclassGetImageState(ebp->iclass, ewb->state,
154 ewin->state.active, EoIsSticky(ewin));
155
156 ts = NULL;
157 txt = NULL;
158 flags = 0;
159 if (ebp->flags & FLAG_TITLE)
160 {
161 txt = EwinGetTitle(ewin);
162 if (txt && ebp->tclass)
163 {
164 ts = TextclassGetTextState(ebp->tclass, ewb->state,
165 ewin->state.active, EoIsSticky(ewin));
166 flags = ITA_BGPMAP | ITA_JUSTV;
167 }
168 }
169 if (ebp->flags & FLAG_MINIICON)
170 {
171 flags |= ITA_BGPMAP;
172 }
173
174 if (!force && ewb->is == is && ewb->ts == ts)
175 return;
176 ewb->is = is;
177 ewb->ts = ts;
178
179 ITApply(ewb->win, ebp->iclass, is, ewb->state, ewin->state.active,
180 EoIsSticky(ewin), ebp->tclass, ts, txt, flags);
181
182 if (ebp->flags & FLAG_MINIICON)
183 {
184 EImage *im;
185
186 im = EwinIconImageGet(ewin, 16, Conf.warplist.icon_mode);
187 if (im)
188 {
189 EX_Pixmap pmap;
190 EImageBorder *pad;
191 int x, y, w, h;
192
193 x = y = 0;
194 w = WinGetW(ewb->win);
195 h = WinGetH(ewb->win);
196 pad = ImageclassGetPadding(ebp->iclass);
197 if (ts)
198 {
199 /* Occupy square on one side */
200 switch (ts->style.orientation)
201 {
202 default:
203 case FONT_TO_RIGHT:
204 case FONT_TO_LEFT:
205 w = h;
206 break;
207 case FONT_TO_UP:
208 y = h - w;
209 /* FALLTHROUGH */
210 case FONT_TO_DOWN:
211 h = w;
212 break;
213 }
214 }
215 if (pad)
216 {
217 /* Probably not quite right for vertical borders */
218 x += pad->left;
219 y += pad->top;
220 w -= pad->left + pad->right;
221 h -= pad->top + pad->bottom;
222 }
223 if (w < 8 || h < 8)
224 goto skip_icon;
225 pmap = EGetWindowBackgroundPixmap(ewb->win);
226 EImageRenderOnDrawable(im, ewb->win, pmap,
227 EIMAGE_BLEND | EIMAGE_ANTI_ALIAS,
228 x, y, w, h);
229 EClearWindow(ewb->win);
230 skip_icon:
231 EImageFree(im);
232 }
233 }
234 }
235
236 static int
_BorderWinpartDraw(EWin * ewin,int i)237 _BorderWinpartDraw(EWin * ewin, int i)
238 {
239 EWinBit *ewb = &ewin->bits[i];
240 int resize = 0, ret = 0;
241
242 if ((ewb->x != ewb->cx) || (ewb->y != ewb->cy))
243 {
244 ewb->cx = ewb->x;
245 ewb->cy = ewb->y;
246 ret = 1;
247 }
248
249 if ((ewb->w != ewb->cw) || (ewb->h != ewb->ch))
250 {
251 resize = 1;
252 ewb->cw = ewb->w;
253 ewb->ch = ewb->h;
254 }
255
256 if ((resize) || (ewb->expose))
257 {
258 _BorderWinpartITclassApply(ewin, i, 1);
259 ewb->expose = 0;
260 ret = 1;
261 }
262
263 return ret;
264 }
265
266 static void
_BorderWinpartChange(EWin * ewin,int i,int force)267 _BorderWinpartChange(EWin * ewin, int i, int force)
268 {
269 _BorderWinpartITclassApply(ewin, i, force);
270
271 if (ewin->update.shape || ewin->border->changes_shape)
272 EwinPropagateShapes(ewin);
273 }
274
275 void
EwinBorderDraw(EWin * ewin,int do_shape,int do_paint)276 EwinBorderDraw(EWin * ewin, int do_shape, int do_paint)
277 {
278 int i;
279
280 #if 0 /* Debug */
281 Eprintf("%s: %#x %s s=%d p=%d\n", __func__,
282 EwinGetClientXwin(ewin), EoGetName(ewin), do_shape, do_paint);
283 #endif
284
285 for (i = 0; i < ewin->border->num_winparts; i++)
286 _BorderWinpartITclassApply(ewin, i, do_shape || do_paint);
287
288 if (do_shape || ewin->update.shape || ewin->border->changes_shape)
289 EwinPropagateShapes(ewin);
290 }
291
292 void
EwinBorderUpdateInfo(EWin * ewin)293 EwinBorderUpdateInfo(EWin * ewin)
294 {
295 int i;
296
297 for (i = 0; i < ewin->border->num_winparts; i++)
298 {
299 if (ewin->border->part[i].flags & FLAG_TITLE)
300 _BorderWinpartITclassApply(ewin, i, 1);
301 }
302 }
303
304 static void
_BorderWinpartCalc(const EWin * ewin,int i,int ww,int hh)305 _BorderWinpartCalc(const EWin * ewin, int i, int ww, int hh)
306 {
307 int x, y, w, h, ox, oy, max, min;
308 int topleft, bottomright;
309
310 topleft = ewin->border->part[i].geom.topleft.originbox;
311 bottomright = ewin->border->part[i].geom.bottomright.originbox;
312 if (topleft >= 0)
313 _BorderWinpartCalc(ewin, topleft, ww, hh);
314 if (bottomright >= 0)
315 _BorderWinpartCalc(ewin, bottomright, ww, hh);
316
317 x = y = 0;
318 if (topleft == -1)
319 {
320 x = ((ewin->border->part[i].geom.topleft.x.percent * ww) >> 10) +
321 ewin->border->part[i].geom.topleft.x.absolute;
322 y = ((ewin->border->part[i].geom.topleft.y.percent * hh) >> 10) +
323 ewin->border->part[i].geom.topleft.y.absolute;
324 }
325 else if (topleft >= 0)
326 {
327 x = ((ewin->border->part[i].geom.topleft.x.percent *
328 ewin->bits[topleft].w) >> 10) +
329 ewin->border->part[i].geom.topleft.x.absolute +
330 ewin->bits[topleft].x;
331 y = ((ewin->border->part[i].geom.topleft.y.percent *
332 ewin->bits[topleft].h) >> 10) +
333 ewin->border->part[i].geom.topleft.y.absolute +
334 ewin->bits[topleft].y;
335 }
336
337 ox = oy = 0;
338 if (bottomright == -1)
339 {
340 ox = ((ewin->border->part[i].geom.bottomright.x.percent * ww) >> 10) +
341 ewin->border->part[i].geom.bottomright.x.absolute;
342 oy = ((ewin->border->part[i].geom.bottomright.y.percent * hh) >> 10) +
343 ewin->border->part[i].geom.bottomright.y.absolute;
344 }
345 else if (bottomright >= 0)
346 {
347 ox = ((ewin->border->part[i].geom.bottomright.x.percent *
348 ewin->bits[bottomright].w) >> 10) +
349 ewin->border->part[i].geom.bottomright.x.absolute +
350 ewin->bits[bottomright].x;
351 oy = ((ewin->border->part[i].geom.bottomright.y.percent *
352 ewin->bits[bottomright].h) >> 10) +
353 ewin->border->part[i].geom.bottomright.y.absolute +
354 ewin->bits[bottomright].y;
355 }
356
357 /*
358 * calculate height before width, because we may need it in order to
359 * determine the font size. But we might do it the other way around for
360 * side borders :-)
361 */
362
363 h = (oy - y) + 1;
364 max = ewin->border->part[i].geom.height.max;
365 min = ewin->border->part[i].geom.height.min;
366
367 /*
368 * If the title bar max size is set to zero, then set the title bar size to
369 * just a little bit more than the size of the title text.
370 */
371
372 if (max == 0 && ewin->border->part[i].flags & FLAG_TITLE)
373 {
374 int dummywidth, wmax, wmin;
375 ImageClass *iclass;
376 TextClass *tclass;
377 EImageBorder *pad;
378
379 /*
380 * calculate width before height, because we need it in order to
381 * determine the font size.
382 */
383
384 w = (ox - x) + 1;
385 wmax = ewin->border->part[i].geom.width.max;
386 wmin = ewin->border->part[i].geom.width.min;
387 if (w > wmax)
388 {
389 w = wmax;
390 x = ((x + ox) - w) >> 1;
391 }
392 else if (w < wmin)
393 {
394 w = wmin;
395 }
396 iclass = ewin->border->part[i].iclass;
397 tclass = ewin->border->part[i].tclass;
398 pad = ImageclassGetPadding(iclass);
399 TextSize(tclass, ewin->state.active, EoIsSticky(ewin),
400 ewin->bits[i].state, EwinGetTitle(ewin), &max, &dummywidth,
401 w - (pad->top + pad->bottom));
402 max += pad->left + pad->right;
403 if (h > max)
404 {
405 y += ((h - max) * TextclassGetJustification(tclass)) >> 10;
406 h = max;
407 }
408 if (h < min)
409 {
410 h = min;
411 }
412 }
413 else
414 {
415 if (h > max)
416 {
417 h = max;
418 y = ((y + oy) - h) >> 1;
419 }
420 else if (h < min)
421 {
422 h = min;
423 }
424 /*
425 * and now the width.
426 */
427
428 w = (ox - x) + 1;
429 max = ewin->border->part[i].geom.width.max;
430 min = ewin->border->part[i].geom.width.min;
431
432 /*
433 * If the title bar max size is set to zero, then set the title bar
434 * size to just a little bit more than the size of the title text.
435 */
436
437 if (max == 0 && ewin->border->part[i].flags & FLAG_TITLE)
438 {
439 int dummyheight;
440 ImageClass *iclass;
441 TextClass *tclass;
442 EImageBorder *pad;
443
444 iclass = ewin->border->part[i].iclass;
445 tclass = ewin->border->part[i].tclass;
446 pad = ImageclassGetPadding(iclass);
447 TextSize(tclass, ewin->state.active, EoIsSticky(ewin),
448 ewin->bits[i].state, EwinGetTitle(ewin), &max,
449 &dummyheight, h - (pad->top + pad->bottom));
450 max += pad->left + pad->right;
451
452 if (w > max)
453 {
454 x += ((w - max) * TextclassGetJustification(tclass)) >> 10;
455 w = max;
456 }
457 }
458 if (w > max)
459 {
460 w = max;
461 x = ((x + ox) - w) >> 1;
462 }
463 else if (w < min)
464 {
465 w = min;
466 }
467 }
468 if ((ewin->state.shaded) && (!ewin->border->part[i].keep_for_shade))
469 {
470 ewin->bits[i].x = -100;
471 ewin->bits[i].y = -100;
472 ewin->bits[i].w = -1;
473 ewin->bits[i].h = -1;
474 }
475 else
476 {
477 ewin->bits[i].x = x;
478 ewin->bits[i].y = y;
479 ewin->bits[i].w = w;
480 ewin->bits[i].h = h;
481 }
482 }
483
484 void
EwinBorderCalcSizes(EWin * ewin,int propagate)485 EwinBorderCalcSizes(EWin * ewin, int propagate)
486 {
487 int i, ww, hh;
488 char reshape;
489
490 if (!ewin->border)
491 return;
492
493 ww = EoGetW(ewin);
494 hh = EoGetH(ewin);
495
496 for (i = 0; i < ewin->border->num_winparts; i++)
497 ewin->bits[i].w = -2;
498 for (i = 0; i < ewin->border->num_winparts; i++)
499 if (ewin->bits[i].w == -2)
500 _BorderWinpartCalc(ewin, i, ww, hh);
501 for (i = 0; i < ewin->border->num_winparts; i++)
502 _BorderWinpartRealise(ewin, i);
503
504 reshape = 0;
505 for (i = 0; i < ewin->border->num_winparts; i++)
506 {
507 reshape |= _BorderWinpartDraw(ewin, i);
508 }
509
510 #if 0 /* Debug */
511 Eprintf("%s: prop=%d reshape=%d\n", __func__, propagate, reshape);
512 #endif
513 if (reshape)
514 ewin->update.shape = 1;
515 if (propagate && ewin->update.shape)
516 EwinPropagateShapes(ewin);
517 }
518
519 static void
_BorderIncRefcount(const Border * b)520 _BorderIncRefcount(const Border * b)
521 {
522 ((Border *) b)->ref_count++;
523 }
524
525 static void
_BorderDecRefcount(const Border * b)526 _BorderDecRefcount(const Border * b)
527 {
528 ((Border *) b)->ref_count--;
529 }
530
531 const char *
BorderGetName(const Border * b)532 BorderGetName(const Border * b)
533 {
534 return (b) ? b->name : NULL;
535 }
536
537 int
BorderCanShade(const Border * b)538 BorderCanShade(const Border * b)
539 {
540 return !b->no_extent;
541 }
542
543 const EImageBorder *
BorderGetSize(const Border * b)544 BorderGetSize(const Border * b)
545 {
546 return &b->border;
547 }
548
549 int
BorderGetShadedir(const Border * b)550 BorderGetShadedir(const Border * b)
551 {
552 return b->shadedir;
553 }
554
555 ActionClass *
BorderGetAclass(const Border * b)556 BorderGetAclass(const Border * b)
557 {
558 return b->aclass;
559 }
560
561 void
EwinBorderSelect(EWin * ewin)562 EwinBorderSelect(EWin * ewin)
563 {
564 const Border *b;
565
566 if (ewin->inh_wm.b.border)
567 {
568 b = BorderFind("BORDERLESS");
569 goto done;
570 }
571
572 /* Quit if we already have a border that isn't an internal one */
573 b = ewin->border;
574 if (b && strncmp(b->name, "__", 2))
575 goto done;
576
577 b = NULL;
578
579 if (ewin->props.no_border)
580 b = BorderFind("BORDERLESS");
581
582 if (!b)
583 b = WindowMatchEwinBorder(ewin);
584
585 if (!b)
586 b = BorderFind("DEFAULT");
587
588 if (!b)
589 b = _BorderGetFallback();
590
591 done:
592 ewin->normal_border = ewin->border = b;
593 }
594
595 void
EwinBorderDetach(EWin * ewin)596 EwinBorderDetach(EWin * ewin)
597 {
598 const Border *b = ewin->border;
599 int i;
600
601 if (!b)
602 return;
603
604 TooltipsSetPending(0, NULL, NULL);
605
606 EventCallbackUnregister(EoGetWin(ewin), _BorderFrameHandleEvents, ewin);
607 for (i = 0; i < b->num_winparts; i++)
608 {
609 EventCallbackUnregister(ewin->bits[i].win,
610 _BorderWinpartHandleEvents, &ewin->bits[i]);
611 if (ewin->bits[i].win)
612 EDestroyWindow(ewin->bits[i].win);
613 }
614 EFREE_NULL(ewin->bits);
615 _BorderDecRefcount(b);
616
617 ewin->border = NULL;
618
619 if (b->throwaway)
620 _BorderDestroy((Border *) b);
621 }
622
623 void
EwinBorderSetTo(EWin * ewin,const Border * b)624 EwinBorderSetTo(EWin * ewin, const Border * b)
625 {
626 int i;
627
628 if (ewin->border == b)
629 return;
630
631 if (!b)
632 {
633 b = ewin->border;
634 ewin->border = NULL;
635 }
636
637 if (ewin->border)
638 EwinBorderDetach(ewin);
639
640 ewin->border = b;
641 _BorderIncRefcount(b);
642 HintsSetWindowBorder(ewin);
643
644 ewin->state.no_border = b->no_extent;
645
646 EventCallbackRegister(EoGetWin(ewin), _BorderFrameHandleEvents, ewin);
647
648 if (b->num_winparts > 0)
649 ewin->bits = EMALLOC(EWinBit, b->num_winparts);
650
651 for (i = 0; i < b->num_winparts; i++)
652 {
653 ewin->bits[i].ewin = ewin; /* Reference to associated Ewin */
654
655 ewin->bits[i].win = ECreateWindow(EoGetWin(ewin), -10, -10, 1, 1, 0);
656 ECursorApply(b->part[i].ec, ewin->bits[i].win);
657 EMapWindow(ewin->bits[i].win);
658 EventCallbackRegister(ewin->bits[i].win,
659 _BorderWinpartHandleEvents, &ewin->bits[i]);
660 if (b->part[i].flags & FLAG_TITLE)
661 ESelectInput(ewin->bits[i].win, EWIN_BORDER_TITLE_EVENT_MASK);
662 else
663 ESelectInput(ewin->bits[i].win, EWIN_BORDER_PART_EVENT_MASK);
664 ewin->bits[i].x = -10;
665 ewin->bits[i].y = -10;
666 ewin->bits[i].w = -10;
667 ewin->bits[i].h = -10;
668 ewin->bits[i].cx = -99;
669 ewin->bits[i].cy = -99;
670 ewin->bits[i].cw = -99;
671 ewin->bits[i].ch = -99;
672 ewin->bits[i].state = 0;
673 ewin->bits[i].expose = 0;
674 ewin->bits[i].left = 0;
675 ewin->bits[i].is = NULL;
676 }
677
678 {
679 EX_Window *wl;
680 int j = 0;
681
682 wl = EMALLOC(EX_Window, b->num_winparts + 1);
683 if (!wl)
684 return;
685 for (i = b->num_winparts - 1; i >= 0; i--)
686 {
687 if (b->part[i].ontop)
688 wl[j++] = WinGetXwin(ewin->bits[i].win);
689 }
690 wl[j++] = WinGetXwin(EwinGetClientConWin(ewin));
691 for (i = b->num_winparts - 1; i >= 0; i--)
692 {
693 if (!b->part[i].ontop)
694 wl[j++] = WinGetXwin(ewin->bits[i].win);
695 }
696 EXRestackWindows(wl, j);
697 Efree(wl);
698 }
699
700 #if USE_CONTAINER_WIN
701 if (!ewin->state.shaded)
702 EMoveWindow(ewin->win_container, b->border.left, b->border.top);
703 #endif
704
705 ewin->update.shape = 1;
706 EwinBorderCalcSizes(ewin, 0);
707 EwinStateUpdate(ewin);
708
709 SnapshotEwinUpdate(ewin, SNAP_USE_BORDER);
710 }
711
712 void
EwinBorderChange(EWin * ewin,const Border * b,int normal)713 EwinBorderChange(EWin * ewin, const Border * b, int normal)
714 {
715 if (!b || ewin->border == b ||
716 ewin->inh_wm.b.border || ewin->state.fullscreen)
717 return;
718
719 EwinBorderSetTo(ewin, b);
720 EwinMoveResize(ewin, EoGetX(ewin), EoGetY(ewin),
721 ewin->client.w, ewin->client.h, 0);
722
723 if (normal)
724 ewin->normal_border = b;
725 }
726
727 static void
_EwinBorderAssign(EWin * ewin,const Border * b)728 _EwinBorderAssign(EWin * ewin, const Border * b)
729 {
730 if (!b || ewin->border == b || ewin->inh_wm.b.border)
731 return;
732
733 if (ewin->border)
734 _BorderDecRefcount(ewin->border);
735 _BorderIncRefcount(b);
736
737 ewin->border = ewin->normal_border = b;
738 }
739
740 void
EwinBorderSetInitially(EWin * ewin,const char * name)741 EwinBorderSetInitially(EWin * ewin, const char *name)
742 {
743 _EwinBorderAssign(ewin, BorderFind(name));
744 }
745
746 const Border *
EwinBorderGetGroupBorder(const EWin * ewin)747 EwinBorderGetGroupBorder(const EWin * ewin)
748 {
749 return BorderFind(ewin->border->group_border_name);
750 }
751
752 static Border *
_BorderCreate(const char * name)753 _BorderCreate(const char *name)
754 {
755 Border *b;
756
757 b = ECALLOC(Border, 1);
758 if (!b)
759 return NULL;
760
761 LIST_PREPEND(Border, &border_list, b);
762
763 b->name = Estrdup(name);
764 b->group_border_name = NULL;
765 b->shadedir = 2;
766
767 return b;
768 }
769
770 static int
_BorderCheck(Border * b)771 _BorderCheck(Border * b)
772 {
773 b->no_extent = b->num_winparts <= 0 ||
774 (b->border.left == 0 && b->border.right == 0 &&
775 b->border.top == 0 && b->border.bottom == 0);
776
777 return 0;
778 }
779
780 static void
_BorderDestroy(Border * b)781 _BorderDestroy(Border * b)
782 {
783 int i;
784
785 if (!b)
786 return;
787
788 if (b->ref_count > 0)
789 {
790 DialogOK("Border Error!", _("%u references remain"), b->ref_count);
791 return;
792 }
793
794 LIST_REMOVE(Border, &border_list, b);
795
796 for (i = 0; i < b->num_winparts; i++)
797 {
798 ImageclassFree(b->part[i].iclass);
799 ActionclassFree(b->part[i].aclass);
800 TextclassFree(b->part[i].tclass);
801 ECursorFree(b->part[i].ec);
802 }
803
804 Efree(b->part);
805 Efree(b->name);
806 Efree(b->group_border_name);
807 ActionclassFree(b->aclass);
808 Efree(b);
809 }
810
811 static int
_BorderMatchName(const void * data,const void * match)812 _BorderMatchName(const void *data, const void *match)
813 {
814 return strcmp(((const Border *)data)->name, (const char *)match);
815 }
816
817 Border *
BorderFind(const char * name)818 BorderFind(const char *name)
819 {
820 if (!name)
821 return NULL;
822 return LIST_FIND(Border, &border_list, _BorderMatchName, name);
823 }
824
825 static void
_BorderWinpartAdd(Border * b,const char * iclass,const char * aclass,const char * tclass,const char * cclass,char ontop,int flags,char isregion __UNUSED__,int wmin,int wmax,int hmin,int hmax,int torigin,int txp,int txa,int typ,int tya,int borigin,int bxp,int bxa,int byp,int bya,char keep_for_shade)826 _BorderWinpartAdd(Border * b, const char *iclass, const char *aclass,
827 const char *tclass, const char *cclass, char ontop, int flags,
828 char isregion __UNUSED__, int wmin, int wmax, int hmin,
829 int hmax, int torigin, int txp, int txa, int typ, int tya,
830 int borigin, int bxp, int bxa, int byp, int bya,
831 char keep_for_shade)
832 {
833 int n;
834
835 b->num_winparts++;
836 n = b->num_winparts;
837
838 b->part = EREALLOC(WinPart, b->part, n);
839
840 b->part[n - 1].iclass = (iclass) ? ImageclassAlloc(iclass, 1) : NULL;
841 b->part[n - 1].aclass = (aclass) ? ActionclassAlloc(aclass) : NULL;
842 b->part[n - 1].tclass = (tclass) ? TextclassAlloc(tclass, 1) : NULL;
843 b->part[n - 1].ec = (cclass) ? ECursorAlloc(cclass) : NULL;
844
845 b->part[n - 1].ontop = ontop;
846 b->part[n - 1].flags = flags;
847 b->part[n - 1].keep_for_shade = keep_for_shade;
848 b->part[n - 1].geom.width.min = wmin;
849 b->part[n - 1].geom.width.max = wmax;
850 b->part[n - 1].geom.height.min = hmin;
851 b->part[n - 1].geom.height.max = hmax;
852 b->part[n - 1].geom.topleft.originbox = torigin;
853 b->part[n - 1].geom.topleft.x.percent = txp;
854 b->part[n - 1].geom.topleft.x.absolute = txa;
855 b->part[n - 1].geom.topleft.y.percent = typ;
856 b->part[n - 1].geom.topleft.y.absolute = tya;
857 b->part[n - 1].geom.bottomright.originbox = borigin;
858 b->part[n - 1].geom.bottomright.x.percent = bxp;
859 b->part[n - 1].geom.bottomright.x.absolute = bxa;
860 b->part[n - 1].geom.bottomright.y.percent = byp;
861 b->part[n - 1].geom.bottomright.y.absolute = bya;
862 }
863
864 void
EwinBorderMinShadeSize(const EWin * ewin,int * mw,int * mh)865 EwinBorderMinShadeSize(const EWin * ewin, int *mw, int *mh)
866 {
867 int i, pw, ph, w, h, min_w, min_h;
868 int leftborderwidth, rightborderwidth;
869 int topborderwidth, bottomborderwidth;
870
871 pw = ewin->client.w + ewin->border->border.left + ewin->border->border.right;
872 ph = ewin->client.h + ewin->border->border.top + ewin->border->border.bottom;
873
874 for (i = 0; i < ewin->border->num_winparts; i++)
875 ewin->bits[i].w = -2;
876 for (i = 0; i < ewin->border->num_winparts; i++)
877 if (ewin->bits[i].w == -2)
878 _BorderWinpartCalc(ewin, i, pw, ph);
879
880 switch (ewin->border->shadedir)
881 {
882 case 0:
883 case 1:
884 /* get the correct width, based on the borderparts that */
885 /*are remaining visible */
886 leftborderwidth = rightborderwidth = 0;
887 for (i = 0; i < ewin->border->num_winparts; i++)
888 {
889 if (!ewin->border->part[i].keep_for_shade)
890 continue;
891
892 w = ewin->border->border.left - ewin->bits[i].x;
893 if (leftborderwidth < w)
894 leftborderwidth = w;
895
896 w = ewin->bits[i].x + ewin->bits[i].w -
897 (pw - ewin->border->border.right);
898 if (rightborderwidth < w)
899 rightborderwidth = w;
900 }
901 pw = rightborderwidth + leftborderwidth;
902 break;
903 case 2:
904 case 3:
905 topborderwidth = bottomborderwidth = 0;
906 for (i = 0; i < ewin->border->num_winparts; i++)
907 {
908 if (!ewin->border->part[i].keep_for_shade)
909 continue;
910
911 h = ewin->border->border.top - ewin->bits[i].y;
912 if (topborderwidth < h)
913 topborderwidth = h;
914
915 h = ewin->bits[i].y + ewin->bits[i].h -
916 (ph - ewin->border->border.bottom);
917 if (bottomborderwidth < h)
918 bottomborderwidth = h;
919 }
920 ph = bottomborderwidth + topborderwidth;
921 break;
922 default:
923 break;
924 }
925
926 for (i = 0; i < ewin->border->num_winparts; i++)
927 ewin->bits[i].w = -2;
928 for (i = 0; i < ewin->border->num_winparts; i++)
929 if (ewin->bits[i].w == -2)
930 _BorderWinpartCalc(ewin, i, pw, ph);
931
932 min_w = 0;
933 min_h = 0;
934 for (i = 0; i < ewin->border->num_winparts; i++)
935 {
936 if (!ewin->border->part[i].keep_for_shade)
937 continue;
938
939 w = ewin->bits[i].x + ewin->bits[i].w;
940 if (min_w < w)
941 min_w = w;
942
943 h = ewin->bits[i].y + ewin->bits[i].h;
944 if (min_h < h)
945 min_h = h;
946 }
947
948 *mw = min_w;
949 *mh = min_h;
950 }
951
952 int
BorderWinpartIndex(EWin * ewin,Win win)953 BorderWinpartIndex(EWin * ewin, Win win)
954 {
955 int i;
956
957 for (i = 0; i < ewin->border->num_winparts; i++)
958 {
959 if (win == ewin->bits[i].win)
960 return i;
961 }
962
963 return -1; /* Not found */
964 }
965
966 /*
967 * Border event handlers
968 */
969 #define DEBUG_BORDER_EVENTS 0
970
971 static void
_BorderWinpartEventMouseDown(EWinBit * wbit,XEvent * ev)972 _BorderWinpartEventMouseDown(EWinBit * wbit, XEvent * ev)
973 {
974 EWin *ewin = wbit->ewin;
975 int part = wbit - ewin->bits;
976
977 #if 0 /* Remove? */
978 GrabPointerSet(wbit->win, 0, 0);
979 #endif
980
981 wbit->state = STATE_CLICKED;
982 #if DEBUG_BORDER_EVENTS
983 Eprintf("%s: %#x %d\n", __func__, WinGetXwin(wbit->win), wbit->state);
984 #endif
985 _BorderWinpartChange(ewin, part, 0);
986
987 FocusHandleClick(ewin, wbit->win);
988
989 if (ewin->border->part[part].aclass)
990 ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
991 }
992
993 static void
_BorderWinpartEventMouseUp(EWinBit * wbit,XEvent * ev)994 _BorderWinpartEventMouseUp(EWinBit * wbit, XEvent * ev)
995 {
996 EWin *ewin = wbit->ewin;
997 int part = wbit - ewin->bits;
998 int left = wbit->left;
999
1000 if (ev)
1001 {
1002 #if 0 /* Remove? */
1003 GrabPointerRelease();
1004 #endif
1005 /*
1006 * During a move/resize operation initiated by a border part click
1007 * the ButtonRelease event may be reported on the border part window
1008 * if the event happens before the pointer grab is moved to the
1009 * move/resize event window.
1010 * This can be tested e.g. by inserting usleep(100000) at the start
1011 * of _BorderWinpartEventMouseDown().
1012 */
1013 MoveResizeEnd(ewin);
1014 }
1015
1016 if ((wbit->state == STATE_CLICKED) && (!wbit->left))
1017 wbit->state = STATE_HILITED;
1018 else
1019 wbit->state = STATE_NORMAL;
1020 #if DEBUG_BORDER_EVENTS
1021 Eprintf("%s: %#x %d\n", __func__, WinGetXwin(wbit->win), wbit->state);
1022 #endif
1023 _BorderWinpartChange(ewin, part, 0);
1024
1025 /* Beware! Actions may destroy the current border */
1026 wbit->left = 0;
1027
1028 if (ev && WinGetXwin(wbit->win) == Mode.events.last_bpress && !left &&
1029 ewin->border->part[part].aclass)
1030 ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
1031 }
1032
1033 static void
_BorderWinpartEventEnter(EWinBit * wbit,XEvent * ev)1034 _BorderWinpartEventEnter(EWinBit * wbit, XEvent * ev)
1035 {
1036 EWin *ewin = wbit->ewin;
1037 int part = wbit - ewin->bits;
1038
1039 #if DEBUG_BORDER_EVENTS
1040 Eprintf("%s: %#x %d\n", __func__, WinGetXwin(wbit->win), wbit->state);
1041 #endif
1042 if (wbit->state == STATE_CLICKED)
1043 wbit->left = 0;
1044
1045 wbit->state = STATE_HILITED;
1046 _BorderWinpartChange(ewin, part, 0);
1047 if (ewin->border->part[part].aclass)
1048 ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
1049 }
1050
1051 static void
_BorderWinpartEventLeave(EWinBit * wbit,XEvent * ev)1052 _BorderWinpartEventLeave(EWinBit * wbit, XEvent * ev)
1053 {
1054 EWin *ewin = wbit->ewin;
1055 int part = wbit - ewin->bits;
1056
1057 #if DEBUG_BORDER_EVENTS
1058 Eprintf("%s: %#x %d\n", __func__, WinGetXwin(wbit->win), wbit->state);
1059 #endif
1060 if (wbit->state == STATE_CLICKED)
1061 wbit->left = 1;
1062 else
1063 {
1064 wbit->state = STATE_NORMAL;
1065 _BorderWinpartChange(ewin, part, 0);
1066 if (ewin->border->part[part].aclass)
1067 ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
1068 }
1069 }
1070
1071 void
BorderCheckState(EWin * ewin,XEvent * ev)1072 BorderCheckState(EWin * ewin, XEvent * ev)
1073 {
1074 int i;
1075
1076 for (i = 0; i < ewin->border->num_winparts; i++)
1077 {
1078 switch (ev->type)
1079 {
1080 default:
1081 break;
1082
1083 case ButtonRelease:
1084 _BorderWinpartEventMouseUp(ewin->bits + i, NULL);
1085 break;
1086 }
1087 }
1088 }
1089
1090 static int
_BorderAutoshadeTimeout(void * data)1091 _BorderAutoshadeTimeout(void *data)
1092 {
1093 EWin *ewin = (EWin *) data;
1094
1095 if (!EwinFindByPtr(ewin)) /* Check, window may be gone */
1096 return 0;
1097
1098 ewin->timer = NULL;
1099 EwinOpShade(ewin, OPSRC_USER, 1);
1100
1101 return 0;
1102 }
1103
1104 static void
_BorderFrameHandleEvents(Win win __UNUSED__,XEvent * ev,void * prm)1105 _BorderFrameHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1106 {
1107 EWin *ewin = (EWin *) prm;
1108 int x, y;
1109
1110 switch (ev->type)
1111 {
1112 case EnterNotify:
1113 if (ewin->props.autoshade)
1114 {
1115 EwinOpShade(ewin, OPSRC_USER, 0);
1116 }
1117 if (ewin->border->aclass)
1118 ActionclassEvent(ewin->border->aclass, ev, ewin);
1119 break;
1120 case LeaveNotify:
1121 if (ewin->props.autoshade && !ewin->state.shaded)
1122 {
1123 EQueryPointer(EoGetWin(ewin), &x, &y, NULL, NULL);
1124 if (x >= 4 && x < EoGetW(ewin) - 4 &&
1125 y >= 4 && y < EoGetH(ewin) - 4)
1126 break;
1127 TIMER_DEL(ewin->timer);
1128 TIMER_ADD(ewin->timer, 500, _BorderAutoshadeTimeout, ewin);
1129 }
1130 if (ewin->border->aclass)
1131 ActionclassEvent(ewin->border->aclass, ev, ewin);
1132 break;
1133 }
1134 }
1135
1136 static ActionClass *
_BorderWinpartGetAclass(void * data)1137 _BorderWinpartGetAclass(void *data)
1138 {
1139 EWinBit *wbit = (EWinBit *) data;
1140 EWin *ewin;
1141 int part;
1142
1143 /* Validate border part */
1144 ewin = Mode.mouse_over_ewin;
1145 if (!ewin)
1146 return NULL;
1147
1148 part = wbit - ewin->bits;
1149 if (part < 0 || part >= ewin->border->num_winparts)
1150 return NULL;
1151
1152 return ewin->border->part[part].aclass;
1153 }
1154
1155 static void
_BorderWinpartHandleTooltip(EWinBit * wbit)1156 _BorderWinpartHandleTooltip(EWinBit * wbit)
1157 {
1158 EWin *ewin = wbit->ewin;
1159 int part = wbit - ewin->bits;
1160
1161 if (!ewin->border->part[part].aclass)
1162 return;
1163 TooltipsSetPending(0, _BorderWinpartGetAclass, wbit);
1164 }
1165
1166 static void
_BorderWinpartHandleEvents(Win win __UNUSED__,XEvent * ev,void * prm)1167 _BorderWinpartHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1168 {
1169 EWinBit *wbit = (EWinBit *) prm;
1170
1171 switch (ev->type)
1172 {
1173 case ButtonPress:
1174 _BorderWinpartEventMouseDown(wbit, ev);
1175 break;
1176 case ButtonRelease:
1177 _BorderWinpartEventMouseUp(wbit, ev);
1178 break;
1179 case EnterNotify:
1180 /* Beware! Actions may destroy the current border */
1181 _BorderWinpartHandleTooltip(wbit);
1182 _BorderWinpartEventEnter(wbit, ev);
1183 break;
1184 case LeaveNotify:
1185 _BorderWinpartEventLeave(wbit, ev);
1186 break;
1187 case MotionNotify:
1188 _BorderWinpartHandleTooltip(wbit);
1189 break;
1190 }
1191 }
1192
1193 /*
1194 * Configuration load/save
1195 */
1196 #include "conf.h"
1197
1198 static int
_BorderPartLoad(FILE * fs,char type __UNUSED__,Border * b)1199 _BorderPartLoad(FILE * fs, char type __UNUSED__, Border * b)
1200 {
1201 int err = 0;
1202 char s[FILEPATH_LEN_MAX];
1203 char s2[FILEPATH_LEN_MAX];
1204 int i1, i2;
1205 char iclass[64], aclass[64], tclass[64], cclass[64];
1206 char *piclass, *paclass, *ptclass, *pcclass;
1207 char ontop = 1;
1208 int flags = FLAG_BUTTON;
1209 char isregion = 0, keepshade = 1;
1210 int wmin = 0, wmax = 0, hmin = 0, hmax = 0, torigin =
1211 0, txp = 0, txa = 0, typ = 0, tya = 0, borigin = 0;
1212 int bxp = 0, bxa = 0, byp = 0, bya = 0;
1213
1214 piclass = paclass = ptclass = pcclass = NULL;
1215
1216 while (GetLine(s, sizeof(s), fs))
1217 {
1218 i1 = ConfigParseline1(s, s2, NULL, NULL);
1219 i2 = atoi(s2);
1220 switch (i1)
1221 {
1222 case CONFIG_CLOSE:
1223 _BorderWinpartAdd(b, piclass, paclass, ptclass, pcclass, ontop,
1224 flags, isregion, wmin, wmax, hmin, hmax,
1225 torigin, txp, txa, typ, tya,
1226 borigin, bxp, bxa, byp, bya, keepshade);
1227 goto done;
1228 case CONFIG_IMAGECLASS:
1229 STRCPY(iclass, s2);
1230 piclass = iclass;
1231 break;
1232 case CONFIG_ACTIONCLASS:
1233 STRCPY(aclass, s2);
1234 paclass = aclass;
1235 break;
1236 case CONFIG_TEXT:
1237 STRCPY(tclass, s2);
1238 ptclass = tclass;
1239 break;
1240 case CONFIG_CURSOR:
1241 STRCPY(cclass, s2);
1242 pcclass = cclass;
1243 break;
1244 case BORDERPART_ONTOP:
1245 ontop = i2;
1246 break;
1247 case BORDERPART_FLAGS:
1248 flags = i2;
1249 break;
1250 case BORDERPART_ISREGION:
1251 isregion = i2;
1252 break;
1253 case BORDERPART_WMIN:
1254 wmin = i2;
1255 if (!wmax)
1256 wmax = wmin;
1257 break;
1258 case BORDERPART_WMAX:
1259 wmax = i2;
1260 break;
1261 case BORDERPART_HMIN:
1262 hmin = i2;
1263 if (!hmax)
1264 hmax = hmin;
1265 break;
1266 case BORDERPART_HMAX:
1267 hmax = i2;
1268 break;
1269 case BORDERPART_TORIGIN:
1270 torigin = i2;
1271 break;
1272 case BORDERPART_TXP:
1273 txp = i2;
1274 break;
1275 case BORDERPART_TXA:
1276 txa = i2;
1277 break;
1278 case BORDERPART_TYP:
1279 typ = i2;
1280 break;
1281 case BORDERPART_TYA:
1282 tya = i2;
1283 break;
1284 case BORDERPART_BORIGIN:
1285 borigin = i2;
1286 break;
1287 case BORDERPART_BXP:
1288 bxp = i2;
1289 break;
1290 case BORDERPART_BXA:
1291 bxa = i2;
1292 break;
1293 case BORDERPART_BYP:
1294 byp = i2;
1295 break;
1296 case BORDERPART_BYA:
1297 bya = i2;
1298 break;
1299 case BORDERPART_KEEPSHADE:
1300 keepshade = i2;
1301 break;
1302 default:
1303 break;
1304 }
1305 }
1306 err = -1;
1307 done:
1308 return err;
1309 }
1310
1311 int
BorderConfigLoad(FILE * fs)1312 BorderConfigLoad(FILE * fs)
1313 {
1314 int err = 0;
1315 Border *b = 0;
1316 char s[FILEPATH_LEN_MAX];
1317 char s2[FILEPATH_LEN_MAX];
1318 int i1, i2;
1319
1320 while (GetLine(s, sizeof(s), fs))
1321 {
1322 i1 = ConfigParseline1(s, s2, NULL, NULL);
1323 i2 = atoi(s2);
1324
1325 switch (i1)
1326 {
1327 case CONFIG_CLASSNAME:
1328 if (BorderFind(s2))
1329 {
1330 SkipTillEnd(fs);
1331 goto done;
1332 }
1333 b = _BorderCreate(s2);
1334 continue;
1335 }
1336
1337 if (!b)
1338 break;
1339
1340 switch (i1)
1341 {
1342 default:
1343 break;
1344 case CONFIG_CLOSE:
1345 _BorderCheck(b);
1346 goto done;
1347 case BORDER_INIT:
1348 if (i2 != CONFIG_OPEN)
1349 break;
1350 err = _BorderPartLoad(fs, i1, b);
1351 if (err)
1352 break;
1353 break;
1354 case BORDER_GROUP_NAME:
1355 b->group_border_name = Estrdup(s2);
1356 break;
1357 case BORDER_LEFT:
1358 b->border.left = i2;
1359 break;
1360 case BORDER_RIGHT:
1361 b->border.right = i2;
1362 break;
1363 case BORDER_TOP:
1364 b->border.top = i2;
1365 break;
1366 case BORDER_BOTTOM:
1367 b->border.bottom = i2;
1368 break;
1369 case BORDER_SHADEDIR:
1370 b->shadedir = i2;
1371 break;
1372 case BORDER_CHANGES_SHAPE:
1373 b->changes_shape = i2;
1374 break;
1375 case CONFIG_ACTIONCLASS:
1376 b->aclass = ActionclassFind(s2);
1377 break;
1378 }
1379 }
1380 err = -1;
1381
1382 done:
1383 return err;
1384 }
1385
1386 Border *
BorderCreateFiller(int w,int h,int sw,int sh)1387 BorderCreateFiller(int w, int h, int sw, int sh)
1388 {
1389 Border *b;
1390 int left, right, top, bottom;
1391
1392 if (w > sw || h > sh) /* Borders must be >= 0 */
1393 return NULL;
1394
1395 b = _BorderCreate("__FILLER");
1396 if (!b)
1397 return b;
1398
1399 left = (sw - w) / 2;
1400 left = (left < 0) ? 0 : left;
1401 right = sw - w - left;
1402 top = (sh - h) / 2;
1403 top = (top < 0) ? 0 : top;
1404 bottom = sh - h - top;
1405
1406 b->throwaway = 1;
1407
1408 b->border.left = left;
1409 b->border.right = right;
1410 b->border.top = top;
1411 b->border.bottom = bottom;
1412
1413 ImageclassGetBlack(); /* Creates the __BLACK ImageClass */
1414
1415 if (top)
1416 _BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1417 1, 99999, 1, 99999,
1418 -1, 0, 0, 0, 0, -1, 1024, -1, 0, top - 1, 1);
1419 if (bottom)
1420 _BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1421 1, 99999, 1, 99999,
1422 -1, 0, 0, 1024, -bottom, -1, 1024, -1, 1024, -1, 1);
1423 if (left)
1424 _BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1425 1, 99999, 1, 99999,
1426 -1, 0, 0, 0, top,
1427 -1, 0, left - 1, 1024, -(bottom + 1), 1);
1428 if (right)
1429 _BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1430 1, 99999, 1, 99999,
1431 -1, 1024, -right, 0, top,
1432 -1, 1024, -1, 1024, -(bottom + 1), 1);
1433
1434 return b;
1435 }
1436
1437 static int
_BorderNameCompare(const void * b1,const void * b2)1438 _BorderNameCompare(const void *b1, const void *b2)
1439 {
1440 if (b1 && b2)
1441 return strcmp((*(const Border **)b1)->name, (*(const Border **)b2)->name);
1442
1443 return 0;
1444 }
1445
1446 Border **
BordersGetList(int * pnum)1447 BordersGetList(int *pnum)
1448 {
1449 Border **lst;
1450
1451 lst = LIST_GET_ITEMS(Border, &border_list, pnum);
1452 qsort(lst, *pnum, sizeof(Border *), _BorderNameCompare);
1453
1454 return lst;
1455 }
1456
1457 static ActionClass *
BorderGetFallbackAclass(void)1458 BorderGetFallbackAclass(void)
1459 {
1460 ActionClass *ac;
1461 Action *aa;
1462
1463 ac = ActionclassFind("__fb_bd_ac");
1464 if (ac)
1465 return ac;
1466
1467 /* Create fallback actionclass for the fallback border */
1468 ac = ActionclassCreate("__fb_bd_ac", 0);
1469 if (!ac)
1470 return ac;
1471
1472 aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 1, 0, NULL, NULL);
1473 ActionclassAddAction(ac, aa);
1474 ActionAddTo(aa, "wop * mo ptr");
1475
1476 aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 2, 0, NULL, NULL);
1477 ActionclassAddAction(ac, aa);
1478 ActionAddTo(aa, "wop * close");
1479
1480 aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 3, 0, NULL, NULL);
1481 ActionclassAddAction(ac, aa);
1482 ActionAddTo(aa, "wop * sz ptr");
1483
1484 return ac;
1485 }
1486
1487 static Border *
_BorderGetFallback(void)1488 _BorderGetFallback(void)
1489 {
1490 /*
1491 * This function creates simple internal data members to be used in
1492 * emergencies - ie when all else fails - ie a button is told to use an
1493 * imageclass that doesn't exist, or no DEFAULT border is defined... at
1494 * least E won't barf on us then.
1495 */
1496 Border *b;
1497
1498 b = BorderFind("__fb_bd");
1499 if (b)
1500 return b;
1501
1502 BorderGetFallbackAclass(); /* Creates the fallback ac */
1503
1504 /* Create fallback border */
1505 b = _BorderCreate("__fb_bd");
1506 if (!b)
1507 return b;
1508
1509 b->border.left = 8;
1510 b->border.right = 8;
1511 b->border.top = 8;
1512 b->border.bottom = 8;
1513 _BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1514 1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 0, 0,
1515 -1, 1024, -1, 0, 7, 1);
1516 _BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1517 1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 1024,
1518 -8, -1, 1024, -1, 1024, -1, 1);
1519 _BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1520 1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 0, 8,
1521 -1, 0, 7, 1024, -9, 1);
1522 _BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1523 1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 1024, -8, 0,
1524 8, -1, 1024, -1, 1024, -9, 1);
1525
1526 return b;
1527 }
1528