![Thomas Lübking](/assets/img/avatar_default.png)
On concurrent shortcuts OnTitlebar implies OnWindow and was so far resolved to OnWindow while OnTitlebar is the more precise condition. This also requires to exclude buttons from the titlebar context, ie. pass the position to the getContext function on press events BUG: 1035 The patch depends on the patch to correctly resolve the tab under the mouse since we're now passing the actual subwindows around
1616 lines
49 KiB
C++
1616 lines
49 KiB
C++
// FbWinFrame.cc for Fluxbox Window Manager
|
|
// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
|
//
|
|
// 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.
|
|
|
|
#include "FbWinFrame.hh"
|
|
|
|
#include "Keys.hh"
|
|
#include "FbWinFrameTheme.hh"
|
|
#include "Screen.hh"
|
|
#include "FocusableTheme.hh"
|
|
#include "IconButton.hh"
|
|
#include "RectangleUtil.hh"
|
|
|
|
#include "FbTk/ImageControl.hh"
|
|
#include "FbTk/EventManager.hh"
|
|
#include "FbTk/App.hh"
|
|
#include "FbTk/SimpleCommand.hh"
|
|
#include "FbTk/Compose.hh"
|
|
#include "FbTk/Transparent.hh"
|
|
#include "FbTk/CompareEqual.hh"
|
|
#include "FbTk/TextUtils.hh"
|
|
#include "FbTk/STLUtil.hh"
|
|
|
|
#include <X11/X.h>
|
|
|
|
#include <algorithm>
|
|
|
|
using std::max;
|
|
using std::mem_fun;
|
|
using std::string;
|
|
|
|
using FbTk::STLUtil::forAll;
|
|
|
|
namespace {
|
|
|
|
enum { UNFOCUS = 0, FOCUS, PRESSED };
|
|
|
|
const int s_button_size = 26;
|
|
const long s_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask;
|
|
|
|
const struct {
|
|
FbWinFrame::TabPlacement where;
|
|
FbTk::Orientation orient;
|
|
FbTk::Container::Alignment align;
|
|
bool is_horizontal;
|
|
} s_place[] = {
|
|
{ /* unused */ },
|
|
{ FbWinFrame::TOPLEFT, FbTk::ROT0, FbTk::Container::LEFT, true },
|
|
{ FbWinFrame::TOP, FbTk::ROT0, FbTk::Container::CENTER, true },
|
|
{ FbWinFrame::TOPRIGHT, FbTk::ROT0, FbTk::Container::RIGHT, true },
|
|
{ FbWinFrame::BOTTOMLEFT, FbTk::ROT0, FbTk::Container::LEFT, true },
|
|
{ FbWinFrame::BOTTOM, FbTk::ROT0, FbTk::Container::CENTER, true },
|
|
{ FbWinFrame::BOTTOMRIGHT,FbTk::ROT0, FbTk::Container::RIGHT, true },
|
|
{ FbWinFrame::LEFTTOP, FbTk::ROT270, FbTk::Container::RIGHT, false },
|
|
{ FbWinFrame::LEFT, FbTk::ROT270, FbTk::Container::CENTER, false },
|
|
{ FbWinFrame::LEFTBOTTOM, FbTk::ROT270, FbTk::Container::LEFT, false },
|
|
{ FbWinFrame::RIGHTTOP, FbTk::ROT90, FbTk::Container::LEFT, false },
|
|
{ FbWinFrame::RIGHT, FbTk::ROT90, FbTk::Container::LEFT, false },
|
|
{ FbWinFrame::RIGHTBOTTOM,FbTk::ROT90, FbTk::Container::LEFT, false },
|
|
};
|
|
|
|
/// renders to pixmap or sets color
|
|
void render(FbTk::Color &col, Pixmap &pm, unsigned int width, unsigned int height,
|
|
const FbTk::Texture &tex,
|
|
FbTk::ImageControl& ictl,
|
|
FbTk::Orientation orient = FbTk::ROT0) {
|
|
|
|
Pixmap tmp = pm;
|
|
if (!tex.usePixmap()) {
|
|
pm = None;
|
|
col = tex.color();
|
|
} else {
|
|
pm = ictl.renderImage(width, height, tex, orient);
|
|
}
|
|
|
|
if (tmp)
|
|
ictl.removeImage(tmp);
|
|
|
|
}
|
|
|
|
void bg_pm_or_color(FbTk::FbWindow& win, const Pixmap& pm, const FbTk::Color& color) {
|
|
if (pm) {
|
|
win.setBackgroundPixmap(pm);
|
|
} else {
|
|
win.setBackgroundColor(color);
|
|
}
|
|
}
|
|
|
|
|
|
} // end anonymous
|
|
|
|
FbWinFrame::FbWinFrame(BScreen &screen, unsigned int client_depth,
|
|
WindowState &state,
|
|
FocusableTheme<FbWinFrameTheme> &theme):
|
|
m_screen(screen),
|
|
m_theme(theme),
|
|
m_imagectrl(screen.imageControl()),
|
|
m_state(state),
|
|
m_window(theme->screenNum(), state.x, state.y, state.width, state.height, s_mask, true, false,
|
|
client_depth, InputOutput,
|
|
(client_depth == screen.rootWindow().maxDepth() ? screen.rootWindow().visual() : CopyFromParent),
|
|
(client_depth == screen.rootWindow().maxDepth() ? screen.rootWindow().colormap() : CopyFromParent)),
|
|
m_layeritem(window(), *screen.layerManager().getLayer(ResourceLayer::NORMAL)),
|
|
m_titlebar(m_window, 0, 0, 100, 16, s_mask, false, false,
|
|
screen.rootWindow().decorationDepth(), InputOutput,
|
|
screen.rootWindow().decorationVisual(),
|
|
screen.rootWindow().decorationColormap()),
|
|
m_tab_container(m_titlebar),
|
|
m_label(m_titlebar, m_theme->font(), FbTk::BiDiString("")),
|
|
m_handle(m_window, 0, 0, 100, 5, s_mask, false, false,
|
|
screen.rootWindow().decorationDepth(), InputOutput,
|
|
screen.rootWindow().decorationVisual(),
|
|
screen.rootWindow().decorationColormap()),
|
|
m_grip_right(m_handle, 0, 0, 10, 4, s_mask, false, false,
|
|
screen.rootWindow().decorationDepth(), InputOutput,
|
|
screen.rootWindow().decorationVisual(),
|
|
screen.rootWindow().decorationColormap()),
|
|
m_grip_left(m_handle, 0, 0, 10, 4, s_mask, false, false,
|
|
screen.rootWindow().decorationDepth(), InputOutput,
|
|
screen.rootWindow().decorationVisual(),
|
|
screen.rootWindow().decorationColormap()),
|
|
m_clientarea(m_window, 0, 0, 100, 100, s_mask),
|
|
m_bevel(1),
|
|
m_use_titlebar(true),
|
|
m_use_tabs(true),
|
|
m_use_handle(true),
|
|
m_visible(false),
|
|
m_tabmode(screen.getDefaultInternalTabs()?INTERNAL:EXTERNAL),
|
|
m_active_orig_client_bw(0),
|
|
m_need_render(true),
|
|
m_button_size(1),
|
|
m_shape(m_window, theme->shapePlace()) {
|
|
|
|
init();
|
|
}
|
|
|
|
FbWinFrame::~FbWinFrame() {
|
|
removeEventHandler();
|
|
removeAllButtons();
|
|
}
|
|
|
|
bool FbWinFrame::setTabMode(TabMode tabmode) {
|
|
if (m_tabmode == tabmode)
|
|
return false;
|
|
|
|
FbTk::Container& tabs = tabcontainer();
|
|
bool ret = true;
|
|
|
|
// setting tabmode to notset forces it through when
|
|
// something is likely to change
|
|
if (tabmode == NOTSET)
|
|
tabmode = m_tabmode;
|
|
|
|
m_tabmode = tabmode;
|
|
|
|
// reparent tab container
|
|
if (tabmode == EXTERNAL) {
|
|
m_label.show();
|
|
tabs.setBorderWidth(m_window.borderWidth());
|
|
tabs.setEventMask(s_mask);
|
|
alignTabs();
|
|
|
|
// TODO: tab position
|
|
if (m_use_tabs && m_visible)
|
|
tabs.show();
|
|
else {
|
|
ret = false;
|
|
tabs.hide();
|
|
}
|
|
|
|
} else {
|
|
tabs.setUpdateLock(true);
|
|
|
|
tabs.setAlignment(FbTk::Container::RELATIVE);
|
|
tabs.setOrientation(FbTk::ROT0);
|
|
if (tabs.parent()->window() == m_screen.rootWindow().window()) {
|
|
m_layeritem.removeWindow(m_tab_container);
|
|
tabs.hide();
|
|
tabs.reparent(m_titlebar, m_label.x(), m_label.y());
|
|
tabs.invalidateBackground();
|
|
tabs.resize(m_label.width(), m_label.height());
|
|
tabs.raise();
|
|
}
|
|
tabs.setBorderWidth(0);
|
|
tabs.setMaxTotalSize(0);
|
|
tabs.setUpdateLock(false);
|
|
tabs.setMaxSizePerClient(0);
|
|
|
|
renderTabContainer();
|
|
applyTabContainer();
|
|
|
|
tabs.clear();
|
|
tabs.raise();
|
|
tabs.show();
|
|
|
|
if (!m_use_tabs)
|
|
ret = false;
|
|
|
|
m_label.hide();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void FbWinFrame::hide() {
|
|
m_window.hide();
|
|
if (m_tabmode == EXTERNAL && m_use_tabs)
|
|
m_tab_container.hide();
|
|
|
|
m_visible = false;
|
|
}
|
|
|
|
void FbWinFrame::show() {
|
|
m_visible = true;
|
|
|
|
if (m_need_render) {
|
|
renderAll();
|
|
applyAll();
|
|
clearAll();
|
|
}
|
|
|
|
if (m_tabmode == EXTERNAL && m_use_tabs)
|
|
m_tab_container.show();
|
|
|
|
m_window.showSubwindows();
|
|
m_window.show();
|
|
}
|
|
|
|
void FbWinFrame::move(int x, int y) {
|
|
moveResize(x, y, 0, 0, true, false);
|
|
}
|
|
|
|
void FbWinFrame::resize(unsigned int width, unsigned int height) {
|
|
moveResize(0, 0, width, height, false, true);
|
|
}
|
|
|
|
// need an atomic moveresize where possible
|
|
void FbWinFrame::moveResizeForClient(int x, int y,
|
|
unsigned int width, unsigned int height,
|
|
int win_gravity,
|
|
unsigned int client_bw,
|
|
bool move, bool resize) {
|
|
// total height for frame
|
|
|
|
if (resize) // these fns check if the elements are "on"
|
|
height += titlebarHeight() + handleHeight();
|
|
|
|
gravityTranslate(x, y, win_gravity, client_bw, false);
|
|
setActiveGravity(win_gravity, client_bw);
|
|
moveResize(x, y, width, height, move, resize);
|
|
}
|
|
|
|
void FbWinFrame::resizeForClient(unsigned int width, unsigned int height,
|
|
int win_gravity, unsigned int client_bw) {
|
|
moveResizeForClient(0, 0, width, height, win_gravity, client_bw, false, true);
|
|
}
|
|
|
|
void FbWinFrame::moveResize(int x, int y, unsigned int width, unsigned int height, bool move, bool resize, bool force) {
|
|
if (!force && move && x == window().x() && y == window().y())
|
|
move = false;
|
|
|
|
if (!force && resize && width == FbWinFrame::width() &&
|
|
height == FbWinFrame::height())
|
|
resize = false;
|
|
|
|
if (!move && !resize)
|
|
return;
|
|
|
|
if (move && resize) {
|
|
m_window.moveResize(x, y, width, height);
|
|
notifyMoved(false); // will reconfigure
|
|
} else if (move) {
|
|
m_window.move(x, y);
|
|
// this stuff will be caught by reconfigure if resized
|
|
notifyMoved(true);
|
|
} else {
|
|
m_window.resize(width, height);
|
|
}
|
|
|
|
m_state.saveGeometry(window().x(), window().y(),
|
|
window().width(), window().height());
|
|
|
|
if (move || (resize && m_screen.getTabPlacement() != TOPLEFT &&
|
|
m_screen.getTabPlacement() != LEFTTOP))
|
|
alignTabs();
|
|
|
|
if (resize) {
|
|
if (m_tabmode == EXTERNAL) {
|
|
unsigned int s = width;
|
|
if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
|
|
s = height;
|
|
}
|
|
m_tab_container.setMaxTotalSize(s);
|
|
}
|
|
reconfigure();
|
|
}
|
|
}
|
|
|
|
void FbWinFrame::quietMoveResize(int x, int y,
|
|
unsigned int width, unsigned int height) {
|
|
m_window.moveResize(x, y, width, height);
|
|
m_state.saveGeometry(window().x(), window().y(),
|
|
window().width(), window().height());
|
|
if (m_tabmode == EXTERNAL) {
|
|
unsigned int s = width;
|
|
if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
|
|
s = height;
|
|
}
|
|
m_tab_container.setMaxTotalSize(s);
|
|
alignTabs();
|
|
}
|
|
}
|
|
|
|
void FbWinFrame::alignTabs() {
|
|
if (m_tabmode != EXTERNAL)
|
|
return;
|
|
|
|
|
|
FbTk::Container& tabs = tabcontainer();
|
|
FbTk::Orientation orig_orient = tabs.orientation();
|
|
unsigned int orig_tabwidth = tabs.maxWidthPerClient();
|
|
|
|
if (orig_tabwidth != m_screen.getTabWidth())
|
|
tabs.setMaxSizePerClient(m_screen.getTabWidth());
|
|
|
|
int bw = window().borderWidth();
|
|
int size = width();
|
|
int tab_x = x();
|
|
int tab_y = y();
|
|
|
|
TabPlacement p = m_screen.getTabPlacement();
|
|
if (orig_orient != s_place[p].orient) {
|
|
tabs.hide();
|
|
}
|
|
if (!s_place[p].is_horizontal) {
|
|
size = height();
|
|
}
|
|
tabs.setOrientation(s_place[p].orient);
|
|
tabs.setAlignment(s_place[p].align);
|
|
tabs.setMaxTotalSize(size);
|
|
|
|
int w = static_cast<int>(width());
|
|
int h = static_cast<int>(height());
|
|
int xo = xOffset();
|
|
int yo = yOffset();
|
|
int tw = static_cast<int>(tabs.width());
|
|
int th = static_cast<int>(tabs.height());
|
|
|
|
switch (p) {
|
|
case TOPLEFT: tab_y -= yo; break;
|
|
case TOP: tab_x += (w - tw)/2; tab_y -= yo; break;
|
|
case TOPRIGHT: tab_x += w - tw; tab_y -= yo; break;
|
|
case BOTTOMLEFT: tab_y += h + bw; break;
|
|
case BOTTOM: tab_x += (w - tw)/2; tab_y += h + bw; break;
|
|
case BOTTOMRIGHT: tab_x += w - tw; tab_y += h + bw; break;
|
|
case LEFTTOP: tab_x -= xo; break;
|
|
case LEFT: tab_x -= xo; tab_y += (h - th)/2; break;
|
|
case LEFTBOTTOM: tab_x -= xo; tab_y += h - th; break;
|
|
case RIGHTTOP: tab_x += w + bw; break;
|
|
case RIGHT: tab_x += w + bw; tab_y += (h - th)/2; break;
|
|
case RIGHTBOTTOM: tab_x += w + bw; tab_y += h - th; break;
|
|
}
|
|
|
|
if (tabs.orientation() != orig_orient ||
|
|
tabs.maxWidthPerClient() != orig_tabwidth) {
|
|
renderTabContainer();
|
|
if (m_visible && m_use_tabs) {
|
|
applyTabContainer();
|
|
tabs.clear();
|
|
tabs.show();
|
|
}
|
|
}
|
|
|
|
if (tabs.parent()->window() != m_screen.rootWindow().window()) {
|
|
tabs.reparent(m_screen.rootWindow(), tab_x, tab_y);
|
|
tabs.clear();
|
|
m_layeritem.addWindow(tabs);
|
|
} else {
|
|
tabs.move(tab_x, tab_y);
|
|
}
|
|
}
|
|
|
|
void FbWinFrame::notifyMoved(bool clear) {
|
|
// not important if no alpha...
|
|
int alpha = getAlpha(m_state.focused);
|
|
if (alpha == 255)
|
|
return;
|
|
|
|
if ((m_tabmode == EXTERNAL && m_use_tabs) || m_use_titlebar) {
|
|
m_tab_container.parentMoved();
|
|
m_tab_container.for_each(mem_fun(&FbTk::Button::parentMoved));
|
|
}
|
|
|
|
if (m_use_titlebar) {
|
|
if (m_tabmode != INTERNAL)
|
|
m_label.parentMoved();
|
|
|
|
m_titlebar.parentMoved();
|
|
|
|
forAll(m_buttons_left, mem_fun(&FbTk::Button::parentMoved));
|
|
forAll(m_buttons_right, mem_fun(&FbTk::Button::parentMoved));
|
|
}
|
|
|
|
if (m_use_handle) {
|
|
m_handle.parentMoved();
|
|
m_grip_left.parentMoved();
|
|
m_grip_right.parentMoved();
|
|
}
|
|
|
|
if (clear && (m_use_handle || m_use_titlebar)) {
|
|
clearAll();
|
|
} else if (clear && m_tabmode == EXTERNAL && m_use_tabs)
|
|
m_tab_container.clear();
|
|
}
|
|
|
|
void FbWinFrame::clearAll() {
|
|
|
|
if (m_use_titlebar) {
|
|
redrawTitlebar();
|
|
forAll(m_buttons_left, mem_fun(&FbTk::Button::clear));
|
|
forAll(m_buttons_right, mem_fun(&FbTk::Button::clear));
|
|
} else if (m_tabmode == EXTERNAL && m_use_tabs)
|
|
m_tab_container.clear();
|
|
|
|
if (m_use_handle) {
|
|
m_handle.clear();
|
|
m_grip_left.clear();
|
|
m_grip_right.clear();
|
|
}
|
|
}
|
|
|
|
void FbWinFrame::setFocus(bool newvalue) {
|
|
if (m_state.focused == newvalue)
|
|
return;
|
|
|
|
m_state.focused = newvalue;
|
|
|
|
if (FbTk::Transparent::haveRender() &&
|
|
getAlpha(true) != getAlpha(false)) { // different alpha for focused and unfocused
|
|
|
|
int alpha = getAlpha(m_state.focused);
|
|
int opaque = 255;
|
|
if (FbTk::Transparent::haveComposite()) {
|
|
std::swap(alpha, opaque);
|
|
}
|
|
m_tab_container.setAlpha(alpha);
|
|
m_window.setOpaque(opaque);
|
|
}
|
|
|
|
setBorderWidth();
|
|
|
|
applyAll();
|
|
clearAll();
|
|
}
|
|
|
|
void FbWinFrame::applyState() {
|
|
applyDecorations(false);
|
|
|
|
const int head = m_screen.getHead(window());
|
|
int new_x = m_state.x, new_y = m_state.y;
|
|
unsigned int new_w = m_state.width, new_h = m_state.height;
|
|
|
|
if (m_state.isMaximizedVert()) {
|
|
new_y = m_screen.maxTop(head);
|
|
new_h = m_screen.maxBottom(head) - new_y - 2*window().borderWidth();
|
|
if (!m_screen.getMaxOverTabs()) {
|
|
new_y += yOffset();
|
|
new_h -= heightOffset();
|
|
}
|
|
}
|
|
if (m_state.isMaximizedHorz()) {
|
|
new_x = m_screen.maxLeft(head);
|
|
new_w = m_screen.maxRight(head) - new_x - 2*window().borderWidth();
|
|
if (!m_screen.getMaxOverTabs()) {
|
|
new_x += xOffset();
|
|
new_w -= widthOffset();
|
|
}
|
|
}
|
|
|
|
if (m_state.shaded) {
|
|
new_h = m_titlebar.height();
|
|
} else if (m_state.fullscreen) {
|
|
new_x = m_screen.getHeadX(head);
|
|
new_y = m_screen.getHeadY(head);
|
|
new_w = m_screen.getHeadWidth(head);
|
|
new_h = m_screen.getHeadHeight(head);
|
|
} else {
|
|
applySizeHints(new_w, new_h, m_state.maximized == WindowState::MAX_NONE ||
|
|
!m_screen.getMaxIgnoreIncrement());
|
|
}
|
|
|
|
moveResize(new_x, new_y, new_w, new_h, true, true, true);
|
|
frameExtentSig().emit();
|
|
}
|
|
|
|
void FbWinFrame::setAlpha(bool focused, int alpha) {
|
|
m_alpha[focused] = alpha;
|
|
if (m_state.focused == focused)
|
|
applyAlpha();
|
|
}
|
|
|
|
void FbWinFrame::applyAlpha() {
|
|
int alpha = getAlpha(m_state.focused);
|
|
if (FbTk::Transparent::haveComposite())
|
|
m_window.setOpaque(alpha);
|
|
else {
|
|
// don't need to setAlpha, since apply updates them anyway
|
|
applyAll();
|
|
clearAll();
|
|
}
|
|
}
|
|
|
|
int FbWinFrame::getAlpha(bool focused) const {
|
|
return m_alpha[focused];
|
|
}
|
|
|
|
void FbWinFrame::setDefaultAlpha() {
|
|
if (getUseDefaultAlpha())
|
|
return;
|
|
m_alpha[UNFOCUS] = theme().unfocusedTheme()->alpha();
|
|
m_alpha[FOCUS] = theme().unfocusedTheme()->alpha();
|
|
applyAlpha();
|
|
}
|
|
|
|
bool FbWinFrame::getUseDefaultAlpha() const {
|
|
if (m_alpha[UNFOCUS] != theme().unfocusedTheme()->alpha()) {
|
|
return false;
|
|
} else if (m_alpha[FOCUS] != theme().focusedTheme()->alpha()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FbWinFrame::addLeftButton(FbTk::Button *btn) {
|
|
if (btn == 0) // valid button?
|
|
return;
|
|
|
|
applyButton(*btn); // setup theme and other stuff
|
|
|
|
m_buttons_left.push_back(btn);
|
|
}
|
|
|
|
void FbWinFrame::addRightButton(FbTk::Button *btn) {
|
|
if (btn == 0) // valid button?
|
|
return;
|
|
|
|
applyButton(*btn); // setup theme and other stuff
|
|
|
|
m_buttons_right.push_back(btn);
|
|
}
|
|
|
|
void FbWinFrame::removeAllButtons() {
|
|
|
|
FbTk::STLUtil::destroyAndClear(m_buttons_left);
|
|
FbTk::STLUtil::destroyAndClear(m_buttons_right);
|
|
}
|
|
|
|
void FbWinFrame::createTab(FbTk::Button &button) {
|
|
button.show();
|
|
button.setEventMask(ExposureMask | ButtonPressMask |
|
|
ButtonReleaseMask | ButtonMotionMask |
|
|
EnterWindowMask);
|
|
FbTk::EventManager::instance()->add(button, button.window());
|
|
|
|
m_tab_container.insertItem(&button);
|
|
}
|
|
|
|
void FbWinFrame::removeTab(IconButton *btn) {
|
|
if (m_tab_container.removeItem(btn))
|
|
delete btn;
|
|
}
|
|
|
|
|
|
void FbWinFrame::moveLabelButtonLeft(FbTk::TextButton &btn) {
|
|
m_tab_container.moveItem(&btn, -1);
|
|
}
|
|
|
|
void FbWinFrame::moveLabelButtonRight(FbTk::TextButton &btn) {
|
|
m_tab_container.moveItem(&btn, +1);
|
|
}
|
|
|
|
void FbWinFrame::moveLabelButtonTo(FbTk::TextButton &btn, int x, int y) {
|
|
m_tab_container.moveItemTo(&btn, x, y);
|
|
}
|
|
|
|
|
|
|
|
void FbWinFrame::moveLabelButtonLeftOf(FbTk::TextButton &btn, const FbTk::TextButton &dest) {
|
|
int dest_pos = m_tab_container.find(&dest);
|
|
int cur_pos = m_tab_container.find(&btn);
|
|
if (dest_pos < 0 || cur_pos < 0)
|
|
return;
|
|
int movement=dest_pos - cur_pos;
|
|
if(movement>0)
|
|
movement-=1;
|
|
// else
|
|
// movement-=1;
|
|
|
|
m_tab_container.moveItem(&btn, movement);
|
|
}
|
|
|
|
void FbWinFrame::moveLabelButtonRightOf(FbTk::TextButton &btn, const FbTk::TextButton &dest) {
|
|
int dest_pos = m_tab_container.find(&dest);
|
|
int cur_pos = m_tab_container.find(&btn);
|
|
if (dest_pos < 0 || cur_pos < 0 )
|
|
return;
|
|
int movement=dest_pos - cur_pos;
|
|
if(movement<0)
|
|
movement+=1;
|
|
|
|
m_tab_container.moveItem(&btn, movement);
|
|
}
|
|
|
|
void FbWinFrame::setClientWindow(FbTk::FbWindow &win) {
|
|
|
|
win.setBorderWidth(0);
|
|
|
|
XChangeSaveSet(win.display(), win.window(), SetModeInsert);
|
|
|
|
m_window.setEventMask(NoEventMask);
|
|
|
|
// we need to mask this so we don't get unmap event
|
|
win.setEventMask(NoEventMask);
|
|
win.reparent(m_window, clientArea().x(), clientArea().y());
|
|
|
|
m_window.setEventMask(ButtonPressMask | ButtonReleaseMask |
|
|
ButtonMotionMask | EnterWindowMask |
|
|
LeaveWindowMask | SubstructureRedirectMask);
|
|
|
|
XFlush(win.display());
|
|
|
|
// remask window so we get events
|
|
XSetWindowAttributes attrib_set;
|
|
attrib_set.event_mask = PropertyChangeMask | StructureNotifyMask | FocusChangeMask | KeyPressMask;
|
|
attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
|
|
ButtonMotionMask;
|
|
|
|
XChangeWindowAttributes(win.display(), win.window(), CWEventMask|CWDontPropagate, &attrib_set);
|
|
|
|
if (isVisible())
|
|
win.show();
|
|
win.raise();
|
|
m_window.showSubwindows();
|
|
|
|
}
|
|
|
|
bool FbWinFrame::hideTabs() {
|
|
if (m_tabmode == INTERNAL || !m_use_tabs) {
|
|
m_use_tabs = false;
|
|
return false;
|
|
}
|
|
|
|
m_use_tabs = false;
|
|
m_tab_container.hide();
|
|
return true;
|
|
}
|
|
|
|
bool FbWinFrame::showTabs() {
|
|
if (m_tabmode == INTERNAL || m_use_tabs) {
|
|
m_use_tabs = true;
|
|
return false; // nothing changed
|
|
}
|
|
|
|
m_use_tabs = true;
|
|
if (m_visible)
|
|
m_tab_container.show();
|
|
return true;
|
|
}
|
|
|
|
bool FbWinFrame::hideTitlebar() {
|
|
if (!m_use_titlebar)
|
|
return false;
|
|
|
|
m_titlebar.hide();
|
|
m_use_titlebar = false;
|
|
|
|
int h = height();
|
|
int th = m_titlebar.height();
|
|
int tbw = m_titlebar.borderWidth();
|
|
|
|
// only take away one borderwidth (as the other border is still the "top"
|
|
// border)
|
|
h = std::max(1, h - th - tbw);
|
|
m_window.resize(m_window.width(), h);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FbWinFrame::showTitlebar() {
|
|
if (m_use_titlebar)
|
|
return false;
|
|
|
|
m_titlebar.show();
|
|
m_use_titlebar = true;
|
|
|
|
// only add one borderwidth (as the other border is still the "top"
|
|
// border)
|
|
m_window.resize(m_window.width(), m_window.height() + m_titlebar.height() +
|
|
m_titlebar.borderWidth());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool FbWinFrame::hideHandle() {
|
|
if (!m_use_handle)
|
|
return false;
|
|
|
|
m_handle.hide();
|
|
m_grip_left.hide();
|
|
m_grip_right.hide();
|
|
m_use_handle = false;
|
|
|
|
int h = m_window.height();
|
|
int hh = m_handle.height();
|
|
int hbw = m_handle.borderWidth();
|
|
|
|
// only take away one borderwidth (as the other border is still the "top"
|
|
// border)
|
|
h = std::max(1, h - hh - hbw);
|
|
m_window.resize(m_window.width(), h);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FbWinFrame::showHandle() {
|
|
if (m_use_handle || theme()->handleWidth() == 0)
|
|
return false;
|
|
|
|
m_use_handle = true;
|
|
|
|
// weren't previously rendered...
|
|
renderHandles();
|
|
applyHandles();
|
|
|
|
m_handle.show();
|
|
m_handle.showSubwindows(); // shows grips
|
|
|
|
m_window.resize(m_window.width(), m_window.height() + m_handle.height() +
|
|
m_handle.borderWidth());
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Set new event handler for the frame's windows
|
|
*/
|
|
void FbWinFrame::setEventHandler(FbTk::EventHandler &evh) {
|
|
|
|
FbTk::EventManager &evm = *FbTk::EventManager::instance();
|
|
evm.add(evh, m_tab_container);
|
|
evm.add(evh, m_label);
|
|
evm.add(evh, m_titlebar);
|
|
evm.add(evh, m_handle);
|
|
evm.add(evh, m_grip_right);
|
|
evm.add(evh, m_grip_left);
|
|
evm.add(evh, m_window);
|
|
}
|
|
|
|
/**
|
|
remove event handler from windows
|
|
*/
|
|
void FbWinFrame::removeEventHandler() {
|
|
FbTk::EventManager &evm = *FbTk::EventManager::instance();
|
|
evm.remove(m_tab_container);
|
|
evm.remove(m_label);
|
|
evm.remove(m_titlebar);
|
|
evm.remove(m_handle);
|
|
evm.remove(m_grip_right);
|
|
evm.remove(m_grip_left);
|
|
evm.remove(m_window);
|
|
}
|
|
|
|
void FbWinFrame::exposeEvent(XExposeEvent &event) {
|
|
FbTk::FbWindow* win = 0;
|
|
if (m_titlebar == event.window) {
|
|
win = &m_titlebar;
|
|
} else if (m_tab_container == event.window) {
|
|
win = &m_tab_container;
|
|
} else if (m_label == event.window) {
|
|
win = &m_label;
|
|
} else if (m_handle == event.window) {
|
|
win = &m_handle;
|
|
} else if (m_grip_left == event.window) {
|
|
win = &m_grip_left;
|
|
} else if (m_grip_right == event.window) {
|
|
win = &m_grip_right;
|
|
} else {
|
|
|
|
if (m_tab_container.tryExposeEvent(event))
|
|
return;
|
|
|
|
// create compare function
|
|
// that we should use with find_if
|
|
FbTk::CompareEqual_base<FbTk::FbWindow, Window> compare(&FbTk::FbWindow::window,
|
|
event.window);
|
|
|
|
ButtonList::iterator it = find_if(m_buttons_left.begin(),
|
|
m_buttons_left.end(),
|
|
compare);
|
|
if (it != m_buttons_left.end()) {
|
|
(*it)->exposeEvent(event);
|
|
return;
|
|
}
|
|
|
|
it = find_if(m_buttons_right.begin(),
|
|
m_buttons_right.end(),
|
|
compare);
|
|
|
|
if (it != m_buttons_right.end())
|
|
(*it)->exposeEvent(event);
|
|
|
|
return;
|
|
}
|
|
|
|
win->clearArea(event.x, event.y, event.width, event.height);
|
|
}
|
|
|
|
void FbWinFrame::handleEvent(XEvent &event) {
|
|
if (event.type == ConfigureNotify && event.xconfigure.window == window().window())
|
|
configureNotifyEvent(event.xconfigure);
|
|
}
|
|
|
|
void FbWinFrame::configureNotifyEvent(XConfigureEvent &event) {
|
|
resize(event.width, event.height);
|
|
}
|
|
|
|
void FbWinFrame::reconfigure() {
|
|
if (m_tab_container.empty())
|
|
return;
|
|
|
|
int grav_x=0, grav_y=0;
|
|
// negate gravity
|
|
gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity, m_active_orig_client_bw, false);
|
|
|
|
m_bevel = theme()->bevelWidth();
|
|
|
|
unsigned int orig_handle_h = handle().height();
|
|
if (m_use_handle && orig_handle_h != theme()->handleWidth())
|
|
m_window.resize(m_window.width(), m_window.height() -
|
|
orig_handle_h + theme()->handleWidth());
|
|
|
|
handle().resize(handle().width(), theme()->handleWidth());
|
|
gripLeft().resize(buttonHeight(), theme()->handleWidth());
|
|
gripRight().resize(gripLeft().width(), gripLeft().height());
|
|
|
|
// align titlebar and render it
|
|
if (m_use_titlebar) {
|
|
reconfigureTitlebar();
|
|
m_titlebar.raise();
|
|
} else
|
|
m_titlebar.lower();
|
|
|
|
if (m_tabmode == EXTERNAL) {
|
|
unsigned int h = buttonHeight();
|
|
unsigned int w = m_tab_container.width();
|
|
if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
|
|
w = m_tab_container.height();
|
|
std::swap(w, h);
|
|
}
|
|
m_tab_container.resize(w, h);
|
|
alignTabs();
|
|
}
|
|
|
|
// leave client+grips alone if we're shaded (it'll get fixed when we unshade)
|
|
if (!m_state.shaded || m_state.fullscreen) {
|
|
int client_top = 0;
|
|
int client_height = m_window.height();
|
|
if (m_use_titlebar) {
|
|
// only one borderwidth as titlebar is really at -borderwidth
|
|
int titlebar_height = m_titlebar.height() + m_titlebar.borderWidth();
|
|
client_top += titlebar_height;
|
|
client_height -= titlebar_height;
|
|
}
|
|
|
|
// align handle and grips
|
|
const int grip_height = m_handle.height();
|
|
const int grip_width = 20; //TODO
|
|
const int handle_bw = static_cast<signed>(m_handle.borderWidth());
|
|
|
|
int ypos = m_window.height();
|
|
|
|
// if the handle isn't on, it's actually below the window
|
|
if (m_use_handle)
|
|
ypos -= grip_height + handle_bw;
|
|
|
|
// we do handle settings whether on or not so that if they get toggled
|
|
// then things are ok...
|
|
m_handle.invalidateBackground();
|
|
m_handle.moveResize(-handle_bw, ypos,
|
|
m_window.width(), grip_height);
|
|
|
|
m_grip_left.invalidateBackground();
|
|
m_grip_left.moveResize(-handle_bw, -handle_bw,
|
|
grip_width, grip_height);
|
|
|
|
m_grip_right.invalidateBackground();
|
|
m_grip_right.moveResize(m_handle.width() - grip_width - handle_bw, -handle_bw,
|
|
grip_width, grip_height);
|
|
|
|
if (m_use_handle) {
|
|
m_handle.raise();
|
|
client_height -= m_handle.height() + m_handle.borderWidth();
|
|
} else {
|
|
m_handle.lower();
|
|
}
|
|
|
|
m_clientarea.moveResize(0, client_top,
|
|
m_window.width(), client_height);
|
|
}
|
|
|
|
gravityTranslate(grav_x, grav_y, sizeHints().win_gravity, m_active_orig_client_bw, false);
|
|
// if the location changes, shift it
|
|
if (grav_x != 0 || grav_y != 0)
|
|
move(grav_x + x(), grav_y + y());
|
|
|
|
// render the theme
|
|
if (isVisible()) {
|
|
// update transparency settings
|
|
if (FbTk::Transparent::haveRender()) {
|
|
int alpha = getAlpha(m_state.focused);
|
|
int opaque = 255;
|
|
if (FbTk::Transparent::haveComposite()) {
|
|
std::swap(alpha, opaque);
|
|
}
|
|
m_tab_container.setAlpha(alpha);
|
|
m_window.setOpaque(opaque);
|
|
}
|
|
renderAll();
|
|
applyAll();
|
|
clearAll();
|
|
} else {
|
|
m_need_render = true;
|
|
}
|
|
|
|
m_shape.setPlaces(getShape());
|
|
m_shape.setShapeOffsets(0, titlebarHeight());
|
|
|
|
// titlebar stuff rendered already by reconftitlebar
|
|
}
|
|
|
|
void FbWinFrame::setShapingClient(FbTk::FbWindow *win, bool always_update) {
|
|
m_shape.setShapeSource(win, 0, titlebarHeight(), always_update);
|
|
}
|
|
|
|
unsigned int FbWinFrame::buttonHeight() const {
|
|
return m_titlebar.height() - m_bevel*2;
|
|
}
|
|
|
|
//--------------------- private area
|
|
|
|
/**
|
|
aligns and redraws title
|
|
*/
|
|
void FbWinFrame::redrawTitlebar() {
|
|
if (!m_use_titlebar || m_tab_container.empty())
|
|
return;
|
|
|
|
if (isVisible()) {
|
|
m_tab_container.clear();
|
|
m_label.clear();
|
|
m_titlebar.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Align buttons with title text window
|
|
*/
|
|
void FbWinFrame::reconfigureTitlebar() {
|
|
if (!m_use_titlebar)
|
|
return;
|
|
|
|
int orig_height = m_titlebar.height();
|
|
// resize titlebar to window size with font height
|
|
int title_height = theme()->font().height() == 0 ? 16 :
|
|
theme()->font().height() + m_bevel*2 + 2;
|
|
if (theme()->titleHeight() != 0)
|
|
title_height = theme()->titleHeight();
|
|
|
|
// if the titlebar grows in size, make sure the whole window does too
|
|
if (orig_height != title_height)
|
|
m_window.resize(m_window.width(), m_window.height()-orig_height+title_height);
|
|
m_titlebar.invalidateBackground();
|
|
m_titlebar.moveResize(-m_titlebar.borderWidth(), -m_titlebar.borderWidth(),
|
|
m_window.width(), title_height);
|
|
|
|
// draw left buttons first
|
|
unsigned int next_x = m_bevel;
|
|
unsigned int button_size = buttonHeight();
|
|
m_button_size = button_size;
|
|
for (size_t i=0; i < m_buttons_left.size(); i++, next_x += button_size + m_bevel) {
|
|
// probably on theme reconfigure, leave bg alone for now
|
|
m_buttons_left[i]->invalidateBackground();
|
|
m_buttons_left[i]->moveResize(next_x, m_bevel,
|
|
button_size, button_size);
|
|
}
|
|
|
|
next_x += m_bevel;
|
|
|
|
// space left on titlebar between left and right buttons
|
|
int space_left = m_titlebar.width() - next_x;
|
|
|
|
if (!m_buttons_right.empty())
|
|
space_left -= m_buttons_right.size() * (button_size + m_bevel);
|
|
|
|
space_left -= m_bevel;
|
|
|
|
if (space_left <= 0)
|
|
space_left = 1;
|
|
|
|
m_label.invalidateBackground();
|
|
m_label.moveResize(next_x, m_bevel, space_left, button_size);
|
|
|
|
m_tab_container.invalidateBackground();
|
|
if (m_tabmode == INTERNAL)
|
|
m_tab_container.moveResize(next_x, m_bevel, space_left, button_size);
|
|
else {
|
|
if (m_use_tabs) {
|
|
if (m_tab_container.orientation() == FbTk::ROT0) {
|
|
m_tab_container.resize(m_tab_container.width(), button_size);
|
|
} else {
|
|
m_tab_container.resize(button_size, m_tab_container.height());
|
|
}
|
|
}
|
|
}
|
|
|
|
next_x += m_label.width() + m_bevel;
|
|
|
|
// finaly set new buttons to the right
|
|
for (size_t i=0; i < m_buttons_right.size();
|
|
++i, next_x += button_size + m_bevel) {
|
|
m_buttons_right[i]->invalidateBackground();
|
|
m_buttons_right[i]->moveResize(next_x, m_bevel,
|
|
button_size, button_size);
|
|
}
|
|
|
|
m_titlebar.raise(); // always on top
|
|
}
|
|
|
|
void FbWinFrame::renderAll() {
|
|
m_need_render = false;
|
|
|
|
renderTitlebar();
|
|
renderHandles();
|
|
renderTabContainer();
|
|
}
|
|
|
|
void FbWinFrame::applyAll() {
|
|
applyTitlebar();
|
|
applyHandles();
|
|
applyTabContainer();
|
|
}
|
|
|
|
void FbWinFrame::renderTitlebar() {
|
|
if (!m_use_titlebar)
|
|
return;
|
|
|
|
if (!isVisible()) {
|
|
m_need_render = true;
|
|
return;
|
|
}
|
|
|
|
typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
|
|
TP& ft = theme().focusedTheme();
|
|
TP& uft = theme().unfocusedTheme();
|
|
|
|
// render pixmaps
|
|
render(m_title_face.color[FOCUS], m_title_face.pm[FOCUS], m_titlebar.width(), m_titlebar.height(),
|
|
ft->titleTexture(), m_imagectrl);
|
|
|
|
render(m_title_face.color[UNFOCUS], m_title_face.pm[UNFOCUS], m_titlebar.width(), m_titlebar.height(),
|
|
uft->titleTexture(), m_imagectrl);
|
|
|
|
//!! TODO: don't render label if internal tabs
|
|
|
|
render(m_label_face.color[FOCUS], m_label_face.pm[FOCUS], m_label.width(), m_label.height(),
|
|
ft->iconbarTheme()->texture(), m_imagectrl);
|
|
|
|
render(m_label_face.color[UNFOCUS], m_label_face.pm[UNFOCUS], m_label.width(), m_label.height(),
|
|
uft->iconbarTheme()->texture(), m_imagectrl);
|
|
}
|
|
|
|
void FbWinFrame::renderTabContainer() {
|
|
if (!isVisible()) {
|
|
m_need_render = true;
|
|
return;
|
|
}
|
|
|
|
typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
|
|
TP& ft = theme().focusedTheme();
|
|
TP& uft = theme().unfocusedTheme();
|
|
FbTk::Container& tabs = tabcontainer();
|
|
const FbTk::Texture *tc_focused = &ft->iconbarTheme()->texture();
|
|
const FbTk::Texture *tc_unfocused = &uft->iconbarTheme()->texture();
|
|
|
|
if (m_tabmode == EXTERNAL && tc_focused->type() & FbTk::Texture::PARENTRELATIVE)
|
|
tc_focused = &ft->titleTexture();
|
|
if (m_tabmode == EXTERNAL && tc_unfocused->type() & FbTk::Texture::PARENTRELATIVE)
|
|
tc_unfocused = &uft->titleTexture();
|
|
|
|
render(m_tabcontainer_face.color[FOCUS], m_tabcontainer_face.pm[FOCUS],
|
|
tabs.width(), tabs.height(), *tc_focused, m_imagectrl, tabs.orientation());
|
|
|
|
render(m_tabcontainer_face.color[UNFOCUS], m_tabcontainer_face.pm[UNFOCUS],
|
|
tabs.width(), tabs.height(), *tc_unfocused, m_imagectrl, tabs.orientation());
|
|
|
|
renderButtons();
|
|
|
|
}
|
|
|
|
void FbWinFrame::applyTitlebar() {
|
|
|
|
int f = m_state.focused;
|
|
int alpha = getAlpha(f);
|
|
m_titlebar.setAlpha(alpha);
|
|
m_label.setAlpha(alpha);
|
|
|
|
if (m_tabmode != INTERNAL) {
|
|
m_label.setGC(theme()->iconbarTheme()->text().textGC());
|
|
m_label.setJustify(theme()->iconbarTheme()->text().justify());
|
|
|
|
bg_pm_or_color(m_label, m_label_face.pm[f], m_label_face.color[f]);
|
|
}
|
|
|
|
bg_pm_or_color(m_titlebar, m_title_face.pm[f], m_title_face.color[f]);
|
|
applyButtons();
|
|
}
|
|
|
|
|
|
void FbWinFrame::renderHandles() {
|
|
if (!m_use_handle)
|
|
return;
|
|
|
|
if (!isVisible()) {
|
|
m_need_render = true;
|
|
return;
|
|
}
|
|
|
|
typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
|
|
TP& ft = theme().focusedTheme();
|
|
TP& uft = theme().unfocusedTheme();
|
|
|
|
render(m_handle_face.color[FOCUS], m_handle_face.pm[FOCUS],
|
|
m_handle.width(), m_handle.height(),
|
|
ft->handleTexture(), m_imagectrl);
|
|
|
|
render(m_handle_face.color[UNFOCUS], m_handle_face.pm[UNFOCUS],
|
|
m_handle.width(), m_handle.height(),
|
|
uft->handleTexture(), m_imagectrl);
|
|
|
|
render(m_grip_face.color[FOCUS], m_grip_face.pm[FOCUS],
|
|
m_grip_left.width(), m_grip_left.height(),
|
|
ft->gripTexture(), m_imagectrl);
|
|
|
|
render(m_grip_face.color[UNFOCUS], m_grip_face.pm[UNFOCUS],
|
|
m_grip_left.width(), m_grip_left.height(),
|
|
uft->gripTexture(), m_imagectrl);
|
|
}
|
|
|
|
void FbWinFrame::applyHandles() {
|
|
|
|
bool f = m_state.focused;
|
|
int alpha = getAlpha(f);
|
|
|
|
m_handle.setAlpha(alpha);
|
|
bg_pm_or_color(m_handle, m_handle_face.pm[f], m_handle_face.color[f]);
|
|
|
|
m_grip_left.setAlpha(alpha);
|
|
m_grip_right.setAlpha(alpha);
|
|
|
|
bg_pm_or_color(m_grip_left, m_grip_face.pm[f], m_grip_face.color[f]);
|
|
bg_pm_or_color(m_grip_right, m_grip_face.pm[f], m_grip_face.color[f]);
|
|
}
|
|
|
|
void FbWinFrame::renderButtons() {
|
|
|
|
if (!isVisible()) {
|
|
m_need_render = true;
|
|
return;
|
|
}
|
|
|
|
typedef FbTk::ThemeProxy<FbWinFrameTheme> TP;
|
|
TP& ft = theme().focusedTheme();
|
|
TP& uft = theme().unfocusedTheme();
|
|
|
|
render(m_button_face.color[UNFOCUS], m_button_face.pm[UNFOCUS],
|
|
m_button_size, m_button_size,
|
|
uft->buttonTexture(), m_imagectrl);
|
|
|
|
render(m_button_face.color[FOCUS], m_button_face.pm[FOCUS],
|
|
m_button_size, m_button_size,
|
|
ft->buttonTexture(), m_imagectrl);
|
|
|
|
render(m_button_face.color[PRESSED], m_button_face.pm[PRESSED],
|
|
m_button_size, m_button_size,
|
|
theme()->buttonPressedTexture(), m_imagectrl);
|
|
|
|
}
|
|
|
|
void FbWinFrame::applyButtons() {
|
|
// setup left and right buttons
|
|
for (size_t i=0; i < m_buttons_left.size(); ++i)
|
|
applyButton(*m_buttons_left[i]);
|
|
|
|
for (size_t i=0; i < m_buttons_right.size(); ++i)
|
|
applyButton(*m_buttons_right[i]);
|
|
}
|
|
|
|
void FbWinFrame::init() {
|
|
|
|
if (theme()->handleWidth() == 0)
|
|
m_use_handle = false;
|
|
|
|
m_alpha[UNFOCUS] = theme().unfocusedTheme()->alpha();
|
|
m_alpha[FOCUS] = theme().focusedTheme()->alpha();
|
|
|
|
m_handle.showSubwindows();
|
|
|
|
// clear pixmaps
|
|
m_title_face.pm[UNFOCUS] = m_title_face.pm[FOCUS] = 0;
|
|
m_label_face.pm[UNFOCUS] = m_label_face.pm[FOCUS] = 0;
|
|
m_tabcontainer_face.pm[UNFOCUS] = m_tabcontainer_face.pm[FOCUS] = 0;
|
|
m_handle_face.pm[UNFOCUS] = m_handle_face.pm[FOCUS] = 0;
|
|
m_button_face.pm[UNFOCUS] = m_button_face.pm[FOCUS] = m_button_face.pm[PRESSED] = 0;
|
|
m_grip_face.pm[UNFOCUS] = m_grip_face.pm[FOCUS] = 0;
|
|
|
|
m_button_size = s_button_size;
|
|
|
|
m_label.setBorderWidth(0);
|
|
|
|
setTabMode(NOTSET);
|
|
|
|
m_label.setEventMask(ExposureMask | ButtonPressMask |
|
|
ButtonReleaseMask | ButtonMotionMask |
|
|
EnterWindowMask);
|
|
|
|
showHandle();
|
|
showTitlebar();
|
|
|
|
// Note: we don't show clientarea yet
|
|
|
|
setEventHandler(*this);
|
|
|
|
// setup cursors for resize grips
|
|
gripLeft().setCursor(theme()->lowerLeftAngleCursor());
|
|
gripRight().setCursor(theme()->lowerRightAngleCursor());
|
|
}
|
|
|
|
/**
|
|
Setups upp background, pressed pixmap/color of the button to current theme
|
|
*/
|
|
void FbWinFrame::applyButton(FbTk::Button &btn) {
|
|
|
|
FbWinFrame::BtnFace& face = m_button_face;
|
|
|
|
if (m_button_face.pm[PRESSED]) {
|
|
btn.setPressedPixmap(face.pm[PRESSED]);
|
|
} else {
|
|
btn.setPressedColor(face.color[PRESSED]);
|
|
}
|
|
|
|
bool f = m_state.focused;
|
|
|
|
btn.setAlpha(getAlpha(f));
|
|
btn.setGC(theme()->buttonPicGC());
|
|
|
|
bg_pm_or_color(btn, face.pm[f], face.color[f]);
|
|
}
|
|
|
|
|
|
void FbWinFrame::applyTabContainer() {
|
|
|
|
FbTk::Container& tabs = tabcontainer();
|
|
FbWinFrame::Face& face = m_tabcontainer_face;
|
|
|
|
tabs.setAlpha(getAlpha(m_state.focused));
|
|
bg_pm_or_color(tabs, face.pm[m_state.focused], face.color[m_state.focused]);
|
|
|
|
// and the labelbuttons in it
|
|
FbTk::Container::ItemList::iterator btn_it = m_tab_container.begin();
|
|
FbTk::Container::ItemList::iterator btn_it_end = m_tab_container.end();
|
|
for (; btn_it != btn_it_end; ++btn_it) {
|
|
IconButton *btn = static_cast<IconButton *>(*btn_it);
|
|
btn->reconfigTheme();
|
|
}
|
|
}
|
|
|
|
int FbWinFrame::getShape() const {
|
|
int shape = theme()->shapePlace();
|
|
if (!m_state.useTitlebar())
|
|
shape &= ~(FbTk::Shape::TOPRIGHT|FbTk::Shape::TOPLEFT);
|
|
if (!m_state.useHandle())
|
|
shape &= ~(FbTk::Shape::BOTTOMRIGHT|FbTk::Shape::BOTTOMLEFT);
|
|
return shape;
|
|
}
|
|
|
|
void FbWinFrame::applyDecorations(bool do_move) {
|
|
int grav_x=0, grav_y=0;
|
|
// negate gravity
|
|
gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity, m_active_orig_client_bw,
|
|
false);
|
|
|
|
bool client_move = setBorderWidth(false);
|
|
|
|
// tab deocration only affects if we're external
|
|
// must do before the setTabMode in case it goes
|
|
// to external and is meant to be hidden
|
|
if (m_state.useTabs())
|
|
client_move |= showTabs();
|
|
else
|
|
client_move |= hideTabs();
|
|
|
|
// we rely on frame not doing anything if it is already shown/hidden
|
|
if (m_state.useTitlebar()) {
|
|
client_move |= showTitlebar();
|
|
if (m_screen.getDefaultInternalTabs())
|
|
client_move |= setTabMode(INTERNAL);
|
|
else
|
|
client_move |= setTabMode(EXTERNAL);
|
|
} else {
|
|
client_move |= hideTitlebar();
|
|
if (m_state.useTabs())
|
|
client_move |= setTabMode(EXTERNAL);
|
|
}
|
|
|
|
if (m_state.useHandle())
|
|
client_move |= showHandle();
|
|
else
|
|
client_move |= hideHandle();
|
|
|
|
// apply gravity once more
|
|
gravityTranslate(grav_x, grav_y, sizeHints().win_gravity, m_active_orig_client_bw,
|
|
false);
|
|
|
|
// if the location changes, shift it
|
|
if (do_move && (grav_x != 0 || grav_y != 0)) {
|
|
move(grav_x + x(), grav_y + y());
|
|
client_move = true;
|
|
}
|
|
|
|
if (do_move) {
|
|
reconfigure();
|
|
m_state.saveGeometry(x(), y(), width(), height());
|
|
}
|
|
if (client_move)
|
|
frameExtentSig().emit();
|
|
}
|
|
|
|
bool FbWinFrame::setBorderWidth(bool do_move) {
|
|
unsigned int border_width = theme()->border().width();
|
|
unsigned int win_bw = m_state.useBorder() ? border_width : 0;
|
|
|
|
if (border_width &&
|
|
theme()->border().color().pixel() != window().borderColor()) {
|
|
FbTk::Color c = theme()->border().color();
|
|
window().setBorderColor(c);
|
|
titlebar().setBorderColor(c);
|
|
handle().setBorderColor(c);
|
|
gripLeft().setBorderColor(c);
|
|
gripRight().setBorderColor(c);
|
|
tabcontainer().setBorderColor(c);
|
|
}
|
|
|
|
if (border_width == handle().borderWidth() &&
|
|
win_bw == window().borderWidth())
|
|
return false;
|
|
|
|
int grav_x=0, grav_y=0;
|
|
// negate gravity
|
|
if (do_move)
|
|
gravityTranslate(grav_x, grav_y, -sizeHints().win_gravity,
|
|
m_active_orig_client_bw, false);
|
|
|
|
int bw_changes = 0;
|
|
// we need to change the size of the window
|
|
// if the border width changes...
|
|
if (m_use_titlebar)
|
|
bw_changes += static_cast<signed>(border_width - titlebar().borderWidth());
|
|
if (m_use_handle)
|
|
bw_changes += static_cast<signed>(border_width - handle().borderWidth());
|
|
|
|
window().setBorderWidth(win_bw);
|
|
|
|
setTabMode(NOTSET);
|
|
|
|
titlebar().setBorderWidth(border_width);
|
|
handle().setBorderWidth(border_width);
|
|
gripLeft().setBorderWidth(border_width);
|
|
gripRight().setBorderWidth(border_width);
|
|
|
|
if (bw_changes != 0)
|
|
resize(width(), height() + bw_changes);
|
|
|
|
if (m_tabmode == EXTERNAL)
|
|
alignTabs();
|
|
|
|
if (do_move) {
|
|
frameExtentSig().emit();
|
|
gravityTranslate(grav_x, grav_y, sizeHints().win_gravity,
|
|
m_active_orig_client_bw, false);
|
|
// if the location changes, shift it
|
|
if (grav_x != 0 || grav_y != 0)
|
|
move(grav_x + x(), grav_y + y());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// this function translates its arguments according to win_gravity
|
|
// if win_gravity is negative, it does an inverse translation
|
|
// This function should be used when a window is mapped/unmapped/pos configured
|
|
void FbWinFrame::gravityTranslate(int &x, int &y,
|
|
int win_gravity, unsigned int client_bw, bool move_frame) {
|
|
bool invert = false;
|
|
if (win_gravity < 0) {
|
|
invert = true;
|
|
win_gravity = -win_gravity; // make +ve
|
|
}
|
|
|
|
/* Ok, so, gravity says which point of the frame is put where the
|
|
* corresponding bit of window would have been
|
|
* Thus, x,y always refers to where top left of the WINDOW would be placed
|
|
* but given that we're wrapping it in a frame, we actually place
|
|
* it so that the given reference point is in the same spot as the
|
|
* window's reference point would have been.
|
|
* i.e. east gravity says that the centre of the right hand side of the
|
|
* frame is placed where the centre of the rhs of the window would
|
|
* have been if there was no frame.
|
|
* Hope that makes enough sense.
|
|
*
|
|
* NOTE: the gravity calculations are INDEPENDENT of the client
|
|
* window width/height.
|
|
*
|
|
* If you get confused with the calculations, draw a picture.
|
|
*
|
|
*/
|
|
|
|
// We calculate offsets based on the gravity and frame aspects
|
|
// and at the end apply those offsets +ve or -ve depending on 'invert'
|
|
|
|
// These will be set to the resulting offsets for adjusting the frame position
|
|
int x_offset = 0;
|
|
int y_offset = 0;
|
|
|
|
// These are the amount that the frame is larger than the client window
|
|
// Note that the client window's x,y is offset by it's borderWidth, which
|
|
// is removed by fluxbox, so the gravity needs to account for this change
|
|
|
|
// these functions already check if the title/handle is used
|
|
int bw = static_cast<int>(m_window.borderWidth());
|
|
int bw_diff = static_cast<int>(client_bw) - bw;
|
|
int height_diff = 2*bw_diff - static_cast<int>(titlebarHeight()) - static_cast<int>(handleHeight());
|
|
int width_diff = 2*bw_diff;
|
|
|
|
if (win_gravity == SouthWestGravity || win_gravity == SouthGravity ||
|
|
win_gravity == SouthEastGravity)
|
|
y_offset = height_diff;
|
|
|
|
if (win_gravity == WestGravity || win_gravity == CenterGravity ||
|
|
win_gravity == EastGravity)
|
|
y_offset = height_diff/2;
|
|
|
|
if (win_gravity == NorthEastGravity || win_gravity == EastGravity ||
|
|
win_gravity == SouthEastGravity)
|
|
x_offset = width_diff;
|
|
|
|
if (win_gravity == NorthGravity || win_gravity == CenterGravity ||
|
|
win_gravity == SouthGravity)
|
|
x_offset = width_diff/2;
|
|
|
|
if (win_gravity == StaticGravity) {
|
|
x_offset = bw_diff;
|
|
y_offset = bw_diff - titlebarHeight();
|
|
}
|
|
|
|
if (invert) {
|
|
x_offset = -x_offset;
|
|
y_offset = -y_offset;
|
|
}
|
|
|
|
x += x_offset;
|
|
y += y_offset;
|
|
|
|
if (move_frame && (x_offset != 0 || y_offset != 0)) {
|
|
move(x, y);
|
|
}
|
|
}
|
|
|
|
int FbWinFrame::widthOffset() const {
|
|
if (m_tabmode != EXTERNAL || !m_use_tabs)
|
|
return 0;
|
|
if (s_place[m_screen.getTabPlacement()].is_horizontal) {
|
|
return 0;
|
|
}
|
|
return m_tab_container.width() + m_window.borderWidth();
|
|
}
|
|
|
|
int FbWinFrame::heightOffset() const {
|
|
if (m_tabmode != EXTERNAL || !m_use_tabs)
|
|
return 0;
|
|
|
|
if (!s_place[m_screen.getTabPlacement()].is_horizontal) {
|
|
return 0;
|
|
}
|
|
return m_tab_container.height() + m_window.borderWidth();
|
|
}
|
|
|
|
int FbWinFrame::xOffset() const {
|
|
if (m_tabmode != EXTERNAL || !m_use_tabs)
|
|
return 0;
|
|
TabPlacement p = m_screen.getTabPlacement();
|
|
if (p == LEFTTOP || p == LEFT || p == LEFTBOTTOM) {
|
|
return m_tab_container.width() + m_window.borderWidth();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int FbWinFrame::yOffset() const {
|
|
if (m_tabmode != EXTERNAL || !m_use_tabs)
|
|
return 0;
|
|
TabPlacement p = m_screen.getTabPlacement();
|
|
if (p == TOPLEFT || p == TOP || p == TOPRIGHT) {
|
|
return m_tab_container.height() + m_window.borderWidth();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void FbWinFrame::applySizeHints(unsigned int &width, unsigned int &height,
|
|
bool maximizing) const {
|
|
const int h = height - titlebarHeight() - handleHeight();
|
|
height = max(h, static_cast<int>(titlebarHeight() + handleHeight()));
|
|
sizeHints().apply(width, height, maximizing);
|
|
height += titlebarHeight() + handleHeight();
|
|
}
|
|
|
|
void FbWinFrame::displaySize(unsigned int width, unsigned int height) const {
|
|
unsigned int i, j;
|
|
sizeHints().displaySize(i, j,
|
|
width, height - titlebarHeight() - handleHeight());
|
|
m_screen.showGeometry(i, j);
|
|
}
|
|
|
|
bool FbWinFrame::insideTitlebar(Window win) const {
|
|
return
|
|
gripLeft().window() != win &&
|
|
gripRight().window() != win &&
|
|
window().window() != win;
|
|
}
|
|
|
|
int FbWinFrame::getContext(Window win, int x, int y, int last_x, int last_y, bool doBorders) {
|
|
int context = 0;
|
|
if (gripLeft().window() == win) return Keys::ON_LEFTGRIP;
|
|
if (gripRight().window() == win) return Keys::ON_RIGHTGRIP;
|
|
if (doBorders) {
|
|
using RectangleUtil::insideBorder;
|
|
int borderw = window().borderWidth();
|
|
if ( // if mouse is currently on the window border, ignore it
|
|
(
|
|
! insideBorder(window(), x, y, borderw)
|
|
&& ( externalTabMode()
|
|
|| ! insideBorder(tabcontainer(), x, y, borderw) )
|
|
)
|
|
|| // or if mouse was on border when it was last clicked
|
|
(
|
|
! insideBorder(window(), last_x, last_y, borderw)
|
|
&& ( externalTabMode()
|
|
|| ! insideBorder(tabcontainer(), last_x, last_y, borderw ) )
|
|
)
|
|
) context = Keys::ON_WINDOWBORDER;
|
|
}
|
|
|
|
if (window().window() == win) return context | Keys::ON_WINDOW;
|
|
// /!\ old code: handle = titlebar in motionNotifyEvent but only there !
|
|
// handle() as border ??
|
|
if (handle().window() == win) return Keys::ON_WINDOWBORDER | Keys::ON_WINDOW;
|
|
if (titlebar().window() == win) {
|
|
const int px = x - this->x() - window().borderWidth();
|
|
if (px < label().x() || px > label().x() + label().width())
|
|
return context; // one of the buttons, asked from a grabbed event
|
|
return context | Keys::ON_TITLEBAR;
|
|
}
|
|
if (label().window() == win) return context | Keys::ON_TITLEBAR;
|
|
// internal tabs are on title bar
|
|
if (tabcontainer().window() == win)
|
|
return context | Keys::ON_TAB | (externalTabMode()?0:Keys::ON_TITLEBAR);
|
|
|
|
|
|
FbTk::Container::ItemList::iterator it = tabcontainer().begin();
|
|
FbTk::Container::ItemList::iterator it_end = tabcontainer().end();
|
|
for (; it != it_end; ++it) {
|
|
if ((*it)->window() == win)
|
|
break;
|
|
}
|
|
// internal tabs are on title bar
|
|
if (it != it_end)
|
|
return context | Keys::ON_TAB | (externalTabMode()?0:Keys::ON_TITLEBAR);
|
|
|
|
return context;
|
|
}
|