340 lines
10 KiB
C++
340 lines
10 KiB
C++
// IconButton.cc
|
|
// Copyright (c) 2003 - 2006 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() {
|
|
// this needs to be a local variable, as this object could be destroyed
|
|
// if the workspace is changed.
|
|
FluxboxWindow &win = m_win;
|
|
if(win.isIconic() || !win.isFocused()) {
|
|
switch(m_tool.deiconifyMode()) {
|
|
case IconbarTool::SEMIFOLLOW:
|
|
if (win.isIconic()) {
|
|
win.screen().sendToWorkspace(win.screen().currentWorkspaceID(), &win);
|
|
} else {
|
|
win.screen().changeWorkspaceID(win.workspaceNumber());
|
|
}
|
|
break;
|
|
case IconbarTool::CURRENT:
|
|
win.screen().sendToWorkspace(win.screen().currentWorkspaceID(), &win);
|
|
break;
|
|
case IconbarTool::FOLLOW:
|
|
default:
|
|
if (!win.isStuck())
|
|
win.screen().changeWorkspaceID(win.workspaceNumber());
|
|
break;
|
|
};
|
|
win.raiseAndFocus();
|
|
} else
|
|
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_cmd(CommandParser::instance().parseLine(cmd)), m_tool(tool) { }
|
|
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,
|
|
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;
|
|
|
|
Display *display = FbTk::App::instance()->display();
|
|
|
|
XWMHints *hints = XGetWMHints(display, m_win.winClient().window());
|
|
if (hints == 0)
|
|
return;
|
|
|
|
int screen = m_win.screen().screenNumber();
|
|
|
|
if (m_use_pixmap && (hints->flags & IconPixmapHint) && hints->icon_pixmap != 0) {
|
|
// setup icon window
|
|
m_icon_window.show();
|
|
unsigned int w = width();
|
|
unsigned int h = height();
|
|
FbTk::translateSize(orientation(), w, h);
|
|
int iconx = 1, icony = 1;
|
|
unsigned int neww = w, newh = h;
|
|
if (newh > 2*static_cast<unsigned>(icony))
|
|
newh -= 2*icony;
|
|
else
|
|
newh = 1;
|
|
neww = newh;
|
|
|
|
FbTk::translateCoords(orientation(), iconx, icony, w, h);
|
|
FbTk::translatePosition(orientation(), iconx, icony, neww, newh, 0);
|
|
|
|
neww = newh;
|
|
m_icon_window.moveResize(iconx, icony, neww, newh);
|
|
|
|
m_icon_pixmap.copy(hints->icon_pixmap, DefaultDepth(display, screen), screen);
|
|
m_icon_pixmap.scale(m_icon_window.width(), m_icon_window.height());
|
|
|
|
// rotate the icon or not?? lets go not for now, and see what they say...
|
|
// need to rotate mask too if we do do this
|
|
m_icon_pixmap.rotate(orientation());
|
|
|
|
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, 0, 0);
|
|
m_icon_mask.scale(m_icon_pixmap.width(), m_icon_pixmap.height());
|
|
m_icon_mask.rotate(orientation());
|
|
} else
|
|
m_icon_mask = 0;
|
|
|
|
XFree(hints);
|
|
hints = 0;
|
|
|
|
#ifdef SHAPE
|
|
|
|
if (m_icon_mask.drawable() != 0) {
|
|
XShapeCombineMask(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, FbTk::FbDrawable *drawable) {
|
|
// offset text
|
|
if (m_icon_pixmap.drawable() != 0)
|
|
FbTk::TextButton::drawText(m_icon_window.x() + m_icon_window.width() + 1, y, drawable);
|
|
else
|
|
FbTk::TextButton::drawText(1, y, drawable);
|
|
}
|
|
|
|
bool IconButton::setOrientation(FbTk::Orientation orient) {
|
|
if (orientation() == orient)
|
|
return true;
|
|
|
|
if (FbTk::TextButton::setOrientation(orient)) {
|
|
int iconx = 1, icony = 1;
|
|
unsigned int tmpw = width(), tmph = height();
|
|
FbTk::translateSize(orient, tmpw, tmph);
|
|
FbTk::translateCoords(orient, iconx, icony, tmpw, tmph);
|
|
FbTk::translatePosition(orient, iconx, icony, m_icon_window.width(), m_icon_window.height(), 0);
|
|
m_icon_window.move(iconx, icony);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|