]> icculus.org git repositories - dana/openbox.git/blob - src/Toolbar.cc
make the styles' doJustify methods stop returning the string length as this is no...
[dana/openbox.git] / src / Toolbar.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2 // Toolbar.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/keysym.h>
30
31 #ifdef HAVE_STRING_H
32 #  include <string.h>
33 #endif // HAVE_STRING_H
34
35 #ifdef    HAVE_STDIO_H
36 #  include <stdio.h>
37 #endif // HAVE_STDIO_H
38
39 #ifdef    TIME_WITH_SYS_TIME
40 # include <sys/time.h>
41 # include <time.h>
42 #else // !TIME_WITH_SYS_TIME
43 # ifdef    HAVE_SYS_TIME_H
44 #  include <sys/time.h>
45 # else // !HAVE_SYS_TIME_H
46 #  include <time.h>
47 # endif // HAVE_SYS_TIME_H
48 #endif // TIME_WITH_SYS_TIME
49 }
50
51 #include <string>
52 using std::string;
53
54 #include "i18n.hh"
55 #include "blackbox.hh"
56 #include "Clientmenu.hh"
57 #include "Font.hh"
58 #include "GCCache.hh"
59 #include "Iconmenu.hh"
60 #include "Image.hh"
61 #include "Rootmenu.hh"
62 #include "Screen.hh"
63 #include "Toolbar.hh"
64 #include "Window.hh"
65 #include "Workspace.hh"
66 #include "Workspacemenu.hh"
67 #include "Slit.hh"
68
69
70 static long aMinuteFromNow(void) {
71   timeval now;
72   gettimeofday(&now, 0);
73   return ((60 - (now.tv_sec % 60)) * 1000);
74 }
75
76
77 Toolbar::Toolbar(BScreen *scrn) {
78   screen = scrn;
79   blackbox = screen->getBlackbox();
80   toolbarstr = (string)"session.screen" + itostring(screen->getScreenNumber())
81     + ".toolbar.";
82   config = blackbox->getConfig();
83
84   load_rc();
85
86   // get the clock updating every minute
87   clock_timer = new BTimer(blackbox, this);
88   clock_timer->setTimeout(aMinuteFromNow());
89   clock_timer->recurring(True);
90   clock_timer->start();
91   frame.minute = frame.hour = -1;
92
93   hide_handler.toolbar = this;
94   hide_timer = new BTimer(blackbox, &hide_handler);
95   hide_timer->setTimeout(blackbox->getAutoRaiseDelay());
96
97   editing = False;
98   new_name_pos = 0;
99   frame.grab_x = frame.grab_y = 0;
100
101   toolbarmenu = new Toolbarmenu(this);
102
103   display = blackbox->getXDisplay();
104   XSetWindowAttributes attrib;
105   unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
106                               CWColormap | CWOverrideRedirect | CWEventMask;
107   attrib.background_pixmap = None;
108   attrib.background_pixel = attrib.border_pixel =
109     screen->getBorderColor()->pixel();
110   attrib.colormap = screen->getColormap();
111   attrib.override_redirect = True;
112   attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
113                       EnterWindowMask | LeaveWindowMask;
114
115   frame.window =
116     XCreateWindow(display, screen->getRootWindow(), 0, 0, 1, 1, 0,
117                   screen->getDepth(), InputOutput, screen->getVisual(),
118                   create_mask, &attrib);
119   blackbox->saveToolbarSearch(frame.window, this);
120
121   attrib.event_mask = ButtonPressMask | ButtonReleaseMask | ExposureMask |
122                       KeyPressMask | EnterWindowMask;
123
124   frame.workspace_label =
125     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
126                   InputOutput, screen->getVisual(), create_mask, &attrib);
127   blackbox->saveToolbarSearch(frame.workspace_label, this);
128
129   frame.window_label =
130     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
131                   InputOutput, screen->getVisual(), create_mask, &attrib);
132   blackbox->saveToolbarSearch(frame.window_label, this);
133
134   frame.clock =
135     XCreateWindow(display, frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
136                   InputOutput, screen->getVisual(), create_mask, &attrib);
137   blackbox->saveToolbarSearch(frame.clock, this);
138
139   frame.psbutton =
140     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
141                   InputOutput, screen->getVisual(), create_mask, &attrib);
142   blackbox->saveToolbarSearch(frame.psbutton, this);
143
144   frame.nsbutton =
145     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
146                   InputOutput, screen->getVisual(), create_mask, &attrib);
147   blackbox->saveToolbarSearch(frame.nsbutton, this);
148
149   frame.pwbutton =
150     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
151                   InputOutput, screen->getVisual(), create_mask, &attrib);
152   blackbox->saveToolbarSearch(frame.pwbutton, this);
153
154   frame.nwbutton =
155     XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0, screen->getDepth(),
156                   InputOutput, screen->getVisual(), create_mask, &attrib);
157   blackbox->saveToolbarSearch(frame.nwbutton, this);
158
159   frame.base = frame.label = frame.wlabel = frame.clk = frame.button =
160     frame.pbutton = None;
161
162   screen->addStrut(&strut);
163
164   reconfigure();
165   mapToolbar();
166 }
167
168
169 Toolbar::~Toolbar(void) {
170   unmapToolbar();
171
172   if (frame.base) screen->getImageControl()->removeImage(frame.base);
173   if (frame.label) screen->getImageControl()->removeImage(frame.label);
174   if (frame.wlabel) screen->getImageControl()->removeImage(frame.wlabel);
175   if (frame.clk) screen->getImageControl()->removeImage(frame.clk);
176   if (frame.button) screen->getImageControl()->removeImage(frame.button);
177   if (frame.pbutton) screen->getImageControl()->removeImage(frame.pbutton);
178
179   blackbox->removeToolbarSearch(frame.window);
180   blackbox->removeToolbarSearch(frame.workspace_label);
181   blackbox->removeToolbarSearch(frame.window_label);
182   blackbox->removeToolbarSearch(frame.clock);
183   blackbox->removeToolbarSearch(frame.psbutton);
184   blackbox->removeToolbarSearch(frame.nsbutton);
185   blackbox->removeToolbarSearch(frame.pwbutton);
186   blackbox->removeToolbarSearch(frame.nwbutton);
187
188   XDestroyWindow(display, frame.workspace_label);
189   XDestroyWindow(display, frame.window_label);
190   XDestroyWindow(display, frame.clock);
191
192   XDestroyWindow(display, frame.window);
193
194   delete hide_timer;
195   delete clock_timer;
196   delete toolbarmenu;
197 }
198
199
200 void Toolbar::mapToolbar() {
201   if (!screen->doHideToolbar()) {
202     //not hidden, so windows should not maximize over the toolbar
203     XMapSubwindows(display, frame.window);
204     XMapWindow(display, frame.window);
205   }
206   updateStrut();
207 }
208
209
210 void Toolbar::unmapToolbar() {
211   if (toolbarmenu->isVisible())
212     toolbarmenu->hide();
213   //hidden so we can maximize over the toolbar
214   XUnmapWindow(display, frame.window);
215   updateStrut();
216 }
217
218
219 void Toolbar::saveOnTop(bool b) {
220   on_top = b;
221   config->setValue(toolbarstr + "onTop", on_top);
222 }
223
224
225 void Toolbar::saveAutoHide(bool b) {
226   do_auto_hide = b;
227   config->setValue(toolbarstr + "autoHide", do_auto_hide);
228 }
229
230
231 void Toolbar::saveWidthPercent(unsigned int w) {
232   width_percent = w;
233   config->setValue(toolbarstr + "widthPercent", width_percent);
234 }
235
236
237 void Toolbar::savePlacement(int p) {
238   placement = p;
239   const char *pname;
240   switch (placement) {
241   case TopLeft: pname = "TopLeft"; break;
242   case BottomLeft: pname = "BottomLeft"; break;
243   case TopCenter: pname = "TopCenter"; break;
244   case TopRight: pname = "TopRight"; break;
245   case BottomRight: pname = "BottomRight"; break;
246   case BottomCenter: default: pname = "BottomCenter"; break;
247   }
248   config->setValue(toolbarstr + "placement", pname);
249 }
250
251
252 void Toolbar::save_rc(void) {
253   saveOnTop(on_top);
254   saveAutoHide(do_auto_hide);
255   saveWidthPercent(width_percent);
256   savePlacement(placement);
257 }
258
259
260 void Toolbar::load_rc(void) {
261   string s;
262   
263   if (! config->getValue(toolbarstr + "onTop", on_top))
264     on_top = false;
265
266   if (! config->getValue(toolbarstr + "autoHide", do_auto_hide))
267     do_auto_hide = false;
268   hidden = do_auto_hide;
269
270   if (! config->getValue(toolbarstr + "widthPercent", width_percent) ||
271       width_percent == 0 || width_percent > 100)
272     width_percent = 66;
273
274   if (config->getValue(toolbarstr + "placement", s)) {
275     if (s == "TopLeft")
276       placement = TopLeft;
277     else if (s == "BottomLeft")
278       placement = BottomLeft;
279     else if (s == "TopCenter")
280       placement = TopCenter;
281     else if (s == "TopRight")
282       placement = TopRight;
283     else if (s == "BottomRight")
284       placement = BottomRight;
285     else //if (s == "BottomCenter")
286       placement = BottomCenter;
287   } else
288     placement = BottomCenter;
289 }
290
291
292 void Toolbar::reconfigure(void) {
293   unsigned int width, height;
294  
295   width = (screen->getWidth() * width_percent) / 100;
296   height = screen->getToolbarStyle()->font->height();
297
298   frame.bevel_w = screen->getBevelWidth();
299   frame.button_w = height;
300   height += 2;
301   frame.label_h = height;
302   height += (frame.bevel_w * 2);
303
304   frame.rect.setSize(width, height);
305
306   int x, y;
307   switch (placement) {
308   case TopLeft:
309   case TopRight:
310   case TopCenter:
311     if (placement == TopLeft)
312       x = 0;
313     else if (placement == TopRight)
314       x = screen->getWidth() - frame.rect.width()
315         - (screen->getBorderWidth() * 2);
316     else
317       x = (screen->getWidth() - frame.rect.width()) / 2;
318
319     y = 0;
320
321     frame.x_hidden = x;
322     frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
323                      - frame.rect.height();
324     break;
325
326   case BottomLeft:
327   case BottomRight:
328   case BottomCenter:
329   default:
330     if (placement == BottomLeft)
331       x = 0;
332     else if (placement == BottomRight)
333       x = screen->getWidth() - frame.rect.width()
334         - (screen->getBorderWidth() * 2);
335     else
336       x = (screen->getWidth() - frame.rect.width()) / 2;
337
338     y = screen->getHeight() - frame.rect.height()
339       - (screen->getBorderWidth() * 2);
340
341     frame.x_hidden = x;
342     frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
343                      - screen->getBorderWidth();
344     break;
345   }
346
347   frame.rect.setPos(x, y);
348
349   updateStrut();
350
351 #ifdef    HAVE_STRFTIME
352   time_t ttmp = time(NULL);
353
354   frame.clock_w = 0;
355   if (ttmp != -1) {
356     struct tm *tt = localtime(&ttmp);
357     if (tt) {
358       char t[1024];
359       int len = strftime(t, 1024, screen->getStrftimeFormat(), tt);
360       if (len == 0) { // invalid time format found
361         screen->saveStrftimeFormat("%I:%M %p"); // so use the default
362         strftime(t, 1024, screen->getStrftimeFormat(), tt);
363       }
364       // find the length of the rendered string and add room for two extra
365       // characters to it.  This allows for variable width output of the fonts
366       BFont *font = screen->getToolbarStyle()->font;
367       frame.clock_w = font->measureString(t) + font->maxCharWidth() * 2;
368     }
369   }
370 #else // !HAVE_STRFTIME
371   {
372     string s = i18n(ToolbarSet, ToolbarNoStrftimeLength, "00:00000");
373     frame.clock_w = screen->getToolbarStyle()->font->measureString(s);
374   }
375 #endif // HAVE_STRFTIME
376
377   frame.workspace_label_w = 0;
378
379   for (unsigned int i = 0; i < screen->getWorkspaceCount(); i++) {
380     const string& workspace_name = screen->getWorkspace(i)->getName();
381     width = screen->getToolbarStyle()->font->measureString(workspace_name);
382     if (width > frame.workspace_label_w) frame.workspace_label_w = width;
383   }
384
385   frame.workspace_label_w = frame.clock_w =
386     std::max(frame.workspace_label_w, frame.clock_w) + (frame.bevel_w * 4);
387
388   // XXX: where'd the +6 come from?
389   frame.window_label_w =
390     (frame.rect.width() - (frame.clock_w + (frame.button_w * 4) +
391                            frame.workspace_label_w + (frame.bevel_w * 8) + 6));
392
393   if (hidden) {
394     XMoveResizeWindow(display, frame.window, frame.x_hidden, frame.y_hidden,
395                       frame.rect.width(), frame.rect.height());
396   } else {
397     XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
398                       frame.rect.width(), frame.rect.height());
399   }
400
401   XMoveResizeWindow(display, frame.workspace_label, frame.bevel_w,
402                     frame.bevel_w, frame.workspace_label_w,
403                     frame.label_h);
404   XMoveResizeWindow(display, frame.psbutton,
405                     ((frame.bevel_w * 2) + frame.workspace_label_w + 1),
406                     frame.bevel_w + 1, frame.button_w, frame.button_w);
407   XMoveResizeWindow(display, frame.nsbutton,
408                     ((frame.bevel_w * 3) + frame.workspace_label_w +
409                      frame.button_w + 2), frame.bevel_w + 1, frame.button_w,
410                     frame.button_w);
411   XMoveResizeWindow(display, frame.window_label,
412                     ((frame.bevel_w * 4) + (frame.button_w * 2) +
413                      frame.workspace_label_w + 3), frame.bevel_w,
414                     frame.window_label_w, frame.label_h);
415   XMoveResizeWindow(display, frame.pwbutton,
416                     ((frame.bevel_w * 5) + (frame.button_w * 2) +
417                      frame.workspace_label_w + frame.window_label_w + 4),
418                     frame.bevel_w + 1, frame.button_w, frame.button_w);
419   XMoveResizeWindow(display, frame.nwbutton,
420                     ((frame.bevel_w * 6) + (frame.button_w * 3) +
421                      frame.workspace_label_w + frame.window_label_w + 5),
422                     frame.bevel_w + 1, frame.button_w, frame.button_w);
423   XMoveResizeWindow(display, frame.clock,
424                     frame.rect.width() - frame.clock_w - (frame.bevel_w * 2),
425                     frame.bevel_w, frame.clock_w, frame.label_h);
426
427   ToolbarStyle *style = screen->getToolbarStyle();
428   frame.base = style->toolbar.render(frame.rect.width(), frame.rect.height(),
429                                      frame.base);
430   if (! frame.base)
431     XSetWindowBackground(display, frame.window,
432                          style->toolbar.color().pixel());
433   else
434     XSetWindowBackgroundPixmap(display, frame.window, frame.base);
435
436   frame.label = style->window.render(frame.window_label_w, frame.label_h,
437                                      frame.label);
438   if (! frame.label)
439     XSetWindowBackground(display, frame.window_label,
440                          style->window.color().pixel());
441   else
442     XSetWindowBackgroundPixmap(display, frame.window_label, frame.label);
443
444   frame.wlabel = style->label.render(frame.workspace_label_w, frame.label_h,
445                                 frame.wlabel);
446   if (! frame.wlabel)
447     XSetWindowBackground(display, frame.workspace_label,
448                          style->label.color().pixel());
449   else
450     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
451
452   frame.clk = style->clock.render(frame.clock_w, frame.label_h, frame.clk);
453   if (! frame.clk)
454     XSetWindowBackground(display, frame.clock, style->clock.color().pixel());
455   else
456     XSetWindowBackgroundPixmap(display, frame.clock, frame.clk);
457
458   frame.button = style->button.render(frame.button_w, frame.button_w,
459                                 frame.button);
460   if (! frame.button) {
461     frame.button_pixel = style->button.color().pixel();
462     XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
463     XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
464     XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
465     XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
466   } else {
467     XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
468     XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
469     XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
470     XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
471   }
472
473   frame.pbutton = style->pressed.render(frame.button_w, frame.button_w,
474                                         frame.pbutton);
475   if (! frame.pbutton)
476     frame.pbutton_pixel = style->pressed.color().pixel();
477
478   XSetWindowBorder(display, frame.window,
479                    screen->getBorderColor()->pixel());
480   XSetWindowBorderWidth(display, frame.window, screen->getBorderWidth());
481
482   XClearWindow(display, frame.window);
483   XClearWindow(display, frame.workspace_label);
484   XClearWindow(display, frame.window_label);
485   XClearWindow(display, frame.clock);
486   XClearWindow(display, frame.psbutton);
487   XClearWindow(display, frame.nsbutton);
488   XClearWindow(display, frame.pwbutton);
489   XClearWindow(display, frame.nwbutton);
490
491   redrawWindowLabel();
492   redrawWorkspaceLabel();
493   redrawPrevWorkspaceButton();
494   redrawNextWorkspaceButton();
495   redrawPrevWindowButton();
496   redrawNextWindowButton();
497   checkClock(True);
498
499   toolbarmenu->reconfigure();
500 }
501
502
503 void Toolbar::updateStrut(void) {
504   // left and right are always 0
505   strut.top = strut.bottom = 0;
506
507   // when hidden only one border is visible
508   unsigned int border_width = screen->getBorderWidth();
509   if (! do_auto_hide)
510     border_width *= 2;
511
512   if (! screen->doHideToolbar()) {
513     switch(placement) {
514     case TopLeft:
515     case TopCenter:
516     case TopRight:
517       strut.top = getExposedHeight() + border_width;
518       break;
519     default:
520       strut.bottom = getExposedHeight() + border_width;
521     }
522   }
523
524   screen->updateAvailableArea();
525 }
526
527
528 #ifdef    HAVE_STRFTIME
529 void Toolbar::checkClock(bool redraw) {
530 #else // !HAVE_STRFTIME
531 void Toolbar::checkClock(bool redraw, bool date) {
532 #endif // HAVE_STRFTIME
533   time_t tmp = 0;
534   struct tm *tt = 0;
535
536   if ((tmp = time(NULL)) != -1) {
537     if (! (tt = localtime(&tmp))) return;
538     if (tt->tm_min != frame.minute || tt->tm_hour != frame.hour) {
539       frame.hour = tt->tm_hour;
540       frame.minute = tt->tm_min;
541       XClearWindow(display, frame.clock);
542       redraw = True;
543     }
544   }
545
546   if (redraw) {
547 #ifdef    HAVE_STRFTIME
548     char t[1024];
549     if (! strftime(t, 1024, screen->getStrftimeFormat(), tt))
550       return;
551 #else // !HAVE_STRFTIME
552     char t[9];
553     if (date) {
554       // format the date... with special consideration for y2k ;)
555       if (screen->getDateFormat() == Blackbox::B_EuropeanDate)
556         sprintf(t, 18n(ToolbarSet, ToolbarNoStrftimeDateFormatEu,
557                        "%02d.%02d.%02d"),
558                 tt->tm_mday, tt->tm_mon + 1,
559                 (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year);
560       else
561         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeDateFormat,
562                         "%02d/%02d/%02d"),
563                 tt->tm_mon + 1, tt->tm_mday,
564                 (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year);
565     } else {
566       if (screen->isClock24Hour())
567         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeTimeFormat24,
568                         "  %02d:%02d "),
569                 frame.hour, frame.minute);
570       else
571         sprintf(t, i18n(ToolbarSet, ToolbarNoStrftimeTimeFormat12,
572                         "%02d:%02d %sm"),
573                 ((frame.hour > 12) ? frame.hour - 12 :
574                  ((frame.hour == 0) ? 12 : frame.hour)), frame.minute,
575                 ((frame.hour >= 12) ?
576                  i18n(ToolbarSet, ToolbarNoStrftimeTimeFormatP, "p") :
577                  i18n(ToolbarSet, ToolbarNoStrftimeTimeFormatA, "a")));
578     }
579 #endif // HAVE_STRFTIME
580
581     ToolbarStyle *style = screen->getToolbarStyle();
582
583     int pos = frame.bevel_w * 2; // this is modified by doJustify()
584     style->doJustify(t, pos, frame.clock_w, frame.bevel_w * 4);
585     style->font->drawString(frame.clock, pos, 1, style->c_text, t);
586   }
587 }
588
589
590 void Toolbar::redrawWindowLabel(bool redraw) {
591   BlackboxWindow *foc = screen->getBlackbox()->getFocusedWindow();
592   if (! foc) {
593     XClearWindow(display, frame.window_label);
594     return;
595   }
596
597   if (redraw)
598     XClearWindow(display, frame.window_label);
599
600   if (foc->getScreen() != screen) return;
601
602   const char *title = foc->getTitle();
603   ToolbarStyle *style = screen->getToolbarStyle();
604
605   int pos = frame.bevel_w * 2; // modified by doJustify()
606   style->doJustify(title, pos, frame.window_label_w, frame.bevel_w * 4);
607   style->font->drawString(frame.window_label, pos, 1, style->w_text, title);
608 }
609
610
611 void Toolbar::redrawWorkspaceLabel(bool redraw) {
612   const string& name = screen->getCurrentWorkspace()->getName();
613
614   if (redraw)
615     XClearWindow(display, frame.workspace_label);
616
617   ToolbarStyle *style = screen->getToolbarStyle();
618
619   int pos = frame.bevel_w * 2;
620   style->doJustify(name.c_str(), pos, frame.workspace_label_w,
621                    frame.bevel_w * 4);
622   style->font->drawString(frame.workspace_label, pos, 1, style->l_text, name);
623 }
624
625
626 void Toolbar::redrawPrevWorkspaceButton(bool pressed, bool redraw) {
627   if (redraw) {
628     if (pressed) {
629       if (frame.pbutton)
630         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.pbutton);
631       else
632         XSetWindowBackground(display, frame.psbutton, frame.pbutton_pixel);
633     } else {
634       if (frame.button)
635         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
636       else
637         XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
638     }
639     XClearWindow(display, frame.psbutton);
640   }
641
642   int hh = frame.button_w / 2, hw = frame.button_w / 2;
643
644   XPoint pts[3];
645   pts[0].x = hw - 2; pts[0].y = hh;
646   pts[1].x = 4; pts[1].y = 2;
647   pts[2].x = 0; pts[2].y = -4;
648
649   ToolbarStyle *style = screen->getToolbarStyle();
650   BPen pen(style->b_pic);
651   XFillPolygon(display, frame.psbutton, pen.gc(),
652                pts, 3, Convex, CoordModePrevious);
653 }
654
655
656 void Toolbar::redrawNextWorkspaceButton(bool pressed, bool redraw) {
657   if (redraw) {
658     if (pressed) {
659       if (frame.pbutton)
660         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.pbutton);
661       else
662         XSetWindowBackground(display, frame.nsbutton, frame.pbutton_pixel);
663     } else {
664       if (frame.button)
665         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
666       else
667         XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
668     }
669     XClearWindow(display, frame.nsbutton);
670   }
671
672   int hh = frame.button_w / 2, hw = frame.button_w / 2;
673
674   XPoint pts[3];
675   pts[0].x = hw - 2; pts[0].y = hh - 2;
676   pts[1].x = 4; pts[1].y =  2;
677   pts[2].x = -4; pts[2].y = 2;
678
679   ToolbarStyle *style = screen->getToolbarStyle();
680   BPen pen(style->b_pic);
681   XFillPolygon(display, frame.nsbutton, pen.gc(),
682                pts, 3, Convex, CoordModePrevious);
683 }
684
685
686 void Toolbar::redrawPrevWindowButton(bool pressed, bool redraw) {
687   if (redraw) {
688     if (pressed) {
689       if (frame.pbutton)
690         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.pbutton);
691       else
692         XSetWindowBackground(display, frame.pwbutton, frame.pbutton_pixel);
693     } else {
694       if (frame.button)
695         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
696       else
697         XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
698     }
699     XClearWindow(display, frame.pwbutton);
700   }
701
702   int hh = frame.button_w / 2, hw = frame.button_w / 2;
703
704   XPoint pts[3];
705   pts[0].x = hw - 2; pts[0].y = hh;
706   pts[1].x = 4; pts[1].y = 2;
707   pts[2].x = 0; pts[2].y = -4;
708
709   ToolbarStyle *style = screen->getToolbarStyle();
710   BPen pen(style->b_pic);
711   XFillPolygon(display, frame.pwbutton, pen.gc(),
712                pts, 3, Convex, CoordModePrevious);
713 }
714
715
716 void Toolbar::redrawNextWindowButton(bool pressed, bool redraw) {
717   if (redraw) {
718     if (pressed) {
719       if (frame.pbutton)
720         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.pbutton);
721       else
722         XSetWindowBackground(display, frame.nwbutton, frame.pbutton_pixel);
723     } else {
724       if (frame.button)
725         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
726       else
727         XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
728     }
729     XClearWindow(display, frame.nwbutton);
730   }
731
732   int hh = frame.button_w / 2, hw = frame.button_w / 2;
733
734   XPoint pts[3];
735   pts[0].x = hw - 2; pts[0].y = hh - 2;
736   pts[1].x = 4; pts[1].y =  2;
737   pts[2].x = -4; pts[2].y = 2;
738
739   ToolbarStyle *style = screen->getToolbarStyle();
740   BPen pen(style->b_pic);
741   XFillPolygon(display, frame.nwbutton, pen.gc(), pts, 3, Convex,
742                CoordModePrevious);
743 }
744
745
746 void Toolbar::edit(void) {
747   Window window;
748   int foo;
749
750   editing = True;
751   XGetInputFocus(display, &window, &foo);
752   if (window == frame.workspace_label)
753     return;
754
755   XSetInputFocus(display, frame.workspace_label,
756                  RevertToPointerRoot, CurrentTime);
757   XClearWindow(display, frame.workspace_label);
758
759   blackbox->setNoFocus(True);
760   if (blackbox->getFocusedWindow())
761     blackbox->getFocusedWindow()->setFocusFlag(False);
762
763   ToolbarStyle *style = screen->getToolbarStyle();
764   BPen pen(style->l_text);
765   XDrawRectangle(display, frame.workspace_label, pen.gc(),
766                  frame.workspace_label_w / 2, 0, 1,
767                  frame.label_h - 1);
768   // change the background of the window to that of an active window label
769   BTexture *texture = &(screen->getWindowStyle()->l_focus);
770   frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
771                                  frame.wlabel);
772   if (! frame.wlabel)
773     XSetWindowBackground(display, frame.workspace_label,
774                          texture->color().pixel());
775   else
776     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
777 }
778
779
780 void Toolbar::buttonPressEvent(XButtonEvent *be) {
781   if (be->button == 1) {
782     if (be->window == frame.psbutton)
783       redrawPrevWorkspaceButton(True, True);
784     else if (be->window == frame.nsbutton)
785       redrawNextWorkspaceButton(True, True);
786     else if (be->window == frame.pwbutton)
787       redrawPrevWindowButton(True, True);
788     else if (be->window == frame.nwbutton)
789       redrawNextWindowButton(True, True);
790 #ifndef   HAVE_STRFTIME
791     else if (be->window == frame.clock) {
792       XClearWindow(display, frame.clock);
793       checkClock(True, True);
794     }
795 #endif // HAVE_STRFTIME
796     else if (! on_top) {
797       Window w[1] = { frame.window };
798       screen->raiseWindows(w, 1);
799     }
800   } else if (be->button == 2 && (! on_top)) {
801     XLowerWindow(display, frame.window);
802   } else if (be->button == 3) {
803     if (toolbarmenu->isVisible()) {
804       toolbarmenu->hide();
805     } else {
806       int x, y;
807
808       x = be->x_root - (toolbarmenu->getWidth() / 2);
809       y = be->y_root - (toolbarmenu->getHeight() / 2);
810
811       if (x < 0)
812         x = 0;
813       else if (x + toolbarmenu->getWidth() > screen->getWidth())
814         x = screen->getWidth() - toolbarmenu->getWidth();
815
816       if (y < 0)
817         y = 0;
818       else if (y + toolbarmenu->getHeight() > screen->getHeight())
819         y = screen->getHeight() - toolbarmenu->getHeight();
820
821       toolbarmenu->move(x, y);
822       toolbarmenu->show();
823     }
824   }
825 }
826
827
828
829 void Toolbar::buttonReleaseEvent(XButtonEvent *re) {
830   if (re->button == 1) {
831     if (re->window == frame.psbutton) {
832       redrawPrevWorkspaceButton(False, True);
833
834       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
835           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
836        if (screen->getCurrentWorkspace()->getID() > 0)
837           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
838                                     getID() - 1);
839         else
840           screen->changeWorkspaceID(screen->getWorkspaceCount() - 1);
841     } else if (re->window == frame.nsbutton) {
842       redrawNextWorkspaceButton(False, True);
843
844       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
845           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
846         if (screen->getCurrentWorkspace()->getID() <
847             (screen->getWorkspaceCount() - 1))
848           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
849                                     getID() + 1);
850         else
851           screen->changeWorkspaceID(0);
852     } else if (re->window == frame.pwbutton) {
853       redrawPrevWindowButton(False, True);
854
855       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
856           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
857         screen->prevFocus();
858     } else if (re->window == frame.nwbutton) {
859       redrawNextWindowButton(False, True);
860
861       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
862           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
863         screen->nextFocus();
864     } else if (re->window == frame.window_label)
865       screen->raiseFocus();
866 #ifndef   HAVE_STRFTIME
867     else if (re->window == frame.clock) {
868       XClearWindow(display, frame.clock);
869       checkClock(True);
870     }
871 #endif // HAVE_STRFTIME
872   }
873 }
874
875
876 void Toolbar::enterNotifyEvent(XCrossingEvent *) {
877   if (! do_auto_hide)
878     return;
879
880   if (hidden) {
881     if (! hide_timer->isTiming()) hide_timer->start();
882   } else {
883     if (hide_timer->isTiming()) hide_timer->stop();
884   }
885 }
886
887 void Toolbar::leaveNotifyEvent(XCrossingEvent *) {
888   if (! do_auto_hide)
889     return;
890
891   if (hidden) {
892     if (hide_timer->isTiming()) hide_timer->stop();
893   } else if (! toolbarmenu->isVisible()) {
894     if (! hide_timer->isTiming()) hide_timer->start();
895   }
896 }
897
898
899 void Toolbar::exposeEvent(XExposeEvent *ee) {
900   if (ee->window == frame.clock) checkClock(True);
901   else if (ee->window == frame.workspace_label && (! editing))
902     redrawWorkspaceLabel();
903   else if (ee->window == frame.window_label) redrawWindowLabel();
904   else if (ee->window == frame.psbutton) redrawPrevWorkspaceButton();
905   else if (ee->window == frame.nsbutton) redrawNextWorkspaceButton();
906   else if (ee->window == frame.pwbutton) redrawPrevWindowButton();
907   else if (ee->window == frame.nwbutton) redrawNextWindowButton();
908 }
909
910
911 void Toolbar::keyPressEvent(XKeyEvent *ke) {
912   if (ke->window == frame.workspace_label && editing) {
913     if (new_workspace_name.empty()) {
914       new_name_pos = 0;
915     }
916
917     KeySym ks;
918     char keychar[1];
919     XLookupString(ke, keychar, 1, &ks, 0);
920
921     // either we are told to end with a return or we hit 127 chars
922     if (ks == XK_Return || new_name_pos == 127) {
923       editing = False;
924
925       blackbox->setNoFocus(False);
926       if (blackbox->getFocusedWindow()) {
927         blackbox->getFocusedWindow()->setInputFocus();
928       } else {
929         blackbox->setFocusedWindow(0);
930       }
931
932       Workspace *wkspc = screen->getCurrentWorkspace();
933       wkspc->setName(new_workspace_name);
934       wkspc->getMenu()->hide();
935
936       screen->getWorkspacemenu()->changeItemLabel(wkspc->getID() + 2,
937                                                   wkspc->getName());
938       screen->getWorkspacemenu()->update();
939
940       new_workspace_name.erase();
941       new_name_pos = 0;
942
943       // reset the background to that of the workspace label (its normal
944       // setting)
945       BTexture *texture = &(screen->getToolbarStyle()->label);
946       frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
947                                      frame.wlabel);
948       if (! frame.wlabel)
949         XSetWindowBackground(display, frame.workspace_label,
950                              texture->color().pixel());
951       else
952         XSetWindowBackgroundPixmap(display, frame.workspace_label,
953                                    frame.wlabel);
954       reconfigure();
955     } else if (! (ks == XK_Shift_L || ks == XK_Shift_R ||
956                   ks == XK_Control_L || ks == XK_Control_R ||
957                   ks == XK_Caps_Lock || ks == XK_Shift_Lock ||
958                   ks == XK_Meta_L || ks == XK_Meta_R ||
959                   ks == XK_Alt_L || ks == XK_Alt_R ||
960                   ks == XK_Super_L || ks == XK_Super_R ||
961                   ks == XK_Hyper_L || ks == XK_Hyper_R)) {
962       if (ks == XK_BackSpace) {
963         if (new_name_pos > 0) {
964           --new_name_pos;
965           new_workspace_name.erase(new_name_pos);
966         } else {
967           new_workspace_name.resize(0);
968         }
969       } else {
970         new_workspace_name += (*keychar);
971         ++new_name_pos;
972       }
973
974       XClearWindow(display, frame.workspace_label);
975       unsigned int tw, x;
976
977       tw = screen->getToolbarStyle()->font->measureString(new_workspace_name);
978       x = (frame.workspace_label_w - tw) / 2;
979
980       if (x < frame.bevel_w) x = frame.bevel_w;
981
982       ToolbarStyle *style = screen->getToolbarStyle();
983       style->font->drawString(frame.workspace_label, x, 1, style->l_text,
984                               new_workspace_name);
985       BPen pen(style->l_text);
986       XDrawRectangle(display, frame.workspace_label, pen.gc(), x + tw, 0, 1,
987                      frame.label_h - 1);
988     }
989   }
990 }
991
992
993 void Toolbar::timeout(void) {
994   checkClock(True);
995
996   clock_timer->setTimeout(aMinuteFromNow());
997 }
998
999
1000 void Toolbar::HideHandler::timeout(void) {
1001   toolbar->hidden = ! toolbar->hidden;
1002   if (toolbar->hidden)
1003     XMoveWindow(toolbar->display, toolbar->frame.window,
1004                 toolbar->frame.x_hidden, toolbar->frame.y_hidden);
1005   else
1006     XMoveWindow(toolbar->display, toolbar->frame.window,
1007                 toolbar->frame.rect.x(), toolbar->frame.rect.y());
1008 }
1009
1010
1011 void Toolbar::toggleAutoHide(void) {
1012   saveAutoHide(! doAutoHide());
1013
1014   updateStrut();
1015   screen->getSlit()->reposition();
1016
1017   if (do_auto_hide == False && hidden) {
1018     // force the slit to be visible
1019     if (hide_timer->isTiming()) hide_timer->stop();
1020     hide_handler.timeout();
1021   }
1022 }
1023
1024
1025 Toolbarmenu::Toolbarmenu(Toolbar *tb) : Basemenu(tb->screen) {
1026   toolbar = tb;
1027
1028   setLabel(i18n(ToolbarSet, ToolbarToolbarTitle, "Toolbar"));
1029   setInternalMenu();
1030
1031   placementmenu = new Placementmenu(this);
1032
1033   insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
1034          placementmenu);
1035   insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
1036   insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
1037   insert(i18n(ToolbarSet, ToolbarEditWkspcName,
1038               "Edit current workspace name"), 3);
1039
1040   update();
1041   setValues();
1042 }
1043
1044
1045 void Toolbarmenu::setValues() {
1046   setItemSelected(1, toolbar->isOnTop());
1047   setItemSelected(2, toolbar->doAutoHide());
1048 }
1049
1050
1051 Toolbarmenu::~Toolbarmenu(void) {
1052   delete placementmenu;
1053 }
1054
1055
1056 void Toolbarmenu::itemSelected(int button, unsigned int index) {
1057   if (button != 1)
1058     return;
1059
1060   BasemenuItem *item = find(index);
1061   if (! item) return;
1062
1063   switch (item->function()) {
1064   case 1: { // always on top
1065     toolbar->saveOnTop(! toolbar->isOnTop());
1066     setItemSelected(1, toolbar->isOnTop());
1067
1068     if (toolbar->isOnTop()) getScreen()->raiseWindows((Window *) 0, 0);
1069     break;
1070   }
1071
1072   case 2: { // auto hide
1073     toolbar->toggleAutoHide();
1074     setItemSelected(2, toolbar->doAutoHide());
1075
1076     break;
1077   }
1078
1079   case 3: { // edit current workspace name
1080     toolbar->edit();
1081     hide();
1082
1083     break;
1084   }
1085   } // switch
1086 }
1087
1088
1089 void Toolbarmenu::internal_hide(void) {
1090   Basemenu::internal_hide();
1091   if (toolbar->doAutoHide() && ! toolbar->isEditing())
1092     toolbar->hide_handler.timeout();
1093 }
1094
1095
1096 void Toolbarmenu::reconfigure(void) {
1097   setValues();
1098   placementmenu->reconfigure();
1099
1100   Basemenu::reconfigure();
1101 }
1102
1103
1104 Toolbarmenu::Placementmenu::Placementmenu(Toolbarmenu *tm)
1105   : Basemenu(tm->toolbar->screen), toolbar(tm->toolbar) {
1106   setLabel(i18n(ToolbarSet, ToolbarToolbarPlacement, "Toolbar Placement"));
1107   setInternalMenu();
1108   setMinimumSublevels(3);
1109
1110   insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
1111          Toolbar::TopLeft);
1112   insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
1113          Toolbar::BottomLeft);
1114   insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
1115          Toolbar::TopCenter);
1116   insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
1117          Toolbar::BottomCenter);
1118   insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
1119          Toolbar::TopRight);
1120   insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
1121          Toolbar::BottomRight);
1122   update();
1123   setValues();
1124 }
1125
1126
1127 void Toolbarmenu::Placementmenu::setValues(void) {
1128   int place = 0;
1129   switch (toolbar->getPlacement()) {
1130   case Toolbar::BottomRight:
1131     place++;
1132   case Toolbar::TopRight:
1133     place++;
1134   case Toolbar::BottomCenter:
1135     place++;
1136   case Toolbar::TopCenter:
1137     place++;
1138   case Toolbar::BottomLeft:
1139     place++;
1140   case Toolbar::TopLeft:
1141     break;
1142   }
1143   setItemSelected(0, 0 == place);
1144   setItemSelected(1, 1 == place);
1145   setItemSelected(2, 2 == place);
1146   setItemSelected(3, 3 == place);
1147   setItemSelected(4, 4 == place);
1148   setItemSelected(5, 5 == place);
1149 }
1150
1151
1152 void Toolbarmenu::Placementmenu::reconfigure(void) {
1153   setValues();
1154   Basemenu::reconfigure();
1155 }
1156
1157
1158 void Toolbarmenu::Placementmenu::itemSelected(int button, unsigned int index) {
1159   if (button != 1)
1160     return;
1161
1162   BasemenuItem *item = find(index);
1163   if (! item) return;
1164
1165   toolbar->savePlacement(item->function());
1166   hide();
1167   toolbar->reconfigure();
1168
1169   // reposition the slit as well to make sure it doesn't intersect the
1170   // toolbar
1171   getScreen()->getSlit()->reposition();
1172 }
1173
1174
1175 void ToolbarStyle::doJustify(const std::string &text, int &start_pos,
1176                              unsigned int max_length,
1177                              unsigned int modifier) const {
1178   size_t text_len = text.size();
1179   unsigned int length;
1180
1181   do {
1182     length = font->measureString(string(text, 0, text_len)) + modifier;
1183   } while (length > max_length && text_len-- > 0);
1184
1185   switch (justify) {
1186   case RightJustify:
1187     start_pos += max_length - length;
1188     break;
1189
1190   case CenterJustify:
1191     start_pos += (max_length - length) / 2;
1192     break;
1193
1194   case LeftJustify:
1195   default:
1196     break;
1197   }
1198 }