1 /**
2  * @file tray.c
3  * @author Joe Wingbermuehle
4  * @date 2004-2006
5  *
6  * @brief Tray functions.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "tray.h"
12 #include "color.h"
13 #include "main.h"
14 #include "pager.h"
15 #include "cursor.h"
16 #include "error.h"
17 #include "taskbar.h"
18 #include "menu.h"
19 #include "timing.h"
20 #include "screen.h"
21 #include "settings.h"
22 #include "event.h"
23 #include "client.h"
24 #include "misc.h"
25 #include "hint.h"
26 
27 #define DEFAULT_TRAY_WIDTH 32
28 #define DEFAULT_TRAY_HEIGHT 32
29 
30 #define TRAY_BORDER_SIZE   1
31 
32 static TrayType *trays;
33 static unsigned int trayCount;
34 
35 static void HandleTrayExpose(TrayType *tp, const XExposeEvent *event);
36 static void HandleTrayEnterNotify(TrayType *tp, const XCrossingEvent *event);
37 
38 static TrayComponentType *GetTrayComponent(TrayType *tp, int x, int y);
39 static void HandleTrayButtonPress(TrayType *tp, const XButtonEvent *event);
40 static void HandleTrayButtonRelease(TrayType *tp, const XButtonEvent *event);
41 static void HandleTrayMotionNotify(TrayType *tp, const XMotionEvent *event);
42 
43 static void ComputeTraySize(TrayType *tp);
44 static int ComputeMaxWidth(TrayType *tp);
45 static int ComputeTotalWidth(TrayType *tp);
46 static int ComputeMaxHeight(TrayType *tp);
47 static int ComputeTotalHeight(TrayType *tp);
48 static char CheckHorizontalFill(TrayType *tp);
49 static char CheckVerticalFill(TrayType *tp);
50 static void LayoutTray(TrayType *tp, int *variableSize,
51                        int *variableRemainder);
52 
53 static void SignalTray(const TimeType *now, int x, int y, Window w,
54                        void *data);
55 
56 
57 /** Initialize tray data. */
InitializeTray(void)58 void InitializeTray(void)
59 {
60    trays = NULL;
61    trayCount = 0;
62 }
63 
64 /** Startup trays. */
StartupTray(void)65 void StartupTray(void)
66 {
67    XSetWindowAttributes attr;
68    unsigned long attrMask;
69    TrayType *tp;
70    TrayComponentType *cp;
71    int variableSize;
72    int variableRemainder;
73    int width, height;
74    int xoffset, yoffset;
75 
76    for(tp = trays; tp; tp = tp->next) {
77 
78       LayoutTray(tp, &variableSize, &variableRemainder);
79 
80       /* Create the tray window. */
81       /* The window is created larger for a border. */
82       attrMask = CWOverrideRedirect;
83       attr.override_redirect = True;
84 
85       /* We can't use PointerMotionHintMask since the exact position
86        * of the mouse on the tray is important for popups. */
87       attrMask |= CWEventMask;
88       attr.event_mask
89          = ButtonPressMask
90          | ButtonReleaseMask
91          | SubstructureNotifyMask
92          | ExposureMask
93          | KeyPressMask
94          | KeyReleaseMask
95          | EnterWindowMask
96          | PointerMotionMask;
97 
98       attrMask |= CWBackPixel;
99       attr.background_pixel = colors[COLOR_TRAY_BG2];
100 
101       Assert(tp->width > 0);
102       Assert(tp->height > 0);
103       tp->window = JXCreateWindow(display, rootWindow,
104                                   tp->x, tp->y, tp->width, tp->height, 0,
105                                   rootDepth, InputOutput,
106                                   rootVisual, attrMask, &attr);
107       SetAtomAtom(tp->window, ATOM_NET_WM_WINDOW_TYPE,
108                   ATOM_NET_WM_WINDOW_TYPE_DOCK);
109 
110       if(settings.trayOpacity < UINT_MAX) {
111          SetCardinalAtom(tp->window, ATOM_NET_WM_WINDOW_OPACITY,
112                          settings.trayOpacity);
113       }
114 
115       SetDefaultCursor(tp->window);
116 
117       /* Create and layout items on the tray. */
118       xoffset = TRAY_BORDER_SIZE;
119       yoffset = TRAY_BORDER_SIZE;
120       for(cp = tp->components; cp; cp = cp->next) {
121 
122          if(cp->Create) {
123             if(tp->layout == LAYOUT_HORIZONTAL) {
124                height = tp->height - TRAY_BORDER_SIZE * 2;
125                width = cp->width;
126                if(width == 0) {
127                   width = variableSize;
128                   if(variableRemainder) {
129                      width += 1;
130                      variableRemainder -= 1;
131                   }
132                }
133             } else {
134                width = tp->width - TRAY_BORDER_SIZE * 2;
135                height = cp->height;
136                if(height == 0) {
137                   height = variableSize;
138                   if(variableRemainder) {
139                      height += 1;
140                      variableRemainder -= 1;
141                   }
142                }
143             }
144             cp->width = Max(1, width);
145             cp->height = Max(1, height);
146             (cp->Create)(cp);
147          }
148 
149          cp->x = xoffset;
150          cp->y = yoffset;
151          cp->screenx = tp->x + xoffset;
152          cp->screeny = tp->y + yoffset;
153 
154          if(cp->window != None) {
155             JXReparentWindow(display, cp->window, tp->window,
156                              xoffset, yoffset);
157          }
158 
159          if(tp->layout == LAYOUT_HORIZONTAL) {
160             xoffset += cp->width;
161          } else {
162             yoffset += cp->height;
163          }
164       }
165 
166       /* Show the tray. */
167       JXMapWindow(display, tp->window);
168 
169       trayCount += 1;
170 
171    }
172 
173    RequirePagerUpdate();
174    RequireTaskUpdate();
175 }
176 
177 /** Shutdown trays. */
ShutdownTray(void)178 void ShutdownTray(void)
179 {
180    TrayType *tp;
181    TrayComponentType *cp;
182 
183    for(tp = trays; tp; tp = tp->next) {
184       for(cp = tp->components; cp; cp = cp->next) {
185          if(cp->Destroy) {
186             (cp->Destroy)(cp);
187          }
188       }
189       JXDestroyWindow(display, tp->window);
190    }
191 }
192 
193 /** Destroy tray data. */
DestroyTray(void)194 void DestroyTray(void)
195 {
196    TrayType *tp;
197    TrayComponentType *cp;
198 
199    while(trays) {
200       tp = trays->next;
201       UnregisterCallback(SignalTray, trays);
202       while(trays->components) {
203          cp = trays->components->next;
204          Release(trays->components);
205          trays->components = cp;
206       }
207       Release(trays);
208 
209       trays = tp;
210    }
211 }
212 
213 /** Create an empty tray. */
CreateTray(void)214 TrayType *CreateTray(void)
215 {
216    TrayType *tp;
217 
218    tp = Allocate(sizeof(TrayType));
219 
220    tp->requestedX = 0;
221    tp->requestedY = -1;
222    tp->x = 0;
223    tp->y = -1;
224    tp->requestedWidth = 0;
225    tp->requestedHeight = 0;
226    tp->width = 0;
227    tp->height = 0;
228    tp->layer = DEFAULT_TRAY_LAYER;
229    tp->layout = LAYOUT_HORIZONTAL;
230    tp->valign = TALIGN_FIXED;
231    tp->halign = TALIGN_FIXED;
232 
233    tp->autoHide = THIDE_OFF;
234    tp->hidden = 0;
235 
236    tp->window = None;
237 
238    tp->components = NULL;
239    tp->componentsTail = NULL;
240 
241    tp->next = trays;
242    trays = tp;
243 
244    RegisterCallback(100, SignalTray, tp);
245 
246    return tp;
247 }
248 
249 /** Create an empty tray component. */
CreateTrayComponent(void)250 TrayComponentType *CreateTrayComponent(void)
251 {
252    TrayComponentType *cp;
253 
254    cp = Allocate(sizeof(TrayComponentType));
255 
256    cp->tray = NULL;
257    cp->object = NULL;
258 
259    cp->x = 0;
260    cp->y = 0;
261    cp->requestedWidth = 0;
262    cp->requestedHeight = 0;
263    cp->width = 0;
264    cp->height = 0;
265    cp->grabbed = 0;
266 
267    cp->window = None;
268    cp->pixmap = None;
269 
270    cp->Create = NULL;
271    cp->Destroy = NULL;
272 
273    cp->SetSize = NULL;
274    cp->Resize = NULL;
275 
276    cp->ProcessButtonPress = NULL;
277    cp->ProcessButtonRelease = NULL;
278    cp->ProcessMotionEvent = NULL;
279    cp->Redraw = NULL;
280 
281    cp->next = NULL;
282 
283    return cp;
284 }
285 
286 /** Add a tray component to a tray. */
AddTrayComponent(TrayType * tp,TrayComponentType * cp)287 void AddTrayComponent(TrayType *tp, TrayComponentType *cp)
288 {
289    cp->tray = tp;
290    if(tp->componentsTail) {
291       tp->componentsTail->next = cp;
292    } else {
293       tp->components = cp;
294    }
295    tp->componentsTail = cp;
296    cp->next = NULL;
297 }
298 
299 /** Compute the max component width. */
ComputeMaxWidth(TrayType * tp)300 int ComputeMaxWidth(TrayType *tp)
301 {
302    TrayComponentType *cp;
303    int result;
304    int temp;
305 
306    result = 0;
307    for(cp = tp->components; cp; cp = cp->next) {
308       temp = cp->width;
309       if(temp > 0) {
310          if(temp > result) {
311             result = temp;
312          }
313       }
314    }
315 
316    return result;
317 }
318 
319 /** Compute the total width of a tray. */
ComputeTotalWidth(TrayType * tp)320 int ComputeTotalWidth(TrayType *tp)
321 {
322    TrayComponentType *cp;
323    int result;
324 
325    result = 2 * TRAY_BORDER_SIZE;
326    for(cp = tp->components; cp; cp = cp->next) {
327       result += cp->width;
328    }
329 
330    return result;
331 }
332 
333 /** Compute the max component height. */
ComputeMaxHeight(TrayType * tp)334 int ComputeMaxHeight(TrayType *tp)
335 {
336    TrayComponentType *cp;
337    int result;
338    int temp;
339 
340    result = 0;
341    for(cp = tp->components; cp; cp = cp->next) {
342       temp = cp->height;
343       if(temp > 0) {
344          if(temp > result) {
345             result = temp;
346          }
347       }
348    }
349 
350    return result;
351 }
352 
353 /** Compute the total height of a tray. */
ComputeTotalHeight(TrayType * tp)354 int ComputeTotalHeight(TrayType *tp)
355 {
356    TrayComponentType *cp;
357    int result;
358 
359    result = 2 * TRAY_BORDER_SIZE;
360    for(cp = tp->components; cp; cp = cp->next) {
361       result += cp->height;
362    }
363 
364    return result;
365 }
366 
367 /** Check if the tray fills the screen horizontally. */
CheckHorizontalFill(TrayType * tp)368 char CheckHorizontalFill(TrayType *tp)
369 {
370    TrayComponentType *cp;
371 
372    for(cp = tp->components; cp; cp = cp->next) {
373       if(cp->width == 0) {
374          return 1;
375       }
376    }
377 
378    return 0;
379 }
380 
381 /** Check if the tray fills the screen vertically. */
CheckVerticalFill(TrayType * tp)382 char CheckVerticalFill(TrayType *tp)
383 {
384    TrayComponentType *cp;
385 
386    for(cp = tp->components; cp; cp = cp->next) {
387       if(cp->height == 0) {
388          return 1;
389       }
390    }
391 
392    return 0;
393 }
394 
395 /** Compute the size of a tray. */
ComputeTraySize(TrayType * tp)396 void ComputeTraySize(TrayType *tp)
397 {
398    TrayComponentType *cp;
399    const ScreenType *sp;
400    int x, y;
401 
402    /* Determine the first dimension. */
403    if(tp->layout == LAYOUT_HORIZONTAL) {
404 
405       if(tp->height == 0) {
406          tp->height = ComputeMaxHeight(tp) + TRAY_BORDER_SIZE * 2;
407       }
408       if(tp->height == 0) {
409          tp->height = DEFAULT_TRAY_HEIGHT;
410       }
411 
412    } else {
413 
414       if(tp->width == 0) {
415          tp->width = ComputeMaxWidth(tp) + TRAY_BORDER_SIZE * 2;
416       }
417       if(tp->width == 0) {
418          tp->width = DEFAULT_TRAY_WIDTH;
419       }
420 
421    }
422 
423    /* Now at least one size is known. Inform the components. */
424    for(cp = tp->components; cp; cp = cp->next) {
425       if(cp->SetSize) {
426          if(tp->layout == LAYOUT_HORIZONTAL) {
427             (cp->SetSize)(cp, 0, tp->height - TRAY_BORDER_SIZE * 2);
428          } else {
429             (cp->SetSize)(cp, tp->width - TRAY_BORDER_SIZE * 2, 0);
430          }
431       }
432    }
433 
434    /* Initialize the coordinates. */
435    tp->x = tp->requestedX;
436    tp->y = tp->requestedY;
437 
438    /* Determine on which screen the tray will reside. */
439    switch(tp->valign) {
440    case TALIGN_TOP:
441       y = 0;
442       break;
443    case TALIGN_BOTTOM:
444       y = rootHeight - 1;
445       break;
446    case TALIGN_CENTER:
447       y = 1 + rootHeight / 2;
448       break;
449    default:
450       if(tp->y < 0) {
451          y = rootHeight + tp->y;
452       } else {
453          y = tp->y;
454       }
455       break;
456    }
457    switch(tp->halign) {
458    case TALIGN_LEFT:
459       x = 0;
460       break;
461    case TALIGN_RIGHT:
462       x = rootWidth - 1;
463       break;
464    case TALIGN_CENTER:
465       x = 1 + rootWidth / 2;
466       break;
467    default:
468       if(tp->x < 0) {
469          x = rootWidth + tp->x;
470       } else {
471          x = tp->x;
472       }
473       break;
474    }
475    sp = GetCurrentScreen(x, y);
476 
477    /* Determine the missing dimension. */
478    if(tp->layout == LAYOUT_HORIZONTAL) {
479       if(tp->width == 0) {
480          if(CheckHorizontalFill(tp)) {
481             tp->width = sp->width + sp->x - x;
482          } else {
483             tp->width = ComputeTotalWidth(tp);
484          }
485          if(tp->width == 0) {
486             tp->width = DEFAULT_TRAY_WIDTH;
487          }
488       }
489    } else {
490       if(tp->height == 0) {
491          if(CheckVerticalFill(tp)) {
492             tp->height = sp->height + sp->y - y;
493          } else {
494             tp->height = ComputeTotalHeight(tp);
495          }
496          if(tp->height == 0) {
497             tp->height = DEFAULT_TRAY_HEIGHT;
498          }
499       }
500    }
501 
502    /* Compute the tray location. */
503    switch(tp->valign) {
504    case TALIGN_TOP:
505       tp->y = sp->y;
506       break;
507    case TALIGN_BOTTOM:
508       tp->y = sp->y + sp->height - tp->height;
509       break;
510    case TALIGN_CENTER:
511       tp->y = sp->y + (sp->height - tp->height) / 2;
512       break;
513    default:
514       if(tp->y < 0) {
515          tp->y = sp->y + sp->height - tp->height + tp->y + 1;
516       }
517       break;
518    }
519 
520    switch(tp->halign) {
521    case TALIGN_LEFT:
522       tp->x = sp->x;
523       break;
524    case TALIGN_RIGHT:
525       tp->x = sp->x + sp->width - tp->width;
526       break;
527    case TALIGN_CENTER:
528       tp->x = sp->x + (sp->width - tp->width) / 2;
529       break;
530    default:
531       if(tp->x < 0) {
532          tp->x = sp->x + sp->width - tp->width + tp->x + 1;
533       }
534       break;
535    }
536 }
537 
538 /** Display a tray (for autohide). */
ShowTray(TrayType * tp)539 void ShowTray(TrayType *tp)
540 {
541    Window win1, win2;
542    int winx, winy;
543    unsigned int mask;
544    int mousex, mousey;
545 
546    if(tp->hidden) {
547 
548       tp->hidden = 0;
549       JXMoveWindow(display, tp->window, tp->x, tp->y);
550 
551       JXQueryPointer(display, rootWindow, &win1, &win2,
552                      &mousex, &mousey, &winx, &winy, &mask);
553       SetMousePosition(mousex, mousey, win2);
554 
555    }
556 }
557 
558 /** Show all trays. */
ShowAllTrays(void)559 void ShowAllTrays(void)
560 {
561    TrayType *tp;
562 
563    if(shouldExit) {
564       return;
565    }
566 
567    for(tp = trays; tp; tp = tp->next) {
568       ShowTray(tp);
569    }
570 }
571 
572 /** Hide a tray (for autohide). */
HideTray(TrayType * tp)573 void HideTray(TrayType *tp)
574 {
575    const ScreenType *sp;
576    int x, y;
577 
578    /* Don't hide if the tray is raised. */
579    if(tp->autoHide & THIDE_RAISED) {
580       return;
581    }
582 
583    tp->hidden = 1;
584 
585    /* Determine where to move the tray. */
586    sp = GetCurrentScreen(tp->x, tp->y);
587    switch(tp->autoHide) {
588    case THIDE_LEFT:
589       x = sp->y - tp->width + 1;
590       y = tp->y;
591       break;
592    case THIDE_RIGHT:
593       x = sp->y + sp->width - 1;
594       y = tp->y;
595       break;
596    case THIDE_TOP:
597       x = tp->x;
598       y = sp->y - tp->height + 1;
599       break;
600    case THIDE_BOTTOM:
601       x = tp->x;
602       y = sp->y + sp->height - 1;
603       break;
604    default:
605       Assert(0);
606       return;
607    }
608 
609    /* Move and redraw. */
610    JXMoveWindow(display, tp->window, x, y);
611    DrawSpecificTray(tp);
612 }
613 
614 /** Process a tray event. */
ProcessTrayEvent(const XEvent * event)615 char ProcessTrayEvent(const XEvent *event)
616 {
617    TrayType *tp;
618 
619    for(tp = trays; tp; tp = tp->next) {
620       if(event->xany.window == tp->window) {
621          switch(event->type) {
622          case Expose:
623             HandleTrayExpose(tp, &event->xexpose);
624             return 1;
625          case EnterNotify:
626             HandleTrayEnterNotify(tp, &event->xcrossing);
627             return 1;
628          case ButtonPress:
629             HandleTrayButtonPress(tp, &event->xbutton);
630             return 1;
631          case ButtonRelease:
632             HandleTrayButtonRelease(tp, &event->xbutton);
633             return 1;
634          case MotionNotify:
635             HandleTrayMotionNotify(tp, &event->xmotion);
636             return 1;
637          default:
638             return 0;
639          }
640       }
641    }
642 
643    return 0;
644 }
645 
646 /** Signal the tray (needed for autohide). */
SignalTray(const TimeType * now,int x,int y,Window w,void * data)647 void SignalTray(const TimeType *now, int x, int y, Window w, void *data)
648 {
649    TrayType *tp = (TrayType*)data;
650    if(tp->autoHide != THIDE_OFF && !tp->hidden && !menuShown) {
651       if(x < tp->x || x >= tp->x + tp->width
652          || y < tp->y || y >= tp->y + tp->height) {
653          HideTray(tp);
654       }
655    }
656 }
657 
658 /** Handle a tray expose event. */
HandleTrayExpose(TrayType * tp,const XExposeEvent * event)659 void HandleTrayExpose(TrayType *tp, const XExposeEvent *event)
660 {
661    DrawSpecificTray(tp);
662 }
663 
664 /** Handle a tray enter notify (for autohide). */
HandleTrayEnterNotify(TrayType * tp,const XCrossingEvent * event)665 void HandleTrayEnterNotify(TrayType *tp, const XCrossingEvent *event)
666 {
667    ShowTray(tp);
668 }
669 
670 /** Get the tray component under the given coordinates. */
GetTrayComponent(TrayType * tp,int x,int y)671 TrayComponentType *GetTrayComponent(TrayType *tp, int x, int y)
672 {
673    TrayComponentType *cp;
674    int xoffset, yoffset;
675 
676    xoffset = 0;
677    yoffset = 0;
678    for(cp = tp->components; cp; cp = cp->next) {
679       const int startx = xoffset;
680       const int starty = yoffset;
681       const int width = cp->width;
682       const int height = cp->height;
683       if(x >= startx && x < startx + width) {
684          if(y >= starty && y < starty + height) {
685             return cp;
686          }
687       }
688       if(tp->layout == LAYOUT_HORIZONTAL) {
689          xoffset += width;
690       } else {
691          yoffset += height;
692       }
693    }
694 
695    return NULL;
696 }
697 
698 /** Handle a button press on a tray. */
HandleTrayButtonPress(TrayType * tp,const XButtonEvent * event)699 void HandleTrayButtonPress(TrayType *tp, const XButtonEvent *event)
700 {
701    TrayComponentType *cp = GetTrayComponent(tp, event->x, event->y);
702    if(cp && cp->ProcessButtonPress) {
703       const int x = event->x - cp->x;
704       const int y = event->y - cp->y;
705       const int mask = event->button;
706       DiscardButtonEvents();
707       (cp->ProcessButtonPress)(cp, x, y, mask);
708    }
709 }
710 
711 /** Handle a button release on a tray. */
HandleTrayButtonRelease(TrayType * tp,const XButtonEvent * event)712 void HandleTrayButtonRelease(TrayType *tp, const XButtonEvent *event)
713 {
714    TrayComponentType *cp;
715 
716    // First inform any components that have a grab.
717    for(cp = tp->components; cp; cp = cp->next) {
718       if(cp->grabbed) {
719          const int x = event->x - cp->x;
720          const int y = event->y - cp->y;
721          const int mask = event->button;
722          (cp->ProcessButtonRelease)(cp, x, y, mask);
723          JXUngrabPointer(display, CurrentTime);
724          cp->grabbed = 0;
725          return;
726       }
727    }
728 
729    cp = GetTrayComponent(tp, event->x, event->y);
730    if(cp && cp->ProcessButtonRelease) {
731       const int x = event->x - cp->x;
732       const int y = event->y - cp->y;
733       const int mask = event->button;
734       (cp->ProcessButtonRelease)(cp, x, y, mask);
735    }
736 }
737 
738 /** Handle a motion notify event. */
HandleTrayMotionNotify(TrayType * tp,const XMotionEvent * event)739 void HandleTrayMotionNotify(TrayType *tp, const XMotionEvent *event)
740 {
741    TrayComponentType *cp = GetTrayComponent(tp, event->x, event->y);
742    if(cp && cp->ProcessMotionEvent) {
743       const int x = event->x - cp->x;
744       const int y = event->y - cp->y;
745       const int mask = event->state;
746       (cp->ProcessMotionEvent)(cp, x, y, mask);
747    }
748 }
749 
750 /** Draw all trays. */
DrawTray(void)751 void DrawTray(void)
752 {
753    TrayType *tp;
754 
755    if(shouldExit) {
756       return;
757    }
758 
759    for(tp = trays; tp; tp = tp->next) {
760       DrawSpecificTray(tp);
761    }
762 }
763 
764 /** Draw a specific tray. */
DrawSpecificTray(const TrayType * tp)765 void DrawSpecificTray(const TrayType *tp)
766 {
767    TrayComponentType *cp;
768 
769    for(cp = tp->components; cp; cp = cp->next) {
770       UpdateSpecificTray(tp, cp);
771    }
772 
773    if(settings.trayDecorations == DECO_MOTIF) {
774       JXSetForeground(display, rootGC, colors[COLOR_TRAY_UP]);
775       JXDrawLine(display, tp->window, rootGC, 0, 0, tp->width - 1, 0);
776       JXDrawLine(display, tp->window, rootGC, 0, tp->height - 1, 0, 0);
777 
778       JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]);
779       JXDrawLine(display, tp->window, rootGC, 0, tp->height - 1,
780                  tp->width - 1, tp->height - 1);
781       JXDrawLine(display, tp->window, rootGC, tp->width - 1, 0,
782                  tp->width - 1, tp->height - 1);
783    } else {
784       JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]);
785       JXDrawRectangle(display, tp->window, rootGC, 0, 0,
786                       tp->width - 1, tp->height - 1);
787    }
788 }
789 
790 /** Raise tray windows. */
RaiseTrays(void)791 void RaiseTrays(void)
792 {
793    TrayType *tp;
794    for(tp = trays; tp; tp = tp->next) {
795       tp->autoHide |= THIDE_RAISED;
796       ShowTray(tp);
797       JXRaiseWindow(display, tp->window);
798    }
799 }
800 
801 /** Lower tray windows. */
LowerTrays(void)802 void LowerTrays(void)
803 {
804    TrayType *tp;
805    for(tp = trays; tp; tp = tp->next) {
806       tp->autoHide &= ~THIDE_RAISED;
807    }
808    RequireRestack();
809 }
810 
811 /** Update a specific component on a tray. */
UpdateSpecificTray(const TrayType * tp,const TrayComponentType * cp)812 void UpdateSpecificTray(const TrayType *tp, const TrayComponentType *cp)
813 {
814    if(JUNLIKELY(shouldExit)) {
815       return;
816    }
817 
818    /* If the tray is hidden, draw only the background. */
819    if(cp->pixmap != None) {
820       JXCopyArea(display, cp->pixmap, tp->window, rootGC, 0, 0,
821                  cp->width, cp->height, cp->x, cp->y);
822    }
823 }
824 
825 /** Layout tray components on a tray. */
LayoutTray(TrayType * tp,int * variableSize,int * variableRemainder)826 void LayoutTray(TrayType *tp, int *variableSize, int *variableRemainder)
827 {
828    TrayComponentType *cp;
829    unsigned int variableCount;
830    int width, height;
831    int temp;
832 
833    if(tp->requestedWidth >= 0) {
834       tp->width = tp->requestedWidth;
835    } else {
836       tp->width = rootWidth + tp->requestedWidth - tp->x;
837    }
838    if(tp->requestedHeight >= 0) {
839       tp->height = tp->requestedHeight;
840    } else {
841       tp->height = rootHeight + tp->requestedHeight - tp->y;
842    }
843 
844    for(cp = tp->components; cp; cp = cp->next) {
845       if(cp->requestedWidth != 0) {
846          cp->width = cp->requestedWidth;
847       } else {
848          cp->width = 0;
849       }
850       if(cp->requestedHeight != 0) {
851          cp->height = cp->requestedHeight;
852       } else {
853          cp->height = 0;
854       }
855    }
856 
857    ComputeTraySize(tp);
858 
859    /* Get the remaining size after setting fixed size components. */
860    /* Also, keep track of the number of variable size components. */
861    width = tp->width - TRAY_BORDER_SIZE * 2;
862    height = tp->height - TRAY_BORDER_SIZE * 2;
863    variableCount = 0;
864    for(cp = tp->components; cp; cp = cp->next) {
865       if(tp->layout == LAYOUT_HORIZONTAL) {
866          temp = cp->width;
867          if(temp > 0) {
868             width -= temp;
869          } else {
870             variableCount += 1;
871          }
872       } else {
873          temp = cp->height;
874          if(temp > 0) {
875             height -= temp;
876          } else {
877             variableCount += 1;
878          }
879       }
880    }
881 
882    /* Distribute excess size among variable size components.
883     * If there are no variable size components, shrink the tray.
884     * If we are out of room, just give them a size of one.
885     */
886    *variableSize = 1;
887    *variableRemainder = 0;
888    if(tp->layout == LAYOUT_HORIZONTAL) {
889       if(variableCount) {
890          if(width >= variableCount) {
891             *variableSize = width / variableCount;
892             *variableRemainder = width % variableCount;
893          }
894       } else if(width > 0) {
895          tp->width -= width;
896       }
897    } else {
898       if(variableCount) {
899          if(height >= variableCount) {
900             *variableSize = height / variableCount;
901             *variableRemainder = height % variableCount;
902          }
903       } else if(height > 0) {
904          tp->height -= height;
905       }
906    }
907 
908    tp->width = Max(1, tp->width);
909    tp->height = Max(1, tp->height);
910 }
911 
912 /** Resize a tray. */
ResizeTray(TrayType * tp)913 void ResizeTray(TrayType *tp)
914 {
915    TrayComponentType *cp;
916    int variableSize;
917    int variableRemainder;
918    int xoffset, yoffset;
919    int width, height;
920 
921    Assert(tp);
922 
923    LayoutTray(tp, &variableSize, &variableRemainder);
924 
925    /* Reposition items on the tray. */
926    xoffset = TRAY_BORDER_SIZE;
927    yoffset = TRAY_BORDER_SIZE;
928    for(cp = tp->components; cp; cp = cp->next) {
929 
930       cp->x = xoffset;
931       cp->y = yoffset;
932       cp->screenx = tp->x + xoffset;
933       cp->screeny = tp->y + yoffset;
934 
935       if(cp->Resize) {
936          if(tp->layout == LAYOUT_HORIZONTAL) {
937             height = tp->height - TRAY_BORDER_SIZE * 2;
938             width = cp->width;
939             if(width == 0) {
940                width = variableSize;
941                if(variableRemainder) {
942                   width += 1;
943                   variableRemainder -= 1;
944                }
945             }
946          } else {
947             width = tp->width - TRAY_BORDER_SIZE * 2;
948             height = cp->height;
949             if(height == 0) {
950                height = variableSize;
951                if(variableRemainder) {
952                   height += 1;
953                   variableRemainder -= 1;
954                }
955             }
956          }
957          cp->width = width;
958          cp->height = height;
959          (cp->Resize)(cp);
960       }
961 
962       if(cp->window != None) {
963          JXMoveWindow(display, cp->window, xoffset, yoffset);
964       }
965 
966       if(tp->layout == LAYOUT_HORIZONTAL) {
967          xoffset += cp->width;
968       } else {
969          yoffset += cp->height;
970       }
971    }
972 
973    JXMoveResizeWindow(display, tp->window, tp->x, tp->y,
974                       tp->width, tp->height);
975 
976    RequireTaskUpdate();
977    DrawSpecificTray(tp);
978 
979    if(tp->hidden) {
980       HideTray(tp);
981    }
982 }
983 
984 /** Draw the tray background on a drawable. */
ClearTrayDrawable(const TrayComponentType * cp)985 void ClearTrayDrawable(const TrayComponentType *cp)
986 {
987    const Drawable d = cp->pixmap != None ? cp->pixmap : cp->window;
988    if(colors[COLOR_TRAY_BG1] == colors[COLOR_TRAY_BG2]) {
989       JXSetForeground(display, rootGC, colors[COLOR_TRAY_BG1]);
990       JXFillRectangle(display, d, rootGC, 0, 0, cp->width, cp->height);
991    } else {
992       DrawHorizontalGradient(d, rootGC, colors[COLOR_TRAY_BG1],
993                              colors[COLOR_TRAY_BG2], 0, 0,
994                              cp->width, cp->height);
995    }
996 }
997 
998 /** Get a linked list of trays. */
GetTrays(void)999 TrayType *GetTrays(void)
1000 {
1001    return trays;
1002 }
1003 
1004 /** Get the number of trays. */
GetTrayCount(void)1005 unsigned int GetTrayCount(void)
1006 {
1007    return trayCount;
1008 }
1009 
1010 /** Determine if a tray should autohide. */
SetAutoHideTray(TrayType * tp,TrayAutoHideType autohide)1011 void SetAutoHideTray(TrayType *tp, TrayAutoHideType autohide)
1012 {
1013    Assert(tp);
1014    tp->autoHide = autohide;
1015 }
1016 
1017 /** Set the x-coordinate of a tray. */
SetTrayX(TrayType * tp,const char * str)1018 void SetTrayX(TrayType *tp, const char *str)
1019 {
1020    Assert(tp);
1021    Assert(str);
1022    tp->requestedX = atoi(str);
1023 }
1024 
1025 /** Set the y-coordinate of a tray. */
SetTrayY(TrayType * tp,const char * str)1026 void SetTrayY(TrayType *tp, const char *str)
1027 {
1028    Assert(tp);
1029    Assert(str);
1030    tp->requestedY = atoi(str);
1031 }
1032 
1033 /** Set the width of a tray. */
SetTrayWidth(TrayType * tp,const char * str)1034 void SetTrayWidth(TrayType *tp, const char *str)
1035 {
1036    tp->requestedWidth = atoi(str);
1037 }
1038 
1039 /** Set the height of a tray. */
SetTrayHeight(TrayType * tp,const char * str)1040 void SetTrayHeight(TrayType *tp, const char *str)
1041 {
1042    tp->requestedHeight = atoi(str);
1043 }
1044 
1045 
1046 /** Set the tray orientation. */
SetTrayLayout(TrayType * tp,const char * str)1047 void SetTrayLayout(TrayType *tp, const char *str)
1048 {
1049    if(!str) {
1050 
1051       /* Compute based on requested size. */
1052 
1053    } else if(!strcmp(str, "horizontal")) {
1054 
1055       tp->layout = LAYOUT_HORIZONTAL;
1056       return;
1057 
1058    } else if(!strcmp(str, "vertical")) {
1059 
1060       tp->layout = LAYOUT_VERTICAL;
1061       return;
1062 
1063    } else {
1064       Warning(_("invalid tray layout: \"%s\""), str);
1065    }
1066 
1067    /* Prefer horizontal layout, but use vertical if
1068     * width is finite and height is larger than width or infinite.
1069     */
1070    if(tp->requestedWidth > 0
1071       && (tp->requestedHeight == 0
1072       || tp->requestedHeight > tp->requestedWidth)) {
1073       tp->layout = LAYOUT_VERTICAL;
1074    } else {
1075       tp->layout = LAYOUT_HORIZONTAL;
1076    }
1077 }
1078 
1079 /** Set the layer for a tray. */
SetTrayLayer(TrayType * tp,WinLayerType layer)1080 void SetTrayLayer(TrayType *tp, WinLayerType layer)
1081 {
1082    tp->layer = layer;
1083 }
1084 
1085 /** Set the horizontal tray alignment. */
SetTrayHorizontalAlignment(TrayType * tp,const char * str)1086 void SetTrayHorizontalAlignment(TrayType *tp, const char *str)
1087 {
1088    static const StringMappingType mapping[] = {
1089       { "center",    TALIGN_CENTER  },
1090       { "fixed",     TALIGN_FIXED   },
1091       { "left",      TALIGN_LEFT    },
1092       { "right",     TALIGN_RIGHT   }
1093    };
1094 
1095    if(!str) {
1096       tp->halign = TALIGN_FIXED;
1097    } else {
1098       const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str);
1099       if(JLIKELY(x >= 0)) {
1100          tp->halign = x;
1101       } else {
1102          Warning(_("invalid tray horizontal alignment: \"%s\""), str);
1103          tp->halign = TALIGN_FIXED;
1104       }
1105    }
1106 }
1107 
1108 /** Set the vertical tray alignment. */
SetTrayVerticalAlignment(TrayType * tp,const char * str)1109 void SetTrayVerticalAlignment(TrayType *tp, const char *str)
1110 {
1111    static const StringMappingType mapping[] = {
1112       { "bottom",    TALIGN_BOTTOM  },
1113       { "center",    TALIGN_CENTER  },
1114       { "fixed",     TALIGN_FIXED   },
1115       { "top",       TALIGN_TOP     }
1116    };
1117 
1118    if(!str) {
1119       tp->valign = TALIGN_FIXED;
1120    } else {
1121       const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str);
1122       if(JLIKELY(x >= 0)) {
1123          tp->valign = x;
1124       } else {
1125          Warning(_("invalid tray vertical alignment: \"%s\""), str);
1126          tp->valign = TALIGN_FIXED;
1127       }
1128    }
1129 }
1130