chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / borders.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "aclass.h"
26 #include "borders.h"
27 #include "cursors.h"
28 #include "e16-ecore_list.h"
29 #include "ewins.h"
30 #include "focus.h"
31 #include "grabs.h"
32 #include "hints.h"
33 #include "iclass.h"
34 #include "snaps.h"
35 #include "tclass.h"
36 #include "timers.h"
37 #include "tooltips.h"
38 #include "windowmatch.h"
39 #include "xwin.h"
40
41 #define EWIN_BORDER_PART_EVENT_MASK \
42   (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \
43    EnterWindowMask | LeaveWindowMask | PointerMotionMask)
44 #define EWIN_BORDER_TITLE_EVENT_MASK \
45   (EWIN_BORDER_PART_EVENT_MASK)
46
47 static Ecore_List  *border_list = NULL;
48
49 static void         BorderDestroy(const Border * b);
50 static void         BorderWinpartHandleEvents(Win win, XEvent * ev, void *prm);
51 static void         BorderFrameHandleEvents(Win win, XEvent * ev, void *prm);
52 static Border      *BorderGetFallback(void);
53
54 static void
55 BorderWinpartRealise(EWin * ewin, int i)
56 {
57    EWinBit            *ewb = &ewin->bits[i];
58
59    if ((ewb->cx != ewb->x) || (ewb->cy != ewb->y) ||
60        (ewb->cw != ewb->w) || (ewb->ch != ewb->h))
61      {
62         if ((ewb->w <= 0) || (ewb->h <= 0))
63           {
64              EUnmapWindow(ewb->win);
65           }
66         else
67           {
68              EMapWindow(ewb->win);
69              EMoveResizeWindow(ewb->win, ewb->x, ewb->y, ewb->w, ewb->h);
70           }
71      }
72 }
73
74 static void
75 BorderWinpartITclassApply(EWin * ewin, int i, int force)
76 {
77    EWinBit            *ewb = &ewin->bits[i];
78    ImageState         *is;
79    TextState          *ts;
80    const char         *txt;
81
82    if (ewb->win == None)
83       return;
84
85 #if 0                           /* Debug */
86    Eprintf("BorderWpITApply: %#lx %#lx %2d %d %s\n",
87            EwinGetClientXwin(ewin), EoGetWin(ewin), i, force,
88            EwinGetTitle(ewin));
89 #endif
90
91    is = ImageclassGetImageState(ewin->border->part[i].iclass, ewb->state,
92                                 ewin->state.active, EoIsSticky(ewin));
93
94    ts = NULL;
95    txt = NULL;
96    switch (ewin->border->part[i].flags)
97      {
98      case FLAG_TITLE:
99         txt = EwinGetTitle(ewin);
100         if (txt && ewin->border->part[i].tclass)
101            ts = TextclassGetTextState(ewin->border->part[i].tclass, ewb->state,
102                                       ewin->state.active, EoIsSticky(ewin));
103         break;
104      case FLAG_MINIICON:
105         break;
106      default:
107         break;
108      }
109
110    if (!force && ewb->is == is && ewb->ts == ts)
111       return;
112    ewb->is = is;
113    ewb->ts = ts;
114
115    ITApply(ewb->win, ewin->border->part[i].iclass, is,
116            ewb->state, ewin->state.active, EoIsSticky(ewin),
117            ST_BORDER, ewin->border->part[i].tclass, ts, txt, 1);
118 }
119
120 static int
121 BorderWinpartDraw(EWin * ewin, int i)
122 {
123    EWinBit            *ewb = &ewin->bits[i];
124    int                 move = 0, resize = 0, ret = 0;
125
126    if ((ewb->x != ewb->cx) || (ewb->y != ewb->cy))
127      {
128         move = 1;
129         ewb->cx = ewb->x;
130         ewb->cy = ewb->y;
131         ret = 1;
132      }
133
134    if ((ewb->w != ewb->cw) || (ewb->h != ewb->ch))
135      {
136         resize = 1;
137         ewb->cw = ewb->w;
138         ewb->ch = ewb->h;
139      }
140
141    if ((resize) || (ewb->expose))
142      {
143         BorderWinpartITclassApply(ewin, i, 1);
144         ewb->expose = 0;
145         ret = 1;
146      }
147
148    return ret;
149 }
150
151 static void
152 BorderWinpartChange(EWin * ewin, int i, int force)
153 {
154    BorderWinpartITclassApply(ewin, i, force);
155
156    if (ewin->update.shape || ewin->border->changes_shape)
157       EwinPropagateShapes(ewin);
158 }
159
160 void
161 EwinBorderDraw(EWin * ewin, int do_shape, int do_paint)
162 {
163    int                 i;
164
165    if (!ewin)
166       return;
167
168 #if 0                           /* Debug */
169    Eprintf("EwinBorderDraw %#lx %s d=%d s=%d p=%d\n",
170            EwinGetClientXwin(ewin), EoGetName(ewin), EoGetDeskNum(ewin),
171            do_shape, do_paint);
172 #endif
173
174    for (i = 0; i < ewin->border->num_winparts; i++)
175       BorderWinpartITclassApply(ewin, i, do_shape || do_paint);
176
177    if (do_shape || ewin->update.shape || ewin->border->changes_shape)
178       EwinPropagateShapes(ewin);
179 }
180
181 void
182 EwinBorderUpdateInfo(EWin * ewin)
183 {
184    int                 i;
185
186    for (i = 0; i < ewin->border->num_winparts; i++)
187      {
188         if (ewin->border->part[i].flags == FLAG_TITLE)
189            BorderWinpartITclassApply(ewin, i, 1);
190      }
191 }
192
193 static void
194 BorderWinpartCalc(EWin * ewin, int i, int ww, int hh)
195 {
196    int                 x, y, w, h, ox, oy, max, min;
197    int                 topleft, bottomright;
198
199    topleft = ewin->border->part[i].geom.topleft.originbox;
200    bottomright = ewin->border->part[i].geom.bottomright.originbox;
201    if (topleft >= 0)
202       BorderWinpartCalc(ewin, topleft, ww, hh);
203    if (bottomright >= 0)
204       BorderWinpartCalc(ewin, bottomright, ww, hh);
205
206    x = y = 0;
207    if (topleft == -1)
208      {
209         x = ((ewin->border->part[i].geom.topleft.x.percent * ww) >> 10) +
210            ewin->border->part[i].geom.topleft.x.absolute;
211         y = ((ewin->border->part[i].geom.topleft.y.percent * hh) >> 10) +
212            ewin->border->part[i].geom.topleft.y.absolute;
213      }
214    else if (topleft >= 0)
215      {
216         x = ((ewin->border->part[i].geom.topleft.x.percent *
217               ewin->bits[topleft].w) >> 10) +
218            ewin->border->part[i].geom.topleft.x.absolute +
219            ewin->bits[topleft].x;
220         y = ((ewin->border->part[i].geom.topleft.y.percent *
221               ewin->bits[topleft].h) >> 10) +
222            ewin->border->part[i].geom.topleft.y.absolute +
223            ewin->bits[topleft].y;
224      }
225
226    ox = oy = 0;
227    if (bottomright == -1)
228      {
229         ox = ((ewin->border->part[i].geom.bottomright.x.percent * ww) >> 10) +
230            ewin->border->part[i].geom.bottomright.x.absolute;
231         oy = ((ewin->border->part[i].geom.bottomright.y.percent * hh) >> 10) +
232            ewin->border->part[i].geom.bottomright.y.absolute;
233      }
234    else if (bottomright >= 0)
235      {
236         ox = ((ewin->border->part[i].geom.bottomright.x.percent *
237                ewin->bits[bottomright].w) >> 10) +
238            ewin->border->part[i].geom.bottomright.x.absolute +
239            ewin->bits[bottomright].x;
240         oy = ((ewin->border->part[i].geom.bottomright.y.percent *
241                ewin->bits[bottomright].h) >> 10) +
242            ewin->border->part[i].geom.bottomright.y.absolute +
243            ewin->bits[bottomright].y;
244      }
245
246    /*
247     * calculate height before width, because we may need it in order to
248     * determine the font size. But we might do it the other way around for
249     * side borders :-)
250     */
251
252    h = (oy - y) + 1;
253    max = ewin->border->part[i].geom.height.max;
254    min = ewin->border->part[i].geom.height.min;
255
256    /*
257     * If the title bar max size is set to zero, then set the title bar size to
258     * just a little bit more than the size of the title text.
259     */
260
261    if (max == 0 && ewin->border->part[i].flags == FLAG_TITLE)
262      {
263         int                 dummywidth, wmax, wmin;
264         ImageClass         *iclass;
265         TextClass          *tclass;
266         EImageBorder       *pad;
267
268         /*
269          * calculate width before height, because we need it in order to
270          * determine the font size.
271          */
272
273         w = (ox - x) + 1;
274         wmax = ewin->border->part[i].geom.width.max;
275         wmin = ewin->border->part[i].geom.width.min;
276         if (w > wmax)
277           {
278              w = wmax;
279              x = ((x + ox) - w) >> 1;
280           }
281         else if (w < wmin)
282           {
283              w = wmin;
284           }
285         iclass = ewin->border->part[i].iclass;
286         tclass = ewin->border->part[i].tclass;
287         pad = ImageclassGetPadding(iclass);
288         TextSize(tclass, ewin->state.active, EoIsSticky(ewin),
289                  ewin->bits[i].state, EwinGetTitle(ewin), &max, &dummywidth,
290                  w - (pad->top + pad->bottom));
291         max += pad->left + pad->right;
292         if (h > max)
293           {
294              y = y + (((h - max) * TextclassGetJustification(tclass)) >> 10);
295              h = max;
296           }
297         if (h < min)
298           {
299              h = min;
300           }
301      }
302    else
303      {
304         if (h > max)
305           {
306              h = max;
307              y = ((y + oy) - h) >> 1;
308           }
309         else if (h < min)
310           {
311              h = min;
312           }
313         /*
314          * and now the width.
315          */
316
317         w = (ox - x) + 1;
318         max = ewin->border->part[i].geom.width.max;
319         min = ewin->border->part[i].geom.width.min;
320
321         /*
322          * If the title bar max size is set to zero, then set the title bar
323          * size to just a little bit more than the size of the title text.
324          */
325
326         if (max == 0 && ewin->border->part[i].flags == FLAG_TITLE)
327           {
328              int                 dummyheight;
329              ImageClass         *iclass;
330              TextClass          *tclass;
331              EImageBorder       *pad;
332
333              iclass = ewin->border->part[i].iclass;
334              tclass = ewin->border->part[i].tclass;
335              pad = ImageclassGetPadding(iclass);
336              TextSize(tclass, ewin->state.active, EoIsSticky(ewin),
337                       ewin->bits[i].state, EwinGetTitle(ewin), &max,
338                       &dummyheight, h - (pad->top + pad->bottom));
339              max += pad->left + pad->right;
340
341              if (w > max)
342                {
343                   x = x +
344                      (((w - max) * TextclassGetJustification(tclass)) >> 10);
345                   w = max;
346                }
347           }
348         if (w > max)
349           {
350              w = max;
351              x = ((x + ox) - w) >> 1;
352           }
353         else if (w < min)
354           {
355              w = min;
356           }
357      }
358    if ((ewin->state.shaded) && (!ewin->border->part[i].keep_for_shade))
359      {
360         ewin->bits[i].x = -100;
361         ewin->bits[i].y = -100;
362         ewin->bits[i].w = -1;
363         ewin->bits[i].h = -1;
364      }
365    else
366      {
367         ewin->bits[i].x = x;
368         ewin->bits[i].y = y;
369         ewin->bits[i].w = w;
370         ewin->bits[i].h = h;
371      }
372 }
373
374 void
375 EwinBorderCalcSizes(EWin * ewin, int propagate)
376 {
377    int                 i, ww, hh;
378    char                reshape;
379
380    if (!ewin)
381       return;
382    if (!ewin->border)
383       return;
384
385    ww = EoGetW(ewin);
386    hh = EoGetH(ewin);
387
388    for (i = 0; i < ewin->border->num_winparts; i++)
389       ewin->bits[i].w = -2;
390    for (i = 0; i < ewin->border->num_winparts; i++)
391       if (ewin->bits[i].w == -2)
392          BorderWinpartCalc(ewin, i, ww, hh);
393    for (i = 0; i < ewin->border->num_winparts; i++)
394       BorderWinpartRealise(ewin, i);
395
396    reshape = 0;
397    for (i = 0; i < ewin->border->num_winparts; i++)
398      {
399         reshape |= BorderWinpartDraw(ewin, i);
400      }
401
402 #if 0                           /* Debug */
403    Eprintf("EwinBorderCalcSizes prop=%d reshape=%d\n", propagate, reshape);
404 #endif
405    if (reshape)
406       ewin->update.shape = 1;
407    if (propagate && ewin->update.shape)
408       EwinPropagateShapes(ewin);
409 }
410
411 static void
412 BorderIncRefcount(const Border * b)
413 {
414    ((Border *) b)->ref_count++;
415 }
416
417 static void
418 BorderDecRefcount(const Border * b)
419 {
420    ((Border *) b)->ref_count--;
421 }
422
423 const char         *
424 BorderGetName(const Border * b)
425 {
426    return (b) ? b->name : NULL;
427 }
428
429 void
430 EwinBorderSelect(EWin * ewin)
431 {
432    const Border       *b;
433
434    if (ewin->inh_wm.b.border)
435      {
436         b = BorderFind("BORDERLESS");
437         goto done;
438      }
439
440    /* Quit if we already have a border that isn't an internal one */
441    b = ewin->border;
442    if (b && strncmp(b->name, "__", 2))
443       goto done;
444
445    b = NULL;
446
447    if (ewin->props.no_border)
448       b = BorderFind("BORDERLESS");
449
450    if (!b)
451       b = WindowMatchEwinBorder(ewin);
452
453    if (!b)
454       b = BorderFind("DEFAULT");
455
456    if (!b)
457       b = BorderGetFallback();
458
459  done:
460    ewin->normal_border = ewin->border = b;
461 }
462
463 void
464 EwinBorderDetach(EWin * ewin)
465 {
466    const Border       *b = ewin->border;
467    int                 i;
468
469    if (!b)
470       return;
471
472    TooltipsSetPending(0, NULL, NULL);
473
474    EventCallbackUnregister(EoGetWin(ewin), 0, BorderFrameHandleEvents, ewin);
475    for (i = 0; i < b->num_winparts; i++)
476      {
477         EventCallbackUnregister(ewin->bits[i].win, 0,
478                                 BorderWinpartHandleEvents, &ewin->bits[i]);
479         if (ewin->bits[i].win)
480            EDestroyWindow(ewin->bits[i].win);
481      }
482    Efree(ewin->bits);
483    ewin->bits = NULL;
484    BorderDecRefcount(b);
485
486    ewin->border = NULL;
487
488    if (b->throwaway)
489       BorderDestroy((Border *) b);
490 }
491
492 void
493 EwinBorderSetTo(EWin * ewin, const Border * b)
494 {
495    int                 i;
496
497    if (ewin->border == b)
498       return;
499
500    if (b == NULL)
501      {
502         b = ewin->border;
503         ewin->border = NULL;
504      }
505
506    if (ewin->border)
507       EwinBorderDetach(ewin);
508
509    ewin->border = b;
510    BorderIncRefcount(b);
511    HintsSetWindowBorder(ewin);
512
513    ewin->state.no_border = b->num_winparts <= 0;
514
515    EventCallbackRegister(EoGetWin(ewin), 0, BorderFrameHandleEvents, ewin);
516
517    if (b->num_winparts > 0)
518       ewin->bits = EMALLOC(EWinBit, b->num_winparts);
519
520    for (i = 0; i < b->num_winparts; i++)
521      {
522         ewin->bits[i].ewin = ewin;      /* Reference to associated Ewin */
523
524         ewin->bits[i].win = ECreateWindow(EoGetWin(ewin), -10, -10, 1, 1, 0);
525         ECursorApply(b->part[i].ec, ewin->bits[i].win);
526         EMapWindow(ewin->bits[i].win);
527         EventCallbackRegister(ewin->bits[i].win, 0,
528                               BorderWinpartHandleEvents, &ewin->bits[i]);
529         if (b->part[i].flags & FLAG_TITLE)
530            ESelectInput(ewin->bits[i].win, EWIN_BORDER_TITLE_EVENT_MASK);
531         else
532            ESelectInput(ewin->bits[i].win, EWIN_BORDER_PART_EVENT_MASK);
533         ewin->bits[i].x = -10;
534         ewin->bits[i].y = -10;
535         ewin->bits[i].w = -10;
536         ewin->bits[i].h = -10;
537         ewin->bits[i].cx = -99;
538         ewin->bits[i].cy = -99;
539         ewin->bits[i].cw = -99;
540         ewin->bits[i].ch = -99;
541         ewin->bits[i].state = 0;
542         ewin->bits[i].expose = 0;
543         ewin->bits[i].left = 0;
544         ewin->bits[i].is = NULL;
545      }
546
547    {
548       Window             *wl;
549       int                 j = 0;
550
551       wl = EMALLOC(Window, b->num_winparts + 1);
552       for (i = b->num_winparts - 1; i >= 0; i--)
553         {
554            if (b->part[i].ontop)
555               wl[j++] = WinGetXwin(ewin->bits[i].win);
556         }
557       wl[j++] = WinGetXwin(ewin->win_container);
558       for (i = b->num_winparts - 1; i >= 0; i--)
559         {
560            if (!b->part[i].ontop)
561               wl[j++] = WinGetXwin(ewin->bits[i].win);
562         }
563       EXRestackWindows(wl, j);
564       Efree(wl);
565    }
566
567    if (!ewin->state.shaded)
568       EMoveWindow(ewin->win_container, b->border.left, b->border.top);
569
570    ewin->update.shape = 1;
571    EwinBorderCalcSizes(ewin, 0);
572    EwinStateUpdate(ewin);
573
574    SnapshotEwinUpdate(ewin, SNAP_USE_BORDER);
575 }
576
577 void
578 EwinBorderChange(EWin * ewin, const Border * b, int normal)
579 {
580    if (!b || ewin->border == b ||
581        ewin->inh_wm.b.border || ewin->state.fullscreen)
582       return;
583
584    EwinBorderSetTo(ewin, b);
585    EwinMoveResize(ewin, EoGetX(ewin), EoGetY(ewin),
586                   ewin->client.w, ewin->client.h);
587
588    if (normal)
589       ewin->normal_border = b;
590 }
591
592 static void
593 EwinBorderAssign(EWin * ewin, const Border * b)
594 {
595    if (!b || ewin->border == b || ewin->inh_wm.b.border)
596       return;
597
598    if (ewin->border)
599       BorderDecRefcount(ewin->border);
600    BorderIncRefcount(b);
601
602    ewin->border = ewin->normal_border = b;
603 }
604
605 void
606 EwinBorderSetInitially(EWin * ewin, const char *name)
607 {
608    EwinBorderAssign(ewin, BorderFind(name));
609 }
610
611 static Border      *
612 BorderCreate(const char *name)
613 {
614    Border             *b;
615
616    b = ECALLOC(Border, 1);
617    if (!b)
618       return NULL;
619
620    if (!border_list)
621       border_list = ecore_list_new();
622    ecore_list_prepend(border_list, b);
623
624    b->name = Estrdup(name);
625    b->group_border_name = NULL;
626    b->shadedir = 2;
627
628    return b;
629 }
630
631 static void
632 BorderDestroy(const Border * b)
633 {
634    int                 i;
635
636    if (!b)
637       return;
638
639    if (b->ref_count > 0)
640      {
641         DialogOK("Border Error!", _("%u references remain\n"), b->ref_count);
642         return;
643      }
644
645    ecore_list_node_remove(border_list, (Border *) b);
646
647    for (i = 0; i < b->num_winparts; i++)
648      {
649         ImageclassFree(b->part[i].iclass);
650         ActionclassFree(b->part[i].aclass);
651         TextclassFree(b->part[i].tclass);
652         ECursorFree(b->part[i].ec);
653      }
654
655    Efree(b->part);
656    Efree(b->name);
657    Efree(b->group_border_name);
658    ActionclassFree(b->aclass);
659 }
660
661 static int
662 _BorderMatchName(const void *data, const void *match)
663 {
664    return strcmp(((const Border *)data)->name, (const char *)match);
665 }
666
667 Border             *
668 BorderFind(const char *name)
669 {
670    if (!name)
671       return NULL;
672    return (Border *) ecore_list_find(border_list, _BorderMatchName, name);
673 }
674
675 static void
676 BorderWinpartAdd(Border * b, const char *iclass, const char *aclass,
677                  const char *tclass, const char *cclass, char ontop, int flags,
678                  char isregion __UNUSED__, int wmin, int wmax, int hmin,
679                  int hmax, int torigin, int txp, int txa, int typ, int tya,
680                  int borigin, int bxp, int bxa, int byp, int bya,
681                  char keep_for_shade)
682 {
683    int                 n;
684
685    b->num_winparts++;
686    n = b->num_winparts;
687
688    b->part = EREALLOC(WinPart, b->part, n);
689
690    b->part[n - 1].iclass = (iclass) ? ImageclassAlloc(iclass, 1) : NULL;
691    b->part[n - 1].aclass = (aclass) ? ActionclassAlloc(aclass) : NULL;
692    b->part[n - 1].tclass = (tclass) ? TextclassAlloc(tclass, 1) : NULL;
693    b->part[n - 1].ec = (cclass) ? ECursorAlloc(cclass) : NULL;
694
695    b->part[n - 1].ontop = ontop;
696    b->part[n - 1].flags = flags;
697    b->part[n - 1].keep_for_shade = keep_for_shade;
698    b->part[n - 1].geom.width.min = wmin;
699    b->part[n - 1].geom.width.max = wmax;
700    b->part[n - 1].geom.height.min = hmin;
701    b->part[n - 1].geom.height.max = hmax;
702    b->part[n - 1].geom.topleft.originbox = torigin;
703    b->part[n - 1].geom.topleft.x.percent = txp;
704    b->part[n - 1].geom.topleft.x.absolute = txa;
705    b->part[n - 1].geom.topleft.y.percent = typ;
706    b->part[n - 1].geom.topleft.y.absolute = tya;
707    b->part[n - 1].geom.bottomright.originbox = borigin;
708    b->part[n - 1].geom.bottomright.x.percent = bxp;
709    b->part[n - 1].geom.bottomright.x.absolute = bxa;
710    b->part[n - 1].geom.bottomright.y.percent = byp;
711    b->part[n - 1].geom.bottomright.y.absolute = bya;
712 }
713
714 void
715 EwinBorderMinShadeSize(EWin * ewin, int *mw, int *mh)
716 {
717    int                 i, pw, ph, w, h, min_w, min_h;
718    int                 leftborderwidth, rightborderwidth;
719    int                 topborderwidth, bottomborderwidth;
720
721    min_w = 32;
722    min_h = 32;
723
724    if (!ewin)
725       goto done;
726
727    pw = EoGetW(ewin);
728    ph = EoGetH(ewin);
729
730    for (i = 0; i < ewin->border->num_winparts; i++)
731       ewin->bits[i].w = -2;
732    for (i = 0; i < ewin->border->num_winparts; i++)
733       if (ewin->bits[i].w == -2)
734          BorderWinpartCalc(ewin, i, pw, ph);
735
736    switch (ewin->border->shadedir)
737      {
738      case 0:
739      case 1:
740         /* get the correct width, based on the borderparts that */
741         /*are remaining visible */
742         leftborderwidth = rightborderwidth = 0;
743         for (i = 0; i < ewin->border->num_winparts; i++)
744           {
745              if (!ewin->border->part[i].keep_for_shade)
746                 continue;
747
748              w = ewin->border->border.left - ewin->bits[i].x;
749              if (leftborderwidth < w)
750                 leftborderwidth = w;
751
752              w = ewin->bits[i].x + ewin->bits[i].w -
753                 (EoGetW(ewin) - ewin->border->border.right);
754              if (rightborderwidth < w)
755                 rightborderwidth = w;
756           }
757         pw = rightborderwidth + leftborderwidth;
758         break;
759      case 2:
760      case 3:
761         topborderwidth = bottomborderwidth = 0;
762         for (i = 0; i < ewin->border->num_winparts; i++)
763           {
764              if (!ewin->border->part[i].keep_for_shade)
765                 continue;
766
767              h = ewin->border->border.top - ewin->bits[i].y;
768              if (topborderwidth < h)
769                 topborderwidth = h;
770
771              h = ewin->bits[i].y + ewin->bits[i].h -
772                 (EoGetH(ewin) - ewin->border->border.bottom);
773              if (bottomborderwidth < h)
774                 bottomborderwidth = h;
775           }
776         ph = bottomborderwidth + topborderwidth;
777         break;
778      default:
779         break;
780      }
781
782    for (i = 0; i < ewin->border->num_winparts; i++)
783       ewin->bits[i].w = -2;
784    for (i = 0; i < ewin->border->num_winparts; i++)
785       if (ewin->bits[i].w == -2)
786          BorderWinpartCalc(ewin, i, pw, ph);
787
788    min_w = 0;
789    min_h = 0;
790    for (i = 0; i < ewin->border->num_winparts; i++)
791      {
792         if (!ewin->border->part[i].keep_for_shade)
793            continue;
794
795         w = ewin->bits[i].x + ewin->bits[i].w;
796         if (min_w < w)
797            min_w = w;
798
799         h = ewin->bits[i].y + ewin->bits[i].h;
800         if (min_h < h)
801            min_h = h;
802      }
803
804  done:
805    *mw = min_w;
806    *mh = min_h;
807 }
808
809 int
810 BorderWinpartIndex(EWin * ewin, Win win)
811 {
812    int                 i;
813
814    for (i = 0; i < ewin->border->num_winparts; i++)
815      {
816         if (win == ewin->bits[i].win)
817            return i;
818      }
819
820    return -1;                   /* Not found */
821 }
822
823 /*
824  * Border event handlers
825  */
826 #define DEBUG_BORDER_EVENTS 0
827
828 static void
829 BorderWinpartEventMouseDown(EWinBit * wbit, XEvent * ev)
830 {
831    EWin               *ewin = wbit->ewin;
832    int                 part = wbit - ewin->bits;
833
834    GrabPointerSet(wbit->win, 0, 0);
835
836    wbit->state = STATE_CLICKED;
837 #if DEBUG_BORDER_EVENTS
838    Eprintf("BorderWinpartEventMouseDown %#lx %d\n", WinGetXwin(wbit->win),
839            wbit->state);
840 #endif
841    BorderWinpartChange(ewin, part, 0);
842
843    FocusHandleClick(ewin, wbit->win);
844
845    if (ewin->border->part[part].aclass)
846       ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
847 }
848
849 static void
850 BorderWinpartEventMouseUp(EWinBit * wbit, XEvent * ev)
851 {
852    EWin               *ewin = wbit->ewin;
853    int                 part = wbit - ewin->bits;
854    int                 left = wbit->left;
855
856    if (ev)
857      {
858         GrabPointerRelease();
859         ActionsEnd(ewin);
860      }
861
862    if ((wbit->state == STATE_CLICKED) && (!wbit->left))
863       wbit->state = STATE_HILITED;
864    else
865       wbit->state = STATE_NORMAL;
866 #if DEBUG_BORDER_EVENTS
867    Eprintf("BorderWinpartEventMouseUp %#lx %d\n", WinGetXwin(wbit->win),
868            wbit->state);
869 #endif
870    BorderWinpartChange(ewin, part, 0);
871
872    /* Beware! Actions may destroy the current border */
873    wbit->left = 0;
874
875    if (ev && WinGetXwin(wbit->win) == Mode.events.last_bpress && !left &&
876        ewin->border->part[part].aclass)
877       ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
878 }
879
880 static void
881 BorderWinpartEventEnter(EWinBit * wbit, XEvent * ev)
882 {
883    EWin               *ewin = wbit->ewin;
884    int                 part = wbit - ewin->bits;
885
886 #if DEBUG_BORDER_EVENTS
887    Eprintf("BorderWinpartEventEnter %#lx %d\n", WinGetXwin(wbit->win),
888            wbit->state);
889 #endif
890    if (wbit->state == STATE_CLICKED)
891       wbit->left = 0;
892 #if 0                           /* Hmmm.. */
893    else
894 #endif
895      {
896         wbit->state = STATE_HILITED;
897         BorderWinpartChange(ewin, part, 0);
898         if (ewin->border->part[part].aclass)
899            ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
900      }
901 }
902
903 static void
904 BorderWinpartEventLeave(EWinBit * wbit, XEvent * ev)
905 {
906    EWin               *ewin = wbit->ewin;
907    int                 part = wbit - ewin->bits;
908
909 #if DEBUG_BORDER_EVENTS
910    Eprintf("BorderWinpartEventLeave %#lx %d\n", WinGetXwin(wbit->win),
911            wbit->state);
912 #endif
913    if (wbit->state == STATE_CLICKED)
914       wbit->left = 1;
915    else
916      {
917         wbit->state = STATE_NORMAL;
918         BorderWinpartChange(ewin, part, 0);
919         if (ewin->border->part[part].aclass)
920            ActionclassEvent(ewin->border->part[part].aclass, ev, ewin);
921      }
922 }
923
924 void
925 BorderCheckState(EWin * ewin, XEvent * ev)
926 {
927    int                 i;
928
929    for (i = 0; i < ewin->border->num_winparts; i++)
930      {
931         switch (ev->type)
932           {
933           default:
934              break;
935
936           case ButtonRelease:
937              BorderWinpartEventMouseUp(ewin->bits + i, NULL);
938              break;
939           }
940      }
941 }
942
943 static int
944 _BorderAutoshadeTimeout(void *data)
945 {
946    EWin               *ewin = (EWin *) data;
947
948    if (!EwinFindByPtr(ewin))    /* Check, window may be gone */
949       return 0;
950
951    ewin->timer = NULL;
952    EwinOpShade(ewin, OPSRC_USER, 1);
953
954    return 0;
955 }
956
957 static void
958 BorderFrameHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
959 {
960    EWin               *ewin = (EWin *) prm;
961    int                 x, y;
962
963    switch (ev->type)
964      {
965      case EnterNotify:
966         if (ewin->props.autoshade)
967           {
968              EwinOpShade(ewin, OPSRC_USER, 0);
969           }
970         if (ewin->border->aclass)
971            ActionclassEvent(ewin->border->aclass, ev, ewin);
972         break;
973      case LeaveNotify:
974         if (ewin->props.autoshade && !ewin->state.shaded)
975           {
976              EQueryPointer(EoGetWin(ewin), &x, &y, NULL, NULL);
977              if (x >= 4 && x < EoGetW(ewin) - 4 &&
978                  y >= 4 && y < EoGetH(ewin) - 4)
979                 break;
980              TIMER_DEL(ewin->timer);
981              TIMER_ADD(ewin->timer, .5, _BorderAutoshadeTimeout, ewin);
982           }
983         if (ewin->border->aclass)
984            ActionclassEvent(ewin->border->aclass, ev, ewin);
985         break;
986      }
987 }
988
989 static ActionClass *
990 BorderWinpartGetAclass(void *data)
991 {
992    EWinBit            *wbit = (EWinBit *) data;
993    EWin               *ewin;
994    int                 part;
995
996    /* Validate border part */
997    ewin = Mode.mouse_over_ewin;
998    if (!ewin)
999       return NULL;
1000
1001    part = wbit - ewin->bits;
1002    if (part < 0 || part >= ewin->border->num_winparts)
1003       return NULL;
1004
1005    return ewin->border->part[part].aclass;
1006 }
1007
1008 static void
1009 BorderWinpartHandleTooltip(EWinBit * wbit)
1010 {
1011    EWin               *ewin = wbit->ewin;
1012    int                 part = wbit - ewin->bits;
1013
1014    if (!ewin->border->part[part].aclass)
1015       return;
1016    TooltipsSetPending(0, BorderWinpartGetAclass, wbit);
1017 }
1018
1019 static void
1020 BorderWinpartHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1021 {
1022    EWinBit            *wbit = (EWinBit *) prm;
1023
1024    switch (ev->type)
1025      {
1026      case ButtonPress:
1027         BorderWinpartEventMouseDown(wbit, ev);
1028         break;
1029      case ButtonRelease:
1030         BorderWinpartEventMouseUp(wbit, ev);
1031         break;
1032      case EnterNotify:
1033         /* Beware! Actions may destroy the current border */
1034         BorderWinpartHandleTooltip(wbit);
1035         BorderWinpartEventEnter(wbit, ev);
1036         break;
1037      case LeaveNotify:
1038         BorderWinpartEventLeave(wbit, ev);
1039         break;
1040      case MotionNotify:
1041         BorderWinpartHandleTooltip(wbit);
1042         break;
1043      }
1044 }
1045
1046 /*
1047  * Configuration load/save
1048  */
1049 #include "conf.h"
1050
1051 static int
1052 BorderPartLoad(FILE * fs, char type __UNUSED__, Border * b)
1053 {
1054    int                 err = 0;
1055    char                s[FILEPATH_LEN_MAX];
1056    char                s2[FILEPATH_LEN_MAX];
1057    int                 i1, i2;
1058    char                iclass[64], aclass[64], tclass[64], cclass[64];
1059    char               *piclass, *paclass, *ptclass, *pcclass;
1060    char                ontop = 1;
1061    int                 flags = FLAG_BUTTON;
1062    char                isregion = 0, keepshade = 1;
1063    int                 wmin = 0, wmax = 0, hmin = 0, hmax = 0, torigin =
1064       0, txp = 0, txa = 0, typ = 0, tya = 0, borigin = 0;
1065    int                 bxp = 0, bxa = 0, byp = 0, bya = 0;
1066
1067    piclass = paclass = ptclass = pcclass = NULL;
1068
1069    while (GetLine(s, sizeof(s), fs))
1070      {
1071         i1 = ConfigParseline1(s, s2, NULL, NULL);
1072         i2 = atoi(s2);
1073         switch (i1)
1074           {
1075           case CONFIG_CLOSE:
1076              BorderWinpartAdd(b, piclass, paclass, ptclass, pcclass, ontop,
1077                               flags, isregion, wmin, wmax, hmin, hmax,
1078                               torigin, txp, txa, typ, tya,
1079                               borigin, bxp, bxa, byp, bya, keepshade);
1080              goto done;
1081           case CONFIG_IMAGECLASS:
1082           case BORDERPART_ICLASS:
1083              STRCPY(iclass, s2);
1084              piclass = iclass;
1085              break;
1086           case CONFIG_ACTIONCLASS:
1087           case BORDERPART_ACLASS:
1088              STRCPY(aclass, s2);
1089              paclass = aclass;
1090              break;
1091           case CONFIG_TEXT:
1092           case BORDERPART_TEXTCLASS:
1093              STRCPY(tclass, s2);
1094              ptclass = tclass;
1095              break;
1096           case CONFIG_CURSOR:
1097              STRCPY(cclass, s2);
1098              pcclass = cclass;
1099              break;
1100           case BORDERPART_ONTOP:
1101              ontop = i2;
1102              break;
1103           case BORDERPART_FLAGS:
1104              flags = i2;
1105              break;
1106           case BORDERPART_ISREGION:
1107              isregion = i2;
1108              break;
1109           case BORDERPART_WMIN:
1110              wmin = i2;
1111              if (!wmax)
1112                 wmax = wmin;
1113              break;
1114           case BORDERPART_WMAX:
1115              wmax = i2;
1116              break;
1117           case BORDERPART_HMIN:
1118              hmin = i2;
1119              if (!hmax)
1120                 hmax = hmin;
1121              break;
1122           case BORDERPART_HMAX:
1123              hmax = i2;
1124              break;
1125           case BORDERPART_TORIGIN:
1126              torigin = i2;
1127              break;
1128           case BORDERPART_TXP:
1129              txp = i2;
1130              break;
1131           case BORDERPART_TXA:
1132              txa = i2;
1133              break;
1134           case BORDERPART_TYP:
1135              typ = i2;
1136              break;
1137           case BORDERPART_TYA:
1138              tya = i2;
1139              break;
1140           case BORDERPART_BORIGIN:
1141              borigin = i2;
1142              break;
1143           case BORDERPART_BXP:
1144              bxp = i2;
1145              break;
1146           case BORDERPART_BXA:
1147              bxa = i2;
1148              break;
1149           case BORDERPART_BYP:
1150              byp = i2;
1151              break;
1152           case BORDERPART_BYA:
1153              bya = i2;
1154              break;
1155           case BORDERPART_KEEPSHADE:
1156              keepshade = i2;
1157              break;
1158           default:
1159              break;
1160           }
1161      }
1162    err = -1;
1163  done:
1164    return err;
1165 }
1166
1167 int
1168 BorderConfigLoad(FILE * fs)
1169 {
1170    int                 err = 0;
1171    Border             *b = 0;
1172    char                s[FILEPATH_LEN_MAX];
1173    char                s2[FILEPATH_LEN_MAX];
1174    int                 i1, i2;
1175
1176    while (GetLine(s, sizeof(s), fs))
1177      {
1178         i1 = ConfigParseline1(s, s2, NULL, NULL);
1179         i2 = atoi(s2);
1180
1181         switch (i1)
1182           {
1183           case CONFIG_CLOSE:
1184              goto done;
1185           case CONFIG_CLASSNAME:
1186           case BORDER_NAME:
1187              if (BorderFind(s2))
1188                {
1189                   SkipTillEnd(fs);
1190                   goto done;
1191                }
1192              b = BorderCreate(s2);
1193              continue;
1194           }
1195
1196         if (!b)
1197            break;
1198
1199         switch (i1)
1200           {
1201           default:
1202              break;
1203           case BORDER_INIT:
1204              if (i2 != CONFIG_OPEN)
1205                 break;
1206              err = BorderPartLoad(fs, i1, b);
1207              if (err)
1208                 break;
1209              break;
1210           case BORDER_GROUP_NAME:
1211              b->group_border_name = Estrdup(s2);
1212              break;
1213           case BORDER_LEFT:
1214              b->border.left = i2;
1215              break;
1216           case BORDER_RIGHT:
1217              b->border.right = i2;
1218              break;
1219           case BORDER_TOP:
1220              b->border.top = i2;
1221              break;
1222           case BORDER_BOTTOM:
1223              b->border.bottom = i2;
1224              break;
1225           case BORDER_SHADEDIR:
1226              b->shadedir = i2;
1227              break;
1228           case BORDER_CHANGES_SHAPE:
1229              b->changes_shape = i2;
1230              break;
1231           case CONFIG_ACTIONCLASS:
1232              b->aclass = ActionclassFind(s2);
1233              break;
1234           }
1235      }
1236    err = -1;
1237
1238  done:
1239    return err;
1240 }
1241
1242 Border             *
1243 BorderCreateFiller(int left, int right, int top, int bottom)
1244 {
1245    Border             *b;
1246
1247    b = BorderCreate("__FILLER");
1248    if (!b)
1249       return b;
1250
1251    b->throwaway = 1;
1252
1253    b->border.left = left;
1254    b->border.right = right;
1255    b->border.top = top;
1256    b->border.bottom = bottom;
1257
1258    ImageclassGetBlack();        /* Creates the __BLACK ImageClass */
1259
1260    if (top)
1261       BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1262                        1, 99999, 1, 99999,
1263                        -1, 0, 0, 0, 0, -1, 1024, -1, 0, top - 1, 1);
1264    if (bottom)
1265       BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1266                        1, 99999, 1, 99999,
1267                        -1, 0, 0, 1024, -bottom, -1, 1024, -1, 1024, -1, 1);
1268    if (left)
1269       BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1270                        1, 99999, 1, 99999,
1271                        -1, 0, 0, 0, top,
1272                        -1, 0, left - 1, 1024, -(bottom + 1), 1);
1273    if (right)
1274       BorderWinpartAdd(b, "__BLACK", NULL, NULL, NULL, 1, FLAG_BUTTON, 0,
1275                        1, 99999, 1, 99999,
1276                        -1, 1024, -right, 0, top,
1277                        -1, 1024, -1, 1024, -(bottom + 1), 1);
1278
1279    return b;
1280 }
1281
1282 void
1283 BordersForeach(void (*func) (Border * b, void *data), void *data)
1284 {
1285    ecore_list_for_each(border_list, (Ecore_For_Each) func, data);
1286 }
1287
1288 Border            **
1289 BordersGetList(int *pnum)
1290 {
1291    return (Border **) ecore_list_items_get(border_list, pnum);
1292 }
1293
1294 static ActionClass *
1295 BorderGetFallbackAclass(void)
1296 {
1297    ActionClass        *ac;
1298    Action             *aa;
1299
1300    ac = ActionclassFind("__fb_bd_ac");
1301    if (ac)
1302       return ac;
1303
1304    /* Create fallback actionclass for the fallback border */
1305    ac = ActionclassCreate("__fb_bd_ac", 0);
1306    if (!ac)
1307       return ac;
1308
1309    aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 1, 0, NULL, NULL);
1310    ActionclassAddAction(ac, aa);
1311    ActionAddTo(aa, "wop * mo ptr");
1312
1313    aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 2, 0, NULL, NULL);
1314    ActionclassAddAction(ac, aa);
1315    ActionAddTo(aa, "wop * close");
1316
1317    aa = ActionCreate(EVENT_MOUSE_DOWN, 1, 0, 0, 3, 0, NULL, NULL);
1318    ActionclassAddAction(ac, aa);
1319    ActionAddTo(aa, "wop * sz ptr");
1320
1321    return ac;
1322 }
1323
1324 static Border      *
1325 BorderGetFallback(void)
1326 {
1327    /*
1328     * This function creates simple internal data members to be used in 
1329     * emergencies - ie when all else fails - ie a button is told to use an
1330     * imageclass that doesn't exist, or no DEFAULT border is defined... at 
1331     * least E won't barf on us then.
1332     */
1333    Border             *b;
1334    ActionClass        *ac;
1335
1336    b = BorderFind("__fb_bd");
1337    if (b)
1338       return b;
1339
1340    ac = BorderGetFallbackAclass();      /* Creates the fallback ac */
1341
1342    /* Create fallback border */
1343    b = BorderCreate("__fb_bd");
1344    if (!b)
1345       return b;
1346
1347    b->border.left = 8;
1348    b->border.right = 8;
1349    b->border.top = 8;
1350    b->border.bottom = 8;
1351    BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1352                     1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 0, 0,
1353                     -1, 1024, -1, 0, 7, 1);
1354    BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1355                     1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 1024,
1356                     -8, -1, 1024, -1, 1024, -1, 1);
1357    BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1358                     1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 0, 0, 0, 8,
1359                     -1, 0, 7, 1024, -9, 1);
1360    BorderWinpartAdd(b, "__fb_ic", "__fb_bd_ac", NULL, NULL,
1361                     1, FLAG_BUTTON, 0, 8, 99999, 8, 99999, -1, 1024, -8, 0,
1362                     8, -1, 1024, -1, 1024, -9, 1);
1363
1364    return b;
1365 }