1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Toolbar.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 //         Bradley T Hughes <bhughes at trolltech.com>
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
24 
25 #include "Toolbar.hh"
26 #include "Iconmenu.hh"
27 #include "Screen.hh"
28 #include "Slit.hh"
29 #include "Toolbarmenu.hh"
30 #include "Window.hh"
31 #include "Windowmenu.hh"
32 #include "Workspacemenu.hh"
33 
34 #include <Pen.hh>
35 #include <PixmapCache.hh>
36 #include <Unicode.hh>
37 
38 #include <X11/Xutil.h>
39 #include <sys/time.h>
40 #include <assert.h>
41 
42 
nextTimeout(int resolution)43 long nextTimeout(int resolution)
44 {
45   timeval now;
46   gettimeofday(&now, 0);
47   return (std::max(1000l, ((((resolution - (now.tv_sec % resolution)) * 1000l))
48                            - (now.tv_usec / 1000l))));
49 }
50 
51 
Toolbar(BScreen * scrn)52 Toolbar::Toolbar(BScreen *scrn) {
53   _screen = scrn;
54   blackbox = _screen->blackbox();
55 
56   // get the clock updating every minute
57   clock_timer = new bt::Timer(blackbox, this);
58   clock_timer->recurring(True);
59 
60   const ToolbarOptions &options = _screen->resource().toolbarOptions();
61 
62   const std::string &time_format = options.strftime_format;
63   if (time_format.find("%S") != std::string::npos
64       || time_format.find("%s") != std::string::npos
65       || time_format.find("%r") != std::string::npos
66       || time_format.find("%T") != std::string::npos) {
67     clock_timer_resolution = 1;
68   } else if (time_format.find("%M") != std::string::npos
69              || time_format.find("%R") != std::string::npos) {
70     clock_timer_resolution = 60;
71   } else {
72     clock_timer_resolution = 3600;
73   }
74 
75   hide_timer = new bt::Timer(blackbox, this);
76   hide_timer->setTimeout(blackbox->resource().autoRaiseDelay());
77 
78   setLayer(options.always_on_top
79            ? StackingList::LayerAbove
80            : StackingList::LayerNormal);
81   hidden = options.auto_hide;
82 
83   new_name_pos = 0;
84 
85   display = blackbox->XDisplay();
86   XSetWindowAttributes attrib;
87   unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
88   attrib.colormap = _screen->screenInfo().colormap();
89   attrib.override_redirect = True;
90   attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
91                       EnterWindowMask | LeaveWindowMask | ExposureMask;
92 
93   frame.window =
94     XCreateWindow(display, _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
95                   _screen->screenInfo().depth(), InputOutput,
96                   _screen->screenInfo().visual(),
97                   create_mask, &attrib);
98   blackbox->insertEventHandler(frame.window, this);
99 
100   attrib.event_mask =
101     ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask;
102 
103   frame.workspace_label =
104     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
105                   _screen->screenInfo().depth(), InputOutput,
106                   _screen->screenInfo().visual(),
107                   create_mask, &attrib);
108   blackbox->insertEventHandler(frame.workspace_label, this);
109 
110   frame.window_label =
111     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
112                   _screen->screenInfo().depth(), InputOutput,
113                   _screen->screenInfo().visual(),
114                   create_mask, &attrib);
115   blackbox->insertEventHandler(frame.window_label, this);
116 
117   frame.clock =
118     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
119                   _screen->screenInfo().depth(), InputOutput,
120                   _screen->screenInfo().visual(),
121                   create_mask, &attrib);
122   blackbox->insertEventHandler(frame.clock, this);
123 
124   frame.psbutton =
125     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
126                   _screen->screenInfo().depth(), InputOutput,
127                   _screen->screenInfo().visual(),
128                   create_mask, &attrib);
129   blackbox->insertEventHandler(frame.psbutton, this);
130 
131   frame.nsbutton =
132     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
133                   _screen->screenInfo().depth(), InputOutput,
134                   _screen->screenInfo().visual(),
135                   create_mask, &attrib);
136   blackbox->insertEventHandler(frame.nsbutton, this);
137 
138   frame.pwbutton =
139     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
140                   _screen->screenInfo().depth(), InputOutput,
141                   _screen->screenInfo().visual(),
142                   create_mask, &attrib);
143   blackbox->insertEventHandler(frame.pwbutton, this);
144 
145   frame.nwbutton =
146     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
147                   _screen->screenInfo().depth(), InputOutput,
148                   _screen->screenInfo().visual(),
149                   create_mask, &attrib);
150   blackbox->insertEventHandler(frame.nwbutton, this);
151 
152   frame.base = frame.slabel = frame.wlabel = frame.clk = frame.button =
153  frame.pbutton = None;
154 
155   _screen->addStrut(&strut);
156 
157   reconfigure();
158 
159   clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
160   clock_timer->start();
161 
162   XMapSubwindows(display, frame.window);
163   XMapWindow(display, frame.window);
164 }
165 
166 
~Toolbar(void)167 Toolbar::~Toolbar(void) {
168   _screen->removeStrut(&strut);
169 
170   XUnmapWindow(display, frame.window);
171 
172   bt::PixmapCache::release(frame.base);
173   bt::PixmapCache::release(frame.slabel);
174   bt::PixmapCache::release(frame.wlabel);
175   bt::PixmapCache::release(frame.clk);
176   bt::PixmapCache::release(frame.button);
177   bt::PixmapCache::release(frame.pbutton);
178 
179   blackbox->removeEventHandler(frame.window);
180   blackbox->removeEventHandler(frame.workspace_label);
181   blackbox->removeEventHandler(frame.window_label);
182   blackbox->removeEventHandler(frame.clock);
183   blackbox->removeEventHandler(frame.psbutton);
184   blackbox->removeEventHandler(frame.nsbutton);
185   blackbox->removeEventHandler(frame.pwbutton);
186   blackbox->removeEventHandler(frame.nwbutton);
187 
188   // all children windows are destroyed by this call as well
189   XDestroyWindow(display, frame.window);
190 
191   delete hide_timer;
192   delete clock_timer;
193 }
194 
195 
exposedHeight(void) const196 unsigned int Toolbar::exposedHeight(void) const {
197   const ToolbarOptions &options = _screen->resource().toolbarOptions();
198   const ToolbarStyle &style = _screen->resource().toolbarStyle();
199   return (options.auto_hide ? style.hidden_height : style.toolbar_height);
200 }
201 
202 
reconfigure(void)203 void Toolbar::reconfigure(void) {
204   ScreenResource &resource = _screen->resource();
205   const ToolbarOptions &options = resource.toolbarOptions();
206   const ToolbarStyle &style = resource.toolbarStyle();
207 
208   unsigned int width = (_screen->screenInfo().width() *
209                         options.width_percent) / 100;
210 
211   const unsigned int border_width = style.toolbar.borderWidth();
212   const unsigned int extra =
213     style.frame_margin == 0 ? style.button.borderWidth() : 0;
214   frame.rect.setSize(width, style.toolbar_height);
215 
216   int x, y;
217   switch (options.placement) {
218   case TopLeft:
219   case TopRight:
220   case TopCenter:
221     switch (options.placement) {
222     case TopLeft:
223       x = 0;
224       break;
225     case TopRight:
226       x = _screen->screenInfo().width() - frame.rect.width();
227       break;
228     default:
229       x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
230       break;
231     }
232     y = 0;
233     frame.y_hidden = style.hidden_height - frame.rect.height();
234     break;
235 
236   case BottomLeft:
237   case BottomRight:
238   case BottomCenter:
239   default:
240     switch (options.placement) {
241     case BottomLeft:
242       x = 0;
243       break;
244     case BottomRight:
245       x = _screen->screenInfo().width() - frame.rect.width();
246       break;
247     default:
248       x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
249       break;
250     }
251     y = _screen->screenInfo().height() - frame.rect.height();
252     frame.y_hidden = _screen->screenInfo().height() - style.hidden_height;
253     break;
254   }
255 
256   frame.rect.setPos(x, y);
257 
258   updateStrut();
259 
260   time_t ttmp = time(NULL);
261 
262   unsigned int clock_w = 0u, label_w = 0u;
263 
264   if (ttmp != -1) {
265     struct tm *tt = localtime(&ttmp);
266     if (tt) {
267       char t[1024];
268       int len = strftime(t, 1024, options.strftime_format.c_str(), tt);
269       if (len == 0) { // invalid time format found
270         // so use the default
271         const_cast<std::string &>(options.strftime_format) = "%I:%M %p";
272         len = strftime(t, 1024, options.strftime_format.c_str(), tt);
273       }
274       /*
275        * find the length of the rendered string and add room for two extra
276        * characters to it.  This allows for variable width output of the fonts.
277        * two 'w' are used to get the widest possible width
278        */
279       clock_w =
280         bt::textRect(_screen->screenNumber(), style.font,
281                      bt::toUnicode(t)).width() +
282         bt::textRect(_screen->screenNumber(), style.font,
283                      bt::toUnicode("ww")).width();
284     }
285   }
286 
287   for (unsigned int i = 0; i < _screen->workspaceCount(); i++) {
288     width =
289       bt::textRect(_screen->screenNumber(), style.font,
290                    _screen->resource().workspaceName(i)).width();
291     label_w = std::max(label_w, width);
292   }
293 
294   label_w = clock_w = std::max(label_w, clock_w) + (style.label_margin * 2);
295 
296   unsigned int window_label_w =
297     (frame.rect.width() - (border_width * 2)
298      - (clock_w + (style.button_width * 4) + label_w
299         + (style.frame_margin * 8))
300      + extra*6);
301 
302   XMoveResizeWindow(display, frame.window, frame.rect.x(),
303                     hidden ? frame.y_hidden : frame.rect.y(),
304                     frame.rect.width(), frame.rect.height());
305 
306   // workspace label
307   frame.slabel_rect.setRect(border_width + style.frame_margin,
308                             border_width + style.frame_margin,
309                             label_w,
310                             style.label_height);
311   // previous workspace button
312   frame.ps_rect.setRect(border_width + (style.frame_margin * 2) + label_w
313                         - extra,
314                         border_width + style.frame_margin,
315                         style.button_width,
316                         style.button_width);
317   // next workspace button
318   frame.ns_rect.setRect(border_width + (style.frame_margin * 3)
319                         + label_w + style.button_width - (extra * 2),
320                         border_width + style.frame_margin,
321                         style.button_width,
322                         style.button_width);
323   // window label
324   frame.wlabel_rect.setRect(border_width + (style.frame_margin * 4)
325                             + (style.button_width * 2) + label_w - (extra * 3),
326                             border_width + style.frame_margin,
327                             window_label_w,
328                             style.label_height);
329   // previous window button
330   frame.pw_rect.setRect(border_width + (style.frame_margin * 5)
331                         + (style.button_width * 2) + label_w
332                         + window_label_w - (extra * 4),
333                         border_width + style.frame_margin,
334                         style.button_width,
335                         style.button_width);
336   // next window button
337   frame.nw_rect.setRect(border_width + (style.frame_margin * 6)
338                         + (style.button_width * 3) + label_w
339                         + window_label_w - (extra * 5),
340                         border_width + style.frame_margin,
341                         style.button_width,
342                         style.button_width);
343   // clock
344   frame.clock_rect.setRect(frame.rect.width() - clock_w - style.frame_margin
345                            - border_width,
346                            border_width + style.frame_margin,
347                            clock_w,
348                            style.label_height);
349 
350   XMoveResizeWindow(display, frame.workspace_label,
351                     frame.slabel_rect.x(), frame.slabel_rect.y(),
352                     frame.slabel_rect.width(), frame.slabel_rect.height());
353   XMoveResizeWindow(display, frame.psbutton,
354                     frame.ps_rect.x(), frame.ps_rect.y(),
355                     frame.ps_rect.width(), frame.ps_rect.height());
356   XMoveResizeWindow(display, frame.nsbutton,
357                     frame.ns_rect.x(), frame.ns_rect.y(),
358                     frame.ns_rect.width(), frame.ns_rect.height());
359   XMoveResizeWindow(display, frame.window_label,
360                     frame.wlabel_rect.x(), frame.wlabel_rect.y(),
361                     frame.wlabel_rect.width(), frame.wlabel_rect.height());
362   XMoveResizeWindow(display, frame.pwbutton,
363                     frame.pw_rect.x(), frame.pw_rect.y(),
364                     frame.pw_rect.width(), frame.pw_rect.height());
365   XMoveResizeWindow(display, frame.nwbutton,
366                     frame.nw_rect.x(), frame.nw_rect.y(),
367                     frame.nw_rect.width(), frame.nw_rect.height());
368   XMoveResizeWindow(display, frame.clock,
369                     frame.clock_rect.x(), frame.clock_rect.y(),
370                     frame.clock_rect.width(), frame.clock_rect.height());
371 
372   frame.base =
373     bt::PixmapCache::find(_screen->screenNumber(), style.toolbar,
374                           frame.rect.width(), frame.rect.height(),
375                           frame.base);
376   frame.slabel =
377     bt::PixmapCache::find(_screen->screenNumber(), style.slabel,
378                           frame.slabel_rect.width(),
379                           frame.slabel_rect.height(),
380                           frame.slabel);
381   frame.wlabel =
382     bt::PixmapCache::find(_screen->screenNumber(), style.wlabel,
383                           frame.wlabel_rect.width(),
384                           frame.wlabel_rect.height(),
385                           frame.wlabel);
386   frame.clk =
387     bt::PixmapCache::find(_screen->screenNumber(), style.clock,
388                           frame.clock_rect.width(),
389                           frame.clock_rect.height(),
390                           frame.clk);
391   frame.button =
392     bt::PixmapCache::find(_screen->screenNumber(), style.button,
393                           style.button_width, style.button_width,
394                           frame.button);
395   frame.pbutton =
396     bt::PixmapCache::find(_screen->screenNumber(),
397                           style.pressed,
398                           style.button_width, style.button_width,
399                           frame.pbutton);
400 
401   XClearArea(display, frame.window, 0, 0,
402              frame.rect.width(), frame.rect.height(), True);
403 
404   XClearArea(display, frame.workspace_label, 0, 0,
405              label_w, style.label_height, True);
406   XClearArea(display, frame.window_label, 0, 0,
407              window_label_w, style.label_height, True);
408   XClearArea(display, frame.clock, 0, 0,
409              clock_w, style.label_height, True);
410 
411   XClearArea(display, frame.psbutton, 0, 0,
412              style.button_width, style.button_width, True);
413   XClearArea(display, frame.nsbutton, 0, 0,
414              style.button_width, style.button_width, True);
415   XClearArea(display, frame.pwbutton, 0, 0,
416              style.button_width, style.button_width, True);
417   XClearArea(display, frame.nwbutton, 0, 0,
418              style.button_width, style.button_width, True);
419 }
420 
421 
updateStrut(void)422 void Toolbar::updateStrut(void) {
423   // left and right are always 0
424   strut.top = strut.bottom = 0;
425 
426   switch(_screen->resource().toolbarOptions().placement) {
427   case TopLeft:
428   case TopCenter:
429   case TopRight:
430     strut.top = exposedHeight();
431     break;
432   default:
433     strut.bottom = exposedHeight();
434   }
435 
436   _screen->updateStrut();
437 }
438 
439 
redrawClockLabel(void)440 void Toolbar::redrawClockLabel(void) {
441   const ToolbarOptions &options = _screen->resource().toolbarOptions();
442   const ToolbarStyle &style = _screen->resource().toolbarStyle();
443 
444   bt::Rect u(0, 0, frame.clock_rect.width(), frame.clock_rect.height());
445   if (frame.clk == ParentRelative) {
446     bt::Rect t(-frame.clock_rect.x(), -frame.clock_rect.y(),
447                frame.rect.width(), frame.rect.height());
448     bt::drawTexture(_screen->screenNumber(), style.toolbar,
449                     frame.clock, t, u, frame.base);
450   } else {
451     bt::drawTexture(_screen->screenNumber(), style.clock,
452                     frame.clock, u, u, frame.clk);
453   }
454 
455   time_t tmp = 0;
456   struct tm *tt = 0;
457   char str[1024];
458   if ((tmp = time(NULL)) == -1)
459     return; // should not happen
460   tt = localtime(&tmp);
461   if (! tt)
462     return; // ditto
463   if (! strftime(str, sizeof(str), options.strftime_format.c_str(), tt))
464     return; // ditto
465 
466   bt::Pen pen(_screen->screenNumber(), style.clock_text);
467   bt::drawText(style.font, pen, frame.clock, u, style.alignment,
468                bt::toUnicode(str));
469 }
470 
471 
redrawWindowLabel(void)472 void Toolbar::redrawWindowLabel(void) {
473   const ToolbarStyle &style = _screen->resource().toolbarStyle();
474 
475   bt::Rect u(0, 0, frame.wlabel_rect.width(), frame.wlabel_rect.height());
476   if (frame.wlabel == ParentRelative) {
477     bt::Rect t(-frame.wlabel_rect.x(), -frame.wlabel_rect.y(),
478                frame.rect.width(), frame.rect.height());
479     bt::drawTexture(_screen->screenNumber(), style.toolbar,
480                     frame.window_label, t, u, frame.base);
481   } else {
482     bt::drawTexture(_screen->screenNumber(), style.wlabel,
483                     frame.window_label, u, u, frame.wlabel);
484   }
485 
486   BlackboxWindow *foc = _screen->blackbox()->focusedWindow();
487   if (! foc || foc->screen() != _screen)
488     return;
489 
490   bt::Pen pen(_screen->screenNumber(), style.wlabel_text);
491   bt::drawText(style.font, pen, frame.window_label, u,
492                style.alignment,
493                bt::ellideText(foc->title(), u.width(), bt::toUnicode("..."),
494                               _screen->screenNumber(), style.font));
495 }
496 
497 
redrawWorkspaceLabel(void)498 void Toolbar::redrawWorkspaceLabel(void) {
499   const bt::ustring &name =
500     _screen->resource().workspaceName(_screen->currentWorkspace());
501   const ToolbarStyle &style = _screen->resource().toolbarStyle();
502 
503   bt::Rect u(0, 0, frame.slabel_rect.width(), frame.slabel_rect.height());
504   if (frame.slabel == ParentRelative) {
505     bt::Rect t(-frame.slabel_rect.x(), -frame.slabel_rect.y(),
506                frame.rect.width(), frame.rect.height());
507     bt::drawTexture(_screen->screenNumber(), style.toolbar,
508                     frame.workspace_label, t, u, frame.base);
509   } else {
510     bt::drawTexture(_screen->screenNumber(), style.slabel,
511                     frame.workspace_label, u, u, frame.slabel);
512   }
513 
514   bt::Pen pen(_screen->screenNumber(), style.slabel_text);
515   bt::drawText(style.font, pen, frame.workspace_label, u,
516                style.alignment, name);
517 }
518 
519 
redrawPrevWorkspaceButton(bool pressed)520 void Toolbar::redrawPrevWorkspaceButton(bool pressed) {
521   const ToolbarStyle &style =
522     _screen->resource().toolbarStyle();
523 
524   Pixmap p = pressed ? frame.pbutton : frame.button;
525   bt::Rect u(0, 0, style.button_width, style.button_width);
526   if (p == ParentRelative) {
527     bt::Rect t(-frame.ps_rect.x(), -frame.ps_rect.y(),
528                frame.rect.width(), frame.rect.height());
529     bt::drawTexture(_screen->screenNumber(), style.toolbar,
530                     frame.psbutton, t, u, frame.base);
531   } else {
532     bt::drawTexture(_screen->screenNumber(),
533                     pressed ? style.pressed : style.button,
534                     frame.psbutton, u, u, p);
535   }
536 
537   const bt::Pen pen(_screen->screenNumber(), style.foreground);
538   bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
539                  pen, frame.psbutton, u);
540 }
541 
542 
redrawNextWorkspaceButton(bool pressed)543 void Toolbar::redrawNextWorkspaceButton(bool pressed) {
544   const ToolbarStyle &style =
545     _screen->resource().toolbarStyle();
546 
547   Pixmap p = pressed ? frame.pbutton : frame.button;
548   bt::Rect u(0, 0, style.button_width, style.button_width);
549   if (p == ParentRelative) {
550     bt::Rect t(-frame.ns_rect.x(), -frame.ns_rect.y(),
551                frame.rect.width(), frame.rect.height());
552     bt::drawTexture(_screen->screenNumber(), style.toolbar,
553                     frame.nsbutton, t, u, frame.base);
554   } else {
555     bt::drawTexture(_screen->screenNumber(),
556                     pressed ? style.pressed : style.button,
557                     frame.nsbutton, u, u, p);
558   }
559 
560   const bt::Pen pen(_screen->screenNumber(), style.foreground);
561   bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
562                  pen, frame.nsbutton, u);
563 }
564 
565 
redrawPrevWindowButton(bool pressed)566 void Toolbar::redrawPrevWindowButton(bool pressed) {
567   const ToolbarStyle &style =
568     _screen->resource().toolbarStyle();
569 
570   Pixmap p = pressed ? frame.pbutton : frame.button;
571   bt::Rect u(0, 0, style.button_width, style.button_width);
572   if (p == ParentRelative) {
573     bt::Rect t(-frame.pw_rect.x(), -frame.pw_rect.y(),
574                frame.rect.width(), frame.rect.height());
575     bt::drawTexture(_screen->screenNumber(), style.toolbar,
576                     frame.pwbutton, t, u, frame.base);
577   } else {
578     bt::drawTexture(_screen->screenNumber(),
579                     pressed ? style.pressed : style.button,
580                     frame.pwbutton, u, u, p);
581   }
582 
583   const bt::Pen pen(_screen->screenNumber(), style.foreground);
584   bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
585                  pen, frame.pwbutton, u);
586 }
587 
588 
redrawNextWindowButton(bool pressed)589 void Toolbar::redrawNextWindowButton(bool pressed) {
590   const ToolbarStyle &style =
591     _screen->resource().toolbarStyle();
592 
593   Pixmap p = pressed ? frame.pbutton : frame.button;
594   bt::Rect u(0, 0, style.button_width, style.button_width);
595   if (p == ParentRelative) {
596     bt::Rect t(-frame.nw_rect.x(), -frame.nw_rect.y(),
597                frame.rect.width(), frame.rect.height());
598     bt::drawTexture(_screen->screenNumber(), style.toolbar,
599                     frame.nwbutton, t, u, frame.base);
600   } else {
601     bt::drawTexture(_screen->screenNumber(),
602                     pressed ? style.pressed : style.button,
603                     frame.nwbutton, u, u, p);
604   }
605 
606   const bt::Pen pen(_screen->screenNumber(), style.foreground);
607   bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
608                  pen, frame.nwbutton, u);
609 }
610 
611 
buttonPressEvent(const XButtonEvent * const event)612 void Toolbar::buttonPressEvent(const XButtonEvent * const event) {
613   if (event->state == Mod1Mask) {
614     if (event->button == 1)
615       _screen->raiseWindow(this);
616     else if (event->button == 2)
617       _screen->lowerWindow(this);
618     return;
619   }
620 
621   if (event->button == 1) {
622     if (event->window == frame.psbutton)
623       redrawPrevWorkspaceButton(True);
624     else if (event->window == frame.nsbutton)
625       redrawNextWorkspaceButton(True);
626     else if (event->window == frame.pwbutton)
627       redrawPrevWindowButton(True);
628     else if (event->window == frame.nwbutton)
629       redrawNextWindowButton(True);
630     return;
631   }
632 
633   if (event->window == frame.window_label) {
634     BlackboxWindow *focus = blackbox->focusedWindow();
635     if (focus && focus->screen() == _screen) {
636       if (event->button == 2) {
637         _screen->lowerWindow(focus);
638       } else if (event->button == 3) {
639         Windowmenu *windowmenu = _screen->windowmenu(focus);
640         windowmenu->popup(event->x_root, event->y_root,
641                           _screen->availableArea());
642       } else if (blackbox->resource().toolbarActionsWithMouseWheel() &&
643                  blackbox->resource().shadeWindowWithMouseWheel() &&
644                  focus->hasWindowFunction(WindowFunctionShade)) {
645         if ((event->button == 4) && !focus->isShaded()) {
646           focus->setShaded(true);
647 		  } else if ((event->button == 5) && focus->isShaded()) {
648           focus->setShaded(false);
649         }
650       }
651     }
652     return;
653   }
654 
655   if ((event->window == frame.pwbutton) || (event->window == frame.nwbutton)) {
656     if (blackbox->resource().toolbarActionsWithMouseWheel()) {
657       if (event->button == 4)
658         _screen->nextFocus();
659       else if (event->button == 5)
660         _screen->prevFocus();
661     }
662     // XXX: current-workspace window-list menu with button 2 or 3 here..
663     return;
664   }
665 
666   // XXX: workspace-menu with button 2 or 3 on workspace-items (prev/next/label) here
667 
668   // default-handlers (scroll through workspaces, popup toolbar-menu)
669   if (event->button == 3) {
670     _screen->toolbarmenu()->popup(event->x_root, event->y_root,
671                                   _screen->availableArea());
672     return;
673   }
674 
675   if (blackbox->resource().toolbarActionsWithMouseWheel()) {
676     if (event->button == 4)
677       _screen->nextWorkspace();
678     else if (event->button == 5)
679       _screen->prevWorkspace();
680   }
681 }
682 
683 
buttonReleaseEvent(const XButtonEvent * const event)684 void Toolbar::buttonReleaseEvent(const XButtonEvent * const event) {
685   if (event->button == 1) {
686     const ToolbarStyle &style =
687       _screen->resource().toolbarStyle();
688 
689     if (event->window == frame.psbutton) {
690       redrawPrevWorkspaceButton(False);
691 
692       if (bt::within(event->x, event->y,
693                      style.button_width, style.button_width)) {
694         _screen->prevWorkspace();
695       }
696     } else if (event->window == frame.nsbutton) {
697       redrawNextWorkspaceButton(False);
698 
699       if (bt::within(event->x, event->y,
700                      style.button_width, style.button_width)) {
701         _screen->nextWorkspace();
702       }
703     } else if (event->window == frame.pwbutton) {
704       redrawPrevWindowButton(False);
705 
706       if (bt::within(event->x, event->y,
707                      style.button_width, style.button_width))
708         _screen->prevFocus();
709     } else if (event->window == frame.nwbutton) {
710       redrawNextWindowButton(False);
711 
712       if (bt::within(event->x, event->y,
713                      style.button_width, style.button_width))
714         _screen->nextFocus();
715     } else if (event->window == frame.window_label)
716       _screen->raiseFocus();
717   }
718 }
719 
720 
enterNotifyEvent(const XCrossingEvent * const)721 void Toolbar::enterNotifyEvent(const XCrossingEvent * const /*unused*/) {
722   if (!_screen->resource().toolbarOptions().auto_hide)
723     return;
724 
725   if (hidden) {
726     if (! hide_timer->isTiming())
727       hide_timer->start();
728   } else if (hide_timer->isTiming()) {
729     hide_timer->stop();
730   }
731 }
732 
leaveNotifyEvent(const XCrossingEvent * const)733 void Toolbar::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
734   if (!_screen->resource().toolbarOptions().auto_hide)
735     return;
736 
737   if (hidden) {
738     if (hide_timer->isTiming())
739       hide_timer->stop();
740   } else if (! hide_timer->isTiming()) {
741     hide_timer->start();
742   }
743 }
744 
745 
exposeEvent(const XExposeEvent * const event)746 void Toolbar::exposeEvent(const XExposeEvent * const event) {
747   if (event->window == frame.clock) redrawClockLabel();
748   else if (event->window == frame.workspace_label) redrawWorkspaceLabel();
749   else if (event->window == frame.window_label) redrawWindowLabel();
750   else if (event->window == frame.psbutton) redrawPrevWorkspaceButton();
751   else if (event->window == frame.nsbutton) redrawNextWorkspaceButton();
752   else if (event->window == frame.pwbutton) redrawPrevWindowButton();
753   else if (event->window == frame.nwbutton) redrawNextWindowButton();
754   else if (event->window == frame.window) {
755     bt::Rect t(0, 0, frame.rect.width(), frame.rect.height());
756     bt::Rect r(event->x, event->y, event->width, event->height);
757     bt::drawTexture(_screen->screenNumber(),
758                     _screen->resource().toolbarStyle().toolbar,
759                     frame.window, t, r & t, frame.base);
760   }
761 }
762 
763 
timeout(bt::Timer * timer)764 void Toolbar::timeout(bt::Timer *timer) {
765   if (timer == clock_timer) {
766     redrawClockLabel();
767 
768     clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
769   } else if (timer == hide_timer) {
770     hidden = ! hidden;
771     if (hidden)
772       XMoveWindow(display, frame.window,
773                   frame.rect.x(), frame.y_hidden);
774     else
775       XMoveWindow(display, frame.window,
776                   frame.rect.x(), frame.rect.y());
777   } else {
778     // this should not happen
779     assert(0);
780   }
781 }
782 
783 
toggleAutoHide(void)784 void Toolbar::toggleAutoHide(void) {
785   bool do_auto_hide = !_screen->resource().toolbarOptions().auto_hide;
786 
787   updateStrut();
788   if (_screen->slit())
789     _screen->slit()->reposition();
790 
791   if (!do_auto_hide && hidden) {
792     // force the toolbar to be visible
793     if (hide_timer->isTiming()) hide_timer->stop();
794     hide_timer->fireTimeout();
795   }
796 }
797 
798 
setPlacement(Placement place)799 void Toolbar::setPlacement(Placement place) {
800   ToolbarOptions &options =
801     const_cast<ToolbarOptions &>(_screen->resource().toolbarOptions());
802   options.placement = place;
803   reconfigure();
804 
805   // reposition the slit as well to make sure it doesn't intersect the
806   // toolbar
807   if (_screen->slit())
808     _screen->slit()->reposition();
809 
810   _screen->saveResource();
811 }
812