]> icculus.org git repositories - dana/openbox.git/blob - src/Toolbar.cc
add Configuration class for generic configuration data load/save-ing.
[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 "GCCache.hh"
58 #include "Iconmenu.hh"
59 #include "Image.hh"
60 #include "Rootmenu.hh"
61 #include "Screen.hh"
62 #include "Toolbar.hh"
63 #include "Window.hh"
64 #include "Workspace.hh"
65 #include "Workspacemenu.hh"
66 #include "Slit.hh"
67
68
69 static long aMinuteFromNow(void) {
70   timeval now;
71   gettimeofday(&now, 0);
72   return ((60 - (now.tv_sec % 60)) * 1000);
73 }
74
75
76 Toolbar::Toolbar(BScreen *scrn) {
77   screen = scrn;
78   blackbox = screen->getBlackbox();
79   toolbarstr = (string)"session.screen" + itostring(screen->getScreenNumber())
80     + ".toolbar.";
81   config = blackbox->getConfig();
82
83   load_rc();
84
85   // get the clock updating every minute
86   clock_timer = new BTimer(blackbox, this);
87   clock_timer->setTimeout(aMinuteFromNow());
88   clock_timer->recurring(True);
89   clock_timer->start();
90   frame.minute = frame.hour = -1;
91
92   hide_handler.toolbar = this;
93   hide_timer = new BTimer(blackbox, &hide_handler);
94   hide_timer->setTimeout(blackbox->getAutoRaiseDelay());
95
96   editing = False;
97   new_name_pos = 0;
98   frame.grab_x = frame.grab_y = 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   screen->addStrut(&strut);
162
163   reconfigure();
164
165   XMapSubwindows(display, frame.window);
166   XMapWindow(display, frame.window);
167 }
168
169
170 Toolbar::~Toolbar(void) {
171   XUnmapWindow(display, frame.window);
172
173   if (frame.base) screen->getImageControl()->removeImage(frame.base);
174   if (frame.label) screen->getImageControl()->removeImage(frame.label);
175   if (frame.wlabel) screen->getImageControl()->removeImage(frame.wlabel);
176   if (frame.clk) screen->getImageControl()->removeImage(frame.clk);
177   if (frame.button) screen->getImageControl()->removeImage(frame.button);
178   if (frame.pbutton) screen->getImageControl()->removeImage(frame.pbutton);
179
180   blackbox->removeToolbarSearch(frame.window);
181   blackbox->removeToolbarSearch(frame.workspace_label);
182   blackbox->removeToolbarSearch(frame.window_label);
183   blackbox->removeToolbarSearch(frame.clock);
184   blackbox->removeToolbarSearch(frame.psbutton);
185   blackbox->removeToolbarSearch(frame.nsbutton);
186   blackbox->removeToolbarSearch(frame.pwbutton);
187   blackbox->removeToolbarSearch(frame.nwbutton);
188
189   XDestroyWindow(display, frame.workspace_label);
190   XDestroyWindow(display, frame.window_label);
191   XDestroyWindow(display, frame.clock);
192
193   XDestroyWindow(display, frame.window);
194
195   delete hide_timer;
196   delete clock_timer;
197   delete toolbarmenu;
198 }
199
200
201 void Toolbar::saveOnTop(bool b) {
202   on_top = b;
203   config->setValue(toolbarstr + "onTop", on_top);
204 }
205
206
207 void Toolbar::saveAutoHide(bool b) {
208   do_auto_hide = b;
209   config->setValue(toolbarstr + "autoHide", do_auto_hide);
210 }
211
212
213 void Toolbar::saveWidthPercent(unsigned int w) {
214   width_percent = w;
215   config->setValue(toolbarstr + "widthPercent", width_percent);
216 }
217
218
219 void Toolbar::savePlacement(int p) {
220   placement = p;
221   const char *pname;
222   switch (placement) {
223   case TopLeft: pname = "TopLeft"; break;
224   case BottomLeft: pname = "BottomLeft"; break;
225   case TopCenter: pname = "TopCenter"; break;
226   case TopRight: pname = "TopRight"; break;
227   case BottomRight: pname = "BottomRight"; break;
228   case BottomCenter: default: pname = "BottomCenter"; break;
229   }
230   config->setValue(toolbarstr + "placement", pname);
231 }
232
233
234 void Toolbar::save_rc(void) {
235   saveOnTop(on_top);
236   saveAutoHide(do_auto_hide);
237   saveWidthPercent(width_percent);
238   savePlacement(placement);
239 }
240
241
242 void Toolbar::load_rc(void) {
243   string s;
244   
245   if (! config->getValue(toolbarstr + "onTop", on_top))
246     on_top = false;
247
248   if (! config->getValue(toolbarstr + "autoHide", do_auto_hide))
249     do_auto_hide = false;
250   hidden = do_auto_hide;
251
252   if (! config->getValue(toolbarstr + "widthPercent", width_percent) ||
253       width_percent == 0 || width_percent > 100)
254     width_percent = 66;
255
256   if (config->getValue(toolbarstr + "placement", s)) {
257     if (s == "TopLeft")
258       placement = TopLeft;
259     else if (s == "BottomLeft")
260       placement = BottomLeft;
261     else if (s == "TopCenter")
262       placement = TopCenter;
263     else if (s == "TopRight")
264       placement = TopRight;
265     else if (s == "BottomRight")
266       placement = BottomRight;
267     else //if (s == "BottomCenter")
268       placement = BottomCenter;
269   } else
270     placement = BottomCenter;
271 }
272
273
274 void Toolbar::reconfigure(void) {
275   unsigned int height = 0,
276     width = (screen->getWidth() * width_percent) / 100;
277
278   if (i18n.multibyte())
279     height = screen->getToolbarStyle()->fontset_extents->max_ink_extent.height;
280   else
281     height = screen->getToolbarStyle()->font->ascent +
282       screen->getToolbarStyle()->font->descent;
283
284   frame.bevel_w = screen->getBevelWidth();
285   frame.button_w = height;
286   height += 2;
287   frame.label_h = height;
288   height += (frame.bevel_w * 2);
289
290   frame.rect.setSize(width, height);
291
292   int x, y;
293   switch (placement) {
294   case TopLeft:
295   case TopRight:
296   case TopCenter:
297     if (placement == TopLeft)
298       x = 0;
299     else if (placement == TopRight)
300       x = screen->getWidth() - frame.rect.width()
301         - (screen->getBorderWidth() * 2);
302     else
303       x = (screen->getWidth() - frame.rect.width()) / 2;
304
305     y = 0;
306
307     frame.x_hidden = x;
308     frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
309                      - frame.rect.height();
310     break;
311
312   case BottomLeft:
313   case BottomRight:
314   case BottomCenter:
315   default:
316     if (placement == BottomLeft)
317       x = 0;
318     else if (placement == BottomRight)
319       x = screen->getWidth() - frame.rect.width()
320         - (screen->getBorderWidth() * 2);
321     else
322       x = (screen->getWidth() - frame.rect.width()) / 2;
323
324     y = screen->getHeight() - frame.rect.height()
325       - (screen->getBorderWidth() * 2);
326
327     frame.x_hidden = x;
328     frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
329                      - screen->getBorderWidth();
330     break;
331   }
332
333   frame.rect.setPos(x, y);
334
335   updateStrut();
336
337 #ifdef    HAVE_STRFTIME
338   time_t ttmp = time(NULL);
339
340   frame.clock_w = 0;
341   if (ttmp != -1) {
342     struct tm *tt = localtime(&ttmp);
343     if (tt) {
344       char t[1024];
345       int len = strftime(t, 1024, screen->getStrftimeFormat(), tt);
346       if (len == 0) { // invalid time format found
347         screen->saveStrftimeFormat("%I:%M %p"); // so use the default
348         len = strftime(t, 1024, screen->getStrftimeFormat(), tt);
349       }
350       // find the length of the rendered string and add room for two extra
351       // characters to it.  This allows for variable width output of the fonts
352       if (i18n.multibyte()) {
353         XRectangle ink, logical;
354         XmbTextExtents(screen->getToolbarStyle()->fontset, t, len,
355                        &ink, &logical);
356         XFontSetExtents* extents = screen->getToolbarStyle()->fontset_extents;
357         frame.clock_w = logical.width +
358           (extents->max_logical_extent.width * 2);
359       } else {
360         XFontStruct* font = screen->getToolbarStyle()->font;
361         frame.clock_w = XTextWidth(font, t, len) +
362           ((font->max_bounds.rbearing - font->min_bounds.lbearing) * 2);
363       }
364     }
365   }
366 #else // !HAVE_STRFTIME
367   frame.clock_w =
368     XTextWidth(screen->getToolbarStyle()->font,
369                i18n(ToolbarSet, ToolbarNoStrftimeLength, "00:00000"),
370                strlen(i18n(ToolbarSet, ToolbarNoStrftimeLength,
371                            "00:00000")));
372 #endif // HAVE_STRFTIME
373
374   frame.workspace_label_w = 0;
375
376   for (unsigned int i = 0; i < screen->getWorkspaceCount(); i++) {
377     const string& workspace_name = screen->getWorkspace(i)->getName();
378     if (i18n.multibyte()) {
379       XRectangle ink, logical;
380       XmbTextExtents(screen->getToolbarStyle()->fontset,
381                      workspace_name.c_str(), workspace_name.length(),
382                      &ink, &logical);
383       width = logical.width;
384     } else {
385       width = XTextWidth(screen->getToolbarStyle()->font,
386                      workspace_name.c_str(), workspace_name.length());
387     }
388
389     if (width > frame.workspace_label_w) frame.workspace_label_w = width;
390   }
391
392   frame.workspace_label_w = frame.clock_w =
393     std::max(frame.workspace_label_w, frame.clock_w) + (frame.bevel_w * 4);
394
395   // XXX: where'd the +6 come from?
396   frame.window_label_w =
397     (frame.rect.width() - (frame.clock_w + (frame.button_w * 4) +
398                            frame.workspace_label_w + (frame.bevel_w * 8) + 6));
399
400   if (hidden) {
401     XMoveResizeWindow(display, frame.window, frame.x_hidden, frame.y_hidden,
402                       frame.rect.width(), frame.rect.height());
403   } else {
404     XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
405                       frame.rect.width(), frame.rect.height());
406   }
407
408   XMoveResizeWindow(display, frame.workspace_label, frame.bevel_w,
409                     frame.bevel_w, frame.workspace_label_w,
410                     frame.label_h);
411   XMoveResizeWindow(display, frame.psbutton,
412                     ((frame.bevel_w * 2) + frame.workspace_label_w + 1),
413                     frame.bevel_w + 1, frame.button_w, frame.button_w);
414   XMoveResizeWindow(display, frame.nsbutton,
415                     ((frame.bevel_w * 3) + frame.workspace_label_w +
416                      frame.button_w + 2), frame.bevel_w + 1, frame.button_w,
417                     frame.button_w);
418   XMoveResizeWindow(display, frame.window_label,
419                     ((frame.bevel_w * 4) + (frame.button_w * 2) +
420                      frame.workspace_label_w + 3), frame.bevel_w,
421                     frame.window_label_w, frame.label_h);
422   XMoveResizeWindow(display, frame.pwbutton,
423                     ((frame.bevel_w * 5) + (frame.button_w * 2) +
424                      frame.workspace_label_w + frame.window_label_w + 4),
425                     frame.bevel_w + 1, frame.button_w, frame.button_w);
426   XMoveResizeWindow(display, frame.nwbutton,
427                     ((frame.bevel_w * 6) + (frame.button_w * 3) +
428                      frame.workspace_label_w + frame.window_label_w + 5),
429                     frame.bevel_w + 1, frame.button_w, frame.button_w);
430   XMoveResizeWindow(display, frame.clock,
431                     frame.rect.width() - frame.clock_w - (frame.bevel_w * 2),
432                     frame.bevel_w, frame.clock_w, frame.label_h);
433
434   ToolbarStyle *style = screen->getToolbarStyle();
435   frame.base = style->toolbar.render(frame.rect.width(), frame.rect.height(),
436                                      frame.base);
437   if (! frame.base)
438     XSetWindowBackground(display, frame.window,
439                          style->toolbar.color().pixel());
440   else
441     XSetWindowBackgroundPixmap(display, frame.window, frame.base);
442
443   frame.label = style->window.render(frame.window_label_w, frame.label_h,
444                                      frame.label);
445   if (! frame.label)
446     XSetWindowBackground(display, frame.window_label,
447                          style->window.color().pixel());
448   else
449     XSetWindowBackgroundPixmap(display, frame.window_label, frame.label);
450
451   frame.wlabel = style->label.render(frame.workspace_label_w, frame.label_h,
452                                 frame.wlabel);
453   if (! frame.wlabel)
454     XSetWindowBackground(display, frame.workspace_label,
455                          style->label.color().pixel());
456   else
457     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
458
459   frame.clk = style->clock.render(frame.clock_w, frame.label_h, frame.clk);
460   if (! frame.clk)
461     XSetWindowBackground(display, frame.clock, style->clock.color().pixel());
462   else
463     XSetWindowBackgroundPixmap(display, frame.clock, frame.clk);
464
465   frame.button = style->button.render(frame.button_w, frame.button_w,
466                                 frame.button);
467   if (! frame.button) {
468     frame.button_pixel = style->button.color().pixel();
469     XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
470     XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
471     XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
472     XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
473   } else {
474     XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
475     XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
476     XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
477     XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
478   }
479
480   frame.pbutton = style->pressed.render(frame.button_w, frame.button_w,
481                                         frame.pbutton);
482   if (! frame.pbutton)
483     frame.pbutton_pixel = style->pressed.color().pixel();
484
485   XSetWindowBorder(display, frame.window,
486                    screen->getBorderColor()->pixel());
487   XSetWindowBorderWidth(display, frame.window, screen->getBorderWidth());
488
489   XClearWindow(display, frame.window);
490   XClearWindow(display, frame.workspace_label);
491   XClearWindow(display, frame.window_label);
492   XClearWindow(display, frame.clock);
493   XClearWindow(display, frame.psbutton);
494   XClearWindow(display, frame.nsbutton);
495   XClearWindow(display, frame.pwbutton);
496   XClearWindow(display, frame.nwbutton);
497
498   redrawWindowLabel();
499   redrawWorkspaceLabel();
500   redrawPrevWorkspaceButton();
501   redrawNextWorkspaceButton();
502   redrawPrevWindowButton();
503   redrawNextWindowButton();
504   checkClock(True);
505
506   toolbarmenu->reconfigure();
507 }
508
509
510 void Toolbar::updateStrut(void) {
511   // left and right are always 0
512   strut.top = strut.bottom = 0;
513
514   switch(placement) {
515   case TopLeft:
516   case TopCenter:
517   case TopRight:
518     strut.top = getExposedHeight() + (screen->getBorderWidth() * 2);
519     break;
520   default:
521     strut.bottom = getExposedHeight() + (screen->getBorderWidth() * 2);
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       dlen = style->doJustify(t, pos, frame.clock_w,
585                               frame.bevel_w * 4, i18n.multibyte());
586     BPen pen(style->c_text, style->font);
587     if (i18n.multibyte())
588       XmbDrawString(display, frame.clock, style->fontset, pen.gc(),
589                     pos, (1 - style->fontset_extents->max_ink_extent.y),
590                     t, dlen);
591     else
592       XDrawString(display, frame.clock, pen.gc(), pos,
593                   (style->font->ascent + 1), t, dlen);
594   }
595 }
596
597
598 void Toolbar::redrawWindowLabel(bool redraw) {
599   BlackboxWindow *foc = screen->getBlackbox()->getFocusedWindow();
600   if (! foc) {
601     XClearWindow(display, frame.window_label);
602     return;
603   }
604
605   if (redraw)
606     XClearWindow(display, frame.window_label);
607
608   if (foc->getScreen() != screen) return;
609
610   const char *title = foc->getTitle();
611   ToolbarStyle *style = screen->getToolbarStyle();
612
613   int pos = frame.bevel_w * 2, // modified by doJustify()
614     dlen = style->doJustify(title, pos, frame.window_label_w,
615                             frame.bevel_w * 4, i18n.multibyte());
616   BPen pen(style->w_text, style->font);
617   if (i18n.multibyte())
618     XmbDrawString(display, frame.window_label, style->fontset, pen.gc(), pos,
619                   (1 - style->fontset_extents->max_ink_extent.y),
620                   title, dlen);
621   else
622     XDrawString(display, frame.window_label, pen.gc(), pos,
623                 (style->font->ascent + 1), title, dlen);
624 }
625
626
627 void Toolbar::redrawWorkspaceLabel(bool redraw) {
628   const string& name = screen->getCurrentWorkspace()->getName();
629
630   if (redraw)
631     XClearWindow(display, frame.workspace_label);
632
633   ToolbarStyle *style = screen->getToolbarStyle();
634
635   int pos = frame.bevel_w * 2,
636     dlen = style->doJustify(name.c_str(), pos, frame.workspace_label_w,
637                             frame.bevel_w * 4, i18n.multibyte());
638   BPen pen(style->l_text, style->font);
639   if (i18n.multibyte())
640     XmbDrawString(display, frame.workspace_label, style->fontset, pen.gc(),
641                   pos, (1 - style->fontset_extents->max_ink_extent.y),
642                   name.c_str(), dlen);
643   else
644     XDrawString(display, frame.workspace_label, pen.gc(), pos,
645                 (style->font->ascent + 1),
646                 name.c_str(), dlen);
647 }
648
649
650 void Toolbar::redrawPrevWorkspaceButton(bool pressed, bool redraw) {
651   if (redraw) {
652     if (pressed) {
653       if (frame.pbutton)
654         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.pbutton);
655       else
656         XSetWindowBackground(display, frame.psbutton, frame.pbutton_pixel);
657     } else {
658       if (frame.button)
659         XSetWindowBackgroundPixmap(display, frame.psbutton, frame.button);
660       else
661         XSetWindowBackground(display, frame.psbutton, frame.button_pixel);
662     }
663     XClearWindow(display, frame.psbutton);
664   }
665
666   int hh = frame.button_w / 2, hw = frame.button_w / 2;
667
668   XPoint pts[3];
669   pts[0].x = hw - 2; pts[0].y = hh;
670   pts[1].x = 4; pts[1].y = 2;
671   pts[2].x = 0; pts[2].y = -4;
672
673   ToolbarStyle *style = screen->getToolbarStyle();
674   BPen pen(style->b_pic, style->font);
675   XFillPolygon(display, frame.psbutton, pen.gc(),
676                pts, 3, Convex, CoordModePrevious);
677 }
678
679
680 void Toolbar::redrawNextWorkspaceButton(bool pressed, bool redraw) {
681   if (redraw) {
682     if (pressed) {
683       if (frame.pbutton)
684         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.pbutton);
685       else
686         XSetWindowBackground(display, frame.nsbutton, frame.pbutton_pixel);
687     } else {
688       if (frame.button)
689         XSetWindowBackgroundPixmap(display, frame.nsbutton, frame.button);
690       else
691         XSetWindowBackground(display, frame.nsbutton, frame.button_pixel);
692     }
693     XClearWindow(display, frame.nsbutton);
694   }
695
696   int hh = frame.button_w / 2, hw = frame.button_w / 2;
697
698   XPoint pts[3];
699   pts[0].x = hw - 2; pts[0].y = hh - 2;
700   pts[1].x = 4; pts[1].y =  2;
701   pts[2].x = -4; pts[2].y = 2;
702
703   ToolbarStyle *style = screen->getToolbarStyle();
704   BPen pen(style->b_pic, style->font);
705   XFillPolygon(display, frame.nsbutton, pen.gc(),
706                pts, 3, Convex, CoordModePrevious);
707 }
708
709
710 void Toolbar::redrawPrevWindowButton(bool pressed, bool redraw) {
711   if (redraw) {
712     if (pressed) {
713       if (frame.pbutton)
714         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.pbutton);
715       else
716         XSetWindowBackground(display, frame.pwbutton, frame.pbutton_pixel);
717     } else {
718       if (frame.button)
719         XSetWindowBackgroundPixmap(display, frame.pwbutton, frame.button);
720       else
721         XSetWindowBackground(display, frame.pwbutton, frame.button_pixel);
722     }
723     XClearWindow(display, frame.pwbutton);
724   }
725
726   int hh = frame.button_w / 2, hw = frame.button_w / 2;
727
728   XPoint pts[3];
729   pts[0].x = hw - 2; pts[0].y = hh;
730   pts[1].x = 4; pts[1].y = 2;
731   pts[2].x = 0; pts[2].y = -4;
732
733   ToolbarStyle *style = screen->getToolbarStyle();
734   BPen pen(style->b_pic, style->font);
735   XFillPolygon(display, frame.pwbutton, pen.gc(),
736                pts, 3, Convex, CoordModePrevious);
737 }
738
739
740 void Toolbar::redrawNextWindowButton(bool pressed, bool redraw) {
741   if (redraw) {
742     if (pressed) {
743       if (frame.pbutton)
744         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.pbutton);
745       else
746         XSetWindowBackground(display, frame.nwbutton, frame.pbutton_pixel);
747     } else {
748       if (frame.button)
749         XSetWindowBackgroundPixmap(display, frame.nwbutton, frame.button);
750       else
751         XSetWindowBackground(display, frame.nwbutton, frame.button_pixel);
752     }
753     XClearWindow(display, frame.nwbutton);
754   }
755
756   int hh = frame.button_w / 2, hw = frame.button_w / 2;
757
758   XPoint pts[3];
759   pts[0].x = hw - 2; pts[0].y = hh - 2;
760   pts[1].x = 4; pts[1].y =  2;
761   pts[2].x = -4; pts[2].y = 2;
762
763   ToolbarStyle *style = screen->getToolbarStyle();
764   BPen pen(style->b_pic, style->font);
765   XFillPolygon(display, frame.nwbutton, pen.gc(), pts, 3, Convex,
766                CoordModePrevious);
767 }
768
769
770 void Toolbar::edit(void) {
771   Window window;
772   int foo;
773
774   editing = True;
775   XGetInputFocus(display, &window, &foo);
776   if (window == frame.workspace_label)
777     return;
778
779   XSetInputFocus(display, frame.workspace_label,
780                  RevertToPointerRoot, CurrentTime);
781   XClearWindow(display, frame.workspace_label);
782
783   blackbox->setNoFocus(True);
784   if (blackbox->getFocusedWindow())
785     blackbox->getFocusedWindow()->setFocusFlag(False);
786
787   ToolbarStyle *style = screen->getToolbarStyle();
788   BPen pen(style->l_text, style->font);
789   XDrawRectangle(display, frame.workspace_label, pen.gc(),
790                  frame.workspace_label_w / 2, 0, 1,
791                  frame.label_h - 1);
792   // change the background of the window to that of an active window label
793   BTexture *texture = &(screen->getWindowStyle()->l_focus);
794   frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
795                                  frame.wlabel);
796   if (! frame.wlabel)
797     XSetWindowBackground(display, frame.workspace_label,
798                          texture->color().pixel());
799   else
800     XSetWindowBackgroundPixmap(display, frame.workspace_label, frame.wlabel);
801 }
802
803
804 void Toolbar::buttonPressEvent(XButtonEvent *be) {
805   if (be->button == 1) {
806     if (be->window == frame.psbutton)
807       redrawPrevWorkspaceButton(True, True);
808     else if (be->window == frame.nsbutton)
809       redrawNextWorkspaceButton(True, True);
810     else if (be->window == frame.pwbutton)
811       redrawPrevWindowButton(True, True);
812     else if (be->window == frame.nwbutton)
813       redrawNextWindowButton(True, True);
814 #ifndef   HAVE_STRFTIME
815     else if (be->window == frame.clock) {
816       XClearWindow(display, frame.clock);
817       checkClock(True, True);
818     }
819 #endif // HAVE_STRFTIME
820     else if (! on_top) {
821       Window w[1] = { frame.window };
822       screen->raiseWindows(w, 1);
823     }
824   } else if (be->button == 2 && (! on_top)) {
825     XLowerWindow(display, frame.window);
826   } else if (be->button == 3) {
827     if (toolbarmenu->isVisible()) {
828       toolbarmenu->hide();
829     } else {
830       int x, y;
831
832       x = be->x_root - (toolbarmenu->getWidth() / 2);
833       y = be->y_root - (toolbarmenu->getHeight() / 2);
834
835       if (x < 0)
836         x = 0;
837       else if (x + toolbarmenu->getWidth() > screen->getWidth())
838         x = screen->getWidth() - toolbarmenu->getWidth();
839
840       if (y < 0)
841         y = 0;
842       else if (y + toolbarmenu->getHeight() > screen->getHeight())
843         y = screen->getHeight() - toolbarmenu->getHeight();
844
845       toolbarmenu->move(x, y);
846       toolbarmenu->show();
847     }
848   }
849 }
850
851
852
853 void Toolbar::buttonReleaseEvent(XButtonEvent *re) {
854   if (re->button == 1) {
855     if (re->window == frame.psbutton) {
856       redrawPrevWorkspaceButton(False, True);
857
858       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
859           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
860        if (screen->getCurrentWorkspace()->getID() > 0)
861           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
862                                     getID() - 1);
863         else
864           screen->changeWorkspaceID(screen->getWorkspaceCount() - 1);
865     } else if (re->window == frame.nsbutton) {
866       redrawNextWorkspaceButton(False, True);
867
868       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
869           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
870         if (screen->getCurrentWorkspace()->getID() <
871             (screen->getWorkspaceCount() - 1))
872           screen->changeWorkspaceID(screen->getCurrentWorkspace()->
873                                     getID() + 1);
874         else
875           screen->changeWorkspaceID(0);
876     } else if (re->window == frame.pwbutton) {
877       redrawPrevWindowButton(False, True);
878
879       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
880           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
881         screen->prevFocus();
882     } else if (re->window == frame.nwbutton) {
883       redrawNextWindowButton(False, True);
884
885       if (re->x >= 0 && re->x < static_cast<signed>(frame.button_w) &&
886           re->y >= 0 && re->y < static_cast<signed>(frame.button_w))
887         screen->nextFocus();
888     } else if (re->window == frame.window_label)
889       screen->raiseFocus();
890 #ifndef   HAVE_STRFTIME
891     else if (re->window == frame.clock) {
892       XClearWindow(display, frame.clock);
893       checkClock(True);
894     }
895 #endif // HAVE_STRFTIME
896   }
897 }
898
899
900 void Toolbar::enterNotifyEvent(XCrossingEvent *) {
901   if (! do_auto_hide)
902     return;
903
904   if (hidden) {
905     if (! hide_timer->isTiming()) hide_timer->start();
906   } else {
907     if (hide_timer->isTiming()) hide_timer->stop();
908   }
909 }
910
911 void Toolbar::leaveNotifyEvent(XCrossingEvent *) {
912   if (! do_auto_hide)
913     return;
914
915   if (hidden) {
916     if (hide_timer->isTiming()) hide_timer->stop();
917   } else if (! toolbarmenu->isVisible()) {
918     if (! hide_timer->isTiming()) hide_timer->start();
919   }
920 }
921
922
923 void Toolbar::exposeEvent(XExposeEvent *ee) {
924   if (ee->window == frame.clock) checkClock(True);
925   else if (ee->window == frame.workspace_label && (! editing))
926     redrawWorkspaceLabel();
927   else if (ee->window == frame.window_label) redrawWindowLabel();
928   else if (ee->window == frame.psbutton) redrawPrevWorkspaceButton();
929   else if (ee->window == frame.nsbutton) redrawNextWorkspaceButton();
930   else if (ee->window == frame.pwbutton) redrawPrevWindowButton();
931   else if (ee->window == frame.nwbutton) redrawNextWindowButton();
932 }
933
934
935 void Toolbar::keyPressEvent(XKeyEvent *ke) {
936   if (ke->window == frame.workspace_label && editing) {
937     if (new_workspace_name.empty()) {
938       new_name_pos = 0;
939     }
940
941     KeySym ks;
942     char keychar[1];
943     XLookupString(ke, keychar, 1, &ks, 0);
944
945     // either we are told to end with a return or we hit 127 chars
946     if (ks == XK_Return || new_name_pos == 127) {
947       editing = False;
948
949       blackbox->setNoFocus(False);
950       if (blackbox->getFocusedWindow()) {
951         blackbox->getFocusedWindow()->setInputFocus();
952       } else {
953         blackbox->setFocusedWindow(0);
954       }
955
956       Workspace *wkspc = screen->getCurrentWorkspace();
957       wkspc->setName(new_workspace_name);
958       wkspc->getMenu()->hide();
959
960       screen->getWorkspacemenu()->changeItemLabel(wkspc->getID() + 2,
961                                                   wkspc->getName());
962       screen->getWorkspacemenu()->update();
963
964       new_workspace_name.erase();
965       new_name_pos = 0;
966
967       // reset the background to that of the workspace label (its normal
968       // setting)
969       BTexture *texture = &(screen->getToolbarStyle()->label);
970       frame.wlabel = texture->render(frame.workspace_label_w, frame.label_h,
971                                      frame.wlabel);
972       if (! frame.wlabel)
973         XSetWindowBackground(display, frame.workspace_label,
974                              texture->color().pixel());
975       else
976         XSetWindowBackgroundPixmap(display, frame.workspace_label,
977                                    frame.wlabel);
978       reconfigure();
979     } else if (! (ks == XK_Shift_L || ks == XK_Shift_R ||
980                   ks == XK_Control_L || ks == XK_Control_R ||
981                   ks == XK_Caps_Lock || ks == XK_Shift_Lock ||
982                   ks == XK_Meta_L || ks == XK_Meta_R ||
983                   ks == XK_Alt_L || ks == XK_Alt_R ||
984                   ks == XK_Super_L || ks == XK_Super_R ||
985                   ks == XK_Hyper_L || ks == XK_Hyper_R)) {
986       if (ks == XK_BackSpace) {
987         if (new_name_pos > 0) {
988           --new_name_pos;
989           new_workspace_name.erase(new_name_pos);
990         } else {
991           new_workspace_name.resize(0);
992         }
993       } else {
994         new_workspace_name += (*keychar);
995         ++new_name_pos;
996       }
997
998       XClearWindow(display, frame.workspace_label);
999       unsigned int l = new_workspace_name.length(), tw, x;
1000
1001       if (i18n.multibyte()) {
1002         XRectangle ink, logical;
1003         XmbTextExtents(screen->getToolbarStyle()->fontset,
1004                        new_workspace_name.c_str(), l, &ink, &logical);
1005         tw = logical.width;
1006       } else {
1007         tw = XTextWidth(screen->getToolbarStyle()->font,
1008                         new_workspace_name.c_str(), l);
1009       }
1010       x = (frame.workspace_label_w - tw) / 2;
1011
1012       if (x < frame.bevel_w) x = frame.bevel_w;
1013
1014       ToolbarStyle *style = screen->getToolbarStyle();
1015       BPen pen(style->l_text, style->font);
1016       if (i18n.multibyte())
1017         XmbDrawString(display, frame.workspace_label, style->fontset,
1018                       pen.gc(), x,
1019                       (1 - style->fontset_extents->max_ink_extent.y),
1020                       new_workspace_name.c_str(), l);
1021       else
1022         XDrawString(display, frame.workspace_label, pen.gc(), x,
1023                     (style->font->ascent + 1),
1024                     new_workspace_name.c_str(), l);
1025       XDrawRectangle(display, frame.workspace_label, pen.gc(), x + tw, 0, 1,
1026                      frame.label_h - 1);
1027     }
1028   }
1029 }
1030
1031
1032 void Toolbar::timeout(void) {
1033   checkClock(True);
1034
1035   clock_timer->setTimeout(aMinuteFromNow());
1036 }
1037
1038
1039 void Toolbar::HideHandler::timeout(void) {
1040   toolbar->hidden = ! toolbar->hidden;
1041   if (toolbar->hidden)
1042     XMoveWindow(toolbar->display, toolbar->frame.window,
1043                 toolbar->frame.x_hidden, toolbar->frame.y_hidden);
1044   else
1045     XMoveWindow(toolbar->display, toolbar->frame.window,
1046                 toolbar->frame.rect.x(), toolbar->frame.rect.y());
1047 }
1048
1049
1050 void Toolbar::toggleAutoHide(void) {
1051   saveAutoHide(! doAutoHide());
1052
1053   updateStrut();
1054   screen->getSlit()->reposition();
1055
1056   if (do_auto_hide == False && hidden) {
1057     // force the slit to be visible
1058     if (hide_timer->isTiming()) hide_timer->stop();
1059     hide_handler.timeout();
1060   }
1061 }
1062
1063
1064 Toolbarmenu::Toolbarmenu(Toolbar *tb) : Basemenu(tb->screen) {
1065   toolbar = tb;
1066
1067   setLabel(i18n(ToolbarSet, ToolbarToolbarTitle, "Toolbar"));
1068   setInternalMenu();
1069
1070   placementmenu = new Placementmenu(this);
1071
1072   insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
1073          placementmenu);
1074   insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
1075   insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
1076   insert(i18n(ToolbarSet, ToolbarEditWkspcName,
1077               "Edit current workspace name"), 3);
1078
1079   update();
1080   setValues();
1081 }
1082
1083
1084 void Toolbarmenu::setValues() {
1085   setItemSelected(1, toolbar->isOnTop());
1086   setItemSelected(2, toolbar->doAutoHide());
1087 }
1088
1089
1090 Toolbarmenu::~Toolbarmenu(void) {
1091   delete placementmenu;
1092 }
1093
1094
1095 void Toolbarmenu::itemSelected(int button, unsigned int index) {
1096   if (button != 1)
1097     return;
1098
1099   BasemenuItem *item = find(index);
1100   if (! item) return;
1101
1102   switch (item->function()) {
1103   case 1: { // always on top
1104     toolbar->saveOnTop(! toolbar->isOnTop());
1105     setItemSelected(1, toolbar->isOnTop());
1106
1107     if (toolbar->isOnTop()) getScreen()->raiseWindows((Window *) 0, 0);
1108     break;
1109   }
1110
1111   case 2: { // auto hide
1112     toolbar->toggleAutoHide();
1113     setItemSelected(2, toolbar->doAutoHide());
1114
1115     break;
1116   }
1117
1118   case 3: { // edit current workspace name
1119     toolbar->edit();
1120     hide();
1121
1122     break;
1123   }
1124   } // switch
1125 }
1126
1127
1128 void Toolbarmenu::internal_hide(void) {
1129   Basemenu::internal_hide();
1130   if (toolbar->doAutoHide() && ! toolbar->isEditing())
1131     toolbar->hide_handler.timeout();
1132 }
1133
1134
1135 void Toolbarmenu::reconfigure(void) {
1136   setValues();
1137   placementmenu->reconfigure();
1138
1139   Basemenu::reconfigure();
1140 }
1141
1142
1143 Toolbarmenu::Placementmenu::Placementmenu(Toolbarmenu *tm)
1144   : Basemenu(tm->toolbar->screen), toolbar(tm->toolbar) {
1145   setLabel(i18n(ToolbarSet, ToolbarToolbarPlacement, "Toolbar Placement"));
1146   setInternalMenu();
1147   setMinimumSublevels(3);
1148
1149   insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
1150          Toolbar::TopLeft);
1151   insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
1152          Toolbar::BottomLeft);
1153   insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
1154          Toolbar::TopCenter);
1155   insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
1156          Toolbar::BottomCenter);
1157   insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
1158          Toolbar::TopRight);
1159   insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
1160          Toolbar::BottomRight);
1161   update();
1162   setValues();
1163 }
1164
1165
1166 void Toolbarmenu::Placementmenu::setValues(void) {
1167   int place = 0;
1168   switch (toolbar->getPlacement()) {
1169   case Toolbar::BottomRight:
1170     place++;
1171   case Toolbar::TopRight:
1172     place++;
1173   case Toolbar::BottomCenter:
1174     place++;
1175   case Toolbar::TopCenter:
1176     place++;
1177   case Toolbar::BottomLeft:
1178     place++;
1179   case Toolbar::TopLeft:
1180     break;
1181   }
1182   setItemSelected(0, 0 == place);
1183   setItemSelected(1, 1 == place);
1184   setItemSelected(2, 2 == place);
1185   setItemSelected(3, 3 == place);
1186   setItemSelected(4, 4 == place);
1187   setItemSelected(5, 5 == place);
1188 }
1189
1190
1191 void Toolbarmenu::Placementmenu::reconfigure(void) {
1192   setValues();
1193   Basemenu::reconfigure();
1194 }
1195
1196
1197 void Toolbarmenu::Placementmenu::itemSelected(int button, unsigned int index) {
1198   if (button != 1)
1199     return;
1200
1201   BasemenuItem *item = find(index);
1202   if (! item) return;
1203
1204   toolbar->savePlacement(item->function());
1205   hide();
1206   toolbar->reconfigure();
1207
1208   // reposition the slit as well to make sure it doesn't intersect the
1209   // toolbar
1210   getScreen()->getSlit()->reposition();
1211 }
1212
1213
1214 int ToolbarStyle::doJustify(const char *text, int &start_pos,
1215                             unsigned int max_length, unsigned int modifier,
1216                             bool multibyte) const {
1217   size_t text_len = strlen(text);
1218   unsigned int length;
1219
1220   do {
1221     if (multibyte) {
1222       XRectangle ink, logical;
1223       XmbTextExtents(fontset, text, text_len, &ink, &logical);
1224       length = logical.width;
1225     } else {
1226       length = XTextWidth(font, text, text_len);
1227     }
1228     length += modifier;
1229   } while (length > max_length && text_len-- > 0);
1230
1231   switch (justify) {
1232   case RightJustify:
1233     start_pos += max_length - length;
1234     break;
1235
1236   case CenterJustify:
1237     start_pos += (max_length - length) / 2;
1238     break;
1239
1240   case LeftJustify:
1241   default:
1242     break;
1243   }
1244
1245   return text_len;
1246 }