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