1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2002 Marko Macek
5  *
6  * TaskBar
7  */
8 
9 #include "config.h"
10 #include "wmtaskbar.h"
11 #include "wmframe.h"
12 #include "wmmgr.h"
13 #include "wmconfig.h"
14 #include "wmprog.h"
15 #include "wmwinmenu.h"
16 #include "wmapp.h"
17 #include "wmwinlist.h"
18 #include <unistd.h>
19 
20 #include "aaddressbar.h"
21 #include "aclock.h"
22 #include "akeyboard.h"
23 #include "acpustatus.h"
24 #include "amemstatus.h"
25 #include "apppstatus.h"
26 #include "amailbox.h"
27 #include "objbar.h"
28 #include "objbutton.h"
29 #include "atasks.h"
30 #include "atray.h"
31 #include "aworkspaces.h"
32 #include "yxtray.h"
33 #include "prefs.h"
34 #include "yprefs.h"
35 #include "wpixmaps.h"
36 #include "aapm.h"
37 
38 #include "intl.h"
39 
40 TaskBar *taskBar;
41 
42 YColorName taskBarBg(&clrDefaultTaskBar);
43 
EdgeTrigger(TaskBar * owner)44 EdgeTrigger::EdgeTrigger(TaskBar *owner):
45     fTaskBar(owner),
46     fHideOrShow(Hide)
47 {
48     setStyle(wsOverrideRedirect | wsInputOnly);
49     setPointer(YWMApp::leftPointer);
50     setDND(enabled());
51     setTitle("IceEdge");
52 }
53 
~EdgeTrigger()54 EdgeTrigger::~EdgeTrigger() {
55 }
56 
startTimer(HideOrShow show)57 void EdgeTrigger::startTimer(HideOrShow show) {
58     fHideOrShow = show;
59     long delay = max(10, show ? autoShowDelay : autoHideDelay);
60     fAutoHideTimer->setTimer(delay, this, true);
61 }
62 
stopTimer()63 void EdgeTrigger::stopTimer() {
64     fHideOrShow = Hide;
65     fAutoHideTimer = null;
66 }
67 
enabled()68 bool EdgeTrigger::enabled() {
69     return (taskBarAutoHide | (taskBarFullscreenAutoShow & !taskBarKeepBelow));
70 }
71 
show(bool enable)72 void EdgeTrigger::show(bool enable) {
73     bool enabled(this->enabled());
74     if (enable && enabled) {
75         YWindow::show();
76     } else {
77         YWindow::hide();
78         if (enabled) {
79             startTimer();
80         }
81     }
82 }
83 
handleCrossing(const XCrossingEvent & crossing)84 void EdgeTrigger::handleCrossing(const XCrossingEvent &crossing) {
85     if (crossing.type == EnterNotify /* && crossing.mode != NotifyNormal */) {
86         unsigned long last = YWindow::getLastEnterNotifySerial();
87         if (crossing.serial != last && crossing.serial != last + 1) {
88             MSG(("enter notify %d %d", crossing.mode, crossing.detail));
89             startTimer(Show);
90         }
91     } else if (crossing.type == LeaveNotify /* && crossing.mode != NotifyNormal */) {
92         MSG(("leave notify"));
93         stopTimer();
94     }
95 }
96 
handleDNDEnter()97 void EdgeTrigger::handleDNDEnter() {
98     startTimer(Show);
99 }
100 
handleDNDLeave()101 void EdgeTrigger::handleDNDLeave() {
102     startTimer();
103 }
104 
handleTimer(YTimer * t)105 bool EdgeTrigger::handleTimer(YTimer *t) {
106     MSG(("taskbar handle timer"));
107     return fTaskBar->autoTimer(fHideOrShow);
108 }
109 
TaskBar(IApp * app,YWindow * aParent,YActionListener * wmActionListener,YSMListener * smActionListener)110 TaskBar::TaskBar(IApp *app, YWindow *aParent, YActionListener *wmActionListener, YSMListener *smActionListener):
111     YFrameClient(aParent, nullptr),
112     fSurface(taskBarBg, taskbackPixmap, taskbackPixbuf),
113     fTasks(nullptr),
114     fCollapseButton(nullptr),
115     fWindowTray(nullptr),
116     fKeyboardStatus(nullptr),
117     fMailBoxControl(nullptr),
118     fMEMStatus(nullptr),
119     fCPUStatus(nullptr),
120     fApm(nullptr),
121     fNetStatus(nullptr),
122     fObjectBar(nullptr),
123     fApplications(nullptr),
124     fWinList(nullptr),
125     fShowDesktop(nullptr),
126     fAddressBar(nullptr),
127     fWorkspaces(nullptr),
128     fDesktopTray(nullptr),
129     fEdgeTrigger(nullptr),
130     wmActionListener(wmActionListener),
131     smActionListener(smActionListener),
132     app(app),
133     fIsHidden(taskBarAutoHide),
134     fFullscreen(false),
135     fIsCollapsed(false),
136     fMenuShown(false),
137     fNeedRelayout(true),
138     fButtonUpdate(false),
139     fWorkspacesUpdate(false)
140 {
141     taskBar = this;
142 
143     addStyle(wsDesktopAware | wsTakeFocus | wsNoExpose);
144     setWinHintsHint(WinHintsSkipFocus |
145                     WinHintsSkipWindowMenu |
146                     WinHintsSkipTaskBar);
147 
148     setWorkspaceHint(AllWorkspaces);
149     updateWinLayer();
150     Atom protocols[2] = {
151       _XA_WM_DELETE_WINDOW,
152       _XA_WM_TAKE_FOCUS
153     };
154     XSetWMProtocols(xapp->display(), handle(), protocols, 2);
155     XWMHints wmhints = { InputHint, False, };
156     ClassHint clhint("icewm", "TaskBar");
157     YTextProperty text("TaskBar");
158     XSetWMProperties(xapp->display(), handle(), &text, &text,
159                      nullptr, 0, nullptr, &wmhints, &clhint);
160     setNetPid();
161 
162     setMwmHints(MwmHints(
163        MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
164        MWM_FUNC_MOVE));
165     setFrameState(NormalState);
166     setPointer(YWMApp::leftPointer);
167     setDND(true);
168 
169     fEdgeTrigger = new EdgeTrigger(this);
170 
171     initApplets();
172 
173     MSG(("taskbar"));
174 }
175 
~TaskBar()176 TaskBar::~TaskBar() {
177     detachDesktopTray();
178     delete fEdgeTrigger; fEdgeTrigger = nullptr;
179     delete fClock; fClock = nullptr;
180     delete fKeyboardStatus; fKeyboardStatus = nullptr;
181     delete fMailBoxControl; fMailBoxControl = nullptr;
182 #ifdef MEM_STATES
183     delete fMEMStatus; fMEMStatus = nullptr;
184 #endif
185     delete fWinList; fWinList = nullptr;
186     delete fApplications; fApplications = nullptr;
187     delete fObjectBar; fObjectBar = nullptr;
188     delete fWorkspaces; fWorkspaces = nullptr;
189 #ifdef MAX_ACPI_BATTERY_NUM
190     delete fApm; fApm = nullptr;
191 #endif
192 #ifdef IWM_STATES
193     delete fCPUStatus; fCPUStatus = nullptr;
194 #endif
195     delete fNetStatus; fNetStatus = nullptr;
196     delete fAddressBar; fAddressBar = nullptr;
197     delete fTasks; fTasks = nullptr;
198     delete fWindowTray; fWindowTray = nullptr;
199     delete fCollapseButton; fCollapseButton = nullptr;
200     delete fShowDesktop; fShowDesktop = nullptr;
201     xapp->dropClipboard();
202     taskBar = nullptr;
203     if (getFrame())
204         getFrame()->unmanage(false);
205     MSG(("taskBar delete"));
206 }
207 
208 class TaskBarMenu : public YMenu {
209 public:
updatePopup()210     void updatePopup() {
211         if (0 < itemCount())
212             return;
213 
214         setActionListener(taskBar);
215         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
216         addItem(_("Tile _Vertically"), -2, KEY_NAME(gKeySysTileVertical), actionTileVertical);
217         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
218         addItem(_("T_ile Horizontally"), -2, KEY_NAME(gKeySysTileHorizontal), actionTileHorizontal);
219         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
220         addItem(_("Ca_scade"), -2, KEY_NAME(gKeySysCascade), actionCascade);
221         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
222         addItem(_("_Arrange"), -2, KEY_NAME(gKeySysArrange), actionArrange);
223         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
224         addItem(_("_Minimize All"), -2, KEY_NAME(gKeySysMinimizeAll), actionMinimizeAll);
225         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
226         addItem(_("_Hide All"), -2, KEY_NAME(gKeySysHideAll), actionHideAll);
227         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
228         addItem(_("_Undo"), -2, KEY_NAME(gKeySysUndoArrange), actionUndoArrange);
229         if (minimizeToDesktop)
230         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
231             addItem(_("Arrange _Icons"), -2, KEY_NAME(gKeySysArrangeIcons), actionArrangeIcons);
232         addSeparator();
233         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
234         addItem(_("_Windows"), -2, actionWindowList, windowListMenu);
235         addSeparator();
236         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
237         addItem(_("_Refresh"), -2, null, actionRefresh);
238         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
239         addItem(_("_About"), -2, actionAbout, nullptr);
240         if (showLogoutMenu) {
241             addSeparator();
242             if (showLogoutSubMenu)
243         // TRANSLATORS: This appears in a group with others items, so please make the hotkeys unique in the set: # T_ile Horizontally, Ca_scade, _Arrange, _Minimize All, _Hide All, _Undo, Arrange _Icons, _Windows, _Refresh, _About, _Logout
244                 addItem(_("_Logout..."), -2, actionLogout, logoutMenu);
245             else
246                 addItem(_("_Logout..."), -2, null, actionLogout);
247         }
248 
249         setTitle("IceMenu");
250         setClassHint("icemenu", "TaskBar");
251         setNetWindowType(_XA_NET_WM_WINDOW_TYPE_POPUP_MENU);
252     }
253 };
254 
initApplets()255 void TaskBar::initApplets() {
256 #ifdef MEM_STATES
257     if (taskBarShowMEMStatus)
258         fMEMStatus = new MEMStatus(this, this);
259     else
260         fMEMStatus = nullptr;
261 #endif
262 
263 #ifdef IWM_STATES
264     if (taskBarShowCPUStatus)
265         fCPUStatus = new CPUStatusControl(smActionListener, this, this);
266     else
267         fCPUStatus = nullptr;
268 #endif
269 
270     if (taskBarShowNetStatus)
271         fNetStatus = new NetStatusControl(app, smActionListener, this, this);
272     else
273         fNetStatus = nullptr;
274 
275     if (taskBarShowClock)
276         fClock = new ClockSet(smActionListener, this, this);
277     else
278         fClock = nullptr;
279 
280 #ifdef MAX_ACPI_BATTERY_NUM
281     if (taskBarShowApm && (access(APMDEV, 0) == 0 ||
282                            access("/sys/class/power_supply", 0) == 0 ||
283                            access("/proc/acpi", 0) == 0 ||
284                            access("/dev/acpi", 0) == 0 ||
285                            access("/proc/pmu", R_OK|X_OK) == 0))
286     {
287         fApm = new YApm(this);
288         fApm->setTitle("IceAPM");
289     }
290     else if (!taskBarShowApm && taskBarShowApmAuto)
291     {
292         fApm = new YApm(this, true);
293         if ( ! fApm->hasBatteries()) {
294                 delete fApm;
295                 fApm = nullptr;
296         }
297         else fApm->setTitle("IceAPM");
298     }
299     else
300         fApm = nullptr;
301 #endif
302 
303     if (taskBarShowCollapseButton) {
304         fCollapseButton = new ObjectButton(this, actionCollapseTaskbar);
305         if (fCollapseButton) {
306             fCollapseButton->setWinGravity(StaticGravity);
307             ref<YImage> image = leftToRight
308                               ? taskbarCollapseImage : taskbarExpandImage;
309             if (image != null) {
310                 fCollapseButton->setImage(image);
311             } else {
312                 fCollapseButton->setText(leftToRight ? ">" : "<");
313             }
314             fCollapseButton->setActionListener(this);
315             fCollapseButton->setToolTip(_("Hide Taskbar"));
316             fCollapseButton->setTitle("Collapse");
317         }
318     } else
319         fCollapseButton = nullptr;
320 
321     if (taskBarShowMailboxStatus) {
322         fMailBoxControl = new MailBoxControl(app, smActionListener, this, this);
323     } else
324         fMailBoxControl = nullptr;
325 
326     if (configKeyboards.nonempty()) {
327         fKeyboardStatus = new KeyboardStatus(wmapp, this, this);
328     } else
329         fKeyboardStatus = nullptr;
330 
331     if (taskBarShowStartMenu) {
332         class LazyRootMenu : public LazyMenu {
333             YMenu* ymenu() { return rootMenu; }
334         };
335         fApplications = new ObjectButton(this, new LazyRootMenu);
336         fApplications->setActionListener(this);
337         fApplications->setImage(taskbarStartImage);
338         fApplications->setToolTip(_("Favorite Applications"));
339         fApplications->setTitle("TaskBarMenu");
340     } else
341         fApplications = nullptr;
342 
343     fObjectBar = new ObjectBar(this);
344     if (fObjectBar) {
345         upath t = app->findConfigFile("toolbar");
346         if (t != null) {
347             MenuLoader(app, smActionListener, wmActionListener)
348             .loadMenus(t, fObjectBar);
349         }
350         if (fObjectBar->nonempty()) {
351             fObjectBar->setTitle("IceToolbar");
352         } else {
353             delete fObjectBar; fObjectBar = nullptr;
354         }
355     }
356     if (taskBarShowWindowListMenu) {
357         class LazyWindowListMenu : public LazyMenu {
358             YMenu* ymenu() { return windowListMenu; }
359         };
360         fWinList = new ObjectButton(this, new LazyWindowListMenu);
361         fWinList->setImage(taskbarWindowsImage);
362         fWinList->setActionListener(this);
363         fWinList->setToolTip(_("Window List Menu"));
364         fWinList->setTitle("ShowWindowList");
365     } else
366         fWinList = nullptr;
367     if (taskBarShowShowDesktopButton) {
368         fShowDesktop = new ObjectButton(this, actionShowDesktop);
369         fShowDesktop->setText("__");
370         fShowDesktop->setImage(taskbarShowDesktopImage);
371         fShowDesktop->setActionListener(wmActionListener);
372         fShowDesktop->setToolTip(_("Show Desktop"));
373         fShowDesktop->setTitle("ShowDesktop");
374     }
375 
376     fWorkspaces = taskBarShowWorkspaces
377                 ? new WorkspacesPane(this)
378                 : new AWorkspaces(this);
379     fWorkspaces->setTitle("Workspaces");
380 
381     if (enableAddressBar) {
382         fAddressBar = new AddressBar(app, this);
383         fAddressBar->setTitle("AddressBar");
384     }
385     if (taskBarShowWindows) {
386         fTasks = new TaskPane(this, this);
387         fTasks->setTitle("TaskPane");
388     } else
389         fTasks = nullptr;
390     if (taskBarShowTray) {
391         fWindowTray = new TrayPane(this, this);
392         fWindowTray->setTitle("TrayPane");
393     } else
394         fWindowTray = nullptr;
395 
396     if (taskBarEnableSystemTray) {
397         const char atomstr[] =
398 #ifdef CONFIG_EXTERNAL_TRAY
399                    "_ICEWM_INTTRAY_S"
400 #else
401                    "_NET_SYSTEM_TRAY_S"
402 #endif
403                    ;
404         YAtom trayatom(atomstr, true);
405         bool isInternal = ('I' == atomstr[1]);
406 
407         fDesktopTray = new YXTray(this, isInternal, trayatom,
408                                   this, trayDrawBevel);
409         fDesktopTray->setTitle("SystemTray");
410         fDesktopTray->relayout();
411     } else {
412         fDesktopTray = nullptr;
413         updateLocation();
414     }
415 
416     if (fCollapseButton) {
417         fCollapseButton->raise();
418     }
419 }
420 
trayChanged()421 void TaskBar::trayChanged() {
422     relayout();
423 }
424 
425 struct LayoutInfo {
426     YWindow *w;
427     bool left;
428     bool row; // 0 = bottom, 1 = top
429     bool show;
430     bool expand;
431     short pre, post;
432 
LayoutInfoLayoutInfo433     LayoutInfo() :
434         w(nullptr), left(false), row(false), show(false),
435         expand(false), pre(0), post(0) {}
LayoutInfoLayoutInfo436     LayoutInfo(YWindow *w, bool l, bool r, bool s, bool e, short p, short o) :
437         w(w), left(l), row(r), show(s), expand(e), pre(p), post(o) {}
438 };
439 
updateLayout(unsigned & size_w,unsigned & size_h)440 void TaskBar::updateLayout(unsigned &size_w, unsigned &size_h) {
441     enum { Over, Here };
442     enum { Bot, Top };
443     enum { Same, Show };
444     enum { Keep, Grow };
445     LayoutInfo nw;
446     YArray<LayoutInfo> wlist(16);
447 
448     bool issue314 = taskBarAtTop;
449     nw = LayoutInfo( fApplications, Here, issue314, Show, Grow, 0, 0 );
450     wlist.append(nw);
451     if (taskBarShowShowDesktopButton == 1) {
452         nw = LayoutInfo( fShowDesktop, Here, !issue314, Show, Grow, 0, 0 );
453         wlist.append(nw);
454     }
455     nw = LayoutInfo( fWinList, Here, !issue314, Show, Grow, 0, 0 );
456     wlist.append(nw);
457     nw = LayoutInfo( fObjectBar, Here, Top, Show, Grow, 4, 0 );
458     wlist.append(nw);
459     nw = LayoutInfo( fCollapseButton, Over, Bot, Show, Grow, 0, 2 );
460     wlist.append(nw);
461     nw = LayoutInfo( fWorkspaces, taskBarWorkspacesLeft,
462                      taskBarDoubleHeight && taskBarWorkspacesTop,
463                      taskBarShowWorkspaces && workspaceCount > 0,
464                      Grow, 4, 4 );
465     wlist.append(nw);
466 
467     if (taskBarShowShowDesktopButton == 2) {
468         nw = LayoutInfo( fShowDesktop, Over, Top, Show, Keep, 1, 1);
469         wlist.append(nw);
470     }
471 
472     if (fClock) {
473         for (YClock* clock : *fClock) {
474             wlist += LayoutInfo( clock, Over, Top, Same, Keep, 2, 2 );
475         }
476     }
477 
478     if (taskBarShowMailboxStatus) {
479         for (auto m = fMailBoxControl->iterator(); ++m; ) {
480             nw = LayoutInfo( *m, Over, Top, Show, Keep, 1, 1 );
481             wlist.append(nw);
482         }
483     }
484     if (fKeyboardStatus) {
485         nw = LayoutInfo( fKeyboardStatus, Over, Top, Show, Keep, 1, 1 );
486         wlist.append(nw);
487     }
488 
489 #ifdef IWM_STATES
490     if (taskBarShowCPUStatus) {
491         auto it = fCPUStatus->getIterator();
492         while (++it)
493         {
494             nw = LayoutInfo(*it, Over, Top, Show, Keep, 2, 2 );
495             wlist.append(nw);
496         }
497     }
498 #endif
499 
500 #ifdef MEM_STATES
501     nw = LayoutInfo( fMEMStatus, Over, Top, Same, Keep, 2, 2 );
502     wlist.append(nw);
503 #endif
504 
505     if (taskBarShowNetStatus) {
506         auto it = fNetStatus->getIterator();
507         while (++it)
508         {
509             if (*it != 0) {
510                 nw = LayoutInfo(*it, Over, Top, Same, Keep, 2, 2 );
511                 wlist.append(nw);
512             }
513         }
514     }
515 #ifdef MAX_ACPI_BATTERY_NUM
516     nw = LayoutInfo( fApm, Over, Top, Show, Keep, 0, 2 );
517     wlist.append(nw);
518 #endif
519     nw = LayoutInfo( fDesktopTray, Over, Top, Show, Keep, 1, 1 );
520     wlist.append(nw);
521     nw = LayoutInfo( fWindowTray, Over, Bot, Show, Grow, 1, 1 );
522     wlist.append(nw);
523     const int wcount = wlist.getCount();
524 
525     int y[2] = { 0, 0 };
526     unsigned h[2] = { 0, 0 };
527     int left[2] = { 0, 0 };
528     int right[2] = { 0, 0 };
529 
530     if (!taskBarDoubleHeight)
531         for (int i = 0; i < wcount; i++)
532             wlist[i].row = false;
533     for (int i = 0; i < wcount; i++) {
534         if (wlist[i].w) {
535             if (h[wlist[i].row] < wlist[i].w->height())
536                 h[wlist[i].row] = wlist[i].w->height();
537         }
538     }
539 
540     unsigned w = (desktop->getScreenGeometry().width()
541                   * unsigned(taskBarWidthPercentage)) / 100U;
542 
543     if (taskBarAtTop) { // !!! for now
544         y[1] = 0;
545         y[0] = h[1] + y[1];
546 #if 0
547         y[0] = 0;
548         if (fIsHidden)
549             y[0]++;
550         y[1] = h[0] + y[0];
551 #endif
552     } else {
553         y[1] = 1;
554         y[0] = h[1] + y[1];
555     }
556 
557     right[0] = w;
558     right[1] = w;
559     if (taskBarShowWindows && fTasks != nullptr) {
560         h[0] = max(h[0], max(YIcon::smallSize() + 8, fTasks->maxHeight()));
561     }
562 
563     for (int i = 0; i < wcount; i++) {
564         if (!wlist[i].w)
565             continue;
566         if (!wlist[i].show && !wlist[i].w->visible())
567             continue;
568 
569         int xx = 0;
570         int yy = 0;
571         int ww = wlist[i].w->width();
572         int hh = h[wlist[i].row];
573 
574         if (wlist[i].expand) {
575             yy = y[wlist[i].row];
576         } else {
577             hh = wlist[i].w->height();
578             yy = y[wlist[i].row] + (h[wlist[i].row] - wlist[i].w->height()) / 2;
579         }
580 
581         if (wlist[i].left) {
582             xx = left[wlist[i].row] + wlist[i].pre;
583 
584             left[wlist[i].row] += ww + wlist[i].pre + wlist[i].post;
585         } else {
586             xx = right[wlist[i].row] - ww - wlist[i].pre;
587 
588             right[wlist[i].row] -= ww + wlist[i].pre + wlist[i].post;
589         }
590         YRect r(xx, yy, ww, hh);
591         if (rightToLeft) {
592             r.xx = w - r.xx - r.ww;
593         }
594         wlist[i].w->setGeometry(r);
595         if (wlist[i].show)
596             wlist[i].w->show();
597     }
598 
599     wlist.clear();
600     /* ----------------------------------------------------------------- */
601 
602     if (taskBarShowWindows && fTasks) {
603         fTasks->hide();
604         YRect r(left[0], y[0], unsigned(max(1, right[0] - left[0])), h[0]);
605         if (rightToLeft) {
606             r.xx = w - r.xx - r.ww;
607         }
608         fTasks->setGeometry(r);
609         fTasks->show();
610         fTasks->relayout();
611     }
612     if (fAddressBar) {
613         int row = taskBarDoubleHeight;
614         YRect r(left[row], y[row] + 2,
615                 max(1U, unsigned(right[row] - left[row])), h[row] - 4);
616         if (rightToLeft) {
617             r.xx = w - r.xx - r.ww;
618         }
619         fAddressBar->setGeometry(r);
620         if (::showAddressBar) {
621             if (taskBarDoubleHeight || !taskBarShowWindows) {
622                 fAddressBar->raise();
623                 fAddressBar->show();
624             }
625         }
626     }
627 
628     size_w = w;
629     size_h = h[0] + h[1] + 1;
630 }
631 
relayoutNow()632 void TaskBar::relayoutNow() {
633     if (fUpdates.nonempty()) {
634         for (int i = fUpdates.getCount(); --i >= 0; ) {
635             if (i < fUpdates.getCount() && fUpdates[i]) {
636                 fUpdates[i]->updateAppStatus();
637             }
638         }
639         fUpdates.clear();
640     }
641     if (windowTrayPane())
642         windowTrayPane()->relayoutNow();
643     if (fNeedRelayout) {
644         updateLocation();
645     }
646     if (taskPane())
647         taskPane()->relayoutNow();
648     if (fButtonUpdate) {
649         fButtonUpdate = false;
650         buttonUpdate();
651     }
652     if (fWorkspacesUpdate) {
653         fWorkspacesUpdate = false;
654         fWorkspaces->repaint();
655     }
656 }
657 
updateFullscreen(bool fullscreen)658 void TaskBar::updateFullscreen(bool fullscreen) {
659     if (fFullscreen != fullscreen && getFrame()) {
660         fFullscreen = fullscreen;
661         fEdgeTrigger->show((fFullscreen | fIsHidden) && !fIsCollapsed);
662     }
663 }
664 
updateLocation()665 void TaskBar::updateLocation() {
666     fNeedRelayout = false;
667 
668     if (getFrame() == nullptr) {
669         showBar();
670     }
671     if (fIsHidden && !fIsCollapsed) {
672         if (getFrame() && visible())
673             getFrame()->wmHide();
674         xapp->sync();
675     }
676 
677     int dx, dy;
678     unsigned dw, dh;
679     desktop->getScreenGeometry(&dx, &dy, &dw, &dh, -1);
680 
681     int x = dx;
682     unsigned int w = 0;
683     unsigned int h = 0;
684 
685     if (taskBarWidthPercentage < 100) {
686         w = (dw * taskBarWidthPercentage + 50) / 100;
687         if (nonempty(taskBarJustify)) {
688             if (strcmp(taskBarJustify, "left") == 0)
689                 x = dx;
690             else if (strcmp(taskBarJustify, "right") == 0)
691                 x = dx + (dw - w);
692             else if (strcmp(taskBarJustify, "center") == 0)
693                 x = dx + (dw - w)/2;
694         }
695     }
696 
697     updateLayout(w, h);
698 
699     if (fIsCollapsed) {
700         if (fCollapseButton) {
701             w = fCollapseButton->width();
702             h = fCollapseButton->height() + 1;
703             fCollapseButton->setPosition(0, 1);
704             fCollapseButton->raise();
705             fCollapseButton->show();
706         }
707         else {
708             w = h = 0;
709         }
710 
711         x = rightToLeft ? dx : dx + (dw - w);
712     }
713 
714     int by = taskBarAtTop ? dy : dy + dh - 1;
715 
716     fEdgeTrigger->setGeometry(YRect(x, by, w, 1U));
717 
718     int y = taskBarAtTop ? dy : dy + dh - h;
719 
720     if ( !fIsHidden || fIsCollapsed) {
721         if (getFrame()) {
722             if (geometry() != YRect(x, y, w, h)) {
723                 XConfigureRequestEvent conf = {};
724                 conf.type = ConfigureRequest;
725                 conf.window = handle();
726                 conf.x = x;
727                 conf.y = y;
728                 conf.width = int(w);
729                 conf.height = int(h);
730                 conf.value_mask = CWX | CWY | CWWidth | CWHeight;
731                 getFrame()->configureClient(conf);
732             }
733             getFrame()->wmShow();
734         } else
735             setGeometry(YRect(x, y, w, h));
736     }
737     fEdgeTrigger->show((fFullscreen | fIsHidden) && !fIsCollapsed);
738 
739     ///!!! fix
740     updateWMHints();
741 }
742 
updateWMHints()743 void TaskBar::updateWMHints() {
744     YStrut strut;
745     if (!taskBarAutoHide && !fIsCollapsed) {
746         YRect geo = desktop->getScreenGeometry();
747         if (y() + height() == geo.y() + geo.height()) {
748             strut.bottom = Atom(height());
749         }
750         else if (y() == geo.y()) {
751             strut.top = Atom(height());
752         }
753     }
754     if (fStrut != strut) {
755         fStrut = strut;
756         MSG(("SET NET WM STRUT"));
757         setProperty(_XA_NET_WM_STRUT, XA_CARDINAL, &strut, 4);
758     }
759 }
760 
updateWinLayer()761 void TaskBar::updateWinLayer() {
762     long layer = (taskBarAutoHide || fFullscreen) ? WinLayerAboveAll
763                : fIsCollapsed ? WinLayerAboveDock
764                : taskBarKeepBelow ? WinLayerBelow : WinLayerDock;
765     if (getFrame()) {
766         getFrame()->wmSetLayer(layer);
767     } else {
768         setLayerHint(layer);
769     }
770 }
771 
handleFocus(const XFocusChangeEvent & focus)772 void TaskBar::handleFocus(const XFocusChangeEvent& focus) {
773     if (focus.type == FocusOut) {
774         if (focus.mode == NotifyUngrab) {
775             if (focus.detail == NotifyPointer) {
776                 updateWMHints();
777             }
778         }
779     }
780 }
781 
handleCrossing(const XCrossingEvent & crossing)782 void TaskBar::handleCrossing(const XCrossingEvent &crossing) {
783     unsigned long last = YWindow::getLastEnterNotifySerial();
784     bool ahwm_hack = (crossing.serial != last &&
785         (crossing.serial != last + 1 || crossing.detail != NotifyVirtual));
786 
787     if (crossing.type == EnterNotify) {
788         fEdgeTrigger->stopTimer();
789     }
790     else if (crossing.type == LeaveNotify) {
791         if (crossing.detail == NotifyInferior ||
792            (crossing.detail == NotifyVirtual && crossing.mode == NotifyGrab) ||
793            (crossing.detail == NotifyAncestor && crossing.mode != NotifyNormal))
794         {
795             if (ahwm_hack) {
796                 fEdgeTrigger->stopTimer();
797             }
798         } else {
799             if (ahwm_hack) {
800                 fEdgeTrigger->startTimer();
801             }
802         }
803     }
804 }
805 
paint(Graphics & g,const YRect & r)806 void TaskBar::paint(Graphics &g, const YRect& r) {
807     if (taskbackPixbuf != null &&
808         (fGradient == null ||
809          fGradient->width() != width() ||
810          fGradient->height() != height()))
811     {
812         if (taskBarDoubleHeight == false) {
813             fGradient = taskbackPixbuf->scale(width(), height());
814         } else {
815             ref<YImage> s = taskbackPixbuf->scale(width(), (height() + 1) / 2);
816             ref<YPixmap> p = YPixmap::create(width(), height(), depth());
817             Graphics g(p);
818             g.clear();
819             g.copyImage(s, 0, 0);
820             g.copyImage(s, 0, height() - s->height());
821             fGradient = YImage::createFromPixmap(p);
822         }
823     }
824 
825     g.setColor(taskBarBg);
826     //g.draw3DRect(0, 0, width() - 1, height() - 1, true);
827 
828     // When TaskBarDoubleHeight=1 this draws the upper half.
829     if (fGradient != null) {
830         g.drawImage(fGradient, r.x(), r.y(), r.width(), r.height(),
831                     r.x(), r.y());
832     }
833     else if (taskbackPixmap != null) {
834         g.fillPixmap(taskbackPixmap, r.x(), r.y(), r.width(), r.height(),
835                      r.x(), r.y());
836     }
837     else if (taskBarAtTop) {
838         bool dh = (r.y() + r.height() == height());
839         g.fillRect(r.x(), r.y(), r.width(), r.height() - dh);
840         if (dh) {
841             g.setColor(taskBarBg->darker());
842             g.drawLine(r.x(), height() - 1, r.x() + r.width(), height() - 1);
843         }
844     }
845     else {
846         bool dy = (r.y() == 0);
847         g.fillRect(r.x(), r.y() + dy, r.width(), r.height() - dy);
848         if (dy) {
849             g.setColor(taskBarBg->brighter());
850             g.drawLine(r.x(), 0, r.x() + r.width(), 0);
851         }
852     }
853 }
854 
handleKey(const XKeyEvent & key)855 bool TaskBar::handleKey(const XKeyEvent &key) {
856     return YWindow::handleKey(key);
857 }
858 
handleButton(const XButtonEvent & button)859 void TaskBar::handleButton(const XButtonEvent &button) {
860     if ((button.type == ButtonRelease) &&
861         (button.button == 1 || button.button == 3) &&
862         xapp->isButton(button.state, Button1Mask + Button3Mask))
863     {
864         windowList->showFocused(button.x_root, button.y_root);
865     }
866     else {
867         if (button.type == ButtonPress) {
868             manager->updateWorkArea();
869             if (button.button == 1) {
870                 if (button.state & xapp->AltMask)
871                     lower();
872                 else if (!(button.state & ControlMask))
873                     raise();
874             }
875         }
876     }
877     YWindow::handleButton(button);
878 }
879 
contextMenu(int x_root,int y_root)880 void TaskBar::contextMenu(int x_root, int y_root) {
881     taskBarMenu->popup(this, nullptr, nullptr, x_root, y_root,
882                        YPopupWindow::pfCanFlipVertical |
883                        YPopupWindow::pfCanFlipHorizontal);
884 }
885 
handleClick(const XButtonEvent & up,int count)886 void TaskBar::handleClick(const XButtonEvent &up, int count) {
887     if (up.button == 1) {
888     } else if (up.button == 2) {
889         windowList->showFocused(up.x_root, up.y_root);
890     } else {
891         if (up.button == 3 && count == 1 && xapp->isButton(up.state, Button3Mask)) {
892             contextMenu(up.x_root, up.y_root);
893         }
894     }
895 }
896 
handleEndDrag(const XButtonEvent &,const XButtonEvent &)897 void TaskBar::handleEndDrag(const XButtonEvent &/*down*/, const XButtonEvent &/*up*/) {
898     xapp->releaseEvents();
899 }
handleDrag(const XButtonEvent &,const XMotionEvent & motion)900 void TaskBar::handleDrag(const XButtonEvent &/*down*/, const XMotionEvent &motion) {
901     bool newPosition = false;
902 
903     xapp->grabEvents(this, YWMApp::movePointer,
904                          ButtonPressMask |
905                          ButtonReleaseMask |
906                          PointerMotionMask);
907 
908 
909     if (motion.y_root < int(desktop->height() / 2))
910         newPosition = true;
911 
912     if (taskBarAtTop != newPosition) {
913         taskBarAtTop = newPosition;
914         updateLocation();
915     }
916 }
917 
popupStartMenu()918 void TaskBar::popupStartMenu() {
919     if (fApplications) {
920         popOut();
921         fApplications->popupMenu();
922     }
923 }
924 
popupWindowListMenu()925 void TaskBar::popupWindowListMenu() {
926     if (fWinList) {
927         popOut();
928         fWinList->popupMenu();
929     }
930 }
931 
autoTimer(bool doShow)932 bool TaskBar::autoTimer(bool doShow) {
933     MSG(("hide taskbar"));
934     if (fFullscreen && doShow && taskBarFullscreenAutoShow) {
935         fIsHidden = false;
936         getFrame()->focus();
937         manager->switchFocusTo(getFrame(), true);
938         manager->updateFullscreenLayer();
939     }
940     if (taskBarAutoHide) {
941         fIsHidden = !doShow && !hasPopup();
942         if (taskBarDoubleHeight == false && taskBarShowWindows) {
943             fIsHidden &= !(addressBar() && addressBar()->visible());
944         }
945         updateLocation();
946     }
947     return fIsHidden == doShow;
948 }
949 
popOut()950 void TaskBar::popOut() {
951     if (fIsCollapsed) {
952         handleCollapseButton();
953     }
954     if (taskBarAutoHide) {
955         fIsHidden = false;
956         updateLocation();
957         fIsHidden = taskBarAutoHide;
958         if (fEdgeTrigger) {
959             MSG(("start hide 4"));
960             fEdgeTrigger->startTimer();
961         }
962     }
963     relayoutNow();
964 }
965 
showBar()966 void TaskBar::showBar() {
967     if (getFrame() == nullptr) {
968         manager->manageClient(this);
969         updateWinLayer();
970         if (getFrame()) {
971             getFrame()->setAllWorkspaces();
972             if (enableAddressBar && ::showAddressBar && taskBarDoubleHeight)
973                 getFrame()->activate(true);
974             parent()->setTitle("TaskBarFrame");
975             getFrame()->updateLayer();
976         }
977     }
978 }
979 
actionPerformed(YAction action,unsigned int modifiers)980 void TaskBar::actionPerformed(YAction action, unsigned int modifiers) {
981     wmActionListener->actionPerformed(action, modifiers);
982 }
983 
handleCollapseButton()984 void TaskBar::handleCollapseButton() {
985     fIsCollapsed = !fIsCollapsed;
986     if (fCollapseButton) {
987         ref<YImage> image = (leftToRight == fIsCollapsed)
988                           ? taskbarExpandImage : taskbarCollapseImage;
989         const char* text = (leftToRight == fIsCollapsed) ? "<" : ">";
990         const char* ttip = fIsCollapsed ? _("Show Taskbar") : _("Hide Taskbar");
991         if (image != null) {
992             fCollapseButton->setImage(image);
993         } else {
994             fCollapseButton->setText(text);
995         }
996         fCollapseButton->setToolTip(ttip);
997         fCollapseButton->repaint();
998     }
999 
1000     if (fIsCollapsed)
1001         updateWinLayer();
1002     relayout();
1003     updateLocation();
1004     if (fIsCollapsed == false)
1005         updateWinLayer();
1006     xapp->sync();
1007 }
1008 
handlePopDown(YPopupWindow *)1009 void TaskBar::handlePopDown(YPopupWindow * /*popup*/) {
1010 }
1011 
configure(const YRect2 & r)1012 void TaskBar::configure(const YRect2& r) {
1013     if (r.resized() && 1 < r.width() && !fIsCollapsed) {
1014         repaint();
1015         clearWindow();
1016     }
1017     updateWMHints();
1018 }
1019 
repaint()1020 void TaskBar::repaint() {
1021     GraphicsBuffer(this).paint();
1022 }
1023 
detachDesktopTray()1024 void TaskBar::detachDesktopTray() {
1025     if (fDesktopTray) {
1026         MSG(("detach Tray"));
1027         fDesktopTray->detachTray();
1028         delete fDesktopTray; fDesktopTray = nullptr;
1029     }
1030 }
1031 
updateFrame(YFrameWindow * frame)1032 void TaskBar::updateFrame(YFrameWindow* frame) {
1033     if (find(fUpdates, frame) < 0)
1034         fUpdates += frame;
1035 }
1036 
delistFrame(YFrameWindow * frame,TaskBarApp * task,TrayApp * tray)1037 void TaskBar::delistFrame(YFrameWindow* frame, TaskBarApp* task, TrayApp* tray) {
1038     findRemove(fUpdates, frame);
1039     if (taskPane() && task)
1040         taskPane()->remove(task);
1041     if (windowTrayPane() && tray)
1042         windowTrayPane()->remove(tray);
1043 }
1044 
addTasksApp(YFrameWindow * frame)1045 TaskBarApp *TaskBar::addTasksApp(YFrameWindow* frame) {
1046     return taskPane() ? taskPane()->addApp(frame) : nullptr;
1047 }
1048 
relayoutTasks()1049 void TaskBar::relayoutTasks() {
1050     if (taskPane())
1051         taskPane()->relayout();
1052 }
1053 
addTrayApp(YFrameWindow * frame)1054 TrayApp *TaskBar::addTrayApp(YFrameWindow* frame) {
1055     return windowTrayPane() ? windowTrayPane()->addApp(frame) : nullptr;
1056 }
1057 
relayoutTray()1058 void TaskBar::relayoutTray() {
1059     if (windowTrayPane())
1060         windowTrayPane()->relayout();
1061 }
1062 
showAddressBar()1063 void TaskBar::showAddressBar() {
1064     popOut();
1065     if (fAddressBar != nullptr)
1066         fAddressBar->showNow();
1067 }
1068 
setWorkspaceActive(long workspace,bool active)1069 void TaskBar::setWorkspaceActive(long workspace, bool active) {
1070     if (taskBarShowWorkspaces && fWorkspaces) {
1071         YDimension dim(fWorkspaces->dimension());
1072         fWorkspaces->setPressed(workspace, active);
1073         if (dim != fWorkspaces->dimension()) {
1074             relayout();
1075         }
1076     }
1077 }
1078 
workspacesRepaint()1079 void TaskBar::workspacesRepaint() {
1080     if (taskBarShowWorkspaces && fWorkspaces) {
1081         fWorkspacesUpdate = true;
1082     }
1083 }
1084 
workspacesUpdateButtons()1085 void TaskBar::workspacesUpdateButtons() {
1086     fButtonUpdate = true;
1087 }
1088 
buttonUpdate()1089 void TaskBar::buttonUpdate() {
1090     if (taskBarShowWorkspaces && fWorkspaces) {
1091         YDimension dim(fWorkspaces->dimension());
1092         fWorkspaces->updateButtons();
1093         if (dim != fWorkspaces->dimension()) {
1094             relayout();
1095         }
1096     }
1097 }
1098 
workspacesRelabelButtons()1099 void TaskBar::workspacesRelabelButtons() {
1100     if (taskBarShowWorkspaces && fWorkspaces) {
1101         YDimension dim(fWorkspaces->dimension());
1102         fWorkspaces->relabelButtons();
1103         if (dim != fWorkspaces->dimension()) {
1104             relayout();
1105         }
1106     }
1107 }
1108 
keyboardUpdate(mstring keyboard)1109 void TaskBar::keyboardUpdate(mstring keyboard) {
1110     if (fKeyboardStatus) {
1111         fKeyboardStatus->updateKeyboard(keyboard);
1112     }
1113 }
1114 
windowTrayRequestDock(Window w)1115 bool TaskBar::windowTrayRequestDock(Window w) {
1116     if (fDesktopTray) {
1117         fDesktopTray->trayRequestDock(w, "SystemTray");
1118         return true;
1119     }
1120     return false;
1121 }
1122 
switchToPrev()1123 void TaskBar::switchToPrev() {
1124     if (taskPane())
1125         taskPane()->switchToPrev();
1126 }
1127 
switchToNext()1128 void TaskBar::switchToNext() {
1129     if (taskPane())
1130         taskPane()->switchToNext();
1131 }
1132 
movePrev()1133 void TaskBar::movePrev() {
1134     if (taskPane())
1135         taskPane()->movePrev();
1136 }
1137 
moveNext()1138 void TaskBar::moveNext() {
1139     if (taskPane())
1140         taskPane()->moveNext();
1141 }
1142 
refresh()1143 void TaskBar::refresh() {
1144     if (fApplications)
1145         fApplications->repaint();
1146     if (fWinList)
1147         fWinList->repaint();
1148     if (fShowDesktop)
1149         fShowDesktop->repaint();
1150     if (fObjectBar)
1151         fObjectBar->refresh();
1152 }
1153 
1154 // vim: set sw=4 ts=4 et:
1155