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