// IconButton.cc // Copyright (c) 2003 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org) // and Simon Bowden (rathnor at users.sourceforge.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$ #include "IconButton.hh" #include "IconbarTool.hh" #include "fluxbox.hh" #include "Screen.hh" #include "Window.hh" #include "WinClient.hh" #include "CommandParser.hh" #include "FbTk/App.hh" #include "FbTk/SimpleCommand.hh" #include "FbTk/EventManager.hh" #include "FbTk/MacroCommand.hh" #include "FbTk/Command.hh" #include "FbTk/RefCount.hh" #include "FbTk/Menu.hh" #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include <X11/Xutil.h> #ifdef SHAPE #include <X11/extensions/shape.h> #endif // SHAPE typedef FbTk::RefCount<FbTk::Command> RefCmd; namespace { class ShowMenu: public FbTk::Command { public: explicit ShowMenu(FluxboxWindow &win):m_win(win) { } void execute() { m_win.screen().hideMenus(); m_win.menu().enableTitle(); // get last button pos const XEvent &event = Fluxbox::instance()->lastEvent(); int x = event.xbutton.x_root - (m_win.menu().width() / 2); int y = event.xbutton.y_root - (m_win.menu().height() / 2); m_win.showMenu(x, y); } private: FluxboxWindow &m_win; }; class FocusCommand: public FbTk::Command { public: explicit FocusCommand(const IconbarTool& tool, FluxboxWindow &win) : m_win(win), m_tool(tool) { } void execute() { if(m_win.isIconic() || !m_win.isFocused()) { switch(m_tool.deiconifyMode()) { case IconbarTool::SEMIFOLLOW: if (m_win.isIconic()) { m_win.screen().sendToWorkspace(m_win.screen().currentWorkspaceID(), &m_win); } else { m_win.screen().changeWorkspaceID(m_win.workspaceNumber()); } break; case IconbarTool::CURRENT: m_win.screen().sendToWorkspace(m_win.screen().currentWorkspaceID(), &m_win); break; case IconbarTool::FOLLOW: default: m_win.screen().changeWorkspaceID(m_win.workspaceNumber()); break; }; m_win.raiseAndFocus(); } else m_win.iconify(); } private: FluxboxWindow &m_win; const IconbarTool& m_tool; }; // simple forwarding of wheeling, but only // if desktopwheeling is enabled class WheelWorkspaceCmd : public FbTk::Command { public: explicit WheelWorkspaceCmd(const IconbarTool& tool, FluxboxWindow &win, const char* cmd) : m_win(win), m_tool(tool), m_cmd(CommandParser::instance().parseLine(cmd)){ } void execute() { switch(m_tool.wheelMode()) { case IconbarTool::ON: m_cmd->execute(); break; case IconbarTool::SCREEN: if(m_win.screen().isDesktopWheeling()) m_cmd->execute(); break; case IconbarTool::OFF: default: break; }; } private: FluxboxWindow &m_win; RefCmd m_cmd; const IconbarTool& m_tool; }; } // end anonymous namespace IconButton::IconButton(const IconbarTool& tool, const FbTk::FbWindow &parent, const FbTk::Font &font, FluxboxWindow &win): FbTk::TextButton(parent, font, win.winClient().title()), m_win(win), m_icon_window(*this, 1, 1, 1, 1, ExposureMask | ButtonPressMask | ButtonReleaseMask), m_use_pixmap(true) { RefCmd next_workspace(new ::WheelWorkspaceCmd(tool, m_win, "nextworkspace")); RefCmd prev_workspace(new ::WheelWorkspaceCmd(tool, m_win, "prevworkspace")); //!! TODO: There're some issues with MacroCommand when // this object dies when the last macrocommand is executed (focused cmd) // In iconbar mode Icons // // RefCmd hidemenus(new FbTk::SimpleCommand<BScreen>(win.screen(), &BScreen::hideMenus)); // FbTk::MacroCommand *focus_macro = new FbTk::MacroCommand(); // focus_macro->add(hidemenus); // focus_macro->add(focus); RefCmd focus_cmd(new ::FocusCommand(tool, m_win)); RefCmd menu_cmd(new ::ShowMenu(m_win)); setOnClick(focus_cmd, 1); setOnClick(menu_cmd, 3); setOnClick(next_workspace, 4); setOnClick(prev_workspace, 5); m_win.hintSig().attach(this); FbTk::EventManager::instance()->add(*this, m_icon_window); update(0); } IconButton::~IconButton() { // ~FbWindow cleans event manager } void IconButton::exposeEvent(XExposeEvent &event) { if (m_icon_window == event.window) m_icon_window.clear(); else FbTk::TextButton::exposeEvent(event); } void IconButton::moveResize(int x, int y, unsigned int width, unsigned int height) { FbTk::TextButton::moveResize(x, y, width, height); if (m_icon_window.width() != FbTk::Button::width() || m_icon_window.height() != FbTk::Button::height()) { update(0); // update icon window } } void IconButton::resize(unsigned int width, unsigned int height) { FbTk::TextButton::resize(width, height); if (m_icon_window.width() != FbTk::Button::width() || m_icon_window.height() != FbTk::Button::height()) { update(0); // update icon window } } void IconButton::clear() { setupWindow(); } void IconButton::clearArea(int x, int y, unsigned int width, unsigned int height, bool exposure) { FbTk::TextButton::clearArea(x, y, width, height, exposure); } void IconButton::setPixmap(bool use) { if (m_use_pixmap != use) { m_use_pixmap = use; update(0); } } void IconButton::update(FbTk::Subject *subj) { // we got signal that either title or // icon pixmap was updated, // so we refresh everything // we need to check our client first if (m_win.clientList().empty()) return; XWMHints *hints = XGetWMHints(FbTk::App::instance()->display(), m_win.winClient().window()); if (hints == 0) return; if (m_use_pixmap && (hints->flags & IconPixmapHint) && hints->icon_pixmap != 0) { // setup icon window m_icon_window.show(); int new_height = height() - 2*m_icon_window.y(); // equally padded int new_width = new_height; m_icon_window.resize((new_width>0) ? new_width : 1, (new_height>0) ? new_height : 1); m_icon_pixmap.copy(hints->icon_pixmap); m_icon_pixmap.scale(m_icon_window.width(), m_icon_window.height()); m_icon_window.setBackgroundPixmap(m_icon_pixmap.drawable()); } else { // no icon pixmap m_icon_window.move(0, 0); m_icon_window.hide(); m_icon_pixmap = 0; } if(m_use_pixmap && (hints->flags & IconMaskHint)) { m_icon_mask.copy(hints->icon_mask); m_icon_mask.scale(m_icon_pixmap.width(), m_icon_pixmap.height()); } else m_icon_mask = 0; XFree(hints); hints = 0; #ifdef SHAPE if (m_icon_mask.drawable() != 0) { XShapeCombineMask(FbTk::App::instance()->display(), m_icon_window.drawable(), ShapeBounding, 0, 0, m_icon_mask.drawable(), ShapeSet); } #endif // SHAPE if (subj != 0) { setupWindow(); } else { m_icon_window.clear(); } } void IconButton::setupWindow() { m_icon_window.clear(); if (!m_win.clientList().empty()) { setText(m_win.winClient().title()); // draw with x offset and y offset } FbTk::TextButton::clear(); } void IconButton::drawText(int x, int y) { // offset text if (m_icon_pixmap.drawable() != 0) FbTk::TextButton::drawText(m_icon_window.x() + m_icon_window.width() + 1, y); else FbTk::TextButton::drawText(1, y); }