fix tabbing

BUG: 290959
BUG: 265160
BUG: 229292
BUG: 238279
BUG: 290758
BUG: 222831
BUG: 278275
BUG: 245747
BUG: 230000

BUG: 253697
BUG: 230570
BUG: 265977
BUG: 225337
BUG: 225339

REVIEW: 103855
icc-effect-5.14.5
Thomas Lübking 2012-01-12 07:42:55 +01:00
parent 7d8ab4c593
commit d245035a17
65 changed files with 1252 additions and 2779 deletions

View File

@ -78,7 +78,7 @@ add_subdirectory( effects )
set(kwin_KDEINIT_SRCS
workspace.cpp
client.cpp
clientgroup.cpp
tabgroup.cpp
placement.cpp
atoms.cpp
utils.cpp

View File

@ -361,8 +361,8 @@ void Workspace::takeActivity(Client* c, int flags, bool handled)
flags &= ~ActivityFocus;
handled = false; // no point, can't get clicks
}
if (c->clientGroup() && c->clientGroup()->visible() != c)
c->clientGroup()->setVisible(c);
if (c->tabGroup() && c->tabGroup()->current() != c)
c->tabGroup()->setCurrent(c);
if (!c->isShown(true)) { // shouldn't happen, call activateClient() if needed
kWarning(1212) << "takeActivity: not shown" ;
return;

View File

@ -39,7 +39,6 @@ Bridge::Bridge(Client* cl)
return c->prototype( args2 ); \
}
BRIDGE_HELPER(bool, isActive, , , const)
BRIDGE_HELPER(bool, isCloseable, , , const)
BRIDGE_HELPER(bool, isMaximizable, , , const)
BRIDGE_HELPER(Bridge::MaximizeMode, maximizeMode, , , const)
@ -62,6 +61,11 @@ BRIDGE_HELPER(void, minimize, , ,)
BRIDGE_HELPER(void, showContextHelp, , ,)
BRIDGE_HELPER(void, setDesktop, int desktop, desktop,)
bool Bridge::isActive() const
{
return c->isActive() || (c->tabGroup() && c->tabGroup()->isActive());
}
void Bridge::setKeepAbove(bool set)
{
if (c->keepAbove() != set)
@ -93,7 +97,15 @@ bool Bridge::isSetShade() const
void Bridge::showWindowMenu(const QPoint &p)
{
c->workspace()->showWindowMenu(p, c);
c->workspace()->showWindowMenu(QRect(p,p), c);
}
void Bridge::showWindowMenu(const QPoint &p, long id)
{
Client *cc = clientForId(id);
if (!cc)
cc = c;
cc->workspace()->showWindowMenu(QRect(p,p), cc);
}
void Bridge::showWindowMenu(const QRect &p)
@ -207,90 +219,119 @@ QRect Bridge::transparentRect() const
return c->transparentRect().translated(-c->decorationRect().topLeft());
}
bool Bridge::isClientGroupActive()
{
if (c->clientGroup())
return c->clientGroup()->containsActiveClient();
return isActive();
}
//BEGIN TABBING
QList< ClientGroupItem > Bridge::clientGroupItems() const
Client *Bridge::clientForId(long id) const
{
if (c->clientGroup())
return c->clientGroup()->items();
QList< ClientGroupItem > items;
QIcon icon(c->icon());
icon.addPixmap(c->miniIcon());
items.append(ClientGroupItem(c->caption(), icon));
return items;
}
long Bridge::itemId(int index)
{
if (!c->clientGroup())
Client* client = reinterpret_cast<Client*>(id);
if (!c->workspace()->hasClient(client)) {
kWarning(1212) << "****** ARBITRARY CODE EXECUTION ATTEMPT DETECTED ******" << id;
return 0;
const ClientList list = c->clientGroup()->clients();
return reinterpret_cast<long>(list.at(index));
}
return client;
}
int Bridge::visibleClientGroupItem()
int Bridge::tabCount() const
{
if (c->clientGroup())
return c->clientGroup()->indexOfVisibleClient();
if (c->tabGroup())
return c->tabGroup()->count();
return 1;
}
long Bridge::tabId(int idx) const
{
if (c->tabGroup())
return tabIdOf(c->tabGroup()->clients().at(idx));
return tabIdOf(c);
}
QIcon Bridge::icon(int idx) const
{
if (c->tabGroup()) {
Client *tabC = c->tabGroup()->clients().at(idx);
QIcon icon(tabC->icon());
icon.addPixmap(tabC->miniIcon());
return icon;
}
return icon();
}
QString Bridge::caption(int idx) const
{
if (c->tabGroup())
return c->tabGroup()->clients().at(idx)->caption();
return c->caption();
}
long Bridge::currentTabId() const
{
if (c->tabGroup())
return tabIdOf(c->tabGroup()->current());
return 0;
}
void Bridge::setVisibleClientGroupItem(int index)
void Bridge::setCurrentTab(long id)
{
if (c->clientGroup())
c->clientGroup()->setVisible(index);
if (c->tabGroup())
c->tabGroup()->setCurrent(clientForId(id));
}
void Bridge::moveItemInClientGroup(int index, int before)
void Bridge::tab_A_before_B(long A, long B)
{
if (c->clientGroup())
c->clientGroup()->move(index, before);
}
void Bridge::moveItemToClientGroup(long itemId, int before)
{
Client* item = reinterpret_cast<Client*>(itemId);
if (!c->workspace()->hasClient(item)) {
kWarning(1212) << "****** ARBITRARY CODE EXECUTION ATTEMPT DETECTED ******";
if (!B) {
if (c->tabGroup()) {
if (Client *a = clientForId(A))
a->untab();
}
return;
}
if (item->clientGroup())
c->workspace()->moveItemToClientGroup(item->clientGroup(), item->clientGroup()->indexOfClient(item),
c->clientGroup(), before);
if (Client *a = clientForId(A))
if (Client *b = clientForId(B))
a->tabBefore(b, true);
}
void Bridge::removeFromClientGroup(int index, const QRect& newGeom)
void Bridge::tab_A_behind_B(long A, long B)
{
if (c->clientGroup())
c->clientGroup()->remove(index, newGeom);
}
void Bridge::closeClientGroupItem(int index)
{
if (!c->clientGroup())
if (!B) {
if (c->tabGroup()) {
if (Client *a = clientForId(A))
a->untab();
}
return;
const ClientList list = c->clientGroup()->clients();
if (index >= 0 || index <= list.count())
list.at(index)->closeWindow();
}
if (Client *a = clientForId(A))
if (Client *b = clientForId(B))
a->tabBehind(b, true);
}
void Bridge::closeAllInClientGroup()
void Bridge::untab(long id, const QRect& newGeom)
{
if (c->clientGroup())
c->clientGroup()->closeAll();
if (c->tabGroup())
if (Client* client = clientForId(id))
if (client->untab(newGeom)) {
if (options->focusPolicyIsReasonable())
c->workspace()->takeActivity(client, ActivityFocus | ActivityRaise, true);
c->workspace()->raiseClient(client);
}
}
void Bridge::displayClientMenu(int index, const QPoint& pos)
void Bridge::closeTab(long id)
{
if (c->clientGroup())
c->clientGroup()->displayClientMenu(index, pos);
if (Client* client = clientForId(id))
client->closeWindow();
}
void Bridge::closeTabGroup()
{
if (c->tabGroup())
c->tabGroup()->closeAll();
}
//END TABBING
KDecoration::WindowOperation Bridge::buttonToWindowOperation(Qt::MouseButtons button)
{
return c->mouseButtonToWindowOperation(button);

View File

@ -79,20 +79,26 @@ public:
virtual QRect transparentRect() const;
// Window tabbing
virtual bool isClientGroupActive();
virtual QList< ClientGroupItem > clientGroupItems() const;
virtual long itemId(int index);
virtual int visibleClientGroupItem();
virtual void setVisibleClientGroupItem(int index);
virtual void moveItemInClientGroup(int index, int before);
virtual void moveItemToClientGroup(long itemId, int before);
virtual void removeFromClientGroup(int index, const QRect& newGeom);
virtual void closeClientGroupItem(int index);
virtual void closeAllInClientGroup();
virtual void displayClientMenu(int index, const QPoint& pos);
virtual QString caption(int idx) const;
virtual void closeTab(long id);
virtual void closeTabGroup();
virtual long currentTabId() const;
virtual QIcon icon(int idx) const;
virtual void setCurrentTab(long id);
virtual void showWindowMenu(const QPoint &, long id);
virtual void tab_A_before_B(long A, long B);
virtual void tab_A_behind_B(long A, long B);
virtual int tabCount() const;
virtual long tabId(int idx) const;
virtual void untab(long id, const QRect& newGeom);
virtual WindowOperation buttonToWindowOperation(Qt::MouseButtons button);
private:
Client *clientForId(long id) const;
static inline long tabIdOf(Client *c) {
return reinterpret_cast<long>(c);
}
Client* c;
};

View File

@ -102,7 +102,7 @@ Client::Client(Workspace* ws)
, delayedMoveResizeTimer(NULL)
, in_group(NULL)
, window_group(None)
, client_group(NULL)
, tab_group(NULL)
, in_layer(UnknownLayer)
, ping_timer(NULL)
, process_killer(NULL)
@ -265,15 +265,14 @@ void Client::releaseWindow(bool on_shutdown)
XUnmapWindow(display(), frameId()); // Destroying decoration would cause ugly visual effect
destroyDecoration();
cleanGrouping();
if (clientGroup())
clientGroup()->remove(this, QRect(), true);
if (!on_shutdown) {
workspace()->removeClient(this, Allowed);
// Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7)
info->setDesktop(0);
desk = 0;
info->setState(0, info->state()); // Reset all state flags
}
} else
untab();
XDeleteProperty(display(), client, atoms->kde_net_wm_user_creation_time);
XDeleteProperty(display(), client, atoms->net_frame_extents);
XDeleteProperty(display(), client, atoms->kde_net_wm_frame_strut);
@ -326,8 +325,6 @@ void Client::destroyClient()
workspace()->clientHidden(this);
destroyDecoration();
cleanGrouping();
if (clientGroup())
clientGroup()->remove(this, QRect(), true);
workspace()->removeClient(this, Allowed);
client = None; // invalidate
XDestroyWindow(display(), wrapper);
@ -791,7 +788,7 @@ bool Client::noBorder() const
bool Client::userCanSetNoBorder() const
{
return !isFullScreen() && !isShade() && (clientGroup() == NULL || !(clientGroup()->items().count() > 1));
return !isFullScreen() && !isShade() && !tabGroup();
}
void Client::setNoBorder(bool set)
@ -993,8 +990,8 @@ void Client::minimize(bool avoid_animation)
emit clientMinimized(this, !avoid_animation);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit minimizedChanged();
}
@ -1019,8 +1016,8 @@ void Client::unminimize(bool avoid_animation)
emit clientUnminimized(this, !avoid_animation);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit minimizedChanged();
}
@ -1123,8 +1120,8 @@ void Client::setShade(ShadeMode mode)
updateWindowRules(Rules::Shade);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit shadeChanged();
}
@ -1156,7 +1153,7 @@ void Client::updateVisibility()
{
if (deleting)
return;
if (hidden && (clientGroup() == NULL || clientGroup()->visible() == this)) {
if (hidden && isCurrentTab()) {
info->setState(NET::Hidden, NET::Hidden);
setSkipTaskbar(true, false); // Also hide from taskbar
if (compositing() && options->hiddenPreviews == HiddenPreviewsAlways)
@ -1165,7 +1162,7 @@ void Client::updateVisibility()
internalHide(Allowed);
return;
}
if (clientGroup() == NULL || clientGroup()->visible() == this)
if (isCurrentTab())
setSkipTaskbar(original_skip_taskbar, false); // Reset from 'hidden'
if (minimized) {
info->setState(NET::Hidden, NET::Hidden);
@ -1584,8 +1581,8 @@ void Client::setDesktop(int desktop)
updateWindowRules(Rules::Desktop);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit desktopChanged();
}
@ -1647,8 +1644,8 @@ void Client::updateActivities(bool includeTransients)
// TODO: add activity rule
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
}
/**
@ -1689,8 +1686,8 @@ void Client::setOnAllDesktops(bool b)
setDesktop(workspace()->currentDesktop());
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
}
/**
@ -1853,9 +1850,7 @@ void Client::setCaption(const QString& _s, bool force)
// Keep the same suffix in iconic name if it's set
info->setVisibleIconName(QString(cap_iconic + cap_suffix).toUtf8());
if (isManaged() && decoration != NULL) {
if (client_group)
client_group->updateItems();
if (isManaged() && decoration) {
decoration->captionChange();
}
emit captionChanged();
@ -1894,21 +1889,62 @@ QString Client::caption(bool full) const
return full ? cap_normal + cap_suffix : cap_normal;
}
void Client::setClientGroup(ClientGroup* group)
bool Client::tabTo(Client *other, bool behind, bool activate)
{
client_group = group;
unsigned long data[1] = {(unsigned long)workspace()->indexOfClientGroup(group)};
XChangeProperty(display(), window(), atoms->kde_net_wm_tab_group, XA_CARDINAL, 32,
PropModeReplace, (unsigned char*)(data), 1);
emit clientGroupChanged();
}
Q_ASSERT(other && other != this);
bool Client::isVisibleInClientGroup() const
{
if (!client_group) {
if (tab_group && tab_group == other->tabGroup()) { // special case: move inside group
tab_group->move(this, other, behind);
return true;
}
return (client_group->visible() == this);
GeometryUpdatesBlocker blocker(this);
const bool wasBlocking = signalsBlocked();
blockSignals(true); // prevent client emitting "retabbed to nowhere" cause it's about to be entabbed the next moment
untab();
blockSignals(wasBlocking);
TabGroup *newGroup = other->tabGroup() ? other->tabGroup() : new TabGroup(other);
if (!newGroup->add(this, other, behind, activate)) {
if (newGroup->count() < 2) { // adding "c" to "to" failed for whatever reason
newGroup->remove(other);
delete newGroup;
}
return false;
}
return true;
}
bool Client::untab(const QRect &toGeometry)
{
TabGroup *group = tab_group;
if (group && group->remove(this, toGeometry)) { // remove sets the tabgroup to "0", therefore the pointer is cached
if (group->isEmpty()) {
delete group;
}
setClientShown(!(isMinimized() || isShade()));
return true;
}
return false;
}
void Client::setTabGroup(TabGroup *group)
{
tab_group = group;
if (group) {
unsigned long data = qHash(group); //->id();
XChangeProperty(display(), window(), atoms->kde_net_wm_tab_group, XA_CARDINAL, 32,
PropModeReplace, (unsigned char*)(&data), 1);
}
else
XDeleteProperty(display(), window(), atoms->kde_net_wm_tab_group);
emit tabGroupChanged();
}
bool Client::isCurrentTab() const
{
return !tab_group || tab_group->current() == this;
}
void Client::dontMoveResize()
@ -1923,26 +1959,20 @@ void Client::setClientShown(bool shown)
{
if (deleting)
return; // Don't change shown status if this client is being deleted
if (shown && hidden) {
if (shown != hidden)
return; // nothing to change
hidden = !shown;
if (options->inactiveTabsSkipTaskbar)
setSkipTaskbar(hidden, false); // TODO: Causes reshuffle of the taskbar
if (shown) {
map(Allowed);
hidden = false;
//updateVisibility();
//updateAllowedActions();
if (options->inactiveTabsSkipTaskbar)
setSkipTaskbar(false, false);
takeFocus(Allowed);
autoRaise();
workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
}
if (!shown && !hidden) {
} else {
unmap(Allowed);
hidden = true;
//updateVisibility();
//updateAllowedActions();
if (options->inactiveTabsSkipTaskbar)
setSkipTaskbar(true, false); // TODO: Causes reshuffle of the taskbar
// Don't move tabs to the end of the list when another tab get's activated
if (!clientGroup() || clientGroup()->visible() == this)
if (isCurrentTab())
workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast);
addWorkspaceRepaint(visibleRect());
}

View File

@ -41,7 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "kdecoration.h"
#include "rules.h"
#include "toplevel.h"
#include "clientgroup.h"
#include "tabgroup.h"
#ifdef HAVE_XSYNC
#include <X11/extensions/sync.h>
@ -221,12 +221,12 @@ class Client
/**
* The "Window Tabs" Group this Client belongs to.
**/
Q_PROPERTY(KWin::ClientGroup* clientGroup READ clientGroup NOTIFY clientGroupChanged)
Q_PROPERTY(KWin::TabGroup* tabGroup READ tabGroup NOTIFY tabGroupChanged)
/**
* Whether this Client is the currently visible Client in its Client Group (Window Tabs).
* For change connect to the visibleChanged signal on the Client's Group.
**/
Q_PROPERTY(bool visibleInClientGroup READ isVisibleInClientGroup)
Q_PROPERTY(bool isCurrentTab READ isCurrentTab)
/**
* Minimum size as specified in WM_NORMAL_HINTS
**/
@ -500,8 +500,14 @@ public:
bool hasStrut() const;
// Tabbing functions
ClientGroup* clientGroup() const; // Returns a pointer to client_group
void setClientGroup(ClientGroup* group);
TabGroup* tabGroup() const; // Returns a pointer to client_group
Q_INVOKABLE inline bool tabBefore(Client *other, bool activate) { return tabTo(other, false, activate); }
Q_INVOKABLE inline bool tabBehind(Client *other, bool activate) { return tabTo(other, true, activate); }
Q_INVOKABLE bool untab(const QRect &toGeometry = QRect());
/**
* Set tab group - this is to be invoked by TabGroup::add/remove(client) and NO ONE ELSE
*/
void setTabGroup(TabGroup* group);
/*
* If shown is true the client is mapped and raised, if false
* the client is unmapped and hidden, this function is called
@ -515,7 +521,7 @@ public:
* client, this function stops it.
*/
void dontMoveResize();
bool isVisibleInClientGroup() const;
bool isCurrentTab() const;
/**
* Whether or not the window has a strut that expands through the invisible area of
@ -628,6 +634,7 @@ private:
bool processDecorationButtonPress(int button, int state, int x, int y, int x_root, int y_root,
bool ignoreMenu = false);
Client* findAutogroupCandidate() const;
protected:
virtual void debug(QDebug& stream) const;
@ -670,10 +677,10 @@ signals:
void iconChanged();
void skipSwitcherChanged();
/**
* Emitted whenever the Client's ClientGroup changed. That is whenever the Client is moved to
* Emitted whenever the Client's TabGroup changed. That is whenever the Client is moved to
* another group, but not when a Client gets added or removed to the Client's ClientGroup.
**/
void clientGroupChanged();
void tabGroupChanged();
private:
void exportMappingState(int s); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1
@ -744,6 +751,8 @@ private:
void updateInputWindow();
bool tabTo(Client *other, bool behind, bool activate);
Window client;
Window wrapper;
KDecoration* decoration;
@ -845,7 +854,7 @@ private:
QString cap_normal, cap_iconic, cap_suffix;
Group* in_group;
Window window_group;
ClientGroup* client_group;
TabGroup* tab_group;
Layer in_layer;
QTimer* ping_timer;
QProcess* process_killer;
@ -994,9 +1003,9 @@ inline Group* Client::group()
return in_group;
}
inline ClientGroup* Client::clientGroup() const
inline TabGroup* Client::tabGroup() const
{
return client_group;
return tab_group;
}
inline bool Client::isMinimized() const
@ -1012,7 +1021,7 @@ inline bool Client::isActive() const
inline bool Client::isShown(bool shaded_is_shown) const
{
return !isMinimized() && (!isShade() || shaded_is_shown) && !hidden &&
(clientGroup() == NULL || clientGroup()->visible() == this);
(!tabGroup() || tabGroup()->current() == this);
}
inline bool Client::isHiddenInternal() const

View File

@ -1,357 +0,0 @@
/*******************************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include "clientgroup.h"
#include "client.h"
#include "effects.h"
namespace KWin
{
ClientGroup::ClientGroup(Client *c)
: clients_()
, items_()
, visible_(0)
, minSize_(0, 0)
, maxSize_(INT_MAX, INT_MAX)
{
clients_.append(c);
updateItems();
updateMinMaxSize();
Workspace::self()->addClientGroup(this);
c->setClientShown(true); // Ensure the client is visible
c->triggerDecorationRepaint(); // TODO: Required? Maybe for creating new group?
}
ClientGroup::~ClientGroup()
{
Workspace::self()->removeClientGroup(this);
}
void ClientGroup::add(Client* c, int before, bool becomeVisible)
{
if (contains(c) || !c->workspace()->decorationSupportsClientGrouping())
return;
// Remove the Client->ClientGroup reference if the client is already in another group so we
// don't change the geometry of other clients in their current group by accident. However
// don't REMOVE them from the actual group until we are certain that the client will be moved.
ClientGroup* oldGroup = NULL;
if (c->clientGroup()) {
oldGroup = c->clientGroup();
c->setClientGroup(NULL);
}
// If it's not possible to have the same states then ungroup them, TODO: Check all states
// We do this here as the ungroup code in updateStates() cannot be called until add() completes
ShadeMode oldShadeMode = c->shadeMode();
if (c->shadeMode() != clients_[visible_]->shadeMode())
c->setShade(clients_[visible_]->shadeMode());
if (c->shadeMode() != clients_[visible_]->shadeMode()) {
if (oldGroup) // Re-add to old group if required
c->setClientGroup(oldGroup);
// One need to trigger decoration repaint on the group to
// make sure hover animations are properly reset.
clients_[visible_]->triggerDecorationRepaint();
return;
}
QRect oldGeom = c->geometry();
if (c->geometry() != clients_[visible_]->geometry())
c->setGeometry(clients_[visible_]->geometry());
if (c->geometry() != clients_[visible_]->geometry()) {
if (c->shadeMode() != oldShadeMode)
c->setShade(oldShadeMode); // Restore old shade mode
if (oldGroup) // Re-add to old group if required
c->setClientGroup(oldGroup);
clients_[visible_]->triggerDecorationRepaint();
return;
}
if (c->desktop() != clients_[visible_]->desktop())
c->setDesktop(clients_[visible_]->desktop());
if (c->desktop() != clients_[visible_]->desktop()) {
if (c->geometry() != oldGeom)
c->setGeometry(oldGeom); // Restore old geometry
if (c->shadeMode() != oldShadeMode)
c->setShade(oldShadeMode); // Restore old shade mode
if (oldGroup) // Re-add to old group if required
c->setClientGroup(oldGroup);
clients_[visible_]->triggerDecorationRepaint();
return;
}
// Tabbed windows MUST have a decoration
if (c->noBorder())
c->setNoBorder(false);
if (clients_[visible_]->noBorder())
clients_[visible_]->setNoBorder(false);
// Re-add to old group if required for the effect hook
if (oldGroup)
c->setClientGroup(oldGroup);
// Notify effects of merge
if (effects != NULL)
static_cast<EffectsHandlerImpl*>(effects)->slotClientGroupItemAdded(
c->effectWindow(), clients_[visible_]->effectWindow());
// Actually remove from old group if required and update
if (c->clientGroup())
c->clientGroup()->remove(c);
c->setClientGroup(this); // Let the client know which group it belongs to
// Actually add to new group
if (before >= 0) {
if (visible_ >= before)
visible_++;
clients_.insert(before, c);
} else
clients_.append(c);
if (!becomeVisible) // Hide before adding
c->setClientShown(false);
updateItems();
updateMinMaxSize();
updateStates(clients_[visible_], c);
if (becomeVisible) // Set visible after settings geometry
setVisible(c);
// Activate the new visible window
clients_[visible_]->setActive(true);
//clients_[visible_]->takeFocus( Allowed );
//clients_[visible_]->workspace()->raiseClient( clients_[visible_] );
clients_[visible_]->triggerDecorationRepaint();
}
void ClientGroup::remove(int index, const QRect& newGeom, bool toNullGroup)
{
remove(clients_[index], newGeom, toNullGroup);
}
void ClientGroup::remove(Client* c, const QRect& newGeom, bool toNullGroup)
{
if (!c)
return;
if (clients_.count() < 2) {
c->setClientGroup(NULL);
Workspace::self()->removeClientGroup(this); // Remove immediately
delete this;
return;
}
ClientList::const_iterator i;
Client* newVisible = clients_[visible_];
if (newVisible == c)
newVisible = (visible_ != clients_.size() - 1) ? clients_[visible_ + 1] : clients_[visible_ - 1];
// Notify effects of removal
if (effects)
static_cast<EffectsHandlerImpl*>(effects)->slotClientGroupItemRemoved(
c->effectWindow(), newVisible->effectWindow());
setVisible(newVisible); // Display new window before removing old one
clients_.removeAll(c);
visible_ = indexOfClient(newVisible); // Index may have changed
updateItems();
updateMinMaxSize();
c->setClientGroup(toNullGroup ? NULL : new ClientGroup(c));
if (newGeom.isValid()) {
// HACK: if the group was maximized, one needs to make some checks on the future client maximize mode
// because the transition from maximized to MaximizeRestore is not handled properly in setGeometry when
// the new geometry size is unchanged.
// since newGeom has the same size as the old client geometry, one just needs to check the topLeft position of newGeom
// and compare that to the group maximize mode.
// when the new mode is predicted to be MaximizeRestore, one must set it manually, in order to avoid decoration artifacts
Client::MaximizeMode groupMaxMode(newVisible->maximizeMode());
if (((groupMaxMode & Client::MaximizeHorizontal) && newGeom.left() != newVisible->geometry().left()) ||
((groupMaxMode & Client::MaximizeVertical) && newGeom.top() != newVisible->geometry().top()))
c->maximize(Client::MaximizeRestore);
c->setGeometry(newGeom);
}
newVisible->triggerDecorationRepaint();
}
void ClientGroup::removeAll()
{
while (clients_.count() > 1)
remove(clients_.at(1));
}
void ClientGroup::closeAll()
{
Client* front;
ClientList list(clients_);
while (!list.isEmpty()) {
front = list.front();
list.pop_front();
if (front != clients_[visible_])
front->closeWindow();
}
clients_[visible_]->closeWindow();
}
void ClientGroup::move(int index, int before)
{
move(clients_[index], (before >= 0 && before < clients_.size()) ? clients_[before] : NULL);
}
void ClientGroup::move(Client* c, Client* before)
{
if (c == before) // Impossible to do
return;
Client* wasVisible = clients_[visible_];
clients_.removeAll(c);
clients_.insert(before ? indexOfClient(before) : clients_.size(), c);
visible_ = indexOfClient(wasVisible);
updateItems();
clients_[visible_]->triggerDecorationRepaint();
}
void ClientGroup::displayClientMenu(int index, const QPoint& pos)
{
if (index == -1)
index = visible_;
displayClientMenu(clients_[index], pos);
}
void ClientGroup::displayClientMenu(Client* c, const QPoint& pos)
{
c->workspace()->showWindowMenu(pos, c);
}
bool ClientGroup::containsActiveClient()
{
return contains(Workspace::self()->activeClient());
}
void ClientGroup::setVisible(int index)
{
setVisible(clients_[index]);
}
void ClientGroup::setVisible(Client* c)
{
if (c == clients_[visible_] || !contains(c))
return;
// Notify effects of switch
if (effects != NULL)
static_cast<EffectsHandlerImpl*>(effects)->slotClientGroupItemSwitched(
clients_[visible_]->effectWindow(), c->effectWindow());
visible_ = indexOfClient(c);
c->setClientShown(true);
for (ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i)
if ((*i) != c)
(*i)->setClientShown(false);
emit visibleChanged();
}
void ClientGroup::updateStates(Client* main, Client* only)
{
ClientList toBeRemoved;
for (ClientList::const_iterator i = clients_.constBegin(), end = clients_.constEnd(); i != end; ++i) {
Client *c = (*i);
if (c != main && (!only || c == only)) {
if (c->isMinimized() != main->isMinimized()) {
if (main->isMinimized())
c->minimize(true);
else
c->unminimize(true);
}
if (c->isShade() != main->isShade())
c->setShade(main->isShade() ? ShadeNormal : ShadeNone);
if (c->geometry() != main->geometry())
c->setGeometry(main->geometry());
if (c->desktop() != main->desktop())
c->setDesktop(main->desktop());
if (c->isOnAllDesktops() != main->isOnAllDesktops())
c->setOnAllDesktops(main->isOnAllDesktops());
if (c->activities() != main->activities())
c->setOnActivities(main->activities());
if (c->keepAbove() != main->keepAbove())
c->setKeepAbove(main->keepAbove());
if (c->keepBelow() != main->keepBelow())
c->setKeepBelow(main->keepBelow());
// If it's not possible to have the same states then ungroup them, TODO: Check all states
if (c->geometry() != main->geometry() || c->desktop() != main->desktop())
toBeRemoved << c;
}
}
for (ClientList::const_iterator i = toBeRemoved.constBegin(), end = toBeRemoved.constEnd(); i != end; ++i)
remove(*i);
}
void ClientGroup::updateItems()
{
items_.clear();
for (ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i) {
QIcon icon((*i)->icon());
icon.addPixmap((*i)->miniIcon());
items_.append(ClientGroupItem((*i)->caption(), icon));
}
}
void ClientGroup::updateMinMaxSize()
{
// Determine entire group's minimum and maximum sizes
minSize_ = QSize(0, 0);
maxSize_ = QSize(INT_MAX, INT_MAX);
for (ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i) {
if ((*i)->minSize().width() > minSize_.width())
minSize_.setWidth((*i)->minSize().width());
if ((*i)->minSize().height() > minSize_.height())
minSize_.setHeight((*i)->minSize().height());
if ((*i)->maxSize().width() < maxSize_.width())
maxSize_.setWidth((*i)->maxSize().width());
if ((*i)->maxSize().height() < maxSize_.height())
maxSize_.setHeight((*i)->maxSize().height());
}
if (minSize_.width() > maxSize_.width() ||
minSize_.height() > maxSize_.height()) {
//kWarning(1212) << "ClientGroup's min size is greater than its max size. Setting max to min.";
maxSize_ = minSize_;
}
// TODO: only emit if really changed
emit minSizeChanged();
emit maxSizeChanged();
// Ensure all windows are within these sizes
const QSize size = clients_[visible_]->clientSize();
QSize newSize(
qBound(minSize_.width(), size.width(), maxSize_.width()),
qBound(minSize_.height(), size.height(), maxSize_.height()));
if (newSize != size)
for (ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i)
// TODO: Doesn't affect shaded windows?
// There seems to be a race condition when using plainResize() which causes the window
// to sometimes be located at new window's location instead of the visible window's location
// when a window with a large min size is added to a group with a small window size.
(*i)->setGeometry(QRect(clients_[visible_]->pos(), (*i)->sizeForClientSize(newSize)));
}
}

View File

@ -1,266 +0,0 @@
/*******************************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#ifndef KWIN_CLIENTGROUP_H
#define KWIN_CLIENTGROUP_H
#include <QObject>
#include "kdecoration.h"
#include "utils.h"
namespace KWin
{
class Client;
/**
* This class represents a group of clients for use in window tabbing. All
* clients in the group share the same geometry and state information; I.e if
* one client changes then all others should also be changed.
*
* Workspace::clientGroups is a list of all currently-existing client groups.
*
* A group MUST contain at least one client and MUST NOT contain multiple
* copies of the same client. A client MUST NOT be in two groups at the same
* time. All decorated clients SHOULD be in a group, even if it's a group of
* one client.
*
* rohanp: Had to convert this object to a QObject to make it easier for adding
* scripting interface to ClientGroup.
*
* If a group contains multiple clients then only one will ever be mapped at
* any given time.
*/
class ClientGroup : public QObject
{
Q_OBJECT
/**
* Currently visible client in this group.
**/
Q_PROPERTY(KWin::Client* visible READ visible WRITE setVisible NOTIFY visibleChanged)
/**
* Combined minimum size of all clients in the group.
**/
Q_PROPERTY(QSize minSize READ minSize NOTIFY minSizeChanged)
/**
* Combined maximum size of all clients in the group.
**/
Q_PROPERTY(QSize maxSize READ maxSize NOTIFY maxSizeChanged)
/**
* The index of the visible Client in this group.
**/
Q_PROPERTY(int visibleClientIndex READ indexOfVisibleClient NOTIFY visibleChanged)
/**
* The Clients in this group.
**/
Q_PROPERTY(QList<KWin::Client*> clients READ clients)
public:
/**
* Creates a new group containing \p c.
*/
ClientGroup(Client* c);
~ClientGroup();
public Q_SLOTS:
/**
* Adds \p c to the group before \p before in the list. If \p becomeVisible is \i true then
* the added client will become also the visible client.
*/
void add(KWin::Client* c, int before = -1, bool becomeVisible = false);
/**
* Remove the client at index \p index from the group. If \p newGeom is set then the client
* will move and resize to the specified geometry, otherwise it will stay where the group
* is located. If \p toNullGroup is not true then the client will be added to a new group
* of its own.
*/
void remove(int index, const QRect& newGeom = QRect(), bool toNullGroup = false);
/**
* Remove \p c from the group. If \p newGeom is set then the client will move and resize to
* the specified geometry, otherwise it will stay where the group is located. If
* \p toNullGroup is not true then the client will be added to a new group of its own.
*/
void remove(KWin::Client* c, const QRect& newGeom = QRect(), bool toNullGroup = false);
/**
* Remove all clients from this group. Results in all clients except the first being moved
to a group of their own.
*/
void removeAll();
/**
* Close all clients in this group.
*/
void closeAll();
/**
* Move the client at index \p index to the position before the client at index \p before
* in the list.
*/
void move(int index, int before);
/**
* Move \p c to the position before \p before in the list.
*/
void move(KWin::Client* c, KWin::Client* before);
/**
* Display the right-click client menu belonging to the client at index \p index at the
* global coordinates specified by \p pos.
*/
void displayClientMenu(int index, const QPoint& pos);
/**
* Display the right-click client menu belonging to \p c at the global coordinates
* specified by \p pos.
*/
void displayClientMenu(KWin::Client* c, const QPoint& pos);
public:
/**
* Returns the list index of \p c.
*/
Q_SCRIPTABLE int indexOfClient(KWin::Client* c);
/**
* Returns the list index of the currently visible client in the group.
*/
int indexOfVisibleClient();
/**
* Returns whether or not this group contains \p c.
*/
Q_SCRIPTABLE bool contains(KWin::Client* c);
/**
* Returns whether or not this group contains the active client.
*/
bool containsActiveClient();
/**
* Returns the list of all the clients contained in this group in their current order.
*/
const ClientList &clients() const;
/**
* Returns a list of the captions and icons of all the clients contained in this group
* in their current order.
*/
QList< ClientGroupItem > items() const;
/**
* Returns the currently visible client.
*/
Client* visible();
/**
* Makes the client at index \p index the visible one in the group.
*/
void setVisible(int index);
/**
* Makes \p c the visible client in the group.
*/
void setVisible(Client* c);
/**
* Returns combined minimum size of all clients in the group.
*/
QSize minSize() const;
/**
* Returns combined maximum size of all clients in the group.
*/
QSize maxSize() const;
/**
* Ensures that all the clients in the group have identical geometries and states using
* \p main as the primary client to copy the settings off. If \p only is set then only
* that client is updated to match \p main.
*/
void updateStates(Client* main, Client* only = NULL);
Q_SIGNALS:
/**
* Emitted when the visible Client in this group changes.
**/
void visibleChanged();
/**
* Emitted when the group's minimum size changes.
**/
void minSizeChanged();
/**
* Emitted when the group's maximum size changes.
**/
void maxSizeChanged();
private:
/**
* Regenerate the list of client captions and icons.
*/
void updateItems();
/**
* Determine the combined minimum and maximum sizes of all clients in the group.
*/
void updateMinMaxSize();
ClientList clients_;
QList< ClientGroupItem > items_;
int visible_;
QSize minSize_;
QSize maxSize_;
friend class Client;
};
inline int ClientGroup::indexOfClient(Client* c)
{
return clients_.indexOf(c);
}
inline int ClientGroup::indexOfVisibleClient()
{
return visible_;
}
inline bool ClientGroup::contains(Client* c)
{
return clients_.contains(c);
}
inline const ClientList &ClientGroup::clients() const
{
return clients_;
}
inline QList< ClientGroupItem > ClientGroup::items() const
{
return items_;
}
inline Client* ClientGroup::visible()
{
return clients_.at(visible_);
}
inline QSize ClientGroup::minSize() const
{
return minSize_;
}
inline QSize ClientGroup::maxSize() const
{
return maxSize_;
}
}
Q_DECLARE_METATYPE(KWin::ClientGroup*)
Q_DECLARE_METATYPE(QList<KWin::ClientGroup*>)
#endif

View File

@ -3,4 +3,3 @@ add_subdirectory( oxygen )
add_subdirectory( plastik )
add_subdirectory( b2 )
add_subdirectory( laptop )
add_subdirectory( tabstrip )

View File

@ -56,7 +56,7 @@ void AuroraeFactory::init()
m_theme->loadTheme(themeName, config);
m_theme->setBorderSize((KDecorationDefines::BorderSize)themeGroup.readEntry<int>("BorderSize", KDecorationDefines::BorderNormal));
m_theme->setButtonSize((KDecorationDefines::BorderSize)themeGroup.readEntry<int>("ButtonSize", KDecorationDefines::BorderNormal));
m_theme->setTabDragMimeType(clientGroupItemDragMimeType());
m_theme->setTabDragMimeType(tabDragMimeType());
// setup the QML engine
foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) {
m_engine->addImportPath(importPath);
@ -117,7 +117,7 @@ bool AuroraeFactory::supports(Ability ability) const
case AbilityButtonHelp:
case AbilityProvidesShadow:
return true; // TODO: correct value from theme
case AbilityClientGrouping:
case AbilityTabbing:
return false;
case AbilityUsesBlurBehind:
return true;

View File

@ -511,7 +511,7 @@ namespace Oxygen
const QRect titleRect( this->titleRect() );
// get tabs
const int items( clientGroupItems().count() );
const int items( tabCount() );
// make sure item data have the correct number of items
while( _itemData.count() < items ) _itemData.push_back( ClientGroupItemData() );
@ -574,7 +574,7 @@ namespace Oxygen
}
// button activity
_itemData.updateButtonActivity( visibleClientGroupItem() );
_itemData.updateButtonActivity( currentTabId() );
// reset buttons location
_itemData.updateButtons( alsoUpdate );
@ -685,7 +685,7 @@ namespace Oxygen
const int titleHeight( layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleEdgeBottom ) + layoutMetric( LM_TitleHeight ) );
// make titlebar background darker for tabbed, non-outline window
if( ( clientGroupItems().count() >= 2 || _itemData.isAnimated() ) && !(configuration().drawTitleOutline() && isActive() ) )
if( ( tabCount() >= 2 || _itemData.isAnimated() ) && !(configuration().drawTitleOutline() && isActive() ) )
{
const QPoint topLeft( r.topLeft()-position );
@ -917,7 +917,6 @@ namespace Oxygen
renderTitleText( painter, rect, color, contrast );
} else if( !caption().isEmpty() ) {
renderTitleText( painter, rect, caption(), color, contrast );
}
@ -942,7 +941,6 @@ namespace Oxygen
if( isMaximized() ) painter->translate( 0, -2 );
} else if( !caption().isEmpty() ) {
renderTitleText( painter, rect, caption(), color, contrast );
}
@ -1019,11 +1017,10 @@ namespace Oxygen
{ textRect.adjust( 0, 0, - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); }
// check if current item is active
const bool active( index == visibleClientGroupItem() );
const bool active( tabId(index) == currentTabId() );
// get current item caption and update text rect
const QList< ClientGroupItem >& items( clientGroupItems() );
const QString caption( itemCount == 1 ? this->caption() : items[index].title() );
const QString caption( itemCount == 1 ? this->caption() : this->caption(index) );
if( !configuration().centerTitleOnFullWidth() )
{ boundRectTo( textRect, titleRect() ); }
@ -1110,7 +1107,7 @@ namespace Oxygen
if(
( index == itemCount-1 && buttonsRightWidth() > 0 ) ||
( index+1 < itemCount && ( _itemData.isTarget( index+1 ) ||
!( index+1 == visibleClientGroupItem() && _itemData[index+1]._boundingRect.isValid() ) ) ) )
!( tabId(index+1) == currentTabId() && _itemData[index+1]._boundingRect.isValid() ) ) ) )
{
const QRect local( item._boundingRect.topRight()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) );
@ -1553,12 +1550,12 @@ namespace Oxygen
}
// make sure ItemData and clientGroupItems are synchronized
// make sure ItemData and tabList are synchronized
/*
this needs to be done before calling RenderWindowBorder
since some painting in there depend on the clientGroups state
*/
if( _itemData.isDirty() || _itemData.count() != clientGroupItems().count() )
if( _itemData.isDirty() || _itemData.count() != tabCount() )
{ updateItemBoundingRects( false ); }
// window background
@ -1606,12 +1603,12 @@ namespace Oxygen
{
const QPoint point = event->pos();
if( itemClicked( point ) < 0 ) return false;
if( tabIndexAt( point ) < 0 ) return false;
_dragPoint = point;
_mouseButton = event->button();
bool accepted( false );
if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
if( buttonToWindowOperation( _mouseButton ) == TabDragOp )
{
accepted = true;
@ -1619,9 +1616,10 @@ namespace Oxygen
} else if( buttonToWindowOperation( _mouseButton ) == OperationsOp ) {
QPoint point = event->pos();
int itemClicked( this->itemClicked( point ) );
const int clickedIndex( tabIndexAt( point ) );
_mouseButton = Qt::NoButton;
displayClientMenu( itemClicked, widget()->mapToGlobal( event->pos() ) );
if ( tabIndexAt( point ) > -1)
showWindowMenu( widget()->mapToGlobal( event->pos() ), tabId(clickedIndex) );
accepted = true; // displayClientMenu can possibly destroy the deco...
}
@ -1639,11 +1637,11 @@ namespace Oxygen
const QPoint point = event->pos();
const int visibleItem = visibleClientGroupItem();
const int itemClicked( this->itemClicked( point ) );
if( itemClicked >= 0 && visibleItem != itemClicked )
const long visibleItem = currentTabId();
const int clickedIndex( tabIndexAt( point ) );
if( clickedIndex >= 0 && visibleItem != tabId(clickedIndex) )
{
setVisibleClientGroupItem( itemClicked );
setCurrentTab( tabId(clickedIndex) );
setForceActive( true );
accepted = true;
}
@ -1664,23 +1662,23 @@ namespace Oxygen
{ return false; }
bool accepted( false );
if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
if( buttonToWindowOperation( _mouseButton ) == TabDragOp )
{
const QPoint point = event->pos();
const int itemClicked( this->itemClicked( point ) );
if( itemClicked < 0 ) return false;
const int clickedIndex( tabIndexAt( point ) );
if( clickedIndex < 0 ) return false;
_titleAnimationData->reset();
QDrag *drag = new QDrag( widget() );
QMimeData *groupData = new QMimeData();
groupData->setData( clientGroupItemDragMimeType(), QString().setNum( itemId( itemClicked )).toAscii() );
groupData->setData( tabDragMimeType(), QString().setNum( tabId(clickedIndex) ).toAscii() );
drag->setMimeData( groupData );
_sourceItem = this->itemClicked( _dragPoint );
_sourceItem = tabIndexAt( _dragPoint );
// get tab geometry
QRect geometry( _itemData[itemClicked]._boundingRect );
QRect geometry( _itemData[clickedIndex]._boundingRect );
// remove space used for buttons
if( _itemData.count() > 1 )
@ -1697,7 +1695,7 @@ namespace Oxygen
}
drag->setPixmap( itemDragPixmap( itemClicked, geometry ) );
drag->setPixmap( itemDragPixmap( clickedIndex, geometry ) );
// note: the pixmap is moved just above the pointer on purpose
// because overlapping pixmap and pointer slows down the pixmap a lot.
@ -1717,7 +1715,7 @@ namespace Oxygen
if( drag->target() == 0 && _itemData.count() > 1 )
{
_itemData.setDirty( true );
removeFromClientGroup( _sourceItem,
untab( tabId(_sourceItem),
widget()->frameGeometry().adjusted(
layoutMetric( LM_OuterPaddingLeft ),
layoutMetric( LM_OuterPaddingTop ),
@ -1743,7 +1741,7 @@ namespace Oxygen
{
// check if drag enter is allowed
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) || hideTitleBar() ) return false;
if( !event->mimeData()->hasFormat( tabDragMimeType() ) || hideTitleBar() ) return false;
//
event->acceptProposedAction();
@ -1751,13 +1749,13 @@ namespace Oxygen
{
const QPoint position( event->pos() );
_itemData.animate( AnimationEnter, itemClicked( position, true ) );
_itemData.animate( AnimationEnter, tabIndexAt( position, true ) );
} else if( _itemData.count() > 1 ) {
const QPoint position( event->pos() );
const int itemClicked( this->itemClicked( position, false ) );
_itemData.animate( AnimationEnter|AnimationSameTarget, itemClicked );
const int clickedIndex( tabIndexAt( position, false ) );
_itemData.animate( AnimationEnter|AnimationSameTarget, clickedIndex );
}
@ -1791,20 +1789,20 @@ namespace Oxygen
{
// check format
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) ) return false;
if( !event->mimeData()->hasFormat( tabDragMimeType() ) ) return false;
if( event->source() != widget() )
{
const QPoint position( event->pos() );
_itemData.animate( AnimationMove, itemClicked( position, true ) );
_itemData.animate( AnimationMove, tabIndexAt( position, true ) );
} else if( _itemData.count() > 1 ) {
if( _dragStartTimer.isActive() ) _dragStartTimer.stop();
const QPoint position( event->pos() );
const int itemClicked( this->itemClicked( position, false ) );
_itemData.animate( AnimationMove|AnimationSameTarget, itemClicked );
const int clickedIndex( tabIndexAt( position, false ) );
_itemData.animate( AnimationMove|AnimationSameTarget, clickedIndex );
}
@ -1820,35 +1818,25 @@ namespace Oxygen
_itemData.animate( AnimationNone );
const QMimeData *groupData = event->mimeData();
if( !groupData->hasFormat( clientGroupItemDragMimeType() ) ) return false;
if( !groupData->hasFormat( tabDragMimeType() ) ) return false;
_itemData.setDirty( true );
if( widget() != event->source() )
setForceActive( true );
const long source = QString( groupData->data( tabDragMimeType() ) ).toLong();
int clickedIndex( tabIndexAt( point, true ) );
if (clickedIndex < 0)
tab_A_behind_B(source, tabId(_itemData.count() - 1));
else if (clickedIndex)
tab_A_behind_B(source, tabId(clickedIndex));
else
tab_A_before_B(source, tabId(clickedIndex));
if( widget() == event->source() )
{
const int from = this->itemClicked( _dragPoint );
int itemClicked( this->itemClicked( point, false ) );
if( itemClicked > from )
{
itemClicked++;
if( itemClicked >= clientGroupItems().count() )
{ itemClicked = -1; }
}
_itemData.setDirty( true );
moveItemInClientGroup( from, itemClicked );
updateTitleRect();
} else {
setForceActive( true );
const int itemClicked( this->itemClicked( point, true ) );
const long source = QString( groupData->data( clientGroupItemDragMimeType() ) ).toLong();
_itemData.setDirty( true );
moveItemToClientGroup( source, itemClicked );
}
_titleAnimationData->reset();
return true;
@ -1881,7 +1869,7 @@ namespace Oxygen
if( button == _itemData[i]._closeButton.data() )
{
_itemData.setDirty( true );
closeClientGroupItem( i );
closeTab( tabId(i) );
return true;
}
}
@ -1892,7 +1880,7 @@ namespace Oxygen
//________________________________________________________________
QPixmap Client::itemDragPixmap( int index, const QRect& geometry )
{
const bool itemValid( index >= 0 && index < clientGroupItems().count() );
const bool itemValid( index >= 0 && index < tabCount() );
QPixmap pixmap( geometry.size() );
QPainter painter( &pixmap );
@ -1904,7 +1892,7 @@ namespace Oxygen
renderWindowBackground( &painter, geometry, widget(), widget()->palette() );
// darken background if item is inactive
const bool itemActive = !( itemValid && index != visibleClientGroupItem() );
const bool itemActive = !( itemValid && tabId(index) != currentTabId() );
if( !itemActive )
{
@ -1924,7 +1912,8 @@ namespace Oxygen
if( itemValid )
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
const QString caption( itemValid ? clientGroupItems()[index].title() : this->caption() );
const QString caption( itemValid ? this->caption(index) : this->caption() );
renderTitleText(
&painter, textRect, caption,
titlebarTextColor( widget()->palette(), isActive() && itemActive ),

View File

@ -89,7 +89,7 @@ namespace Oxygen
//! true when decoration is forced active
bool isForcedActive( void ) const
{ return _forceActive && clientGroupItems().count() > 1; }
{ return _forceActive && tabCount() > 1; }
//! true when separator is to be drawn
bool drawSeparator( void ) const
@ -116,7 +116,7 @@ namespace Oxygen
return
configuration().hideTitleBar() &&
!isShade() &&
clientGroupItems().count() == 1;
tabCount() == 1;
}
//@}
@ -333,7 +333,7 @@ namespace Oxygen
virtual bool closeItem( const Button* );
//! index of item matching point
int itemClicked( const QPoint& position, bool between = false ) const
int tabIndexAt( const QPoint& position, bool between = false ) const
{ return _itemData.itemAt( position , between ); }
//! return pixmap corresponding to a given tab, for dragging
@ -358,7 +358,7 @@ namespace Oxygen
bool hasTitleOutline( void ) const
{
return
clientGroupItems().count() >= 2 ||
tabCount() >= 2 ||
_itemData.isAnimated() ||
( (isActive()||glowIsAnimated()) && configuration().drawTitleOutline() );
}

View File

@ -214,7 +214,7 @@ namespace Oxygen
if( count() <= 2 )
{
item._endBoundingRect = _client.defaultTitleRect( index == _client.visibleClientGroupItem() );
item._endBoundingRect = _client.defaultTitleRect( _client.tabId(index) == _client.currentTabId() );
} else {
@ -260,7 +260,7 @@ namespace Oxygen
}
//____________________________________________________________________________
void ClientGroupItemDataList::updateButtonActivity( int visibleItem ) const
void ClientGroupItemDataList::updateButtonActivity( long visibleItem ) const
{
for( int index = 0; index < count(); index++ )
@ -268,7 +268,7 @@ namespace Oxygen
const ClientGroupItemData& item( at(index) );
if( item._closeButton )
{ item._closeButton.data()->setForceInactive( index != visibleItem ); }
{ item._closeButton.data()->setForceInactive( _client.tabId(index) != visibleItem ); }
}

View File

@ -151,7 +151,7 @@ namespace Oxygen
{ return animation().data()->isRunning(); }
//! update button activity
void updateButtonActivity( int visibleItem ) const;
void updateButtonActivity( long visibleItem ) const;
//! update buttons
void updateButtons( bool alsoUpdate ) const;

View File

@ -171,7 +171,7 @@ namespace Oxygen
return true;
// tabs
case AbilityClientGrouping:
case AbilityTabbing:
return true;
// no colors supported at this time

View File

@ -1,15 +0,0 @@
add_subdirectory( config )
set(kwin_tabstrip_SRCS
tabstripbutton.cpp
tabstripdecoration.cpp
tabstripfactory.cpp
)
kde4_add_plugin(kwin3_tabstrip ${kwin_tabstrip_SRCS})
target_link_libraries(kwin3_tabstrip kdecorations)
install(TARGETS kwin3_tabstrip DESTINATION ${PLUGIN_INSTALL_DIR})
install( FILES tabstrip.desktop DESTINATION ${DATA_INSTALL_DIR}/kwin )

View File

@ -1,10 +0,0 @@
set(kwin_tabstrip_config_SRCS tabstripconfig.cpp )
kde4_add_ui_files(kwin_tabstrip_config_SRCS tabstripconfig.ui )
kde4_add_plugin(kwin_tabstrip_config ${kwin_tabstrip_config_SRCS})
target_link_libraries(kwin_tabstrip_config ${KDE4_KDEUI_LIBS} ${QT_QTGUI_LIBRARY})
install(TARGETS kwin_tabstrip_config DESTINATION ${PLUGIN_INSTALL_DIR} )

View File

@ -1,96 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "tabstripconfig.h"
#include "tabstripconfig.moc"
#include <KConfig>
#include <KGlobal>
#include <KLocale>
#include <QString>
#include <QRadioButton>
extern "C"
{
KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent )
{
return new TabstripConfig( conf, parent );
}
}
TabstripConfig::TabstripConfig( KConfig *c, QWidget *parent )
{
Q_UNUSED(c);
KGlobal::locale()->insertCatalog( "kwin_tabstrip_config" );
config = new KConfig( "tabstriprc" );
KConfigGroup cg( config, "General" );
ui = new TabstripConfigDialog( parent );
connect( ui->left, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui->center, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui->right, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui->showIcon, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui->outlineColor, SIGNAL(clicked()), SIGNAL(changed()) );
load( cg );
ui->show();
}
TabstripConfig::~TabstripConfig()
{
delete ui;
delete config;
}
void TabstripConfig::load( KConfigGroup &c )
{
c = KConfigGroup( config, "General" );
QString align = c.readEntry( "TitleAlignment", "Center" );
ui->left->setChecked( align == "Left" );
ui->center->setChecked( align == "Center" );
ui->right->setChecked( align == "Right" );
ui->showIcon->setChecked( c.readEntry( "ShowIcon", true ) );
ui->outlineColor->setColor( c.readEntry( "OutlineColor", QColor( Qt::white ) ) );
}
void TabstripConfig::save( KConfigGroup &c )
{
c = KConfigGroup( config, "General" );
if( ui->left->isChecked() )
c.writeEntry( "TitleAlignment", "Left" );
else if( ui->center->isChecked() )
c.writeEntry( "TitleAlignment", "Center" );
else
c.writeEntry( "TitleAlignment", "Right" );
c.writeEntry( "ShowIcon", ui->showIcon->isChecked() );
c.writeEntry( "OutlineColor", ui->outlineColor->color() );
config->sync();
}
void TabstripConfig::defaults()
{
ui->left->setChecked( false );
ui->center->setChecked( true );
ui->right->setChecked( false );
ui->showIcon->setChecked( true );
ui->outlineColor->setColor( Qt::white );
emit changed();
}

View File

@ -1,56 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef TABSTRIPCONFIG_H
#define TABSTRIPCONFIG_H
#include "ui_tabstripconfig.h"
#include <KConfig>
#include <QObject>
class TabstripConfigDialog : public QWidget, public Ui::TabstripConfigUi
{
public:
TabstripConfigDialog( QWidget *parent ) : QWidget( parent )
{
setupUi( this );
}
};
class TabstripConfig : public QObject
{
Q_OBJECT
public:
TabstripConfig( KConfig *c, QWidget *parent );
~TabstripConfig();
signals:
void changed();
public slots:
void load( KConfigGroup &c );
void save( KConfigGroup &c );
void defaults();
private:
KConfig *config;
TabstripConfigDialog *ui;
};
#endif

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabstripConfigUi</class>
<widget class="QWidget" name="TabstripConfigUi">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>276</width>
<height>98</height>
</rect>
</property>
<property name="windowTitle">
<string>Tabstrip</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Title &amp;Alignment</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="left">
<property name="text">
<string>&amp;Left</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="center">
<property name="text">
<string>&amp;Center</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="right">
<property name="text">
<string>&amp;Right</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showIcon">
<property name="text">
<string>Display window icons</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="outlineColorLayout">
<item>
<widget class="QLabel" name="outline">
<property name="text">
<string>O&amp;utline Color:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>outlineColor</cstring>
</property>
</widget>
</item>
<item>
<widget class="KColorButton" name="outlineColor"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,63 +0,0 @@
[Desktop Entry]
Name=Tabstrip
Name[ar]=شريط الألسنة
Name[ast]=Tira de llingüetes
Name[bs]=Traka jezičaka
Name[ca]=Barra de pestanyes
Name[ca@valencia]=Barra de pestanyes
Name[cs]=Pruh karet
Name[csb]=Tabstrip
Name[da]=Tabstrip
Name[de]=Tabstrip
Name[el]=Tabstrip
Name[en_GB]=Tabstrip
Name[es]=Tira de pestañas
Name[et]=Sakiriba
Name[eu]=Tabstrip
Name[fi]=Välilehtilista
Name[fr]=Tabstrip
Name[fy]=Ljepperstrip
Name[ga]=Tabstrip
Name[he]=רצועת לשוניות
Name[hi]=टैबपट्टी
Name[hr]=Kartični prikaz
Name[hu]=Lapsáv
Name[ia]=Banda de scheda (Tabstrip)
Name[id]=Tabstrip
Name[is]=Tabstrip
Name[it]=Linguette
Name[ja]=Tabstrip
Name[kk]=Қойынды жолағы
Name[km]=Tabstrip
Name[kn]=Tabstrip
Name[ko]=탭 표시줄
Name[lt]=Tabstrip
Name[lv]=Tabstrip
Name[ml]=ടാബ്സ്ട്രിപ്പ്
Name[nb]=Fanestripe
Name[nds]=Tabstrip
Name[nl]=Strip met tabbladen
Name[nn]=Fanestripe
Name[pa]=ਟੈਬ-ਪੱਟੀ
Name[pl]=Tabstrip
Name[pt]=Barra de Páginas
Name[pt_BR]=Barra de abas
Name[ro]=Bandă cu file
Name[ru]=Tabstrip
Name[si]=ටැබ් ඉර
Name[sk]=Tabstrip
Name[sl]=Tabstrip
Name[sr]=Трака језичака
Name[sr@ijekavian]=Трака језичака
Name[sr@ijekavianlatin]=Traka jezičaka
Name[sr@latin]=Traka jezičaka
Name[sv]=Flikrad
Name[th]=รูปแบบ Tabstrip
Name[tr]=Tabstrip
Name[ug]=بەتكۈچ بالداق
Name[uk]=Смужка вкладок
Name[wa]=Binde di linwetes
Name[x-test]=xxTabstripxx
Name[zh_CN]=Tabstrip
Name[zh_TW]=Tabstrip
X-KDE-Library=kwin3_tabstrip

View File

@ -1,317 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include <kcommondecoration.h>
#include "tabstripbutton.h"
#include "tabstripdecoration.h"
#include "tabstripfactory.h"
#include <KLocale>
#include <QPainter>
#include <QPalette>
#include <QPixmap>
#include <QRect>
TabstripButton::TabstripButton( ButtonType type, TabstripDecoration *parent, QString tip )
: KCommonDecorationButton( type, parent ), SIZE( 16 )
{
setAutoFillBackground( false );
setFixedSize( SIZE, SIZE );
setCursor( Qt::ArrowCursor );
client = parent;
btype = type;
setToolTip( tip );
active_item = true;
hovering = false;
}
TabstripButton::~TabstripButton()
{
}
void TabstripButton::reset( unsigned long )
{
update();
}
QSize TabstripButton::sizeHint() const
{
return QSize( SIZE, SIZE );
}
void TabstripButton::paintEvent( QPaintEvent * )
{
QPainter p( this );
const bool active = client->isActive() && active_item;
// Icon geometry
QRect geom = QRect( 3, 3, width() - 6, height() - 6 );
// Determine painting colors
QColor bgColor = client->options()->color( KDecoration::ColorTitleBar, active );
QColor textColor = client->options()->color( KDecoration::ColorFont, active );
if( hovering )
{ // Invert if the mouse cursor is hovering over the button
textColor.setRed( 255 - textColor.red() );
textColor.setGreen( 255 - textColor.green() );
textColor.setBlue( 255 - textColor.blue() );
}
// Slight optimization as we are drawing solid straight lines
p.setRenderHint( QPainter::Antialiasing, false );
// Background
p.fillRect( 0, 0, width(), height(), bgColor );
//p.fillRect( 0, 0, width(), height(), QColor( 255, 0, 0 ));
// Paint buttons with the text color
p.setPen( textColor );
switch( btype )
{
case HelpButton:
{
QFont font;
font.setBold( true );
p.setFont( font );
p.drawText( geom.adjusted( 0, 1, 0, 0), Qt::AlignVCenter | Qt::AlignHCenter, "?" );
}
break;
case MaxButton:
switch( client->maximizeMode() )
{
case TabstripDecoration::MaximizeRestore:
case TabstripDecoration::MaximizeVertical:
case TabstripDecoration::MaximizeHorizontal:
// TL
p.drawLine( geom.x() + 3, geom.y(),
geom.x(), geom.y() );
p.drawLine( geom.x(), geom.y() + 3,
geom.x(), geom.y() );
p.drawLine( geom.x() + 3, geom.y() + 1,
geom.x(), geom.y() + 1 );
p.drawLine( geom.x() + 1, geom.y() + 3,
geom.x(), geom.y() + 1 );
// TR
p.drawLine( geom.x() + geom.width() - 3, geom.y(),
geom.x() + geom.width(), geom.y() );
p.drawLine( geom.x() + geom.width(), geom.y() + 3,
geom.x() + geom.width(), geom.y() );
p.drawLine( geom.x() + geom.width() - 3, geom.y() + 1,
geom.x() + geom.width(), geom.y() + 1 );
p.drawLine( geom.x() + geom.width() - 1, geom.y() + 3,
geom.x() + geom.width() - 1, geom.y() );
// BL
p.drawLine( geom.x() + 3, geom.y() + geom.height(),
geom.x(), geom.y() + geom.height() );
p.drawLine( geom.x(), geom.y() + geom.height() - 3,
geom.x(), geom.y() + geom.height() );
p.drawLine( geom.x() + 3, geom.y() + geom.height() - 1,
geom.x(), geom.y() + geom.height() - 1 );
p.drawLine( geom.x() + 1, geom.y() + geom.height() - 3,
geom.x() + 1, geom.y() + geom.height() );
// BR
p.drawLine( geom.x() + geom.width() - 3, geom.y() + geom.height(),
geom.x() + geom.width(), geom.y() + geom.height() );
p.drawLine( geom.x() + geom.width(), geom.y() + geom.height() - 3,
geom.x() + geom.width(), geom.y() + geom.height() );
p.drawLine( geom.x() + geom.width() - 3, geom.y() + geom.height() - 1,
geom.x() + geom.width(), geom.y() + geom.height() - 1 );
p.drawLine( geom.x() + geom.width() - 1, geom.y() + geom.height() - 3,
geom.x() + geom.width() - 1, geom.y() + geom.height() );
break;
case TabstripDecoration::MaximizeFull:
// TL
p.drawLine( geom.x() + 2, geom.y(),
geom.x() + 2, geom.y() + 2 );
p.drawLine( geom.x(), geom.y() + 2,
geom.x() + 2, geom.y() + 2 );
p.drawLine( geom.x() + 3, geom.y(),
geom.x() + 3, geom.y() + 3 );
p.drawLine( geom.x(), geom.y() + 3,
geom.x() + 3, geom.y() + 3 );
// TR
p.drawLine( geom.x() + geom.width() - 2, geom.y(),
geom.x() + geom.width() - 2, geom.y() + 2 );
p.drawLine( geom.x() + geom.width(), geom.y() + 2,
geom.x() + geom.width() - 2, geom.y() + 2 );
p.drawLine( geom.x() + geom.width() - 3, geom.y(),
geom.x() + geom.width() - 3, geom.y() + 3 );
p.drawLine( geom.x() + geom.width(), geom.y() + 3,
geom.x() + geom.width() - 3, geom.y() + 3 );
// BL
p.drawLine( geom.x() + 2, geom.y() + geom.height(),
geom.x() + 2, geom.y() + geom.height() - 2 );
p.drawLine( geom.x(), geom.y() + geom.height() - 2,
geom.x() + 2, geom.y() + geom.height() - 2 );
p.drawLine( geom.x() + 3, geom.y() + geom.height(),
geom.x() + 3, geom.y() + geom.height() - 3 );
p.drawLine( geom.x(), geom.y() + geom.height() - 3,
geom.x() + 3, geom.y() + geom.height() - 3 );
// BR
p.drawLine( geom.x() + geom.width() - 2, geom.y() + geom.height(),
geom.x() + geom.width() - 2, geom.y() + geom.height() - 2 );
p.drawLine( geom.x() + geom.width(), geom.y() + geom.height() - 2,
geom.x() + geom.width() - 2, geom.y() + geom.height() - 2 );
p.drawLine( geom.x() + geom.width() - 3, geom.y() + geom.height(),
geom.x() + geom.width() - 3, geom.y() + geom.height() - 3 );
p.drawLine( geom.x() + geom.width(), geom.y() + geom.height() - 3,
geom.x() + geom.width() - 3, geom.y() + geom.height() - 3 );
break;
}
break;
case MinButton:
// B
p.drawLine( geom.x(), geom.y() + geom.height(),
geom.x() + geom.width(), geom.y() + geom.height() );
p.drawLine( geom.x(), geom.y() + geom.height() - 1,
geom.x() + geom.width(), geom.y() + geom.height() - 1 );
// L
p.drawLine( geom.x(), geom.y() + geom.height() - 3,
geom.x(), geom.y() + geom.height() );
p.drawLine( geom.x() + 1, geom.y() + geom.height() - 3,
geom.x() + 1, geom.y() + geom.height() );
// R
p.drawLine( geom.x() + geom.width(), geom.y() + geom.height() - 3,
geom.x() + geom.width(), geom.y() + geom.height() );
p.drawLine( geom.x() + geom.width() - 1, geom.y() + geom.height() - 3,
geom.x() + geom.width() - 1, geom.y() + geom.height() );
break;
case CloseButton:
case ItemCloseButton:
// TL-BR
p.drawLine( geom.x() + 1, geom.y() + 1,
geom.x() + geom.width() - 1, geom.y() + geom.height() - 1 );
p.drawLine( geom.x() + 2, geom.y() + 1,
geom.x() + geom.width() - 1, geom.y() + geom.height() - 2 );
p.drawLine( geom.x() + 1, geom.y() + 2,
geom.x() + geom.width() - 2, geom.y() + geom.height() - 1 );
// TR-BL
p.drawLine( geom.x() + 1, geom.y() + geom.height() - 1,
geom.x() + geom.width() - 1, geom.y() + 1 );
p.drawLine( geom.x() + 2, geom.y() + geom.height() - 1,
geom.x() + geom.width() - 1, geom.y() + 2 );
p.drawLine( geom.x() + 1, geom.y() + geom.height() - 2,
geom.x() + geom.width() - 2, geom.y() + 1 );
break;
case MenuButton:
if( client->clientGroupItems().count() > 1 || !TabstripFactory::showIcon() )
{
p.drawRect( geom.x(), geom.y() + geom.height() / 2 - 5,
1, 1 );
p.drawRect( geom.x(), geom.y() + geom.height() / 2 - 1,
1, 1 );
p.drawRect( geom.x(), geom.y() + geom.height() / 2 + 3,
1, 1 );
p.drawRect( geom.x() + 4, geom.y() + geom.height() / 2 - 5,
geom.width() - 5, 1 );
p.drawRect( geom.x() + 4, geom.y() + geom.height() / 2 - 1,
geom.width() - 5, 1 );
p.drawRect( geom.x() + 4, geom.y() + geom.height() / 2 + 3,
geom.width() - 5, 1 );
}
else
p.drawPixmap( 0, 0, client->icon().pixmap( SIZE ));
break;
case OnAllDesktopsButton:
{
if( isChecked() )
p.fillRect( geom.x() + geom.width() / 2 - 1, geom.y() + geom.height() / 2 - 1,
3, 3, textColor );
else
{
p.fillRect( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 - 4,
3, 3, textColor );
p.fillRect( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 + 2,
3, 3, textColor );
p.fillRect( geom.x() + geom.width() / 2 + 2, geom.y() + geom.height() / 2 - 4,
3, 3, textColor );
p.fillRect( geom.x() + geom.width() / 2 + 2, geom.y() + geom.height() / 2 + 2,
3, 3, textColor );
}
}
break;
case AboveButton:
{
int o = -2;
if( isChecked() )
{
o = -4;
p.drawRect( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 + 3,
8, 1 );
}
p.drawPoint( geom.x() + geom.width() / 2, geom.y() + geom.height() / 2 + o );
p.drawLine( geom.x() + geom.width() / 2 - 1, geom.y() + geom.height() / 2 + o + 1,
geom.x() + geom.width() / 2 + 1, geom.y() + geom.height() / 2 + o + 1 );
p.drawLine( geom.x() + geom.width() / 2 - 2, geom.y() + geom.height() / 2 + o + 2,
geom.x() + geom.width() / 2 + 2, geom.y() + geom.height() / 2 + o + 2 );
p.drawLine( geom.x() + geom.width() / 2 - 3, geom.y() + geom.height() / 2 + o + 3,
geom.x() + geom.width() / 2 + 3, geom.y() + geom.height() / 2 + o + 3 );
p.drawLine( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 + o + 4,
geom.x() + geom.width() / 2 + 4, geom.y() + geom.height() / 2 + o + 4 );
}
break;
case BelowButton:
{
int o = 1;
if( isChecked() )
{
o = 3;
p.drawRect( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 - 5,
8, 1 );
}
p.drawPoint( geom.x() + geom.width() / 2, geom.y() + geom.height() / 2 + o );
p.drawLine( geom.x() + geom.width() / 2 - 1, geom.y() + geom.height() / 2 + o - 1,
geom.x() + geom.width() / 2 + 1, geom.y() + geom.height() / 2 + o - 1 );
p.drawLine( geom.x() + geom.width() / 2 - 2, geom.y() + geom.height() / 2 + o - 2,
geom.x() + geom.width() / 2 + 2, geom.y() + geom.height() / 2 + o - 2 );
p.drawLine( geom.x() + geom.width() / 2 - 3, geom.y() + geom.height() / 2 + o - 3,
geom.x() + geom.width() / 2 + 3, geom.y() + geom.height() / 2 + o - 3 );
p.drawLine( geom.x() + geom.width() / 2 - 4, geom.y() + geom.height() / 2 + o - 4,
geom.x() + geom.width() / 2 + 4, geom.y() + geom.height() / 2 + o - 4 );
}
break;
case ShadeButton:
p.drawLine( geom.x(), geom.y(),
geom.x() + geom.width(), geom.y() );
p.drawLine( geom.x(), geom.y() + 1,
geom.x() + geom.width(), geom.y() + 1 );
break;
case NumButtons:
default:
break;
};
}
void TabstripButton::enterEvent( QEvent *e )
{
hovering = true;
repaint();
KCommonDecorationButton::enterEvent( e );
}
void TabstripButton::leaveEvent( QEvent *e )
{
hovering = false;
repaint();
KCommonDecorationButton::leaveEvent( e );
}

View File

@ -1,55 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef TABSTRIPBUTTON_H
#define TABSTRIPBUTTON_H
#include <kcommondecoration.h>
#include <QSize>
#include <QString>
class TabstripDecoration;
class TabstripButton : public KCommonDecorationButton
{
public:
TabstripButton( ButtonType type, TabstripDecoration *parent, QString tip );
~TabstripButton();
void reset( unsigned long changed );
QSize sizeHint() const;
void setActive( bool active );
private:
void paintEvent( QPaintEvent *e );
void leaveEvent( QEvent *e );
void enterEvent( QEvent *e );
TabstripDecoration *client;
ButtonType btype;
const int SIZE;
bool active_item;
bool hovering;
};
inline void TabstripButton::setActive( bool active )
{
active_item = active;
}
#endif

View File

@ -1,443 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "tabstripdecoration.h"
#include "tabstripbutton.h"
#include "tabstripfactory.h"
#include <KLocale>
#include <QPainter>
TabstripDecoration::TabstripDecoration( KDecorationBridge *bridge, KDecorationFactory *factory )
: KCommonDecorationUnstable( bridge, factory )
{
click_in_progress = false;
drag_in_progress = false;
button = Qt::NoButton;
}
KCommonDecorationButton *TabstripDecoration::createButton( ButtonType type )
{
switch( type )
{
case HelpButton:
return ( new TabstripButton( type, this, i18n("Help") ) );
break;
case MaxButton:
return ( new TabstripButton( type, this, i18n("Maximize") ) );
break;
case MinButton:
return ( new TabstripButton( type, this, i18n("Minimize") ) );
break;
case CloseButton:
return ( new TabstripButton( type, this, i18n("Close") ) );
break;
case MenuButton:
return ( new TabstripButton( type, this, i18n("Menu") ) );
break;
case OnAllDesktopsButton:
return ( new TabstripButton( type, this, i18n("All Desktops") ) );
break;
case AboveButton:
return ( new TabstripButton( type, this, i18n("Above") ) );
break;
case BelowButton:
return ( new TabstripButton( type, this, i18n("Below") ) );
break;
case ShadeButton:
return ( new TabstripButton( type, this, i18n("Shade") ) );
break;
default:
break;
};
return 0;
}
void TabstripDecoration::paintTab( QPainter &painter, const QRect &geom, ClientGroupItem &item, bool active )
{
// Determine painting colors
QColor bgColor = options()->color( ColorTitleBar, active );
QColor textColor = options()->color( ColorFont, active );
// Draw border around the tab
painter.setPen( Qt::black );
painter.drawRect( geom.adjusted( 0, 0, -1, -1 ));
painter.setPen( TabstripFactory::outlineColor() );
painter.drawRect( geom.adjusted( 1, 1, -2, -2 ));
// Background
painter.fillRect( geom.adjusted( 2, 2, -2, -2 ), bgColor );
// Window title and icon
painter.setPen( textColor );
if( TabstripFactory::showIcon() )
{
QRect rect( geom.x() + 25, geom.y(), geom.width() - 48, geom.height() );
QRect text;
QFont font = options()->font( active );
QFontMetrics metrics( font );
QString string = metrics.elidedText( item.title(), Qt::ElideRight, rect.width() );
painter.setFont( font );
painter.drawText( rect, TabstripFactory::titleAlign() | Qt::AlignVCenter, string, &text );
painter.drawPixmap( text.x() - 22, rect.y() + 3, item.icon().pixmap( 16 ));
}
else
{
QRect rect( geom.x() + 5, geom.y(), geom.width() - 28, geom.height() );
QFont font = options()->font( active );
QFontMetrics metrics( font );
QString string = metrics.elidedText( item.title(), Qt::ElideRight, rect.width() );
painter.setFont( font );
painter.drawText( rect, TabstripFactory::titleAlign() | Qt::AlignVCenter, string );
}
}
void TabstripDecoration::paintEvent( QPaintEvent * )
{
QPainter painter( widget() );
// Determine painting colors
QColor bgColor = options()->color( ColorTitleBar, isClientGroupActive() );
QColor textColor = options()->color( ColorFont, isClientGroupActive() );
// Determine section geometry
QRect frame( QPoint( 0, 0 ), widget()->frameGeometry().size() );
QRect titlebar( frame.topLeft(), QSize( frame.width(),
layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleHeight ) +
layoutMetric( LM_TitleEdgeBottom ) - 1 // Titlebar and main frame overlap by 1px
));
// Slight optimization as we are drawing solid straight lines
painter.setRenderHint( QPainter::Antialiasing, false );
// Draw black/white border around the main window
painter.setPen( Qt::black );
painter.drawRect( 0, titlebar.height() - 1, frame.width() - 1, frame.height() - titlebar.height() );
painter.setPen( TabstripFactory::outlineColor() );
painter.drawRect( 1, titlebar.height(), frame.width() - 3, frame.height() - titlebar.height() - 2 );
QList< ClientGroupItem > tabList = clientGroupItems();
const int tabCount = tabList.count();
// Delete unneeded tab close buttons
while( tabCount < closeButtons.size() || ( tabCount == 1 && closeButtons.size() > 0 ))
{
TabstripButton *btn = closeButtons.takeFirst();
btn->hide();
btn->deleteLater();
}
if( tabCount > 1 )
{
QRect allTabGeom = titleRect().adjusted( -1, -layoutMetric( LM_TitleEdgeTop ), 1, 0 );
QRect tabGeom = allTabGeom;
tabGeom.setWidth( tabGeom.width() / tabCount + 1 ); // Split titlebar evenly
for( int i = 0; i < tabCount; ++i )
{
// Last tab may have a different width due to rounding
if( i == tabCount - 1 )
tabGeom.setWidth( allTabGeom.width() - tabGeom.width() * i + i - 1 );
// Actually paint the tab
paintTab( painter, tabGeom, tabList[i], isActive() && visibleClientGroupItem() == i );
// Create new close button if required
if( i >= closeButtons.size() )
closeButtons.append( new TabstripButton( ItemCloseButton, this, i18n( "Close Item" ) ) );
closeButtons[ i ]->setActive( isActive() && visibleClientGroupItem() == i );
closeButtons[ i ]->move( tabGeom.right() - 18, tabGeom.bottom() - 18 );
closeButtons[ i ]->installEventFilter( this );
closeButtons[ i ]->show();
// Prepare for next iteration
tabGeom.translate( tabGeom.width() - 1, 0 );
}
// Draw border around the buttons
painter.setPen( Qt::black );
painter.drawRect( 0, 0, allTabGeom.left(), allTabGeom.height() - 1 );
painter.drawRect( allTabGeom.right() - 1, 0, frame.width() - allTabGeom.right(), allTabGeom.height() - 1 );
painter.setPen( TabstripFactory::outlineColor() );
painter.drawRect( 1, 1, allTabGeom.left() - 2, allTabGeom.height() - 3 );
painter.drawRect( allTabGeom.right(), 1, frame.width() - allTabGeom.right() - 2, allTabGeom.height() - 3 );
// Background behind the buttons
painter.fillRect( 2, 2, allTabGeom.left() - 3, allTabGeom.height() - 4, bgColor );
painter.fillRect( allTabGeom.right() + 1, 2, frame.width() - allTabGeom.right() - 3, allTabGeom.height() - 4, bgColor );
}
else
{
// Draw border around the titlebar
painter.setPen( Qt::black );
painter.drawRect( titlebar.adjusted( 0, 0, -1, -1 ));
painter.setPen( TabstripFactory::outlineColor() );
painter.drawRect( titlebar.adjusted( 1, 1, -2, -2 ));
// Background
painter.fillRect( titlebar.adjusted( 2, 2, -2, -2 ), bgColor );
// Window title
painter.setPen( textColor );
QRect rect( titleRect().x() + 2, titleRect().y(),
titleRect().width() - 6, titleRect().height() - 3 );
QFont font = options()->font( isActive() );
QFontMetrics metrics( font );
QString string = metrics.elidedText( caption(), Qt::ElideRight, rect.width() );
painter.setFont( font );
painter.drawText( rect, TabstripFactory::titleAlign() | Qt::AlignVCenter, string );
}
}
QString TabstripDecoration::visibleName() const
{
return i18n("Tabstrip");
}
void TabstripDecoration::init()
{
KCommonDecoration::init();
widget()->setAutoFillBackground( false );
widget()->setAttribute( Qt::WA_OpaquePaintEvent );
widget()->setAcceptDrops( true );
}
bool TabstripDecoration::eventFilter( QObject* o, QEvent* e )
{
if( TabstripButton *btn = dynamic_cast< TabstripButton* >( o ))
{
if( e->type() == QEvent::MouseButtonPress )
return true; // No-op
else if( e->type() == QEvent::MouseButtonRelease )
{
const QMouseEvent* me = static_cast< QMouseEvent* >( e );
if( me->button() == Qt::LeftButton && btn->rect().contains( me->pos() ))
closeClientGroupItem( closeButtons.indexOf( btn ));
return true;
}
}
bool state = false;
if( e->type() == QEvent::MouseButtonPress )
state = mouseButtonPressEvent( static_cast< QMouseEvent* >( e ));
else if( e->type() == QEvent::MouseButtonRelease && widget() == o )
state = mouseButtonReleaseEvent( static_cast< QMouseEvent* >( e ));
else if( e->type() == QEvent::MouseMove )
state = mouseMoveEvent( static_cast< QMouseEvent* >( e ));
else if( e->type() == QEvent::DragEnter && widget() == o )
state = dragEnterEvent( static_cast< QDragEnterEvent* >( e ));
else if( e->type() == QEvent::DragMove && widget() == o )
state = dragMoveEvent( static_cast< QDragMoveEvent* >( e ));
else if( e->type() == QEvent::DragLeave && widget() == o )
state = dragLeaveEvent( static_cast< QDragLeaveEvent* >( e ));
else if( e->type() == QEvent::Drop && widget() == o )
state = dropEvent( static_cast< QDropEvent* >( e ));
return state || KCommonDecorationUnstable::eventFilter( o, e );
}
bool TabstripDecoration::mouseButtonPressEvent( QMouseEvent* e )
{
click = widget()->mapToParent( e->pos() );
int item = itemClicked( click );
if( buttonToWindowOperation( e->button() ) == OperationsOp )
{
displayClientMenu( item, widget()->mapToGlobal( click ));
return true;
}
if( item >= 0 )
{
click_in_progress = true;
button = e->button();
return true;
}
click_in_progress = false;
return false;
}
bool TabstripDecoration::mouseButtonReleaseEvent( QMouseEvent* e )
{
release = e->pos();
int item = itemClicked( release );
if( click_in_progress && item >= 0 )
{
click_in_progress = false;
setVisibleClientGroupItem( item );
return true;
}
click_in_progress = false;
return false;
}
bool TabstripDecoration::mouseMoveEvent( QMouseEvent* e )
{
QPoint c = e->pos();
int item = itemClicked( c );
if( item >= 0 && click_in_progress && buttonToWindowOperation( button ) == ClientGroupDragOp &&
( c - click ).manhattanLength() >= 4 )
{
click_in_progress = false;
drag_in_progress = true;
QDrag *drag = new QDrag( widget() );
QMimeData *group_data = new QMimeData();
group_data->setData( clientGroupItemDragMimeType(), QString().setNum( itemId( item )).toAscii() );
drag->setMimeData( group_data );
// Create draggable tab pixmap
QList< ClientGroupItem > tabList = clientGroupItems();
const int tabCount = tabList.count();
QRect frame( QPoint( 0, 0 ), widget()->frameGeometry().size() );
QRect titlebar( frame.topLeft(), QSize( frame.width(),
layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleHeight ) +
layoutMetric( LM_TitleEdgeBottom ) - 1 // Titlebar and main frame overlap by 1px
));
QRect geom = titleRect().adjusted( -1, -layoutMetric( LM_TitleEdgeTop ), 1, 0 );
geom.setWidth( geom.width() / tabCount + 1 ); // Split titlebar evenly
geom.translate( geom.width() * item - item, 0 );
QPixmap pix( geom.size() );
QPainter painter( &pix );
paintTab( painter, QRect( QPoint( 0, 0 ), geom.size() ), tabList[item],
isActive() && visibleClientGroupItem() == item );
drag->setPixmap( pix );
// If the cursor is on top of the pixmap then it makes the movement jerky on some systems
//drag->setHotSpot( QPoint( c.x() - geom.x(), c.y() - geom.y() ));
drag->setHotSpot( QPoint( c.x() - geom.x(), -1 ));
drag->exec( Qt::MoveAction );
drag_in_progress = false;
if( drag->target() == 0 && tabList.count() > 1 )
{ // Remove window from group and move to where the cursor is located
QPoint pos = QCursor::pos();
frame.moveTo( pos.x() - c.x(), pos.y() - c.y() );
removeFromClientGroup( itemClicked( click ), frame );
}
return true;
}
return false;
}
bool TabstripDecoration::dragEnterEvent( QDragEnterEvent* e )
{
if( e->source() != 0 && e->source()->objectName() == "decoration widget" )
{
drag_in_progress = true;
e->acceptProposedAction();
QPoint point = widget()->mapToParent( e->pos() );
targetTab = itemClicked( point );
widget()->update();
return true;
}
return false;
}
bool TabstripDecoration::dropEvent( QDropEvent* e )
{
QPoint point = widget()->mapToParent( e->pos() );
drag_in_progress = false;
int tabClick = itemClicked( point );
if( tabClick >= 0 )
{
const QMimeData *group_data = e->mimeData();
if( group_data->hasFormat( clientGroupItemDragMimeType() ) )
{
if( widget() == e->source() )
{
int from = itemClicked( click );
moveItemInClientGroup( from, itemClicked( point, true ));
}
else
{
long source = QString( group_data->data( clientGroupItemDragMimeType() ) ).toLong();
moveItemToClientGroup( source, itemClicked( point, true ));
}
return true;
}
}
return false;
}
bool TabstripDecoration::dragMoveEvent( QDragMoveEvent* )
{
return false;
}
bool TabstripDecoration::dragLeaveEvent( QDragLeaveEvent* )
{
return false;
}
int TabstripDecoration::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *button) const
{
switch ( lm )
{
case LM_BorderBottom:
return 2;
case LM_BorderLeft:
case LM_BorderRight:
return 2;
case LM_TitleHeight:
return 17;
case LM_TitleBorderLeft:
return 3;
case LM_TitleBorderRight:
return 1;
case LM_TitleEdgeTop:
case LM_TitleEdgeBottom:
case LM_TitleEdgeLeft:
case LM_TitleEdgeRight:
return 3;
case LM_ButtonWidth:
case LM_ButtonHeight:
return 16;
case LM_ButtonSpacing:
return 6;
case LM_ExplicitButtonSpacer:
return -2;
default:
return KCommonDecoration::layoutMetric( lm, respectWindowState, button );
break;
}
}
int TabstripDecoration::itemClicked( const QPoint &point, bool between )
{
QRect frame = widget()->frameGeometry();
QList< ClientGroupItem > list = clientGroupItems();
int tabs = list.count();
int t_x = titleRect().x();
int t_y = frame.y();
int t_w = titleRect().width();
int t_h = layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleHeight ) + layoutMetric( LM_TitleEdgeBottom );
int tabWidth = t_w/tabs;
if( between ) // We are inserting a new tab between two existing ones
t_x -= tabWidth / 2;
int rem = t_w%tabs;
int tab_x = t_x;
for( int i = 0; i < tabs; ++i )
{
QRect tabRect( tab_x, t_y, i<rem?tabWidth+1:tabWidth, t_h );
if( tabRect.contains( point ) )
return i;
tab_x += tabRect.width();
}
return -1;
}

View File

@ -1,60 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef TABSTRIPDECORATION_H
#define TABSTRIPDECORATION_H
#include <kdecoration.h>
#include <kcommondecoration.h>
#include <QPaintEvent>
#include <kdebug.h>
class TabstripButton;
class TabstripDecoration : public KCommonDecorationUnstable
{
public:
TabstripDecoration( KDecorationBridge *bridge, KDecorationFactory *factory );
KCommonDecorationButton *createButton( ButtonType type );
void init();
QString visibleName() const;
virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton *button = 0) const;
virtual bool eventFilter( QObject* o, QEvent* e );
private:
void paintTab( QPainter &painter, const QRect &geom, ClientGroupItem &item, bool active );
void paintEvent( QPaintEvent *e );
bool mouseSingleClickEvent( QMouseEvent* e );
bool mouseMoveEvent( QMouseEvent* e );
bool mouseButtonPressEvent( QMouseEvent* e );
bool mouseButtonReleaseEvent( QMouseEvent* e );
bool dragMoveEvent( QDragMoveEvent* e );
bool dragLeaveEvent( QDragLeaveEvent* e );
bool dragEnterEvent( QDragEnterEvent* e );
bool dropEvent( QDropEvent* e );
int itemClicked( const QPoint &point, bool between = false );
QList< TabstripButton* > closeButtons;
QPoint click, release;
int targetTab;
bool click_in_progress, drag_in_progress;
Qt::MouseButton button;
};
#endif

View File

@ -1,113 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "tabstripfactory.h"
#include "tabstripdecoration.h"
#include <KConfig>
#include <KConfigGroup>
#include <KDebug>
extern "C"
{
KDE_EXPORT KDecorationFactory *create_factory()
{
return new TabstripFactory();
}
}
Qt::AlignmentFlag TabstripFactory::titlealign = Qt::AlignCenter;
bool TabstripFactory::show_icon = true;
QColor TabstripFactory::outline_color = Qt::white;
TabstripFactory::TabstripFactory()
{
initialized = false;
readConfig();
initialized = true;
}
TabstripFactory::~TabstripFactory()
{
}
KDecoration *TabstripFactory::createDecoration( KDecorationBridge *bridge )
{
return ( new TabstripDecoration( bridge, this ) )->decoration();
}
bool TabstripFactory::supports( Ability ability ) const
{
switch( ability )
{
case AbilityButtonMenu:
case AbilityAnnounceColors:
case AbilityButtonOnAllDesktops:
case AbilityButtonSpacer:
case AbilityButtonHelp:
case AbilityButtonMinimize:
case AbilityButtonMaximize:
case AbilityButtonClose:
case AbilityButtonAboveOthers:
case AbilityButtonBelowOthers:
case AbilityButtonShade:
case AbilityClientGrouping:
return true;
default:
return false;
};
}
bool TabstripFactory::readConfig()
{
KConfig config( "tabstriprc" );
KConfigGroup cg = config.group( "General" );
Qt::AlignmentFlag oldalign = titlealign;
QString align = cg.readEntry( "TitleAlignment", "Center" );
if( align == "Left" )
titlealign = Qt::AlignLeft;
else if( align == "Center" )
titlealign = Qt::AlignHCenter;
else if( align == "Right" )
titlealign = Qt::AlignRight;
show_icon = cg.readEntry( "ShowIcon", true );
outline_color = cg.readEntry( "OutlineColor", QColor( Qt::white ) );
return ( titlealign != oldalign );
}
bool TabstripFactory::reset( unsigned long changed )
{
initialized = false;
bool c_change = readConfig();
initialized = true;
if( c_change || ( changed & ( SettingDecoration | SettingButtons | SettingBorder ) ) )
return true;
else
{
resetDecorations( changed );
return false;
}
}
QList< KDecorationDefines::BorderSize > TabstripFactory::borderSizes() const
{
return QList< BorderSize >() << BorderNormal;
}

View File

@ -1,63 +0,0 @@
/********************************************************************
Tabstrip KWin window decoration
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.com>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef TABSTRIPFACTORY_H
#define TABSTRIPFACTORY_H
#include <kdecorationfactory.h>
#include <kdecoration.h>
#include <kdecorationbridge.h>
class TabstripFactory : public KDecorationFactoryUnstable
{
public:
TabstripFactory();
~TabstripFactory();
KDecoration *createDecoration( KDecorationBridge *bridge );
bool supports( Ability ability ) const;
bool reset( unsigned long changed );
QList< KDecorationDefines::BorderSize > borderSizes() const;
static Qt::AlignmentFlag titleAlign();
static bool showIcon();
static QColor &outlineColor();
private:
bool readConfig();
bool initialized;
static Qt::AlignmentFlag titlealign;
static bool show_icon;
static QColor outline_color;
};
inline Qt::AlignmentFlag TabstripFactory::titleAlign()
{
return titlealign;
}
inline bool TabstripFactory::showIcon()
{
return show_icon;
}
inline QColor &TabstripFactory::outlineColor()
{
return outline_color;
}
#endif

View File

@ -385,11 +385,13 @@ void EffectsHandlerImpl::slotUnmanagedAdded(Unmanaged *u)
emit windowAdded(u->effectWindow());
}
void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *c)
void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t)
{
Q_ASSERT(dynamic_cast<Client*>(c));
setupClientConnections(static_cast<Client*>(c));
emit windowAdded(c->effectWindow());
Q_ASSERT(dynamic_cast<Client*>(t));
Client *c = static_cast<Client*>(t);
setupClientConnections(c);
if (!c->tabGroup()) // the "window" has already been there
emit windowAdded(c->effectWindow());
}
void EffectsHandlerImpl::slotDeletedRemoved(KWin::Deleted *d)
@ -424,19 +426,19 @@ void EffectsHandlerImpl::slotClientUnminimized(Client* c, bool animate)
}
}
void EffectsHandlerImpl::slotClientGroupItemSwitched(EffectWindow* from, EffectWindow* to)
void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to)
{
emit clientGroupItemSwitched(from, to);
emit currentTabAboutToChange(from, to);
}
void EffectsHandlerImpl::slotClientGroupItemAdded(EffectWindow* from, EffectWindow* to)
void EffectsHandlerImpl::slotTabAdded(EffectWindow* w, EffectWindow* to)
{
emit clientGroupItemAdded(from, to);
emit tabAdded(w, to);
}
void EffectsHandlerImpl::slotClientGroupItemRemoved(EffectWindow* c, EffectWindow* group)
void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow* leaderOfFormerGroup)
{
emit clientGroupItemRemoved(c, group);
emit tabRemoved(w, leaderOfFormerGroup);
}
void EffectsHandlerImpl::slotDesktopChanged(int old)

View File

@ -175,9 +175,9 @@ public:
QStringList activeEffects() const;
public Q_SLOTS:
void slotClientGroupItemSwitched(EffectWindow* from, EffectWindow* to);
void slotClientGroupItemAdded(EffectWindow* from, EffectWindow* to);
void slotClientGroupItemRemoved(EffectWindow* c, EffectWindow* group);
void slotCurrentTabAboutToChange(EffectWindow* from, EffectWindow* to);
void slotTabAdded(EffectWindow* from, EffectWindow* to);
void slotTabRemoved(EffectWindow* c, EffectWindow* newActiveWindow);
void slotShowOutline(const QRect &geometry);
void slotHideOutline();

View File

@ -96,7 +96,7 @@ void BoxSwitchEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data,
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
if (windows.contains(w)) {
if (w == selected_window)
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP);
else
data.setTranslucent();
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE | EffectWindow::PAINT_DISABLED_BY_DESKTOP);

View File

@ -383,7 +383,7 @@ void DesktopGridEffect::slotWindowAdded(EffectWindow* w)
if (!activated)
return;
if (isUsingPresentWindows()) {
if (w->isDesktop() || w->isDock() || !w->visibleInClientGroup())
if (w->isDesktop() || w->isDock() || !w->isCurrentTab())
return; // don't add
if (w->isOnAllDesktops()) {
for (int i = 0; i < effects->numberOfDesktops(); i++) {
@ -1103,7 +1103,7 @@ void DesktopGridEffect::setup()
WindowMotionManager manager;
foreach (EffectWindow * w, effects->stackingOrder()) {
if (w->isOnDesktop(i) && w->screen() == j && !w->isDesktop() && !w->isDock() &&
w->visibleInClientGroup() && !w->isSkipSwitcher() && w->isOnCurrentActivity()) {
w->isCurrentTab() && !w->isSkipSwitcher() && w->isOnCurrentActivity()) {
manager.manage(w);
}
}
@ -1319,7 +1319,7 @@ void DesktopGridEffect::desktopsAdded(int old)
WindowMotionManager manager;
foreach (EffectWindow * w, effects->stackingOrder()) {
if (w->isOnDesktop(i) && w->screen() == j && !w->isDesktop() && !w->isDock() &&
w->visibleInClientGroup()) {
w->isCurrentTab()) {
manager.manage(w);
}
}
@ -1358,7 +1358,7 @@ void DesktopGridEffect::desktopsRemoved(int old)
WindowMotionManager& manager = m_managers[(desktop-1)*(effects->numScreens())+j ];
foreach (EffectWindow * w, effects->stackingOrder()) {
if (!manager.isManaging(w) && w->isOnDesktop(desktop) && w->screen() == j &&
!w->isDesktop() && !w->isDock() && w->visibleInClientGroup()) {
!w->isDesktop() && !w->isDock() && w->isCurrentTab()) {
manager.manage(w);
}
}

View File

@ -471,8 +471,8 @@ void FlipSwitchEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data,
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
if (w->isMinimized())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE);
if (!w->visibleInClientGroup())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
if (!w->isCurrentTab())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP);
} else {
if ((m_start || m_stop) && !w->isDesktop() && w->isOnCurrentDesktop())
data.setTranslucent();

View File

@ -53,7 +53,7 @@ HighlightWindowEffect::~HighlightWindowEffect()
static bool isInitiallyHidden(EffectWindow* w)
{
// Is the window initially hidden until it is highlighted?
return w->isMinimized() || !w->visibleInClientGroup() || !w->isOnCurrentDesktop();
return w->isMinimized() || !w->isCurrentTab() || !w->isOnCurrentDesktop();
}
void HighlightWindowEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
@ -97,8 +97,8 @@ void HighlightWindowEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData&
if (opacity != m_windowOpacity.end() && *opacity > 0.01) {
if (w->isMinimized())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE);
if (!w->visibleInClientGroup())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
if (!w->isCurrentTab())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP);
if (!w->isOnCurrentDesktop())
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
}

View File

@ -277,7 +277,7 @@ void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &d
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); // Display always
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
if (winData->visible)
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP);
// Calculate window's opacity
// TODO: Minimized windows or windows not on the current desktop are only 75% visible?
@ -1711,7 +1711,7 @@ void PresentWindowsEffect::setActive(bool active, bool closingTab)
DataHash::iterator winData = m_windowData.find(w);
if (winData != m_windowData.end())
winData->visible = (w->isOnDesktop(desktop) || w->isOnAllDesktops()) &&
!w->isMinimized() && (w->visibleInClientGroup() || winData->visible);
!w->isMinimized() && (w->isCurrentTab() || winData->visible);
}
delete m_closeView;
m_closeView = 0;
@ -1766,7 +1766,7 @@ bool PresentWindowsEffect::isSelectableWindow(EffectWindow *w)
return false;
if (!w->acceptsFocus())
return false;
if (!w->visibleInClientGroup())
if (!w->isCurrentTab())
return false;
if (w->isSkipSwitcher())
return false;

View File

@ -334,7 +334,7 @@ bool SlideBackEffect::isWindowOnTop(EffectWindow* w)
bool SlideBackEffect::isWindowUsable(EffectWindow* w)
{
return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized()
&& w->visibleInClientGroup();
&& w->isCurrentTab();
}
bool SlideBackEffect::intersects(EffectWindow* windowUnder, const QRect &windowOverGeometry)

View File

@ -1207,7 +1207,7 @@ bool Client::processDecorationButtonPress(int button, int /*state*/, int x, int
if (button == Button1
&& com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
&& com != Options::MouseMinimize // mouse release event
&& com != Options::MouseClientGroupDrag) {
&& com != Options::MouseDragTab) {
mode = mousePosition(QPoint(x, y));
buttonDown = true;
moveOffset = QPoint(x - padding_left, y - padding_top);
@ -1227,7 +1227,7 @@ bool Client::processDecorationButtonPress(int button, int /*state*/, int x, int
com == Options::MouseActivate ||
com == Options::MouseActivateRaiseAndPassClick ||
com == Options::MouseActivateAndPassClick ||
com == Options::MouseClientGroupDrag ||
com == Options::MouseDragTab ||
com == Options::MouseNothing);
}
@ -1276,7 +1276,7 @@ bool Client::buttonReleaseEvent(Window w, int /*button*/, int state, int x, int
// mouse position is still relative to old Client position, adjust it
QPoint mousepos(x_root - x + padding_left, y_root - y + padding_top);
mode = mousePosition(mousepos);
} else if (workspace()->decorationSupportsClientGrouping())
} else if (workspace()->decorationSupportsTabbing())
return false;
updateCursor();
}

View File

@ -416,7 +416,7 @@ QPoint Workspace::adjustClientPosition(Client* c, QPoint pos, bool unrestricted,
if ((((*l)->isOnDesktop(c->desktop()) && !(*l)->isMinimized())
|| (c->isOnDesktop(NET::OnAllDesktops) && (*l)->isOnDesktop(Workspace::currentDesktop())
&& !(*l)->isMinimized()))
&& (!(*l)->clientGroup() || (*l) == (*l)->clientGroup()->visible())
&& (!(*l)->tabGroup() || (*l) == (*l)->tabGroup()->current())
&& (*l) != c) {
lx = (*l)->x();
ly = (*l)->y();
@ -1257,8 +1257,8 @@ QSize Client::sizeForClientSize(const QSize& wsize, Sizemode mode, bool noframe)
// basesize, minsize, maxsize, paspect and resizeinc have all values defined,
// even if they're not set in flags - see getWmNormalHints()
QSize min_size = clientGroup() ? clientGroup()->minSize() : minSize();
QSize max_size = clientGroup() ? clientGroup()->maxSize() : maxSize();
QSize min_size = tabGroup() ? tabGroup()->minSize() : minSize();
QSize max_size = tabGroup() ? tabGroup()->maxSize() : maxSize();
if (decoration != NULL) {
QSize decominsize = decoration->minimumSize();
QSize border_size(border_left + border_right, border_top + border_bottom);
@ -1458,8 +1458,8 @@ void Client::getWmNormalHints()
xSizeHint.win_gravity = NorthWestGravity;
// Update min/max size of this group
if (clientGroup())
clientGroup()->updateMinMaxSize();
if (tabGroup())
tabGroup()->updateMinMaxSize();
if (isManaged()) {
// update to match restrictions
@ -1803,8 +1803,8 @@ bool Client::isResizable() const
if (rules()->checkSize(QSize()).isValid()) // forced size
return false;
QSize min = clientGroup() ? clientGroup()->minSize() : minSize();
QSize max = clientGroup() ? clientGroup()->maxSize() : maxSize();
QSize min = tabGroup() ? tabGroup()->minSize() : minSize();
QSize max = tabGroup() ? tabGroup()->maxSize() : maxSize();
return min.width() < max.width() || min.height() < max.height();
}
@ -1919,8 +1919,8 @@ void Client::setGeometry(int x, int y, int w, int h, ForceGeometry_t force)
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
// TODO: this signal is emitted too often
emit geometryChanged();
@ -1986,8 +1986,8 @@ void Client::plainResize(int w, int h, ForceGeometry_t force)
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
// TODO: this signal is emitted too often
emit geometryChanged();
}
@ -2033,8 +2033,8 @@ void Client::move(int x, int y, ForceGeometry_t force)
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
}
void Client::blockGeometryUpdates(bool block)
@ -2075,8 +2075,8 @@ void Client::setMaximize(bool vertically, bool horizontally)
emit clientMaximizedStateChanged(this, vertically, horizontally);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
}
static bool changeMaximizeRecursion = false;

View File

@ -475,55 +475,60 @@ QRect KDecorationPreviewBridge::transparentRect() const
// Window tabbing
bool KDecorationPreviewBridge::isClientGroupActive()
int KDecorationPreviewBridge::tabCount() const
{
return active;
return 1;
}
QList< ClientGroupItem > KDecorationPreviewBridge::clientGroupItems() const
QString KDecorationPreviewBridge::caption(int) const
{
return QList< ClientGroupItem >() << ClientGroupItem(
active ? "Active Window" : "Inactive Window", icon());
return active ? "Active Window" : "Inactive Window";
}
long KDecorationPreviewBridge::itemId(int)
QIcon KDecorationPreviewBridge::icon(int) const
{
return icon();
}
long KDecorationPreviewBridge::tabId(int) const
{
return 0;
}
int KDecorationPreviewBridge::visibleClientGroupItem()
long KDecorationPreviewBridge::currentTabId() const
{
return 0;
}
void KDecorationPreviewBridge::setVisibleClientGroupItem(int)
void KDecorationPreviewBridge::setCurrentTab(long)
{
}
void KDecorationPreviewBridge::moveItemInClientGroup(int, int)
void KDecorationPreviewBridge::tab_A_before_B(long, long)
{
}
void KDecorationPreviewBridge::moveItemToClientGroup(long, int)
void KDecorationPreviewBridge::tab_A_behind_B(long, long)
{
}
void KDecorationPreviewBridge::removeFromClientGroup(int, const QRect&)
void KDecorationPreviewBridge::untab(long, const QRect&)
{
}
void KDecorationPreviewBridge::closeClientGroupItem(int)
void KDecorationPreviewBridge::closeTab(long)
{
}
void KDecorationPreviewBridge::closeAllInClientGroup()
void KDecorationPreviewBridge::closeTabGroup()
{
}
void KDecorationPreviewBridge::displayClientMenu(int, const QPoint&)
void KDecorationPreviewBridge::showWindowMenu(const QPoint &, long)
{
}
KDecoration::WindowOperation KDecorationPreviewBridge::buttonToWindowOperation(Qt::MouseButtons)
{
return KDecoration::NoOp;

View File

@ -120,17 +120,18 @@ public:
virtual QRect transparentRect() const;
// Window tabbing
virtual bool isClientGroupActive();
virtual QList< ClientGroupItem > clientGroupItems() const;
virtual long itemId(int index);
virtual int visibleClientGroupItem();
virtual void setVisibleClientGroupItem(int index);
virtual void moveItemInClientGroup(int index, int before);
virtual void moveItemToClientGroup(long itemId, int before);
virtual void removeFromClientGroup(int index, const QRect& newGeom);
virtual void closeClientGroupItem(int index);
virtual void closeAllInClientGroup();
virtual void displayClientMenu(int index, const QPoint& pos);
virtual QString caption(int idx) const;
virtual void closeTab(long id);
virtual void closeTabGroup();
virtual long currentTabId() const;
virtual QIcon icon(int idx) const;
virtual void setCurrentTab(long id);
virtual void showWindowMenu(const QPoint &, long id);
virtual void tab_A_before_B(long A, long B);
virtual void tab_A_behind_B(long A, long B);
virtual int tabCount() const;
virtual long tabId(int idx) const;
virtual void untab(long id, const QRect& newGeom);
virtual WindowOperation buttonToWindowOperation(Qt::MouseButtons button);
private:

View File

@ -59,9 +59,9 @@ a->setText(i18n("System"));
a = actionCollection->addAction("Group:Navigation");
a->setText(i18n("Navigation"));
DEF(I18N_NOOP("Walk Through Window Tabs"), 0, slotSwitchToTabRight());
DEF(I18N_NOOP("Walk Through Window Tabs (Reverse)"), 0, slotSwitchToTabLeft());
DEF(I18N_NOOP("Remove Window From Group"), 0, slotRemoveFromGroup());
DEF(I18N_NOOP("Walk Through Window Tabs"), 0, slotActivateNextTab());
DEF(I18N_NOOP("Walk Through Window Tabs (Reverse)"), 0, slotActivatePrevTab());
DEF(I18N_NOOP("Remove Window From Group"), 0, slotUntab());
a = actionCollection->addAction("Group:Windows");
a->setText(i18n("Windows"));

View File

@ -782,8 +782,8 @@ void Client::setKeepAbove(bool b)
updateWindowRules(Rules::Above);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit keepAboveChanged();
}
@ -806,8 +806,8 @@ void Client::setKeepBelow(bool b)
updateWindowRules(Rules::Below);
// Update states of all other windows in this group
if (clientGroup())
clientGroup()->updateStates(this);
if (tabGroup())
tabGroup()->updateStates(this);
emit keepBelowChanged();
}

View File

@ -1277,59 +1277,64 @@ bool KCommonDecorationUnstable::compositingActive() const
// Window tabbing
bool KCommonDecorationUnstable::isClientGroupActive()
int KCommonDecorationUnstable::tabCount() const
{
return static_cast<KDecorationUnstable*>(decoration())->isClientGroupActive();
return static_cast<const KDecorationUnstable*>(decoration())->tabCount();
}
QList< ClientGroupItem > KCommonDecorationUnstable::clientGroupItems() const
QString KCommonDecorationUnstable::caption(int idx) const
{
return static_cast<const KDecorationUnstable*>(decoration())->clientGroupItems();
return static_cast<const KDecorationUnstable*>(decoration())->caption(idx);
}
long KCommonDecorationUnstable::itemId(int index)
QIcon KCommonDecorationUnstable::icon(int idx) const
{
return static_cast<KDecorationUnstable*>(decoration())->itemId(index);
return static_cast<const KDecorationUnstable*>(decoration())->icon(idx);
}
int KCommonDecorationUnstable::visibleClientGroupItem()
long KCommonDecorationUnstable::tabId(int idx) const
{
return static_cast<KDecorationUnstable*>(decoration())->visibleClientGroupItem();
return static_cast<const KDecorationUnstable*>(decoration())->tabId(idx);
}
void KCommonDecorationUnstable::setVisibleClientGroupItem(int index)
long KCommonDecorationUnstable::currentTabId() const
{
static_cast<KDecorationUnstable*>(decoration())->setVisibleClientGroupItem(index);
return static_cast<const KDecorationUnstable*>(decoration())->currentTabId();
}
void KCommonDecorationUnstable::moveItemInClientGroup(int index, int before)
void KCommonDecorationUnstable::setCurrentTab(long id)
{
static_cast<KDecorationUnstable*>(decoration())->moveItemInClientGroup(index, before);
static_cast<KDecorationUnstable*>(decoration())->setCurrentTab(id);
}
void KCommonDecorationUnstable::moveItemToClientGroup(long itemId, int before)
void KCommonDecorationUnstable::tab_A_before_B(long A, long B)
{
static_cast<KDecorationUnstable*>(decoration())->moveItemToClientGroup(itemId, before);
static_cast<KDecorationUnstable*>(decoration())->tab_A_before_B(A, B);
}
void KCommonDecorationUnstable::removeFromClientGroup(int index, const QRect& newGeom)
void KCommonDecorationUnstable::tab_A_behind_B(long A, long B)
{
static_cast<KDecorationUnstable*>(decoration())->removeFromClientGroup(index, newGeom);
static_cast<KDecorationUnstable*>(decoration())->tab_A_behind_B(A, B);
}
void KCommonDecorationUnstable::closeClientGroupItem(int index)
void KCommonDecorationUnstable::untab(long id, const QRect& newGeom)
{
static_cast<KDecorationUnstable*>(decoration())->closeClientGroupItem(index);
static_cast<KDecorationUnstable*>(decoration())->untab(id, newGeom);
}
void KCommonDecorationUnstable::closeAllInClientGroup()
void KCommonDecorationUnstable::closeTab(long id)
{
static_cast<KDecorationUnstable*>(decoration())->closeAllInClientGroup();
static_cast<KDecorationUnstable*>(decoration())->closeTab(id);
}
void KCommonDecorationUnstable::displayClientMenu(int index, const QPoint& pos)
void KCommonDecorationUnstable::closeTabGroup()
{
static_cast<KDecorationUnstable*>(decoration())->displayClientMenu(index, pos);
static_cast<KDecorationUnstable*>(decoration())->closeTabGroup();
}
void KCommonDecorationUnstable::showWindowMenu(const QPoint &pos, long id)
{
static_cast<KDecorationUnstable*>(decoration())->showWindowMenu(pos, id);
}
KDecoration::WindowOperation KCommonDecorationUnstable::buttonToWindowOperation(Qt::MouseButtons button)

View File

@ -395,17 +395,19 @@ public:
bool compositingActive() const;
// Window tabbing
bool isClientGroupActive();
QList< ClientGroupItem > clientGroupItems() const;
long itemId(int index);
int visibleClientGroupItem();
void setVisibleClientGroupItem(int index);
void moveItemInClientGroup(int index, int before);
void moveItemToClientGroup(long itemId, int before = -1);
void removeFromClientGroup(int index, const QRect& newGeom = QRect());
void closeClientGroupItem(int index);
void closeAllInClientGroup();
void displayClientMenu(int index, const QPoint& pos);
using KCommonDecoration::caption;
QString caption(int idx) const;
void closeTab(long id);
void closeTabGroup();
long currentTabId() const;
QIcon icon(int idx = 0) const;
void setCurrentTab(long id);
void showWindowMenu(const QPoint &, long id);
void tab_A_before_B(long A, long B);
void tab_A_behind_B(long A, long B);
int tabCount() const;
long tabId(int idx) const;
void untab(long id, const QRect& newGeom);
WindowOperation buttonToWindowOperation(Qt::MouseButtons button);
virtual bool eventFilter(QObject* o, QEvent* e);

View File

@ -412,63 +412,70 @@ void KDecorationUnstable::padding(int &left, int &right, int &top, int &bottom)
left = right = top = bottom = 0;
}
// Window tabbing
//BEGIN Window tabbing
bool KDecorationUnstable::isClientGroupActive()
int KDecorationUnstable::tabCount() const
{
return static_cast< KDecorationBridgeUnstable* >(bridge_)->isClientGroupActive();
return static_cast< KDecorationBridgeUnstable* >(bridge_)->tabCount();
}
QList< ClientGroupItem > KDecorationUnstable::clientGroupItems() const
long KDecorationUnstable::tabId(int idx) const
{
return static_cast< KDecorationBridgeUnstable* >(bridge_)->clientGroupItems();
return static_cast< KDecorationBridgeUnstable* >(bridge_)->tabId(idx);
}
long KDecorationUnstable::itemId(int index)
QString KDecorationUnstable::caption(int idx) const
{
return static_cast< KDecorationBridgeUnstable* >(bridge_)->itemId(index);
return static_cast< KDecorationBridgeUnstable* >(bridge_)->caption(idx);
}
int KDecorationUnstable::visibleClientGroupItem()
QIcon KDecorationUnstable::icon(int idx) const
{
return static_cast< KDecorationBridgeUnstable* >(bridge_)->visibleClientGroupItem();
return static_cast< KDecorationBridgeUnstable* >(bridge_)->icon(idx);
}
void KDecorationUnstable::setVisibleClientGroupItem(int index)
long KDecorationUnstable::currentTabId() const
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->setVisibleClientGroupItem(index);
return static_cast< KDecorationBridgeUnstable* >(bridge_)->currentTabId();
}
void KDecorationUnstable::moveItemInClientGroup(int index, int before)
void KDecorationUnstable::setCurrentTab(long id)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->moveItemInClientGroup(index, before);
static_cast< KDecorationBridgeUnstable* >(bridge_)->setCurrentTab(id);
}
void KDecorationUnstable::moveItemToClientGroup(long itemId, int before)
void KDecorationUnstable::tab_A_before_B(long A, long B)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->moveItemToClientGroup(itemId, before);
static_cast< KDecorationBridgeUnstable* >(bridge_)->tab_A_before_B(A, B);
}
void KDecorationUnstable::removeFromClientGroup(int index, const QRect& newGeom)
void KDecorationUnstable::tab_A_behind_B(long A, long B)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->removeFromClientGroup(index, newGeom);
static_cast< KDecorationBridgeUnstable* >(bridge_)->tab_A_behind_B(A, B);
}
void KDecorationUnstable::closeClientGroupItem(int index)
void KDecorationUnstable::untab(long id, const QRect& newGeom)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->closeClientGroupItem(index);
static_cast< KDecorationBridgeUnstable* >(bridge_)->untab(id, newGeom);
}
void KDecorationUnstable::closeAllInClientGroup()
void KDecorationUnstable::closeTab(long id)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->closeAllInClientGroup();
static_cast< KDecorationBridgeUnstable* >(bridge_)->closeTab(id);
}
void KDecorationUnstable::displayClientMenu(int index, const QPoint& pos)
void KDecorationUnstable::closeTabGroup()
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->displayClientMenu(index, pos);
static_cast< KDecorationBridgeUnstable* >(bridge_)->closeTabGroup();
}
void KDecorationUnstable::showWindowMenu(const QPoint &pos, long id)
{
static_cast< KDecorationBridgeUnstable* >(bridge_)->showWindowMenu(pos, id);
}
//END tabbing
KDecoration::WindowOperation KDecorationUnstable::buttonToWindowOperation(Qt::MouseButtons button)
{
return static_cast< KDecorationBridgeUnstable* >(bridge_)->buttonToWindowOperation(button);
@ -479,7 +486,7 @@ QRegion KDecorationUnstable::region(KDecorationDefines::Region)
return QRegion();
}
QString KDecorationDefines::clientGroupItemDragMimeType()
QString KDecorationDefines::tabDragMimeType()
{
return "text/ClientGroupItem";
}

View File

@ -104,12 +104,12 @@ public:
NoOp,
SetupWindowShortcutOp,
ApplicationRulesOp,
RemoveClientFromGroupOp, // Remove from group
CloseClientGroupOp, // Close the group
MoveClientInGroupLeftOp, // Move left in the group
MoveClientInGroupRightOp, // Move right in the group
RemoveTabFromGroupOp, // Remove from group
CloseTabGroupOp, // Close the group
ActivateNextTabOp, // Move left in the group
ActivatePreviousTabOp, // Move right in the group
ToggleClientTiledStateOp, // put a floating client into tiling
ClientGroupDragOp
TabDragOp
};
/**
* Basic color types that should be recognized by all decoration styles.
@ -197,7 +197,7 @@ public:
AbilityUsesBlurBehind = 3003, ///< The decoration wants the background to be blurred, when the blur plugin is enabled.
/// @since 4.6
// Tabbing
AbilityClientGrouping = 4000, ///< The decoration supports tabbing
AbilityTabbing = 4000, ///< The decoration supports tabbing
// TODO colors for individual button types
ABILITY_DUMMY = 10000000
};
@ -228,28 +228,10 @@ public:
/**
* Returns the mimeType used to drag and drop clientGroupItems
*/
static QString clientGroupItemDragMimeType();
static QString tabDragMimeType();
};
class KWIN_EXPORT ClientGroupItem
{
public:
ClientGroupItem(QString t, QIcon i) {
title_ = t;
icon_ = i;
}
inline QIcon icon() const {
return icon_;
}
inline QString title() const {
return title_;
}
private:
QString title_;
QIcon icon_;
};
class KDecorationProvides
: public KDecorationDefines
{
@ -942,56 +924,67 @@ public:
/**
* Returns whether or not this client group contains the active client.
*/
bool isClientGroupActive();
bool isInActiveTabGroup();
/**
* Return a list of all the clients in the group that contains the client that this
* decoration is attached to.
* Return the amount of tabs in this group
*/
QList< ClientGroupItem > clientGroupItems() const;
int tabCount() const;
/**
* Returns a unique identifier for the client at index \p index of the client group list.
* \see moveItemToClientGroup()
* Return the icon for the tab at index \p idx (\p idx must be smaller than tabCount())
*/
long itemId(int index);
QIcon icon(int idx) const;
/**
* Returns the list index of the currently visible client in this group.
* Return the caption for the tab at index \p idx (\p idx must be smaller than tabCount())
*/
int visibleClientGroupItem();
QString caption(int idx) const;
/**
* Switch the currently visible client to the one at list index \p index.
* Return the unique id for the tab at index \p idx (\p idx must be smaller than tabCount())
*/
void setVisibleClientGroupItem(int index);
long tabId(int idx) const;
/**
* Move the client at index \p index to the position before the client at index \p before.
* Returns the id of the currently active client in this group.
*/
void moveItemInClientGroup(int index, int before);
long currentTabId() const;
/**
* Move the client that's unique identifier is \p itemId to the position before the client
* at index \p before if set, otherwise the end of the list. This call is to move clients
* between two different groups, if moving in the same group then use
* moveItemInClientGroup() instead.
* \see itemId()
* Activate tab for the window with the id \p id.
*/
void moveItemToClientGroup(long itemId, int before = -1);
void setCurrentTab(long id);
/**
* Remove the client at index \p index from the group. If \p newGeom is set then the client
* will move and resize to the specified geometry, otherwise it will stay where the group
* is located.
* Entab windw with id \p A beFORE the window with the id \p B.
*/
void removeFromClientGroup(int index, const QRect& newGeom = QRect());
virtual void tab_A_before_B(long A, long B);
/**
* Close the client at index \p index.
* Entab windw with id \p A beHIND the window with the id \p B.
*/
void closeClientGroupItem(int index);
virtual void tab_A_behind_B(long A, long B);
/**
* Remove the window with the id \p id from its tabgroup and place it at \p newGeom
*/
virtual void untab(long id, const QRect& newGeom);
/**
* Close the client with the id \p id.
*/
void closeTab(long id);
/**
* Close all windows in this group.
*/
void closeAllInClientGroup();
void closeTabGroup();
/**
* Display the right-click client menu belonging to the client at index \p index at the
* global coordinates specified by \p pos.
*/
void displayClientMenu(int index, const QPoint& pos);
void showWindowMenu(const QPoint& pos, long id);
/**
* unshadow virtuals
*/
using KDecoration::caption;
using KDecoration::icon;
using KDecoration::showWindowMenu;
/**
* Determine which action the user has mapped \p button to. Useful for determining whether
* a button press was for window tab dragging or for displaying the client menu.

View File

@ -97,17 +97,21 @@ public:
virtual QRect transparentRect() const = 0;
// Window tabbing
virtual bool isClientGroupActive() = 0;
virtual QList< ClientGroupItem > clientGroupItems() const = 0;
virtual long itemId(int index) = 0;
virtual int visibleClientGroupItem() = 0;
virtual void setVisibleClientGroupItem(int index) = 0;
virtual void moveItemInClientGroup(int index, int before) = 0;
virtual void moveItemToClientGroup(long itemId, int before) = 0;
virtual void removeFromClientGroup(int index, const QRect& newGeom) = 0;
virtual void closeClientGroupItem(int index) = 0;
virtual void closeAllInClientGroup() = 0;
virtual void displayClientMenu(int index, const QPoint& pos) = 0;
using KDecorationBridge::caption;
virtual QString caption(int idx) const = 0;
virtual void closeTab(long id) = 0;
virtual void closeTabGroup() = 0;
virtual long currentTabId() const = 0;
using KDecorationBridge::icon;
virtual QIcon icon(int idx) const = 0;
virtual void setCurrentTab(long id) = 0;
using KDecorationBridge::showWindowMenu;
virtual void showWindowMenu(const QPoint& pos, long id) = 0;
virtual void tab_A_before_B(long A, long B) = 0;
virtual void tab_A_behind_B(long A, long B) = 0;
virtual int tabCount() const = 0;
virtual long tabId(int idx) const = 0;
virtual void untab(long id, const QRect& newGeom) = 0;
virtual WindowOperation buttonToWindowOperation(Qt::MouseButtons button) = 0;
};

View File

@ -380,7 +380,7 @@ WINDOW_HELPER_DEFAULT(bool, isSpecialWindow, "specialWindow", true)
WINDOW_HELPER_DEFAULT(bool, acceptsFocus, "wantsInput", true) // We don't actually know...
WINDOW_HELPER_DEFAULT(QPixmap, icon, "icon", QPixmap())
WINDOW_HELPER_DEFAULT(bool, isSkipSwitcher, "skipSwitcher", false)
WINDOW_HELPER_DEFAULT(bool, visibleInClientGroup, "visibleInClientGroup", false)
WINDOW_HELPER_DEFAULT(bool, isCurrentTab, "isCurrentTab", false)
#undef WINDOW_HELPER_DEFAULT

View File

@ -988,9 +988,9 @@ Q_SIGNALS:
* @since 4.7
**/
void tabBoxKeyEvent(QKeyEvent* event);
void clientGroupItemSwitched(EffectWindow* from, EffectWindow* to);
void clientGroupItemAdded(EffectWindow* from, EffectWindow* to); // from merged with to
void clientGroupItemRemoved(EffectWindow* c, EffectWindow* group); // c removed from group
void currentTabAboutToChange(EffectWindow* from, EffectWindow* to);
void tabAdded(EffectWindow* from, EffectWindow* to); // from merged with to
void tabRemoved(EffectWindow* c, EffectWindow* group); // c removed from group
/**
* Signal emitted when mouse changed.
* If an effect needs to get updated mouse positions, it needs to first call @link startMousePolling.
@ -1074,7 +1074,7 @@ public:
/** Window will not be painted because it is minimized */
PAINT_DISABLED_BY_MINIMIZE = 1 << 3,
/** Window will not be painted because it is not the active window in a client group */
PAINT_DISABLED_BY_CLIENT_GROUP = 1 << 4,
PAINT_DISABLED_BY_TAB_GROUP = 1 << 4,
/** Window will not be painted because it's not on the current activity */
PAINT_DISABLED_BY_ACTIVITY = 1 << 5
};
@ -1265,7 +1265,7 @@ public:
void unminimize();
void closeWindow() const;
bool visibleInClientGroup() const;
bool isCurrentTab() const;
/**
* Can be used to by effects to store arbitrary data in the EffectWindow.

View File

@ -305,39 +305,47 @@ bool Client::manage(Window w, bool isMapped)
// Create client group if the window will have a decoration
bool dontKeepInArea = false;
setTabGroup(NULL);
if (!noBorder()) {
setClientGroup(NULL);
bool autogrouping = rules()->checkAutogrouping(options->autogroupSimilarWindows);
const bool autogrouping = rules()->checkAutogrouping(options->autogroupSimilarWindows);
const bool autogroupInFg = rules()->checkAutogroupInForeground(options->autogroupInForeground);
// Automatically add to previous groups on session restore
if (session && session->clientGroupClient && session->clientGroupClient != this && session->clientGroupClient->clientGroup())
session->clientGroupClient->clientGroup()->add(this, -1, true);
else if (isMapped && autogrouping)
if (session && session->tabGroupClient && session->tabGroupClient != this) {
tabBehind(session->tabGroupClient, autogroupInFg);
} else if (isMapped && autogrouping) {
// If the window is already mapped (Restarted KWin) add any windows that already have the
// same geometry to the same client group. (May incorrectly handle maximized windows)
foreach (ClientGroup * group, workspace()->clientGroups)
if (geom == QRect(group->visible()->pos(), group->visible()->clientSize()) &&
desk == group->visible()->desktop() &&
activities() == group->visible()->activities() &&
group->visible()->maximizeMode() != MaximizeFull) {
group->add(this, -1, true);
break;
}
if (!client_group && !isMapped && !session) {
// Attempt to automatically group similar windows
const Client* similar = workspace()->findSimilarClient(this);
if (similar && similar->clientGroup() && !similar->noBorder()) {
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
updateDecoration(false);
similar->clientGroup()->add(this, -1,
rules()->checkAutogroupInForeground(options->autogroupInForeground));
// Don't move entire group
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
placementDone = true;
dontKeepInArea = true;
foreach (Client *other, workspace()->clientList()) {
if (other->maximizeMode() != MaximizeFull &&
geom == QRect(other->pos(), other->clientSize()) &&
desk == other->desktop() && activities() == other->activities()) {
tabBehind(other, autogroupInFg);
break;
}
}
}
if (autogrouping && !tab_group && !isMapped && !session) {
// Attempt to automatically group similar windows
Client* similar = findAutogroupCandidate();
if (similar && !similar->noBorder()) {
if (autogroupInFg) {
similar->setDesktop(desk); // can happen when grouping by id. ...
similar->setMinimized(false); // ... or anyway - still group, but "here" and visible
}
if (!similar->isMinimized()) { // do not attempt to tab in background of a hidden group
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
updateDecoration(false);
if (tabBehind(similar, autogroupInFg)) {
// Don't move entire group
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
placementDone = true;
dontKeepInArea = true;
}
}
}
}
if (!client_group)
setClientGroup(new ClientGroup(this));
}
updateDecoration(false); // Also gravitates
@ -658,4 +666,63 @@ void Client::embedClient(Window w, const XWindowAttributes& attr)
updateMouseGrab();
}
// To accept "mainwindow#1" to "mainwindow#2"
static QByteArray truncatedWindowRole(QByteArray a)
{
int i = a.indexOf('#');
if (i == -1)
return a;
QByteArray b(a);
b.truncate(i);
return b;
}
Client* Client::findAutogroupCandidate() const
{
// Attempt to find a similar window to the input. If we find multiple possibilities that are in
// different groups then ignore all of them. This function is for automatic window grouping.
Client *found = NULL;
// See if the window has a group ID to match with
QString wGId = rules()->checkAutogroupById(QString());
if (!wGId.isEmpty()) {
foreach (Client *c, workspace()->clientList()) {
if (activities() != c->activities())
continue; // don't cross activities
if (wGId == c->rules()->checkAutogroupById(QString())) {
if (found && found->tabGroup() != c->tabGroup()) { // We've found two, ignore both
found = NULL;
break; // Continue to the next test
}
found = c;
}
}
if (found)
return found;
}
// If this is a transient window don't take a guess
if (isTransient())
return NULL;
// If we don't have an ID take a guess
if (rules()->checkAutogrouping(options->autogroupSimilarWindows)) {
QByteArray wRole = truncatedWindowRole(windowRole());
foreach (Client *c, workspace()->clientList()) {
if (desktop() != c->desktop() || activities() != c->activities())
continue;
QByteArray wRoleB = truncatedWindowRole(c->windowRole());
if (resourceClass() == c->resourceClass() && // Same resource class
wRole == wRoleB && // Same window role
c->isNormalWindow()) { // Normal window TODO: Can modal windows be "normal"?
if (found && found->tabGroup() != c->tabGroup()) // We've found two, ignore both
return NULL;
found = c;
}
}
}
return found;
}
} // namespace

View File

@ -389,7 +389,7 @@ Options::MouseCommand Options::mouseCommand(const QString &name, bool restricted
if (lowerName == "resize") return restricted ? MouseResize : MouseUnrestrictedResize;
if (lowerName == "shade") return MouseShade;
if (lowerName == "minimize") return MouseMinimize;
if (lowerName == "start window tab drag") return MouseClientGroupDrag;
if (lowerName == "start window tab drag") return MouseDragTab;
if (lowerName == "close") return MouseClose;
if (lowerName == "increase opacity") return MouseOpacityMore;
if (lowerName == "decrease opacity") return MouseOpacityLess;
@ -406,9 +406,9 @@ Options::MouseWheelCommand Options::mouseWheelCommand(const QString &name)
if (lowerName == "above/below") return MouseWheelAboveBelow;
if (lowerName == "previous/next desktop") return MouseWheelPreviousNextDesktop;
if (lowerName == "change opacity") return MouseWheelChangeOpacity;
if (lowerName == "switch to window tab to the left/right") return MouseWheelChangeGroupWindow;
if (lowerName == "switch to window tab to the left/right") return MouseWheelChangeCurrentTab;
if (lowerName == "nothing") return MouseWheelNothing;
return MouseWheelChangeGroupWindow;
return MouseWheelChangeCurrentTab;
}
bool Options::showGeometryTip()
@ -477,8 +477,8 @@ Options::MouseCommand Options::wheelToMouseCommand(MouseWheelCommand com, int de
return delta > 0 ? MousePreviousDesktop : MouseNextDesktop;
case MouseWheelChangeOpacity:
return delta > 0 ? MouseOpacityMore : MouseOpacityLess;
case MouseWheelChangeGroupWindow:
return delta > 0 ? MouseLeftGroupWindow : MouseRightGroupWindow;
case MouseWheelChangeCurrentTab:
return delta > 0 ? MousePreviousTab : MouseNextTab;
default:
return MouseNothing;
}

View File

@ -197,14 +197,14 @@ public:
MouseNextDesktop, MousePreviousDesktop,
MouseAbove, MouseBelow,
MouseOpacityMore, MouseOpacityLess,
MouseClose, MouseLeftGroupWindow, MouseRightGroupWindow, MouseClientGroupDrag,
MouseClose, MousePreviousTab, MouseNextTab, MouseDragTab,
MouseNothing
};
enum MouseWheelCommand {
MouseWheelRaiseLower, MouseWheelShadeUnshade, MouseWheelMaximizeRestore,
MouseWheelAboveBelow, MouseWheelPreviousNextDesktop,
MouseWheelChangeOpacity, MouseWheelChangeGroupWindow,
MouseWheelChangeOpacity, MouseWheelChangeCurrentTab,
MouseWheelNothing
};

View File

@ -526,8 +526,8 @@ void Scene::Window::resetPaintingEnabled()
if (Client* c = dynamic_cast< Client* >(toplevel)) {
if (c->isMinimized())
disable_painting |= PAINT_DISABLED_BY_MINIMIZE;
if (c->clientGroup() && c != c->clientGroup()->visible())
disable_painting |= PAINT_DISABLED_BY_CLIENT_GROUP;
if (c->tabGroup() && c != c->tabGroup()->current())
disable_painting |= PAINT_DISABLED_BY_TAB_GROUP;
else if (c->isHiddenInternal())
disable_painting |= PAINT_DISABLED;
}

View File

@ -195,7 +195,7 @@ public:
// Window will not be painted because it is minimized
PAINT_DISABLED_BY_MINIMIZE = 1 << 3,
// Window will not be painted because it is not the active window in a client group
PAINT_DISABLED_BY_CLIENT_GROUP = 1 << 4,
PAINT_DISABLED_BY_TAB_GROUP = 1 << 4,
// Window will not be painted because it's not on the current activity
PAINT_DISABLED_BY_ACTIVITY = 1 << 5
};

View File

@ -20,25 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "meta.h"
#include "client.h"
#include "clientgroup.h"
#include "tabgroup.h"
#include <QtScript/QScriptEngine>
using namespace KWin::MetaScripting;
// Meta for KWin::ClientGroup* objects
QScriptValue ClientGroup::toScriptValue(QScriptEngine* eng, const KClientGroupRef& cGrp)
{
return eng->newQObject(cGrp, QScriptEngine::QtOwnership,
QScriptEngine::ExcludeChildObjects | QScriptEngine::ExcludeDeleteLater | QScriptEngine::PreferExistingWrapperObject);
}
void ClientGroup::fromScriptValue(const QScriptValue& obj, KWin::ClientGroup*& cGrp)
{
cGrp = qobject_cast<KWin::ClientGroup*>(obj.toQObject());
}
// End of metas for KWin::ClientGroup* objects
// Meta for QPoint object
QScriptValue Point::toScriptValue(QScriptEngine* eng, const QPoint& point)
{
@ -128,10 +115,8 @@ void KWin::MetaScripting::registration(QScriptEngine* eng)
qScriptRegisterMetaType<QSize>(eng, Size::toScriptValue, Size::fromScriptValue);
qScriptRegisterMetaType<QRect>(eng, Rect::toScriptValue, Rect::fromScriptValue);
qScriptRegisterMetaType<KClientRef>(eng, Client::toScriptValue, Client::fromScriptValue);
qScriptRegisterMetaType<KClientGroupRef>(eng, ClientGroup::toScriptValue, ClientGroup::fromScriptValue);
qScriptRegisterSequenceMetaType<QStringList>(eng);
qScriptRegisterSequenceMetaType< QList<KWin::ClientGroup*> >(eng);
qScriptRegisterSequenceMetaType< QList<KWin::Client*> >(eng);
}

View File

@ -31,26 +31,15 @@ class QSize;
namespace KWin {
class Client;
class ClientGroup;
}
typedef KWin::Client* KClientRef;
typedef KWin::ClientGroup* KClientGroupRef;
namespace KWin
{
namespace MetaScripting
{
/**
* The toScriptValue and fromScriptValue functions used in qScriptRegisterMetaType.
* Conversion functions for KWin::ClientGroup*
*/
namespace ClientGroup
{
QScriptValue toScriptValue(QScriptEngine*, const KClientGroupRef&);
void fromScriptValue(const QScriptValue&, KClientGroupRef&);
}
/**
* The toScriptValue and fromScriptValue functions used in qScriptRegisterMetaType.

View File

@ -131,10 +131,6 @@ SLOTWRAPPER(slotWindowToDesktopLeft)
SLOTWRAPPER(slotWindowToDesktopUp)
SLOTWRAPPER(slotWindowToDesktopDown)
SLOTWRAPPER(slotSwitchToTabLeft)
SLOTWRAPPER(slotSwitchToTabRight)
SLOTWRAPPER(slotRemoveFromGroup)
#undef SLOTWRAPPER
void WorkspaceWrapper::setActiveClient(KWin::Client* client)

View File

@ -215,10 +215,6 @@ public Q_SLOTS:
void slotWindowToDesktopUp();
void slotWindowToDesktopDown();
void slotSwitchToTabLeft(); // Slot to move left the active Client.
void slotSwitchToTabRight(); // Slot to move right the active Client.
void slotRemoveFromGroup(); // Slot to remove the active client from its group.
private Q_SLOTS:
void setupClientConnections(KWin::Client* client);
};

24
sm.cpp
View File

@ -163,12 +163,8 @@ void Workspace::storeClient(KConfigGroup &cg, int num, Client *c)
cg.writeEntry(QString("windowType") + n, windowTypeToTxt(c->windowType()));
cg.writeEntry(QString("shortcut") + n, c->shortcut().toString());
cg.writeEntry(QString("stackingOrder") + n, unconstrained_stacking_order.indexOf(c));
int group = 0;
if (c->clientGroup())
group = c->clientGroup()->clients().count() > 1 ?
// KConfig doesn't support long so we need to live with less precision on 64-bit systems
static_cast<int>(reinterpret_cast<long>(c->clientGroup())) : 0;
cg.writeEntry(QString("clientGroup") + n, group);
// KConfig doesn't support long so we need to live with less precision on 64-bit systems
cg.writeEntry(QString("tabGroup") + n, static_cast<int>(reinterpret_cast<long>(c->tabGroup())));
cg.writeEntry(QString("activities") + n, c->activities());
}
@ -322,8 +318,8 @@ void Workspace::addSessionInfo(KConfigGroup &cg)
info->shortcut = cg.readEntry(QString("shortcut") + n, QString());
info->active = (active_client == i);
info->stackingOrder = cg.readEntry(QString("stackingOrder") + n, -1);
info->clientGroup = cg.readEntry(QString("clientGroup") + n, 0);
info->clientGroupClient = NULL;
info->tabGroup = cg.readEntry(QString("tabGroup") + n, 0);
info->tabGroupClient = NULL;
info->activities = cg.readEntry(QString("activities") + n, QStringList());
}
}
@ -414,11 +410,13 @@ SessionInfo* Workspace::takeSessionInfo(Client* c)
}
}
// Set clientGroupClient for other clients in the same group
if (realInfo && realInfo->clientGroup)
foreach (SessionInfo * info, session)
if (!info->clientGroupClient && info->clientGroup == realInfo->clientGroup)
info->clientGroupClient = c;
// Set tabGroupClient for other clients in the same group
if (realInfo && realInfo->tabGroup) {
foreach (SessionInfo * info, session) {
if (!info->tabGroupClient && info->tabGroup == realInfo->tabGroup)
info->tabGroupClient = c;
}
}
return realInfo;
}

4
sm.h
View File

@ -66,9 +66,9 @@ struct SessionInfo {
bool active; // means 'was active in the saved session'
int stackingOrder;
float opacity;
int clientGroup; // Unique identifier for the client group that this window is in
int tabGroup; // Unique identifier for the client group that this window is in
Client* clientGroupClient; // The first client created that has an identical identifier
Client* tabGroupClient; // The first client created that has an identical identifier
QStringList activities;
};

295
tabgroup.cpp Normal file
View File

@ -0,0 +1,295 @@
/*******************************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2011/2012 The KWin team <kwin@kde.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include "tabgroup.h"
#include "client.h"
#include "effects.h"
namespace KWin
{
TabGroup::TabGroup(Client *c)
: m_clients()
, m_current(c)
, m_minSize(c->minSize())
, m_maxSize(c->maxSize())
{
QIcon icon(c->icon());
icon.addPixmap(c->miniIcon());
m_clients << c;
c->setTabGroup(this);
c->setClientShown(true);
}
TabGroup::~TabGroup()
{
}
void TabGroup::activateNext()
{
int index = m_clients.indexOf(m_current);
setCurrent(m_clients.at((index < m_clients.count() - 1) ? index + 1 : 0));
}
void TabGroup::activatePrev()
{
int index = m_clients.indexOf(m_current);
setCurrent(m_clients.at((index > 0) ? index - 1 : m_clients.count() - 1));
}
bool TabGroup::add(Client* c, Client *other, bool after, bool becomeVisible)
{
Q_ASSERT(!c->tabGroup());
if (!c->workspace()->decorationSupportsTabbing() || contains(c) || !contains(other))
return false;
// Tabbed windows MUST have a decoration
c->setNoBorder(false);
if (c->noBorder())
return false;
// If it's not possible to have the same states then ungroup them, TODO: Check all states
// We do this here as the ungroup code in updateStates() cannot be called until add() completes
bool cannotTab = false;
ShadeMode oldShadeMode = c->shadeMode();
QRect oldGeom = c->geometry();
int oldDesktop = c->desktop();
c->setShade(m_current->shadeMode());
cannotTab = c->shadeMode() != m_current->shadeMode();
if (!cannotTab) {
c->setDesktop(m_current->desktop());
cannotTab = c->desktop() != m_current->desktop();
}
if (!cannotTab) {
c->setGeometry(m_current->geometry());
cannotTab = c->geometry() != m_current->geometry();
}
if (cannotTab) {
c->setShade(oldShadeMode);
c->setDesktop(oldDesktop);
c->setGeometry(oldGeom);
// trigger decoration repaint on the group to make sure that hover animations are properly reset.
m_current->triggerDecorationRepaint();
return false; // cannot tab
}
// Actually add to new group ----------------------------------------
// Notify effects of merge
if (effects)
static_cast<EffectsHandlerImpl*>(effects)->slotTabAdded(c->effectWindow(), other->effectWindow());
int index = other ? m_clients.indexOf(other) : m_clients.size();
index += after;
if (index > m_clients.size())
index = m_clients.size();
m_clients.insert(index, c);
c->setTabGroup(this); // Let the client know which group it belongs to
updateMinMaxSize();
updateStates(m_current, c);
if (!becomeVisible)
c->setClientShown(false);
else {
c->setClientShown(true);
if (!effects || c->readyForPainting()) {
setCurrent(c);
if (options->focusPolicyIsReasonable())
m_current->workspace()->requestFocus( c );
}
else {
if (options->focusPolicyIsReasonable())
m_current->workspace()->requestFocus( m_current );
m_current = c; // setCurrent will be called by Toplevel::setReadyForPainting()
}
}
m_current->triggerDecorationRepaint();
return true;
}
bool TabGroup::remove(Client* c, const QRect& newGeom)
{
if (!c)
return false;
int index = m_clients.indexOf(c);
if (index < 0)
return false;
c->setTabGroup(NULL);
m_clients.removeAt(index);
updateMinMaxSize();
if (c == m_current) {
m_current = index < m_clients.count() ? m_clients.at(index) : m_clients.last();
m_current->setClientShown(true);
if (effects) // "c -> m_current" because c was m_current
static_cast<EffectsHandlerImpl*>(effects)->slotCurrentTabAboutToChange(c->effectWindow(), m_current->effectWindow());
}
if (newGeom.isValid()) {
c->maximize(Client::MaximizeRestore); // explicitly calling for a geometry -> unmaximize - in doubt
c->setGeometry(newGeom);
}
// Notify effects of removal
if (effects)
static_cast<EffectsHandlerImpl*>(effects)->slotTabRemoved(c->effectWindow(), m_current->effectWindow());
m_current->triggerDecorationRepaint();
return true;
}
void TabGroup::closeAll()
{
// NOTICE - in theory it's OK to use the list because closing sends an event to the client and
// due to the async X11 nature, the client would be released and thus removed from m_clients
// after this function exits.
// However later Wayland support or similar might not share this bahaviour - and we really had
// enough trouble with a polluted client list around the tabbing code ....
ClientList list(m_clients);
for (ClientList::const_iterator i = list.constBegin(), end = list.constEnd(); i != end; ++i)
if (*i != m_current)
(*i)->closeWindow();
m_current->closeWindow();
}
void TabGroup::move(Client *c, Client *other, bool after)
{
if (c == other)
return;
int from = m_clients.indexOf(c);
if (from < 0)
return;
int to = other ? m_clients.indexOf(other) : m_clients.size() - 1;
if (to < 0)
return;
to += after;
if (to >= m_clients.size())
to = m_clients.size() - 1;
if (from == to)
return;
m_clients.move(from, to);
m_current->triggerDecorationRepaint();
}
bool TabGroup::isActive() const
{
return contains(Workspace::self()->activeClient());
}
void TabGroup::setCurrent(Client* c, bool force)
{
if ((c == m_current && !force) || !contains(c))
return;
// Notify effects of switch
if (effects)
static_cast<EffectsHandlerImpl*>(effects)->slotCurrentTabAboutToChange(m_current->effectWindow(), c->effectWindow());
m_current = c;
c->setClientShown(true); // reduce flicker?
for (ClientList::const_iterator i = m_clients.constBegin(), end = m_clients.constEnd(); i != end; ++i)
(*i)->setClientShown((*i) == m_current);
}
void TabGroup::updateMinMaxSize()
{
// Determine entire group's minimum and maximum sizes
// TODO this used to be signalled out but i didn't find a receiver - or got an idea what this would be good for
// find purpose & solution or kick it
// QSize oldMin = m_minSize;
// QSize oldMax = m_maxSize;
m_minSize = QSize(0, 0);
m_maxSize = QSize(INT_MAX, INT_MAX);
for (ClientList::const_iterator i = m_clients.constBegin(); i != m_clients.constEnd(); ++i) {
m_minSize = m_minSize.expandedTo((*i)->minSize());
m_maxSize = m_maxSize.boundedTo((*i)->maxSize());
}
// TODO: this actually resolves a conflict that should be catched when adding?
m_maxSize = m_maxSize.expandedTo(m_minSize);
// calculate this _once_ to get a common size.
// TODO this leaves another unresolved conflict about the base increment (luckily not used too often)
const QSize size = m_current->clientSize().expandedTo(m_minSize).boundedTo(m_maxSize);
if (size != m_current->clientSize()) {
const QRect r(m_current->pos(), m_current->sizeForClientSize(size));
for (ClientList::const_iterator i = m_clients.constBegin(), end = m_clients.constEnd(); i != end; ++i)
(*i)->setGeometry(r);
}
}
void TabGroup::updateStates(Client* main, Client* only)
{
ClientList toBeRemoved;
for (ClientList::const_iterator i = m_clients.constBegin(), end = m_clients.constEnd(); i != end; ++i) {
Client *c = (*i);
if (c != main && (!only || c == only)) {
if (c->isMinimized() != main->isMinimized()) {
if (main->isMinimized())
c->minimize(true);
else
c->unminimize(true);
}
if (c->isShade() != main->isShade())
c->setShade(main->isShade() ? ShadeNormal : ShadeNone);
if (c->geometry() != main->geometry())
c->setGeometry(main->geometry());
if (c->isOnAllDesktops() != main->isOnAllDesktops())
c->setOnAllDesktops(main->isOnAllDesktops());
if (c->desktop() != main->desktop())
c->setDesktop(main->desktop());
if (c->activities() != main->activities())
c->setOnActivities(main->activities());
if (c->keepAbove() != main->keepAbove())
c->setKeepAbove(main->keepAbove());
if (c->keepBelow() != main->keepBelow())
c->setKeepBelow(main->keepBelow());
// If it's not possible to have the same states then ungroup them, TODO: Check all states
if (c->geometry() != main->geometry() || c->desktop() != main->desktop())
toBeRemoved << c;
}
}
for (ClientList::const_iterator i = toBeRemoved.constBegin(), end = toBeRemoved.constEnd(); i != end; ++i)
remove(*i);
}
}

186
tabgroup.h Normal file
View File

@ -0,0 +1,186 @@
/*******************************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2011/2012 The KWin team <kwin@kde.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#ifndef KWIN_TABGROUP_H
#define KWIN_TABGROUP_H
#include <QObject>
#include "kdecoration.h"
#include "utils.h"
namespace KWin
{
class Client;
/**
* This class represents a group of clients for use in window tabbing. All
* clients in the group share the same geometry and state information; I.e if
* one client changes then all others should also be changed.
*
* A group contains at least one client and DOES NOT contain multiple
* copies of the same client. A client MUST NOT be in two groups at the same
* time. All decorated clients SHOULD be in a group, even if it's a group of
* one client.
*
* rohanp: Had to convert this object to a QObject to make it easier for adding
* scripting interface to TabGroup.
*
* If a group contains multiple clients then only one will ever be mapped at
* any given time.
*/
class TabGroup
{
public:
/**
* Creates a new group containing \p c.
*/
TabGroup(Client* c);
~TabGroup();
/**
* Activate next tab (flips)
*/
void activateNext();
/**
* Activate previous tab (flips)
*/
void activatePrev();
/**
* Close all clients in this group.
*/
void closeAll();
/**
* Whether client \p c is member of this group
*/
bool contains(Client* c) const;
/**
* The amount of clients in this group
*/
int count() const;
/**
* Returns whether or not this group contains the active client.
*/
bool isActive() const;
/**
* Returns whether this group is empty (used by workspace to remove it)
*/
bool isEmpty() const;
/**
* Returns the list of all the clients contained in this group in their current order.
*/
const ClientList &clients() const;
/**
* Returns the currently visible client.
*/
Client* current() const;
/**
* Makes \p c the visible client in the group - force is only used when the window becomes ready for painting.
* Any other usage just causes pointless action
*/
void setCurrent(Client* c, bool force = false);
/**
* Returns combined minimum size of all clients in the group.
*/
QSize minSize() const;
/**
* Returns combined maximum size of all clients in the group.
*/
QSize maxSize() const;
/**
* Ensures that all the clients in the group have identical geometries and states using
* \p main as the primary client to copy the settings off. If \p only is set then only
* that client is updated to match \p main.
*/
void updateStates(Client* main, Client* only = NULL);
/**
* updates geometry restrictions of this group, basically called from Client::getWmNormalHints(), otherwise rather private
*/
void updateMinMaxSize();
signals:
void minSizeChanged();
void maxSizeChanged();
private:
friend class Client;
// friend bool Client::tabTo(Client*, bool, bool);
bool add(KWin::Client *c, Client *other, bool behind, bool activateC);
void move(KWin::Client* c, KWin::Client* before, bool behind);
// friend bool Client::untab(const QRect&);
bool remove(KWin::Client *c, const QRect &newGeom = QRect());
ClientList m_clients;
Client *m_current;
QSize m_minSize;
QSize m_maxSize;
};
inline bool TabGroup::contains(Client* c) const
{
return c && m_clients.contains(c);
}
inline int TabGroup::count() const
{
return m_clients.count();
}
inline const ClientList &TabGroup::clients() const
{
return m_clients;
}
inline bool TabGroup::isEmpty() const
{
return m_clients.isEmpty();
}
inline Client* TabGroup::current() const
{
return m_current;
}
inline QSize TabGroup::minSize() const
{
return m_minSize;
}
inline QSize TabGroup::maxSize() const
{
return m_maxSize;
}
}
#endif

View File

@ -340,6 +340,10 @@ void Toplevel::setReadyForPainting()
if (compositing()) {
addRepaintFull();
emit windowShown(this);
if (Client *cl = dynamic_cast<Client*>(this)) {
if (cl->tabGroup() && cl->tabGroup()->current() == cl)
cl->tabGroup()->setCurrent(cl, true);
}
}
}
}

View File

@ -160,19 +160,19 @@ QMenu* Workspace::clientPopup()
popup->addSeparator();
// Actions for window tabbing
if (decorationSupportsClientGrouping()) {
mRemoveTabGroup = popup->addAction(i18n("Remove &From Group"));
kaction = qobject_cast<KAction*>(keys->action("Remove TabGroup"));
if (decorationSupportsTabbing()) {
mRemoveFromTabGroup = popup->addAction(i18n("&Untab"));
kaction = qobject_cast<KAction*>(keys->action("Untab"));
if (kaction != 0)
mRemoveTabGroup->setShortcut(kaction->globalShortcut().primary());
mRemoveTabGroup->setData(Options::RemoveClientFromGroupOp);
mRemoveFromTabGroup->setShortcut(kaction->globalShortcut().primary());
mRemoveFromTabGroup->setData(Options::RemoveTabFromGroupOp);
mCloseGroup = popup->addAction(i18n("Close Entire &Group"));
mCloseGroup->setIcon(KIcon("window-close"));
mCloseTabGroup = popup->addAction(i18n("Close Entire &Group"));
mCloseTabGroup->setIcon(KIcon("window-close"));
kaction = qobject_cast<KAction*>(keys->action("Close TabGroup"));
if (kaction != 0)
mCloseGroup->setShortcut(kaction->globalShortcut().primary());
mCloseGroup->setData(Options::CloseClientGroupOp);
mCloseTabGroup->setShortcut(kaction->globalShortcut().primary());
mCloseTabGroup->setData(Options::CloseTabGroupOp);
popup->addSeparator();
}
@ -294,124 +294,115 @@ void Workspace::clientPopupAboutToShow()
}
mTilingStateOpAction->setVisible(m_tiling->isEnabled());
#endif
delete switch_to_tab_popup;
switch_to_tab_popup = 0;
delete add_tabs_popup;
add_tabs_popup = 0;
if (decorationSupportsClientGrouping()) {
const int tabGroupSize = active_popup_client->clientGroup() ?
active_popup_client->clientGroup()->items().count() : 1;
if (tabGroupSize > 1)
initSwitchToTab();
initAddToTabGroup();
mRemoveTabGroup->setVisible(tabGroupSize > 1);
mCloseGroup->setVisible(tabGroupSize > 1);
if (decorationSupportsTabbing()) {
initTabbingPopups();
} else {
delete add_tabs_popup;
add_tabs_popup = 0;
}
}
void Workspace::initSwitchToTab()
void Workspace::selectPopupClientTab(QAction* action)
{
if (switch_to_tab_popup)
if (!(active_popup_client && active_popup_client->tabGroup()) || !action->data().isValid())
return;
switch_to_tab_popup = new QMenu(popup);
switch_to_tab_popup->setFont(KGlobalSettings::menuFont());
connect(switch_to_tab_popup, SIGNAL(triggered(QAction*)),
this, SLOT(slotSwitchToTab(QAction*)));
connect(switch_to_tab_popup, SIGNAL(aboutToShow()),
this, SLOT(switchToTabPopupAboutToShow()));
QAction* action = switch_to_tab_popup->menuAction();
popup->insertAction(mRemoveTabGroup, action);
action->setText(i18n("Switch to Window Tab"));
}
void Workspace::slotSwitchToTab(QAction* action)
{
int side = action->data().toInt();
int c_id = active_popup_client->clientGroup()->indexOfVisibleClient();
int size = active_popup_client->clientGroup()->clients().count();
if (side == 0) { // Left
if (c_id > 0)
active_popup_client->clientGroup()->setVisible(c_id - 1);
else
active_popup_client->clientGroup()->setVisible(size - 1);
} else if (side == 1) { // Right
if (c_id < size - 1)
active_popup_client->clientGroup()->setVisible(c_id + 1);
else
active_popup_client->clientGroup()->setVisible(0);
} else { // Find the client
side -= 2;
for (QList<ClientGroup*>::const_iterator i = clientGroups.constBegin(); i != clientGroups.constEnd(); ++i) {
if ((*i)->contains(active_popup_client)) {
(*i)->setVisible(side);
break;
}
}
if (Client *other = action->data().value<Client*>()) {
active_popup_client->tabGroup()->setCurrent(other);
return;
}
// failed conversion, try "1" & "2", being prev and next
int direction = action->data().toInt();
if (direction == 1)
active_popup_client->tabGroup()->activatePrev();
else if (direction == 2)
active_popup_client->tabGroup()->activateNext();
}
void Workspace::switchToTabPopupAboutToShow()
static QString shortCaption(const QString &s)
{
if (!switch_to_tab_popup)
return;
if (s.length() < 64)
return s;
QString ss = s;
return ss.replace(32,s.length()-64,"...");
}
void Workspace::rebuildTabListPopup()
{
Q_ASSERT(switch_to_tab_popup);
switch_to_tab_popup->clear();
QAction* action = switch_to_tab_popup->addAction(i18n("To the Left"));
action->setData(0);
action = switch_to_tab_popup->addAction(i18n("To the Right"));
action->setData(1);
// whatever happens "0x1" and "0x2" are no heap positions ;-)
switch_to_tab_popup->addAction(i18nc("Switch to tab -> Previous", "Previous"))->setData(1);
switch_to_tab_popup->addAction(i18nc("Switch to tab -> Next", "Next"))->setData(2);
switch_to_tab_popup->addSeparator();
int index = 2;
foreach (Client * c, active_popup_client->clientGroup()->clients()) {
if (c != active_popup_client->clientGroup()->visible()) {
action = switch_to_tab_popup->addAction(c->caption());
action->setData(index);
}
index++;
for (QList<Client*>::const_iterator i = active_popup_client->tabGroup()->clients().constBegin(),
end = active_popup_client->tabGroup()->clients().constEnd(); i != end; ++i) {
if ((*i)->noBorder() || *i == active_popup_client->tabGroup()->current())
continue; // cannot tab there anyway
switch_to_tab_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
}
}
void Workspace::initAddToTabGroup()
void Workspace::entabPopupClient(QAction* action)
{
if (add_tabs_popup)
if (!active_popup_client || !action->data().isValid())
return;
add_tabs_popup = new QMenu(popup);
add_tabs_popup->setFont(KGlobalSettings::menuFont());
connect(add_tabs_popup, SIGNAL(triggered(QAction*)),
this, SLOT(slotAddToTabGroup(QAction*))); // Merge to a group
connect(add_tabs_popup, SIGNAL(aboutToShow()),
this, SLOT(groupTabPopupAboutToShow())); // Show the possible groups to add
QAction* action = add_tabs_popup->menuAction();
popup->insertAction(mRemoveTabGroup, action);
action->setText(i18n("Move Window to Group"));
Client *other = action->data().value<Client*>();
if (!clients.contains(other)) // might have been lost betwenn pop-up and selection
return;
active_popup_client->tabBehind(other, true);
if (options->focusPolicyIsReasonable())
requestFocus(active_popup_client);
}
void Workspace::slotAddToTabGroup(QAction* action)
void Workspace::rebuildTabGroupPopup()
{
if (!action->data().isValid() || !active_popup_client->clientGroup())
return;
moveItemToClientGroup(active_popup_client->clientGroup(),
active_popup_client->clientGroup()->indexOfClient(active_popup_client),
clientGroups[action->data().toInt()], -1);
}
Q_ASSERT(add_tabs_popup);
void Workspace::groupTabPopupAboutToShow()
{
if (!add_tabs_popup)
return;
add_tabs_popup->clear();
int index = 0;
for (QList<ClientGroup*>::const_iterator i = clientGroups.constBegin(); i != clientGroups.constEnd(); ++i, index++) {
if (!(*i)->contains(active_popup_client)) {
QAction* action = add_tabs_popup->addAction((*i)->visible()->caption());
action->setData(index);
}
QList<Client*> handled;
for (QList<Client*>::const_iterator i = clientList().constBegin(), end = clientList().constEnd(); i != end; ++i) {
if (*i == active_popup_client || (*i)->noBorder())
continue;
add_tabs_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
}
}
void Workspace::initTabbingPopups()
{
bool needTabManagers = false;
if (active_popup_client->tabGroup() && active_popup_client->tabGroup()->count() > 1) {
needTabManagers = true;
if (!switch_to_tab_popup) {
switch_to_tab_popup = new QMenu(i18n("Switch to Tab"), popup);
switch_to_tab_popup->setFont(KGlobalSettings::menuFont());
connect(switch_to_tab_popup, SIGNAL(triggered(QAction*)), SLOT(selectPopupClientTab(QAction*)));
connect(switch_to_tab_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabListPopup()));
popup->insertMenu(mRemoveFromTabGroup, switch_to_tab_popup);
}
} else {
delete switch_to_tab_popup;
switch_to_tab_popup = 0;
}
if (!add_tabs_popup) {
add_tabs_popup = new QMenu(i18n("Tab behind..."), popup);
add_tabs_popup->setFont(KGlobalSettings::menuFont());
connect(add_tabs_popup, SIGNAL(triggered(QAction*)), SLOT(entabPopupClient(QAction*)));
connect(add_tabs_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabGroupPopup()));
popup->insertMenu(mRemoveFromTabGroup, add_tabs_popup);
}
mRemoveFromTabGroup->setVisible(needTabManagers);
mCloseTabGroup->setVisible(needTabManagers);
}
void Workspace::initDesktopPopup()
{
if (desk_popup)
@ -739,36 +730,24 @@ void Workspace::performWindowOperation(Client* c, Options::WindowOperation op)
case Options::LowerOp:
lowerClient(c);
break;
case Options::ClientGroupDragOp: // Handled by decoration itself
case Options::TabDragOp: // Handled by decoration itself
case Options::NoOp:
break;
case Options::RemoveClientFromGroupOp:
c->clientGroup()->remove(c);
case Options::RemoveTabFromGroupOp:
if (c->untab())
if (options->focusPolicyIsReasonable())
takeActivity(c, ActivityFocus | ActivityRaise, true);
break;
case Options::MoveClientInGroupLeftOp: {
if (c->clientGroup()) {
int c_id = c->clientGroup()->indexOfClient(c);
int size = c->clientGroup()->clients().count();
if (c_id > 0)
c->clientGroup()->setVisible(c_id - 1);
else
c->clientGroup()->setVisible(size - 1);
}
case Options::ActivateNextTabOp:
if (c->tabGroup())
c->tabGroup()->activateNext();
break;
}
case Options::MoveClientInGroupRightOp: {
if (c->clientGroup()) {
int c_id = c->clientGroup()->indexOfClient(c);
int size = c->clientGroup()->clients().count();
if (c_id < size - 1)
c->clientGroup()->setVisible(c_id + 1);
else
c->clientGroup()->setVisible(0);
}
case Options::ActivatePreviousTabOp:
if (c->tabGroup())
c->tabGroup()->activatePrev();
break;
}
case Options::CloseClientGroupOp:
c->clientGroup()->closeAll();
case Options::CloseTabGroupOp:
c->tabGroup()->closeAll();
case Options::ToggleClientTiledStateOp: {
#ifdef KWIN_BUILD_TILING
int desktop = c->desktop();
@ -799,8 +778,8 @@ Options::WindowOperation Client::mouseButtonToWindowOperation(Qt::MouseButtons b
com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
// TODO: Complete the list
if (com == Options::MouseClientGroupDrag)
return Options::ClientGroupDragOp;
if (com == Options::MouseDragTab)
return Options::TabDragOp;
if (com == Options::MouseOperationsMenu)
return Options::OperationsOp;
return Options::NoOp;
@ -970,28 +949,18 @@ bool Client::performMouseCommand(Options::MouseCommand command, const QPoint &gl
if (!isDesktop()) // No point in changing the opacity of the desktop
setOpacity(qMax(opacity() - 0.1, 0.1));
break;
case Options::MouseLeftGroupWindow: {
int c_id = clientGroup()->indexOfClient(this);
int size = clientGroup()->clients().count();
if (c_id > 0)
clientGroup()->setVisible(c_id - 1);
else
clientGroup()->setVisible(size - 1);
}
case Options::MousePreviousTab:
if (tabGroup())
tabGroup()->activatePrev();
break;
case Options::MouseRightGroupWindow: {
int c_id = clientGroup()->indexOfClient(this);
int size = clientGroup()->clients().count();
if (c_id < size - 1)
clientGroup()->setVisible(c_id + 1);
else
clientGroup()->setVisible(0);
}
case Options::MouseNextTab:
if (tabGroup())
tabGroup()->activateNext();
break;
case Options::MouseClose:
closeWindow();
break;
case Options::MouseClientGroupDrag:
case Options::MouseDragTab:
case Options::MouseNothing:
replay = true;
break;
@ -1405,35 +1374,22 @@ void Workspace::slotWindowToDesktopDown()
}
}
void Workspace::slotSwitchToTabRight()
void Workspace::slotActivateNextTab()
{
if (!active_client || !active_client->clientGroup())
return;
int c_id = active_client->clientGroup()->indexOfClient(active_client);
int size = active_client->clientGroup()->clients().count();
if (c_id < size - 1)
active_client->clientGroup()->setVisible(c_id + 1);
else
active_client->clientGroup()->setVisible(0);
if (active_client && active_client->tabGroup())
active_client->tabGroup()->activateNext();
}
void Workspace::slotSwitchToTabLeft()
void Workspace::slotActivatePrevTab()
{
if (!active_client || !active_client->clientGroup())
return;
int c_id = active_client->clientGroup()->indexOfClient(active_client);
int size = active_client->clientGroup()->clients().count();
if (c_id > 0)
active_client->clientGroup()->setVisible(c_id - 1);
else
active_client->clientGroup()->setVisible(size - 1);
if (active_client && active_client->tabGroup())
active_client->tabGroup()->activatePrev();
}
void Workspace::slotRemoveFromGroup()
void Workspace::slotUntab()
{
if (!active_client || !active_client->clientGroup())
return;
active_client->clientGroup()->remove(active_client);
if (active_client)
active_client->untab();
}
/*!

View File

@ -632,6 +632,8 @@ void Workspace::removeClient(Client* c, allowed_t)
if (c == active_popup_client)
closeActivePopup();
c->untab();
if (client_keys_client == c)
setupWindowShortcutDone(false);
if (!c->shortcut().isEmpty()) {
@ -782,7 +784,7 @@ void Workspace::updateToolWindows(bool also_hide)
// TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
if (!options->hideUtilityWindowsForInactive) {
for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
if (!(*it)->clientGroup() || (*it)->clientGroup()->visible() == *it)
if (!(*it)->tabGroup() || (*it)->tabGroup()->current() == *it)
(*it)->hideClient(false);
return;
}
@ -952,21 +954,18 @@ void Workspace::slotReconfigure()
//curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() );
//curtain.show();
for (ClientList::ConstIterator it = clients.constBegin();
it != clients.constEnd();
++it)
for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
(*it)->updateDecoration(true, true);
// If the new decoration doesn't supports tabs then ungroup clients
if (!decorationSupportsClientGrouping()) {
QList<ClientGroup*> tmpGroups = clientGroups; // Prevent crashing
for (QList<ClientGroup*>::const_iterator i = tmpGroups.constBegin(); i != tmpGroups.constEnd(); ++i)
(*i)->removeAll();
if (!decorationSupportsTabbing()) {
foreach (Client * c, clients)
c->untab();
}
mgr->destroyPreviousPlugin();
} else {
forEachClient(CheckBorderSizesProcedure());
foreach (Client * c, clients)
c->triggerDecorationRepaint();
c->triggerDecorationRepaint();
}
#ifdef KWIN_BUILD_SCREENEDGES
@ -2081,87 +2080,6 @@ void Workspace::checkCursorPos()
}
}
int Workspace::indexOfClientGroup(ClientGroup* group)
{
return clientGroups.indexOf(group);
}
void Workspace::moveItemToClientGroup(ClientGroup* oldGroup, int oldIndex,
ClientGroup* group, int index)
{
Client* c = oldGroup->clients().at(oldIndex);
group->add(c, index, true);
}
void Workspace::removeClientGroup(ClientGroup* group)
{
int index = clientGroups.indexOf(group);
if (index == -1) {
return;
}
clientGroups.removeAt(index);
for (; index < clientGroups.size(); index++) {
foreach (Client *c, clientGroups.at(index)->clients()) {
c->setClientGroup(c->clientGroup());
}
}
}
// To accept "mainwindow#1" to "mainwindow#2"
static QByteArray truncatedWindowRole(QByteArray a)
{
int i = a.indexOf('#');
if (i == -1)
return a;
QByteArray b(a);
b.truncate(i);
return b;
}
Client* Workspace::findSimilarClient(Client* c)
{
// Attempt to find a similar window to the input. If we find multiple possibilities that are in
// different groups then ignore all of them. This function is for automatic window grouping.
Client* found = NULL;
// See if the window has a group ID to match with
QString wGId = c->rules()->checkAutogroupById(QString());
if (!wGId.isEmpty()) {
foreach (Client * cl, clients) {
if (wGId == cl->rules()->checkAutogroupById(QString())) {
if (found && found->clientGroup() != cl->clientGroup()) { // We've found two, ignore both
found = NULL;
break; // Continue to the next test
}
found = cl;
}
}
if (found)
return found;
}
// If this is a transient window don't take a guess
if (c->isTransient())
return NULL;
// If we don't have an ID take a guess
if (c->rules()->checkAutogrouping(options->autogroupSimilarWindows)) {
QByteArray wRole = truncatedWindowRole(c->windowRole());
foreach (Client * cl, clients) {
QByteArray wRoleB = truncatedWindowRole(cl->windowRole());
if (c->resourceClass() == cl->resourceClass() && // Same resource class
wRole == wRoleB && // Same window role
cl->isNormalWindow()) { // Normal window TODO: Can modal windows be "normal"?
if (found && found->clientGroup() != cl->clientGroup()) // We've found two, ignore both
return NULL;
found = cl;
}
}
}
return found;
}
Outline* Workspace::outline()
{

View File

@ -78,7 +78,6 @@ class Tile;
class Tiling;
class TilingLayout;
#endif
class ClientGroup;
#ifdef KWIN_BUILD_DESKTOPCHANGEOSD
class DesktopChangeOSD;
#endif
@ -367,16 +366,6 @@ public:
return client_keys;
}
// Tabbing
void addClientGroup(ClientGroup* group);
void removeClientGroup(ClientGroup* group);
/// Returns the index of c in clientGroupList.
int indexOfClientGroup(ClientGroup* group);
/// Change the client c_id to the group with index g_id
void moveItemToClientGroup(ClientGroup* oldGroup, int oldIndex, ClientGroup* group, int index = -1);
Client* findSimilarClient(Client* c);
QList<ClientGroup*> clientGroups; // List of existing clients groups with no special order
/**
* Returns the list of clients sorted in stacking order, with topmost client
* at the last position
@ -440,7 +429,7 @@ public:
bool hasDecorationShadows() const;
Qt::Corner decorationCloseButtonCorner();
bool decorationHasAlpha() const;
bool decorationSupportsClientGrouping() const; // Returns true if the decoration supports tabs.
bool decorationSupportsTabbing() const; // Returns true if the decoration supports tabs.
bool decorationSupportsFrameOverlap() const;
bool decorationSupportsBlurBehind() const;
@ -632,15 +621,15 @@ public slots:
// NOTE: debug method
void dumpTiles() const;
void slotSwitchToTabLeft(); // Slot to move left the active Client.
void slotSwitchToTabRight(); // Slot to move right the active Client.
void slotRemoveFromGroup(); // Slot to remove the active client from its group.
void slotActivateNextTab(); // Slot to move left the active Client.
void slotActivatePrevTab(); // Slot to move right the active Client.
void slotUntab(); // Slot to remove the active client from its group.
private slots:
void groupTabPopupAboutToShow(); // Popup to add to another group
void switchToTabPopupAboutToShow(); // Popup to move in the group
void slotAddToTabGroup(QAction*); // Add client to a group
void slotSwitchToTab(QAction*); // Change the tab
void rebuildTabGroupPopup();
void rebuildTabListPopup();
void entabPopupClient(QAction*);
void selectPopupClientTab(QAction*);
void desktopPopupAboutToShow();
void activityPopupAboutToShow();
void clientPopupAboutToShow();
@ -699,6 +688,7 @@ private:
void initShortcuts();
void initDesktopPopup();
void initActivityPopup();
void initTabbingPopups();
void restartKWin(const QString &reason);
void discardPopup();
void setupWindowShortcut(Client* c);
@ -842,16 +832,13 @@ private:
QAction* mNoBorderOpAction;
QAction* mMinimizeOpAction;
QAction* mCloseOpAction;
QAction* mRemoveTabGroup; // Remove client from group
QAction* mCloseGroup; // Close all clients in the group
QAction* mRemoveFromTabGroup; // Remove client from group
QAction* mCloseTabGroup; // Close all clients in the group
ShortcutDialog* client_keys_dialog;
Client* client_keys_client;
bool global_shortcuts_disabled;
bool global_shortcuts_disabled_for_client;
void initAddToTabGroup(); // Load options for menu add_tabs_popup
void initSwitchToTab(); // Load options for menu switch_to_tab_popup
PluginMgr* mgr;
RootInfo* rootInfo;
@ -1194,12 +1181,12 @@ inline bool Workspace::decorationHasAlpha() const
return mgr->factory()->supports(AbilityUsesAlphaChannel);
}
inline bool Workspace::decorationSupportsClientGrouping() const
inline bool Workspace::decorationSupportsTabbing() const
{
if (!hasDecorationPlugin()) {
return false;
}
return mgr->factory()->supports(AbilityClientGrouping);
return mgr->factory()->supports(AbilityTabbing);
}
inline bool Workspace::decorationSupportsFrameOverlap() const
@ -1218,11 +1205,6 @@ inline bool Workspace::decorationSupportsBlurBehind() const
return mgr->factory()->supports(AbilityUsesBlurBehind);
}
inline void Workspace::addClientGroup(ClientGroup* group)
{
clientGroups.append(group);
}
} // namespace
#endif