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