// Toolbar.cc for Fluxbox // Copyright (c) 2002 - 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) // // Toolbar.cc for Blackbox - an X11 Window manager // Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // $Id: Toolbar.cc,v 1.93 2003/06/23 13:17:57 fluxgen Exp $ #include "Toolbar.hh" #include "IconBar.hh" #include "I18n.hh" #include "fluxbox.hh" #include "Screen.hh" #include "Window.hh" #include "Workspace.hh" #include "ImageControl.hh" #include "ToolbarTheme.hh" #include "EventManager.hh" #include "Text.hh" #include "ArrowButton.hh" #include "SimpleCommand.hh" #include "IntResMenuItem.hh" #include "MacroCommand.hh" #include "RootTheme.hh" #include "BoolMenuItem.hh" #include "FbWinFrameTheme.hh" #include "Xinerama.hh" #include "Strut.hh" // use GNU extensions #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // _GNU_SOURCE #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include <X11/Xutil.h> #include <X11/keysym.h> #include <cstring> #include <cstdio> #ifdef TIME_WITH_SYS_TIME #include <sys/time.h> #include <time.h> #else // !TIME_WITH_SYS_TIME #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #else // !HAVE_SYS_TIME_H #include <time.h> #endif // HAVE_SYS_TIME_H #endif // TIME_WITH_SYS_TIME #include <iostream> using namespace std; template<> void FbTk::Resource<Toolbar::Placement>:: setFromString(const char *strval) { if (strcasecmp(strval, "TopLeft")==0) m_value = Toolbar::TOPLEFT; else if (strcasecmp(strval, "BottomLeft")==0) m_value = Toolbar::BOTTOMLEFT; else if (strcasecmp(strval, "TopCenter")==0) m_value = Toolbar::TOPCENTER; else if (strcasecmp(strval, "BottomCenter")==0) m_value = Toolbar::BOTTOMCENTER; else if (strcasecmp(strval, "TopRight")==0) m_value = Toolbar::TOPRIGHT; else if (strcasecmp(strval, "BottomRight")==0) m_value = Toolbar::BOTTOMRIGHT; else if (strcasecmp(strval, "LeftTop") == 0) m_value = Toolbar::LEFTTOP; else if (strcasecmp(strval, "LeftCenter") == 0) m_value = Toolbar::LEFTCENTER; else if (strcasecmp(strval, "LeftBottom") == 0) m_value = Toolbar::LEFTBOTTOM; else if (strcasecmp(strval, "RightTop") == 0) m_value = Toolbar::RIGHTTOP; else if (strcasecmp(strval, "RightCenter") == 0) m_value = Toolbar::RIGHTCENTER; else if (strcasecmp(strval, "RightBottom") == 0) m_value = Toolbar::RIGHTBOTTOM; else setDefaultValue(); } string FbTk::Resource<Toolbar::Placement>:: getString() { switch (m_value) { case Toolbar::TOPLEFT: return string("TopLeft"); break; case Toolbar::BOTTOMLEFT: return string("BottomLeft"); break; case Toolbar::TOPCENTER: return string("TopCenter"); break; case Toolbar::BOTTOMCENTER: return string("BottomCenter"); break; case Toolbar::TOPRIGHT: return string("TopRight"); break; case Toolbar::BOTTOMRIGHT: return string("BottomRight"); break; case Toolbar::LEFTTOP: return string("LeftTop"); break; case Toolbar::LEFTCENTER: return string("LeftCenter"); break; case Toolbar::LEFTBOTTOM: return string("LeftBottom"); break; case Toolbar::RIGHTTOP: return string("RightTop"); break; case Toolbar::RIGHTCENTER: return string("RightCenter"); break; case Toolbar::RIGHTBOTTOM: return string("RightBottom"); break; } //default string return string("BottomCenter"); } namespace { class SetToolbarPlacementCmd: public FbTk::Command { public: explicit SetToolbarPlacementCmd(Toolbar &tbar, Toolbar::Placement place):m_tbar(tbar), m_place(place) { } void execute() { m_tbar.setPlacement(m_place); m_tbar.reconfigure(); Fluxbox::instance()->save_rc(); } private: Toolbar &m_tbar; Toolbar::Placement m_place; }; }; // end anonymous // toolbar frame Toolbar::Frame::Frame(FbTk::EventHandler &evh, int screen_num): window(screen_num, // screen (parent) 0, 0, // pos 10, 10, // size // event mask ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask, true), // override redirect workspace_label(window, // parent 0, 0, //pos 1, 1, // size // event mask ButtonPressMask | ButtonReleaseMask | ExposureMask | KeyPressMask | EnterWindowMask | LeaveWindowMask), window_label(window, // parent 0, 0, // pos 1, 1, // size // event mask ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask), clock(window, //parent 0, 0, // pos 1, 1, // size // event mask ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask), psbutton(ArrowButton::LEFT, // arrow type window, // parent 0, 0, // pos 1, 1), // size nsbutton(ArrowButton::RIGHT, // arrow type window, // parent 0, 0, // pos 1, 1), // size pwbutton(ArrowButton::LEFT, // arrow type window, // parent 0, 0, // pos 1, 1), // size nwbutton(ArrowButton::RIGHT, // arrow type window, // parent 0, 0, // pos 1, 1), // size hour(-1), // start with invalid number to force update minute(-1) { FbTk::EventManager &evm = *FbTk::EventManager::instance(); // add windows to eventmanager evm.add(evh, window); evm.add(evh, workspace_label); evm.add(evh, window_label); evm.add(evh, clock); psbutton.setMouseMotionHandler(&evh); nsbutton.setMouseMotionHandler(&evh); pwbutton.setMouseMotionHandler(&evh); nwbutton.setMouseMotionHandler(&evh); } Toolbar::Frame::~Frame() { FbTk::EventManager &evm = *FbTk::EventManager::instance(); // remove windows from eventmanager evm.remove(window); evm.remove(workspace_label); evm.remove(window_label); evm.remove(clock); } Toolbar::Toolbar(BScreen &scrn, FbTk::XLayer &layer, FbTk::Menu &menu, size_t width): m_editing(false), m_hidden(false), frame(*this, scrn.screenNumber()), m_screen(scrn), m_clock_timer(this), // get the clock updating every minute m_hide_timer(&hide_handler), m_toolbarmenu(menu), m_placementmenu(*scrn.menuTheme(), scrn.screenNumber(), scrn.imageControl()), m_layermenu(*scrn.menuTheme(), scrn.screenNumber(), scrn.imageControl(), *scrn.layerManager().getLayer(Fluxbox::instance()->getMenuLayer()), this, true), m_theme(scrn.screenNumber()), m_themelistener(*this), m_layeritem(frame.window, layer), m_strut(0), m_rc_auto_hide(scrn.resourceManager(), false, scrn.name() + ".toolbar.autoHide", scrn.altName() + ".Toolbar.AutoHide"), m_rc_width_percent(scrn.resourceManager(), 65, scrn.name() + ".toolbar.widthPercent", scrn.altName() + ".Toolbar.WidthPercent"), m_rc_layernum(scrn.resourceManager(), Fluxbox::Layer(Fluxbox::instance()->getDesktopLayer()), scrn.name() + ".toolbar.layer", scrn.altName() + ".Toolbar.Layer"), m_rc_on_head(scrn.resourceManager(), 0, scrn.name() + ".toolbar.onhead", scrn.altName() + ".Toolbar.onHead"), m_rc_placement(scrn.resourceManager(), Toolbar::BOTTOMCENTER, scrn.name() + ".toolbar.placement", scrn.altName() + ".Toolbar.Placement") { // we need to get notified when the theme is reloaded m_theme.addListener(m_themelistener); m_layermenu.setInternalMenu(); m_placementmenu.setInternalMenu(); setupMenus(); // geometry settings frame.width = width; frame.height = frame.label_h = 10; frame.window_label_w = frame.workspace_label_w = frame.clock_w = width/3; frame.button_w = 20; frame.bevel_w = 1; timeval delay; delay.tv_sec = 1; delay.tv_usec = 0; m_clock_timer.setTimeout(delay); m_clock_timer.start(); m_theme.font().setAntialias(screen().antialias()); hide_handler.toolbar = this; m_hide_timer.setTimeout(Fluxbox::instance()->getAutoRaiseDelay()); m_hide_timer.fireOnce(true); frame.grab_x = frame.grab_y = 0; frame.base = frame.label = frame.wlabel = frame.clk = frame.button = frame.pbutton = None; m_iconbar.reset(new IconBar(screen(), frame.window_label.window(), m_theme.font())); // finaly: setup Commands for the buttons in the frame typedef FbTk::SimpleCommand<BScreen> ScreenCmd; FbTk::RefCount<FbTk::Command> nextworkspace(new ScreenCmd(screen(), &BScreen::nextWorkspace)); FbTk::RefCount<FbTk::Command> prevworkspace(new ScreenCmd(screen(), &BScreen::prevWorkspace)); FbTk::RefCount<FbTk::Command> nextwindow(new ScreenCmd(screen(), &BScreen::nextFocus)); FbTk::RefCount<FbTk::Command> prevwindow(new ScreenCmd(screen(), &BScreen::prevFocus)); frame.psbutton.setOnClick(prevworkspace); frame.nsbutton.setOnClick(nextworkspace); frame.pwbutton.setOnClick(prevwindow); frame.nwbutton.setOnClick(nextwindow); reconfigure(); // get everything together frame.window.showSubwindows(); frame.window.show(); } Toolbar::~Toolbar() { clearStrut(); FbTk::ImageControl &image_ctrl = screen().imageControl(); if (frame.base) image_ctrl.removeImage(frame.base); if (frame.label) image_ctrl.removeImage(frame.label); if (frame.wlabel) image_ctrl.removeImage(frame.wlabel); if (frame.clk) image_ctrl.removeImage(frame.clk); if (frame.button) image_ctrl.removeImage(frame.button); if (frame.pbutton) image_ctrl.removeImage(frame.pbutton); } void Toolbar::clearStrut() { if (m_strut) { screen().clearStrut(m_strut); m_strut = 0; } } void Toolbar::updateStrut() { bool had_strut = m_strut ? true : false; clearStrut(); // we should request space if we're in autohide mode or // if the user dont want to request space for toolbar. if (doAutoHide()) { if (had_strut) screen().updateAvailableWorkspaceArea(); return; } // request area on screen int top = 0, bottom = 0, left = 0, right = 0; switch (placement()) { case TOPLEFT: case TOPCENTER: case TOPRIGHT: top = height(); break; case BOTTOMLEFT: case BOTTOMCENTER: case BOTTOMRIGHT: bottom = height(); break; case RIGHTTOP: case RIGHTCENTER: case RIGHTBOTTOM: right = width(); break; case LEFTTOP: case LEFTCENTER: case LEFTBOTTOM: left = width(); break; }; m_strut = screen().requestStrut(left, right, top, bottom); screen().updateAvailableWorkspaceArea(); } bool Toolbar::isVertical() const { return (placement() == RIGHTCENTER || placement() == RIGHTTOP || placement() == RIGHTBOTTOM || placement() == LEFTCENTER || placement() == LEFTTOP || placement() == LEFTBOTTOM); } void Toolbar::addIcon(FluxboxWindow *w) { if (m_iconbar.get() != 0) FbTk::EventManager::instance()->add(*this, m_iconbar->addIcon(w)); } void Toolbar::delIcon(FluxboxWindow *w) { if (m_iconbar.get() != 0) FbTk::EventManager::instance()->remove(m_iconbar->delIcon(w)); } void Toolbar::delAllIcons() { if (m_iconbar.get() == 0) return; IconBar::WindowList *deleted = m_iconbar->delAllIcons(); IconBar::WindowList::iterator it = deleted->begin(); IconBar::WindowList::iterator it_end = deleted->end(); for (; it != it_end; ++it) { FbTk::EventManager::instance()->remove(*it); } delete deleted; } bool Toolbar::containsIcon(const FluxboxWindow &win) const { return (m_iconbar->findIcon(&win) != 0); } void Toolbar::enableIconBar() { if (m_iconbar.get() != 0) return; // already on m_iconbar.reset(new IconBar(screen(), frame.window_label.window(), m_theme.font())); } void Toolbar::disableIconBar() { if (m_iconbar.get() == 0) return; // already off delAllIcons(); m_iconbar.reset(0); // destroy iconbar } void Toolbar::raise() { m_layeritem.raise(); } void Toolbar::lower() { m_layeritem.lower(); } void Toolbar::reconfigure() { if (doAutoHide()) m_hide_timer.start(); bool vertical = isVertical(); if (m_iconbar.get()) m_iconbar->setVertical(vertical); frame.bevel_w = screen().rootTheme().bevelWidth(); // recallibrate size setPlacement(placement()); #ifdef HAVE_STRFTIME time_t ttmp = time(0); struct tm *tt = 0; if (ttmp != -1) { tt = localtime(&ttmp); if (tt) { char t[1024], *time_string = (char *) 0; int len = strftime(t, 1024, screen().getStrftimeFormat(), tt); time_string = new char[len + 1]; memset(time_string, '0', len); *(time_string + len) = '\0'; frame.clock_w = m_theme.font().textWidth(time_string, len); frame.clock_w += (frame.bevel_w * 4); delete [] time_string; } else frame.clock_w = 0; } else frame.clock_w = 0; #else // !HAVE_STRFTIME I18n *i18n = I18n::instance(); frame.clock_w = m_theme.font().textWidth( i18n-> getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeLength, "00:00000"), strlen(i18n-> getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeLength, "00:00000"))) + (frame.bevel_w * 4); #endif // HAVE_STRFTIME unsigned int i; unsigned int w = 0; frame.workspace_label_w = 0; for (i = 0; i < screen().getCount(); i++) { w = m_theme.font().textWidth(screen().getWorkspace(i)->name().c_str(), screen().getWorkspace(i)->name().size()); w += (frame.bevel_w * 4); if (w > frame.workspace_label_w) frame.workspace_label_w = w; } if (frame.workspace_label_w < frame.clock_w) frame.workspace_label_w = frame.clock_w; else if (frame.workspace_label_w > frame.clock_w) frame.clock_w = frame.workspace_label_w; // Right, let's break this one down.... // full width, minus clock, workspace label and the 4 arrow buttons. // each of the (6) aforementioned items are separated by a bevel width, // plus outside (+1), plus the window label (+1). i = frame.clock_w + (frame.button_w * 4) + frame.workspace_label_w + (frame.bevel_w * 8) + 6; // of course if your toolbar is set too small, this could go negative. // which is bad mmmkay. Since we are unsigned, we check that *first*. if (vertical) w = frame.height; else w = frame.width; if (i > w) frame.window_label_w = 0; else frame.window_label_w = w - i; if (isHidden()) frame.window.moveResize(frame.x_hidden, frame.y_hidden, frame.width, frame.height); else { frame.window.moveResize(frame.x, frame.y, frame.width, frame.height); } unsigned int next_x = frame.workspace_label_w; unsigned int next_y = frame.window.height(); unsigned int text_x=0, text_y=0; if (vertical) text_x = frame.bevel_w; else text_y = frame.bevel_w; if (vertical) { next_x = frame.window.width(); next_y = frame.workspace_label_w; } frame.workspace_label.moveResize(frame.bevel_w, frame.bevel_w, next_x, next_y); next_x = 0; next_y = 0; if (vertical) { next_y += frame.workspace_label.height() + 1 + frame.bevel_w * 2; } else { next_x += frame.workspace_label.width() + 1 + frame.bevel_w * 2; } frame.psbutton.moveResize(next_x , next_y, frame.button_w, frame.button_w); if (vertical) next_y += frame.psbutton.height() + 1; else next_x += frame.psbutton.width() + 1; frame.nsbutton.moveResize(next_x, next_y, frame.button_w, frame.button_w); size_t label_w = frame.window_label_w; size_t label_h = frame.height; if (vertical) { next_y += frame.nsbutton.height() + 1; label_w = frame.width; label_h = frame.window_label_w/* - frame.width + frame.height*/; } else next_x += frame.nsbutton.width() + 1; frame.window_label.moveResize(next_x, next_y, label_w, label_h); if (vertical) next_y += frame.window_label.height() + 1; else next_x += frame.window_label.width() + 1; frame.pwbutton.moveResize(next_x, next_y, frame.button_w, frame.button_w); if (vertical) next_y += frame.pwbutton.height() + 1; else next_x += frame.pwbutton.width() + 1; frame.nwbutton.moveResize(next_x, next_y, frame.button_w, frame.button_w); size_t clock_w = frame.width - next_x - frame.nwbutton.width() - 1; size_t clock_h = frame.height; if (vertical) { next_y += frame.nwbutton.height() + 1; clock_w = frame.width; clock_h = frame.height - next_y; } else next_x += frame.nwbutton.width() + 1; frame.clock.moveResize(next_x + text_x, next_y + text_y, clock_w, clock_h); FbTk::ImageControl &image_ctrl = screen().imageControl(); Pixmap tmp = frame.base; const FbTk::Texture *texture = &(m_theme.toolbar()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.base = None; frame.window.setBackgroundColor(texture->color()); } else { frame.base = image_ctrl.renderImage(frame.window.width(), frame.window.height(), *texture); frame.window.setBackgroundPixmap(frame.base); } if (tmp) image_ctrl.removeImage(tmp); tmp = frame.label; texture = &(m_theme.window()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.label = None; frame.window_label.setBackgroundColor(texture->color()); } else { frame.label = image_ctrl.renderImage(frame.window_label.width(), frame.window_label.height(), *texture); frame.window_label.setBackgroundPixmap(frame.label); } if (tmp) image_ctrl.removeImage(tmp); tmp = frame.wlabel; texture = &(m_theme.label()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.wlabel = None; frame.workspace_label.setBackgroundColor(texture->color()); } else { frame.wlabel = image_ctrl.renderImage(frame.workspace_label.width(), frame.workspace_label.height(), *texture); frame.workspace_label.setBackgroundPixmap(frame.wlabel); } if (tmp) image_ctrl.removeImage(tmp); tmp = frame.clk; texture = &(m_theme.clock()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.clk = None; frame.clock.setBackgroundColor(texture->color()); } else { frame.clk = image_ctrl.renderImage(frame.clock.width(), frame.clock.height(), *texture); frame.clock.setBackgroundPixmap(frame.clk); } if (tmp) image_ctrl.removeImage(tmp); tmp = frame.button; texture = &(m_theme.button()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.button = None; const FbTk::Color &color = texture->color(); frame.psbutton.setBackgroundColor(color); frame.nsbutton.setBackgroundColor(color); frame.pwbutton.setBackgroundColor(color); frame.nwbutton.setBackgroundColor(color); } else { frame.button = image_ctrl.renderImage(frame.button_w, frame.button_w, *texture); frame.psbutton.setBackgroundPixmap(frame.button); frame.nsbutton.setBackgroundPixmap(frame.button); frame.pwbutton.setBackgroundPixmap(frame.button); frame.nwbutton.setBackgroundPixmap(frame.button); } if (tmp) image_ctrl.removeImage(tmp); // pressed button pixmap tmp = frame.pbutton; texture = &(m_theme.pressedButton()); if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { frame.pbutton = None; } else { frame.pbutton = image_ctrl.renderImage(frame.button_w, frame.button_w, *texture); frame.psbutton.setPressedPixmap(frame.pbutton); frame.nsbutton.setPressedPixmap(frame.pbutton); frame.pwbutton.setPressedPixmap(frame.pbutton); frame.nwbutton.setPressedPixmap(frame.pbutton); } // setup button gc frame.psbutton.setGC(m_theme.buttonPicGC()); frame.nsbutton.setGC(m_theme.buttonPicGC()); frame.pwbutton.setGC(m_theme.buttonPicGC()); frame.nwbutton.setGC(m_theme.buttonPicGC()); if (tmp) image_ctrl.removeImage(tmp); frame.window.setBorderColor(screen().rootTheme().borderColor()); frame.window.setBorderWidth(screen().rootTheme().borderWidth()); frame.window.clear(); frame.workspace_label.clear(); frame.window_label.clear(); frame.clock.clear(); frame.psbutton.clear(); frame.nsbutton.clear(); frame.pwbutton.clear(); frame.nwbutton.clear(); redrawWindowLabel(); if (m_iconbar.get()) m_iconbar->reconfigure(); redrawWorkspaceLabel(); checkClock(true); m_toolbarmenu.reconfigure(); // we're done with all resizing and stuff now we can request a new // area to be reserv on screen updateStrut(); } void Toolbar::checkClock(bool redraw, bool date) { time_t tmp = 0; struct tm *tt = 0; if ((tmp = time(0)) != -1) { if (! (tt = localtime(&tmp))) return; if (tt->tm_min != frame.minute || tt->tm_hour != frame.hour) { frame.hour = tt->tm_hour; frame.minute = tt->tm_min; frame.clock.clear(); redraw = true; } } if (!redraw) return; frame.clock.clear(); #ifdef HAVE_STRFTIME char t[1024]; if (! strftime(t, 1024, screen().getStrftimeFormat(), tt)) return; #else // !HAVE_STRFTIME I18n *i18n = I18n::instance(); char t[9]; if (date) { // format the date... with special consideration for y2k ;) if (screen().getDateFormat() == Fluxbox::B_EUROPEANDATE) { sprintf(t, i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeDateFormatEu, "%02d.%02d.%02d"), tt->tm_mday, tt->tm_mon + 1, (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year); } else { sprintf(t, i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeDateFormat, "%02d/%02d/%02d"), tt->tm_mon + 1, tt->tm_mday, (tt->tm_year >= 100) ? tt->tm_year - 100 : tt->tm_year); } } else { if (screen().isClock24Hour()) { sprintf(t, i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeTimeFormat24, " %02d:%02d "), frame.hour, frame.minute); } else { sprintf(t, i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeTimeFormat12, "%02d:%02d %sm"), ((frame.hour > 12) ? frame.hour - 12 : ((frame.hour == 0) ? 12 : frame.hour)), frame.minute, ((frame.hour >= 12) ? i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeTimeFormatP, "p") : i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarNoStrftimeTimeFormatA, "a"))); } } #endif // HAVE_STRFTIME unsigned int newlen = strlen(t); int dx = FbTk::doAlignment(frame.clock_w, frame.bevel_w*2, m_theme.justify(), m_theme.font(), t, strlen(t), newlen); int dy = 1 + m_theme.font().ascent(); if (m_theme.font().isRotated()) { int tmp = dy; dy = frame.clock.height() - dx; dx = tmp; } frame.clock.clear(); m_theme.font().drawText(frame.clock.window(), screen().screenNumber(), m_theme.clockTextGC(), t, newlen, dx, dy); } void Toolbar::redrawWindowLabel(bool redraw) { if (Fluxbox::instance()->getFocusedWindow()) { if (redraw) frame.window_label.clear(); FluxboxWindow *foc = Fluxbox::instance()->getFocusedWindow(); // don't draw focused window if it's not on the same screen if (&foc->screen() != &screen() || foc->title().size() == 0) return; unsigned int newlen = foc->title().size(); int dx = FbTk::doAlignment(frame.window_label_w, frame.bevel_w*2, m_theme.justify(), m_theme.font(), foc->title().c_str(), foc->title().size(), newlen); int dy = 1 + m_theme.font().ascent(); if (m_theme.font().isRotated()) { int tmp = dy; dy = frame.window_label.height() - dx; dx = tmp + frame.bevel_w; } else dy += frame.bevel_w; m_theme.font().drawText( frame.window_label.window(), screen().screenNumber(), m_theme.windowTextGC(), foc->title().c_str(), newlen, dx, dy); } else frame.window_label.clear(); } void Toolbar::redrawWorkspaceLabel(bool redraw) { if (screen().currentWorkspace()->name().size()==0) return; if (redraw) frame.workspace_label.clear(); const char *text = screen().currentWorkspace()->name().c_str(); size_t textlen = screen().currentWorkspace()->name().size(); unsigned int newlen = textlen; int dx = FbTk::doAlignment(frame.workspace_label_w, frame.bevel_w, m_theme.justify(), m_theme.font(), text, textlen, newlen); int dy = 1 + m_theme.font().ascent(); if (m_theme.font().isRotated()) { int tmp = dy; dy = frame.workspace_label_w - dx; dx = tmp; } m_theme.font().drawText( frame.workspace_label.window(), screen().screenNumber(), m_theme.labelTextGC(), text, newlen, dx, dy); } void Toolbar::edit() { Window window; int foo; m_editing = true; //mark for editing Display *display = FbTk::App::instance()->display(); //workspace label already has intput focus ? if (XGetInputFocus(display, &window, &foo) && window == frame.workspace_label) return; //set input focus to workspace label XSetInputFocus(display, frame.workspace_label.window(), ((screen().isSloppyFocus() || screen().isSemiSloppyFocus()) ? RevertToPointerRoot : RevertToParent), CurrentTime); frame.workspace_label.clear(); Fluxbox * const fluxbox = Fluxbox::instance(); if (fluxbox->getFocusedWindow()) //disable focus on current focused window fluxbox->getFocusedWindow()->setFocusFlag(false); frame.workspace_label.drawRectangle(screen().winFrameTheme().labelTextFocusGC(), frame.workspace_label_w / 2, 0, 1, frame.label_h - 1); } void Toolbar::buttonPressEvent(XButtonEvent &be) { FluxboxWindow *fluxboxwin=0; if (be.button == 1) { if ( m_iconbar.get() != 0 ) { if ( (fluxboxwin = m_iconbar->findWindow(be.window)) ) fluxboxwin->deiconify(); } #ifndef HAVE_STRFTIME else if (be.window == frame.clock) { frame.clock.clear(); checkClock(true, true); } #endif // HAVE_STRFTIME } else if (be.button == 3) { FluxboxWindow *fluxboxwin = 0; // if we clicked on a icon then show window menu if ( m_iconbar.get() != 0 && (fluxboxwin = m_iconbar->findWindow(be.window)) ) { const FbTk::Menu &wm = fluxboxwin->menu(); int menu_y = be.y_root - wm.height(); int menu_x = be.x_root; // make sure the menu is visible if (menu_y < 0) { menu_y = 0; } if (menu_x < 0) { menu_x = 0; } else if (menu_x + wm.width() > screen().width()) { menu_x = screen().width() - wm.width(); } fluxboxwin->showMenu(menu_x, menu_y); } else if (! m_toolbarmenu.isVisible()) { int x, y; x = be.x_root - (m_toolbarmenu.width() / 2); y = be.y_root - (m_toolbarmenu.height() / 2); if (x < 0) x = 0; else if (x + m_toolbarmenu.width() > screen().width()) x = screen().width() - m_toolbarmenu.width(); if (y < 0) y = 0; else if (y + m_toolbarmenu.height() > screen().height()) y = screen().height() - m_toolbarmenu.height(); m_toolbarmenu.move(x, y); m_toolbarmenu.show(); } else m_toolbarmenu.hide(); } } void Toolbar::buttonReleaseEvent(XButtonEvent &re) { if (re.button == 1) { raise(); if (re.window == frame.workspace_label) { FbTk::Menu *menu = screen().getWorkspacemenu(); //move the workspace label and make it visible menu->move(re.x_root, re.y_root); // make sure the entire menu is visible //!!TODO: this is repeated by other menus, make a function!) int newx = menu->x(); // new x position of menu int newy = menu->y(); // new y position of menu if (menu->x() < 0) newx = 0; else if (menu->x() + menu->width() > screen().width()) newx = screen().width() - menu->width(); if (menu->y() < 0) newy = 0; else if (menu->y() + menu->height() > screen().height()) newy = screen().height() - menu->height(); // move and show menu menu->move(newx, newy); menu->show(); } else if (re.window == frame.window_label) screen().raiseFocus(); #ifndef HAVE_STRFTIME else if (re.window == frame.clock) { frame.clock.clear(); checkClock(true); } #endif // HAVE_STRFTIME } else if (re.button == 4) //mousewheel scroll up screen().nextWorkspace(1); else if (re.button == 5) //mousewheel scroll down screen().prevWorkspace(1); } void Toolbar::enterNotifyEvent(XCrossingEvent ¬_used) { if (! doAutoHide()) return; if (isHidden()) { if (! m_hide_timer.isTiming()) m_hide_timer.start(); } else { if (m_hide_timer.isTiming()) m_hide_timer.stop(); } } void Toolbar::leaveNotifyEvent(XCrossingEvent ¬_used) { if (! doAutoHide()) return; if (isHidden()) { if (m_hide_timer.isTiming()) m_hide_timer.stop(); } else if (! m_toolbarmenu.isVisible() && ! m_hide_timer.isTiming()) m_hide_timer.start(); } void Toolbar::exposeEvent(XExposeEvent &ee) { if (ee.window == frame.clock) checkClock(true); else if (ee.window == frame.workspace_label && (! isEditing())) redrawWorkspaceLabel(); else if (m_iconbar.get() != 0) m_iconbar->exposeEvent(&ee); } void Toolbar::keyPressEvent(XKeyEvent &ke) { if (ke.window != frame.workspace_label.window() || !isEditing()) return; KeySym ks; char keychar[1]; XLookupString(&ke, keychar, 1, &ks, 0); if (ks == XK_Return || ks == XK_Escape) { m_editing = false; Fluxbox * const fluxbox = Fluxbox::instance(); if (fluxbox->getFocusedWindow()) { fluxbox->getFocusedWindow()->setInputFocus(); fluxbox->getFocusedWindow()->setFocusFlag(true); } else XSetInputFocus(FbTk::App::instance()->display(), PointerRoot, None, CurrentTime); if (ks == XK_Return) //change workspace name if keypress = Return screen().currentWorkspace()->setName(m_new_workspace_name.c_str()); m_new_workspace_name.erase(); //erase temporary workspace name reconfigure(); //save workspace names Fluxbox::instance()->save_rc(); } else if (! IsModifierKey(ks) && !IsCursorKey(ks)) { if (ks == XK_BackSpace && m_new_workspace_name.size()) m_new_workspace_name.erase(m_new_workspace_name.size() - 1); else m_new_workspace_name += keychar[0]; frame.workspace_label.clear(); int l = m_new_workspace_name.size(), tw, x; tw = m_theme.font().textWidth(m_new_workspace_name.c_str(), l); x = (frame.workspace_label_w - tw) / 2; if (x < (signed) frame.bevel_w) x = frame.bevel_w; int dy = 1 + m_theme.font().ascent(); if (m_theme.font().isRotated()) { int tmp = dy; dy = frame.workspace_label_w - x; x = tmp; } m_theme.font().drawText(frame.workspace_label.window(), screen().screenNumber(), screen().winFrameTheme().labelTextFocusGC(), m_new_workspace_name.c_str(), l, x, dy); frame.workspace_label.drawRectangle(screen().winFrameTheme().labelTextFocusGC(), x + tw, 0, 1, frame.label_h - 1); } } void Toolbar::timeout() { checkClock(true); timeval delay; delay.tv_sec = 1; delay.tv_usec = 0; m_clock_timer.setTimeout(delay); } void Toolbar::setPlacement(Toolbar::Placement where) { *m_rc_placement = where; int head_x = 0, head_y = 0, head_w, head_h; #ifdef XINERAMA if (screen().hasXinerama()) { int head = *m_rc_on_head; head_x = screen().getHeadX(head); head_y = screen().getHeadY(head); head_w = screen().getHeadWidth(head); head_h = screen().getHeadHeight(head); } else #endif // XINERAMA { head_w = screen().width(); head_h = screen().height(); } frame.width = head_w * (*m_rc_width_percent) / 100; frame.height = m_theme.font().height(); frame.height += 2; frame.height += (frame.bevel_w * 2); // should we flipp sizes? if (isVertical()) { frame.width = frame.height; frame.height = head_h * (*m_rc_width_percent) / 100; if (!m_theme.font().isRotated()) m_theme.font().rotate(90); // rotate to vertical text frame.label_h = frame.width; frame.button_w = frame.width; } else { // horizontal toolbar if (m_theme.font().isRotated()) m_theme.font().rotate(0); // rotate to horizontal text frame.label_h = frame.height; frame.button_w = frame.height; } switch (where) { case TOPLEFT: frame.x = head_x; frame.y = head_y; frame.x_hidden = head_x; frame.y_hidden = head_y + screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth() - frame.height; break; case BOTTOMLEFT: frame.x = head_x; frame.y = head_y + head_h - frame.height - screen().rootTheme().borderWidth()*2; frame.x_hidden = head_x; frame.y_hidden = head_y + head_h - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); break; case TOPCENTER: frame.x = head_x + ((head_w - frame.width) / 2); frame.y = head_y; frame.x_hidden = frame.x; frame.y_hidden = head_y + screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth() - frame.height; break; case TOPRIGHT: frame.x = head_x + head_w - frame.width - screen().rootTheme().borderWidth()*2; frame.y = head_y; frame.x_hidden = frame.x; break; case BOTTOMRIGHT: frame.x = head_x + head_w - frame.width - screen().rootTheme().borderWidth()*2; frame.y = head_y + head_h - frame.height - screen().rootTheme().borderWidth()*2; frame.x_hidden = frame.x; frame.y_hidden = head_y + head_h - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); break; case BOTTOMCENTER: // default is BOTTOMCENTER default: frame.x = head_x + (head_w - frame.width) / 2; frame.y = head_y + head_h - frame.height - screen().rootTheme().borderWidth()*2; frame.x_hidden = frame.x; frame.y_hidden = head_y + head_h - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); break; case LEFTCENTER: frame.x = head_x; frame.y = head_y + (head_h - frame.height)/2; frame.x_hidden = frame.x - frame.width + screen().rootTheme().bevelWidth() + screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; case LEFTTOP: frame.x = head_x; frame.y = head_y; frame.x_hidden = frame.x - frame.width + screen().rootTheme().bevelWidth() + screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; case LEFTBOTTOM: frame.x = head_x; frame.y = head_y + head_h - frame.height - screen().rootTheme().borderWidth()*2; frame.x_hidden = frame.x - frame.width + screen().rootTheme().bevelWidth() + screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; case RIGHTCENTER: frame.x = head_x + head_w - frame.width - screen().rootTheme().borderWidth()*2; frame.y = head_y + (head_h - frame.height)/2; frame.x_hidden = frame.x + frame.width - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; case RIGHTTOP: frame.x = head_x + head_w - frame.width - screen().rootTheme().borderWidth()*2; frame.y = head_y; frame.x_hidden = frame.x + frame.width - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; case RIGHTBOTTOM: frame.x = head_x + head_w - frame.width - screen().rootTheme().borderWidth()*2; frame.y = head_y + head_h - frame.height - screen().rootTheme().borderWidth()*2; frame.x_hidden = frame.x + frame.width - screen().rootTheme().bevelWidth() - screen().rootTheme().borderWidth(); frame.y_hidden = frame.y; break; } } void Toolbar::HideHandler::timeout() { if (toolbar->isEditing()) { // don't hide if we're editing workspace label toolbar->m_hide_timer.fireOnce(false); toolbar->m_hide_timer.start(); // restart timer and try next timeout return; } toolbar->m_hide_timer.fireOnce(true); // toggle hidden toolbar->m_hidden = ! toolbar->m_hidden; if (toolbar->isHidden()) { toolbar->frame.window.move(toolbar->frame.x_hidden, toolbar->frame.y_hidden); } else { toolbar->frame.window.move(toolbar->frame.x, toolbar->frame.y); } } void Toolbar::moveToLayer(int layernum) { m_layeritem.moveToLayer(layernum); *m_rc_layernum = layernum; } void Toolbar::setupMenus() { Toolbar &tbar = *this; I18n *i18n = I18n::instance(); using namespace FBNLS; using namespace FbTk; FbTk::Menu &menu = tbar.menu(); RefCount<Command> start_edit(new SimpleCommand<Toolbar>(tbar, &Toolbar::edit)); menu.insert(i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarEditWkspcName, "Edit current workspace name"), start_edit); menu.setLabel(i18n->getMessage(FBNLS::ToolbarSet, FBNLS::ToolbarToolbarTitle, "Toolbar")); FbTk::MenuItem *toolbar_menuitem = new IntResMenuItem("Toolbar width percent", m_rc_width_percent, 0, 100); // min/max value FbTk::RefCount<FbTk::Command> reconfig_toolbar(new FbTk:: SimpleCommand<Toolbar> (tbar, &Toolbar::reconfigure)); FbTk::RefCount<FbTk::Command> save_resources(new FbCommands::SaveResources()); FbTk::MacroCommand *toolbar_menuitem_macro = new FbTk::MacroCommand(); toolbar_menuitem_macro->add(reconfig_toolbar); toolbar_menuitem_macro->add(save_resources); FbTk::RefCount<FbTk::Command> reconfig_toolbar_and_save_resource(toolbar_menuitem_macro); toolbar_menuitem->setCommand(reconfig_toolbar_and_save_resource); menu.insert(toolbar_menuitem); menu.insert(new BoolMenuItem(i18n->getMessage(FBNLS::CommonSet, FBNLS::CommonAutoHide, "Auto hide"), *m_rc_auto_hide, reconfig_toolbar_and_save_resource)); menu.insert("Layer...", &tbar.layermenu()); if (tbar.screen().hasXinerama()) { menu.insert("On Head...", new XineramaHeadMenu<Toolbar>( *tbar.screen().menuTheme(), tbar.screen(), tbar.screen().imageControl(), *tbar.screen().layerManager().getLayer(Fluxbox::instance()->getMenuLayer()), &tbar )); } // setup items in placement menu struct { int set; int base; const char *default_str; Toolbar::Placement placement; } place_menu[] = { {0, 0, "Top Left", Toolbar::TOPLEFT}, {0, 0, "Left Top", Toolbar::LEFTTOP}, {0, 0, "Left Center", Toolbar::LEFTCENTER}, {0, 0, "Left Bottom", Toolbar::LEFTBOTTOM}, {0, 0, "Bottom Left", Toolbar::BOTTOMLEFT}, {0, 0, "Top Center", Toolbar::TOPCENTER}, {0, 0, 0, Toolbar::TOPCENTER}, {0, 0, 0, Toolbar::BOTTOMCENTER}, {0, 0, 0, Toolbar::BOTTOMCENTER}, {0, 0, "Bottom Center", Toolbar::BOTTOMCENTER}, {0, 0, "Top Right", Toolbar::TOPRIGHT}, {0, 0, "Right Top", Toolbar::RIGHTTOP}, {0, 0, "Right Center", Toolbar::RIGHTCENTER}, {0, 0, "Right Bottom", Toolbar::RIGHTBOTTOM}, {0, 0, "Bottom Right", Toolbar::BOTTOMRIGHT} }; tbar.placementMenu().setMinimumSublevels(3); // create items in sub menu for (size_t i=0; i<15; ++i) { if (place_menu[i].default_str == 0) { tbar.placementMenu().insert(""); } else { const char *i18n_str = i18n->getMessage(place_menu[i].set, place_menu[i].base, place_menu[i].default_str); RefCount<FbTk::Command> setplace(new SetToolbarPlacementCmd(tbar, place_menu[i].placement)); tbar.placementMenu().insert(i18n_str, setplace); } } menu.insert("Placement", &tbar.placementMenu()); tbar.placementMenu().update(); menu.update(); }