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