Merged r970865:1049322 from /branches/work/kwin-tabbing

Adds window tabbing support to KWin.
FEATURE: 42023

svn path=/trunk/KDE/kdebase/workspace/; revision=1049334
icc-effect-5.14.5
Lucas Murray 2009-11-15 03:24:04 +00:00
parent 3c63918fd5
commit a0d07d12a2
78 changed files with 5185 additions and 138 deletions

View File

@ -59,6 +59,7 @@ endif( KWIN_HAVE_COMPOSITING )
set(kwin_KDEINIT_SRCS
workspace.cpp
client.cpp
clientgroup.cpp
placement.cpp
atoms.cpp
utils.cpp

View File

@ -367,6 +367,13 @@ void Workspace::takeActivity( Client* c, int flags, bool handled )
}
if( !c->isShown( true )) // shouldn't happen, call activateClient() if needed
{
int group_size = c->clientGroup()->clients().count();
if( group_size > 1 && c->clientGroup()->visible() != c ) // the tab is hidden, make it visible and call this function again
{
c->clientGroup()->setVisible( c );
takeActivity( c, flags, handled );
return;
}
kWarning( 1212 ) << "takeActivity: not shown" ;
return;
}

View File

@ -208,4 +208,64 @@ bool Bridge::compositingActive() const
return c->workspace()->compositingActive();
}
bool Bridge::isClientGroupActive()
{
return c->clientGroup()->containsActiveClient();
}
QList< ClientGroupItem > Bridge::clientGroupItems() const
{
return c->clientGroup()->items();
}
int Bridge::itemId( int index )
{
const ClientList list = c->clientGroup()->clients();
return reinterpret_cast<int>( list.at( index ));
}
int Bridge::visibleClientGroupItem()
{
return c->clientGroup()->indexOfVisibleClient();
}
void Bridge::setVisibleClientGroupItem( int index )
{
c->clientGroup()->setVisible( index );
}
void Bridge::moveItemInClientGroup( int index, int before )
{
c->clientGroup()->move( index, before );
}
void Bridge::moveItemToClientGroup( int itemId, int before )
{
Client* item = reinterpret_cast<Client*>( itemId );
c->workspace()->moveItemToClientGroup( item->clientGroup(), item->clientGroup()->indexOfClient( item ),
c->clientGroup(), before );
}
void Bridge::removeFromClientGroup( int index, const QRect& newGeom )
{
c->clientGroup()->remove( index, newGeom );
}
void Bridge::closeClientGroupItem( int index )
{
const ClientList list = c->clientGroup()->clients();
if( index >= 0 || index <= list.count() )
list.at( index )->closeWindow();
}
void Bridge::closeAllInClientGroup()
{
c->clientGroup()->closeAll();
}
void Bridge::displayClientMenu( int index, const QPoint& pos )
{
c->clientGroup()->displayClientMenu( index, pos );
}
} // namespace

View File

@ -76,6 +76,20 @@ class Bridge : public KDecorationBridgeUnstable
virtual void grabXServer( bool grab );
virtual bool compositingActive() const;
// Window tabbing
virtual bool isClientGroupActive();
virtual QList< ClientGroupItem > clientGroupItems() const;
virtual int itemId( int index );
virtual int visibleClientGroupItem();
virtual void setVisibleClientGroupItem( int index );
virtual void moveItemInClientGroup( int index, int before );
virtual void moveItemToClientGroup( int 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 );
private:
Client* c;
};

View File

@ -97,6 +97,7 @@ Client::Client( Workspace* ws )
, delayedMoveResizeTimer( NULL )
, in_group( NULL )
, window_group( None )
, client_group( NULL )
, in_layer( UnknownLayer )
, ping_timer( NULL )
, process_killer( NULL )
@ -244,6 +245,8 @@ void Client::releaseWindow( bool on_shutdown )
XUnmapWindow( display(), frameId()); // Destroying decoration would cause ugly visual effect
destroyDecoration();
cleanGrouping();
if( clientGroup() )
clientGroup()->remove( this );
if( !on_shutdown )
{
workspace()->removeClient( this, Allowed );
@ -306,6 +309,8 @@ void Client::destroyClient()
workspace()->clientHidden( this );
destroyDecoration();
cleanGrouping();
if( clientGroup() )
clientGroup()->remove( this );
workspace()->removeClient( this, Allowed );
client = None; // invalidate
XDestroyWindow( display(), wrapper );
@ -656,7 +661,7 @@ bool Client::noBorder() const
bool Client::userCanSetNoBorder() const
{
return !isFullScreen() && !isShade();
return !isFullScreen() && !isShade() && ( clientGroup() == NULL || !(clientGroup()->items().count() > 1));
}
void Client::setNoBorder( bool set )
@ -849,6 +854,10 @@ void Client::minimize( bool avoid_animation )
workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
if( effects && !avoid_animation ) // TODO: Shouldn't it tell effects at least about the change?
static_cast<EffectsHandlerImpl*>(effects)->windowMinimized( effectWindow());
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::unminimize( bool avoid_animation )
@ -864,6 +873,10 @@ void Client::unminimize( bool avoid_animation )
updateWindowRules();
if( effects && !avoid_animation )
static_cast<EffectsHandlerImpl*>( effects )->windowUnminimized( effectWindow() );
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
QRect Client::iconGeometry() const
@ -967,6 +980,10 @@ void Client::setShade( ShadeMode mode )
workspace()->updateMinimizedOfTransients( this );
decoration->shadeChange();
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::shadeHover()
@ -996,7 +1013,7 @@ void Client::updateVisibility()
{
if( deleting )
return;
if( hidden )
if( hidden && ( clientGroup() == NULL || clientGroup()->visible() == this ))
{
info->setState( NET::Hidden, NET::Hidden );
setSkipTaskbar( true, false ); // Also hide from taskbar
@ -1006,7 +1023,8 @@ void Client::updateVisibility()
internalHide( Allowed );
return;
}
setSkipTaskbar( original_skip_taskbar, false ); // Reset from 'hidden'
if( clientGroup() == NULL || clientGroup()->visible() == this )
setSkipTaskbar( original_skip_taskbar, false ); // Reset from 'hidden'
if( minimized )
{
info->setState( NET::Hidden, NET::Hidden );
@ -1396,6 +1414,10 @@ void Client::setDesktop( int desktop )
workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
updateVisibility();
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
/**
@ -1418,6 +1440,10 @@ void Client::setOnAllDesktops( bool b )
setDesktop( NET::OnAllDesktops );
else
setDesktop( workspace()->currentDesktop());
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
/**
@ -1568,7 +1594,10 @@ void Client::setCaption( const QString& _s, bool force )
info->setVisibleIconName( ( cap_iconic + cap_suffix ).toUtf8() );
if( isManaged() && decoration != NULL )
{
client_group->updateItems();
decoration->captionChange();
}
}
}
@ -1606,6 +1635,37 @@ QString Client::caption( bool full ) const
return full ? cap_normal + cap_suffix : cap_normal;
}
void Client::dontMoveResize()
{
buttonDown = false;
stopDelayedMoveResize();
if( moveResizeMode )
finishMoveResize( false );
}
void Client::setClientShown( bool shown )
{
if( !shown )
{
unmap( Allowed );
hidden = true;
//updateVisibility();
//updateAllowedActions();
workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
addWorkspaceRepaint( visibleRect() );
}
else
{
map( Allowed );
hidden = false;
updateVisibility();
//updateAllowedActions();
takeFocus( Allowed );
autoRaise();
workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
}
}
void Client::getWMHints()
{
XWMHints* hints = XGetWMHints( display(), window() );

View File

@ -41,6 +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"
#ifdef HAVE_XSYNC
#include <X11/extensions/sync.h>
@ -301,6 +302,24 @@ class Client
StrutRect strutRect( StrutArea area ) const;
StrutRects strutRects() const;
bool hasStrut() const;
// Tabbing functions
ClientGroup* clientGroup() const; // Returns a pointer to client_group
void setClientGroup( ClientGroup* group );
/*
* If shown is true the client is mapped and raised, if false
* the client is unmapped and hidden, this function is called
* when the tabbing group of the client switches its visible
* client.
*/
void setClientShown( bool shown );
/*
* When a click is done in the decoration and it calls the group
* to change the visible client it starts to move-resize the new
* client, this function stops it.
*/
void dontMoveResize();
/**
* Whether or not the window has a strut that expands through the invisible area of
* an xinerama setup where the monitors are not the same resolution.
@ -570,6 +589,7 @@ class Client
QString cap_normal, cap_iconic, cap_suffix;
Group* in_group;
Window window_group;
ClientGroup* client_group;
Layer in_layer;
QTimer* ping_timer;
QProcess* process_killer;
@ -703,6 +723,16 @@ inline Group* Client::group()
return in_group;
}
inline void Client::setClientGroup( ClientGroup* group )
{
client_group = group;
}
inline ClientGroup* Client::clientGroup() const
{
return client_group;
}
inline bool Client::isMinimized() const
{
return minimized;
@ -715,7 +745,8 @@ inline bool Client::isActive() const
inline bool Client::isShown( bool shaded_is_shown ) const
{
return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden;
return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden &&
( clientGroup() == NULL || clientGroup()->visible() == this );
}
inline bool Client::isHiddenInternal() const

291
clientgroup.cpp Normal file
View File

@ -0,0 +1,291 @@
/*******************************************************************************
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;
// Client must not already be grouped
assert( c->clientGroup()->clients().size() == 1 );
// Tabbed windows MUST have a decoration
if( c->noBorder() )
c->setNoBorder( false );
if( clients_[visible_]->noBorder() )
clients_[visible_]->setNoBorder( false );
// Notify effects of merge
if( effects != NULL )
static_cast<EffectsHandlerImpl*>(effects)->clientGroupItemAdded(
c->effectWindow(), clients_[visible_]->effectWindow() );
// Delete old group and update
delete c->clientGroup(); // Delete old group as it's now empty
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 );
c->setGeometry( clients_[visible_]->geometry() );
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 )
{
remove( clients_[index], newGeom );
}
void ClientGroup::remove( Client* c, const QRect& newGeom )
{
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)->clientGroupItemRemoved(
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( new ClientGroup( c ));
if( newGeom.isValid() )
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 )
{
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)->clientGroupItemSwitched(
clients_[visible_]->effectWindow(), c->effectWindow() );
visible_ = indexOfClient( c );
c->setClientShown( true );
for( ClientList::const_iterator i = clients_.begin(); i != clients_.end(); i++ )
if( (*i) != c )
(*i)->setClientShown( false );
}
void ClientGroup::updateStates( Client* main, Client* only )
{
for( ClientList::const_iterator i = clients_.begin(); i != clients_.end(); i++ )
if( (*i) != main && ( !only || (*i) == only ))
{
if( (*i)->isMinimized() != main->isMinimized() )
{
if( main->isMinimized() )
(*i)->minimize( true );
else
(*i)->unminimize( true );
}
if( (*i)->isShade() != main->isShade() )
(*i)->setShade( main->isShade() ? ShadeNormal : ShadeNone );
if( (*i)->geometry() != main->geometry() )
(*i)->setGeometry( main->geometry() );
if( (*i)->desktop() != main->desktop() )
(*i)->setDesktop( main->desktop() );
if( (*i)->isOnAllDesktops() != main->isOnAllDesktops() )
(*i)->setOnAllDesktops( main->isOnAllDesktops() );
if( (*i)->keepAbove() != main->keepAbove() )
(*i)->setKeepAbove( main->keepAbove() );
if( (*i)->keepBelow() != main->keepBelow() )
(*i)->setKeepBelow( main->keepBelow() );
}
}
void ClientGroup::updateItems()
{
items_.clear();
for( ClientList::const_iterator i = clients_.begin(); i != clients_.end(); 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_.begin(); i != clients_.end(); 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_;
}
// Ensure all windows are within these sizes
const QSize size = clients_[visible_]->size();
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_.begin(); i != clients_.end(); 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(), newSize ));
}
}

220
clientgroup.h Normal file
View File

@ -0,0 +1,220 @@
/*******************************************************************************
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 "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.
*
* If a group contains multiple clients then only one will ever be mapped at
* any given time.
*/
class ClientGroup
{
public:
/**
* Creates a new group containing \p c.
*/
ClientGroup( Client* c );
~ClientGroup();
/**
* 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( 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.
*/
void remove( int index, const QRect& newGeom = QRect() );
/**
* 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.
*/
void remove( Client* c, const QRect& newGeom = QRect() );
/**
* 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( Client* c, 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( Client* c, const QPoint& pos );
/**
* Returns the list index of \p c.
*/
int indexOfClient( 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.
*/
bool contains( 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.
*/
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 );
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 ClientList ClientGroup::clients() const
{
return clients_;
}
inline QList< ClientGroupItem > ClientGroup::items() const
{
return items_;
}
inline Client* ClientGroup::visible()
{
return clients_[visible_];
}
inline QSize ClientGroup::minSize() const
{
return minSize_;
}
inline QSize ClientGroup::maxSize() const
{
return maxSize_;
}
}
#endif

View File

@ -10,3 +10,4 @@ add_subdirectory( oxygen )
add_subdirectory( quartz )
add_subdirectory( redmond )
add_subdirectory( web )
add_subdirectory( tabstrip )

View File

@ -9,6 +9,7 @@ set(kwin_oxygen_SRCS
oxygen.cpp
oxygenbutton.cpp
oxygenclient.cpp
oxygenclientgroupitemdata.cpp
oxygenconfiguration.cpp
oxygenexception.cpp
oxygenexceptionlist.cpp

View File

@ -129,6 +129,7 @@ namespace Oxygen
configurationGroup.writeEntry( OxygenConfig::DRAW_SEPARATOR, userInterface_->ui.drawSeparator->isChecked() );
configurationGroup.writeEntry( OxygenConfig::DRAW_TITLE_OUTLINE, userInterface_->ui.titleOutline->isChecked() );
configurationGroup.writeEntry( OxygenConfig::USE_OXYGEN_SHADOWS, userInterface_->ui.useOxygenShadows->isChecked() );
configurationGroup.writeEntry( OxygenConfig::TABS_ENABLED, userInterface_->ui.tabsEnabled->isChecked() );
// write exceptions
userInterface_->ui.exceptions->exceptions().write( *configuration_ );
@ -191,6 +192,7 @@ namespace Oxygen
userInterface_->ui.drawSeparator->setChecked( configuration.drawSeparator() );
userInterface_->ui.titleOutline->setChecked( configuration.drawTitleOutline() );
userInterface_->ui.useOxygenShadows->setChecked( configuration.useOxygenShadows() );
userInterface_->ui.tabsEnabled->setChecked( configuration.tabsEnabled() );
}

View File

@ -109,6 +109,7 @@ namespace Oxygen
connect( ui.blendColor, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()) );
connect( ui.sizeGripMode, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()) );
connect( ui.tabsEnabled, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui.drawSeparator, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui.titleOutline, SIGNAL(clicked()), SIGNAL(changed()) );
connect( ui.useOxygenShadows, SIGNAL(clicked()), SIGNAL(changed()) );

View File

@ -73,7 +73,7 @@
</property>
</spacer>
</item>
<item row="3" column="1">
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -86,6 +86,13 @@
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="tabsEnabled">
<property name="text">
<string>Enable window grouping</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">

View File

@ -60,11 +60,7 @@ namespace Oxygen
//___________________________________________________
KDecoration* OxygenFactory::createDecoration(KDecorationBridge* bridge )
{
OxygenClient* client( new OxygenClient( bridge, this ) );
connect( this, SIGNAL( configurationChanged() ), client, SLOT( resetConfiguration() ) );
return client->decoration();
}
{ return (new OxygenClient( bridge, this ))->decoration(); }
//___________________________________________________
bool OxygenFactory::reset(unsigned long changed)
@ -75,7 +71,6 @@ namespace Oxygen
bool configuration_changed = readConfig();
setInitialized( true );
emit configurationChanged();
if( configuration_changed || (changed & (SettingDecoration | SettingButtons | SettingBorder)) )
{
@ -174,6 +169,10 @@ namespace Oxygen
case AbilityUsesAlphaChannel:
return true;
// tabs
case AbilityClientGrouping:
return defaultConfiguration().tabsEnabled();
// no colors supported at this time
default:
return false;

View File

@ -62,7 +62,6 @@ namespace Oxygen
// shows the window menu for one tab
ButtonItemMenu
};
Q_DECLARE_FLAGS(ButtonTypes, ButtonType)
@ -82,10 +81,6 @@ namespace Oxygen
HFRAMESIZE = 4
};
#if !KDE_IS_VERSION(4,3,90)
enum{ SettingCompositing = 1 << 6 };
#endif
//! window decoration factory
class OxygenFactory: public QObject, public KDecorationFactoryUnstable
{
@ -124,18 +119,13 @@ namespace Oxygen
//! get configuration for a give client
virtual OxygenConfiguration configuration( const OxygenClient& );
signals:
//! configuration has changed
void configurationChanged( void );
private:
//! read configuration from KConfig
bool readConfig();
//! default configuration
const OxygenConfiguration& defaultConfiguration( void )
const OxygenConfiguration& defaultConfiguration( void ) const
{ return defaultConfiguration_; }
//! initialization

View File

@ -38,7 +38,6 @@
#include <KColorUtils>
#include <KColorScheme>
#include <kcommondecoration.h>
#include <KDebug>
namespace Oxygen
{
@ -52,7 +51,7 @@ namespace Oxygen
helper_( parent.helper() ),
type_(type),
forceInactive_( false ),
timeLine_( 200, this )
timeLine_( 150, this )
{
setAutoFillBackground(false);
setAttribute(Qt::WA_NoSystemBackground);
@ -69,6 +68,7 @@ namespace Oxygen
timeLine_.setCurveShape( QTimeLine::EaseInOutCurve );
connect( &timeLine_, SIGNAL( frameChanged( int ) ), SLOT( update() ) );
connect( &timeLine_, SIGNAL( finished() ), SLOT( update() ) );
reset(0);
}
@ -79,11 +79,11 @@ namespace Oxygen
//_______________________________________________
QColor OxygenButton::buttonDetailColor(const QPalette &palette)
{
if( client_.timeLineIsRunning() && !forceInactive_ ) return KColorUtils::mix(
if( client_.timeLineIsRunning() && !forceInactive_ && !client_.isForcedActive()) return KColorUtils::mix(
buttonDetailColor( palette, false ),
buttonDetailColor( palette, true ),
client_.opacity() );
else return buttonDetailColor( palette, isActive() );
else return buttonDetailColor( palette, isActive() || client_.isForcedActive() );
}
//_______________________________________________
@ -115,6 +115,12 @@ namespace Oxygen
return QSize( size, size );
}
//___________________________________________________
void OxygenButton::reset( unsigned long )
{
timeLine_.setDuration( client_.configuration().animationsDuration() );
}
//___________________________________________________
void OxygenButton::enterEvent(QEvent *e)
{

View File

@ -63,10 +63,6 @@ namespace Oxygen
//! destructor
QSize sizeHint() const;
//! reset
void reset(long unsigned int)
{repaint();}
//! button type
ButtonType type( void ) const
{ return type_; }
@ -76,6 +72,9 @@ namespace Oxygen
void setForceInactive( const bool& value )
{ forceInactive_ = value; }
//! configuration reset
virtual void reset( unsigned long );
protected:
//! press event

View File

@ -36,14 +36,13 @@
#include <cassert>
#include <cmath>
#include <kdeversion.h>
#include <KLocale>
#include <KColorUtils>
#include <KDebug>
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QPainter>
#include <QtGui/QApplication>
#include <QtGui/QBitmap>
namespace Oxygen
{
@ -86,7 +85,11 @@ namespace Oxygen
sizeGrip_( 0 ),
timeLine_( 200, this ),
titleTimeLine_( 200, this ),
initialized_( false )
initialized_( false ),
forceActive_( false ),
mouseButton_( Qt::NoButton ),
itemData_( this ),
sourceItem_( -1 )
{}
//___________________________________________
@ -109,12 +112,14 @@ namespace Oxygen
KCommonDecoration::init();
widget()->setAttribute(Qt::WA_NoSystemBackground );
widget()->setAutoFillBackground( false );
widget()->setAcceptDrops( true );
// initialize timeLine
timeLine_.setFrameRange( maxAnimationIndex/5, maxAnimationIndex );
timeLine_.setCurveShape( QTimeLine::EaseOutCurve );
connect( &timeLine_, SIGNAL( frameChanged( int ) ), widget(), SLOT( update() ) );
connect( &timeLine_, SIGNAL( finished() ), widget(), SLOT( update() ) );
connect( &timeLine_, SIGNAL( finished() ), this, SLOT( clearForceActive() ) );
// initialize titleTimeLine
titleTimeLine_.setFrameRange( 0, maxAnimationIndex );
@ -123,7 +128,8 @@ namespace Oxygen
connect( &titleTimeLine_, SIGNAL( finished() ), widget(), SLOT( update() ) );
connect( &titleTimeLine_, SIGNAL( finished() ), this, SLOT( updateOldCaption() ) );
initialized_ = true;
// lists
connect( &itemData_.timeLine(), SIGNAL( finished() ), this, SLOT( clearTargetItem() ) );
// in case of preview, one wants to make the label used
// for the central widget transparent. This allows one to have
@ -139,20 +145,57 @@ namespace Oxygen
}
resetConfiguration();
initialized_ = true;
// first reset is needed to store Oxygen configuration
reset(0);
}
//___________________________________________
void OxygenClient::reset( unsigned long changed )
{
KCommonDecorationUnstable::reset( changed );
// update window mask when compositing is changed
if( !initialized_ ) return;
if( changed & SettingCompositing )
{
updateWindowShape();
widget()->update();
}
KCommonDecorationUnstable::reset( changed );
configuration_ = factory_->configuration( *this );
// animations duration
timeLine_.setDuration( configuration_.animationsDuration() );
titleTimeLine_.setDuration( configuration_.animationsDuration() );
itemData_.timeLine().setDuration( configuration_.animationsDuration() );
// should also update animations for buttons
resetButtons();
// also reset tab buttons
for( int index = 0; index < itemData_.count(); index++ )
{
ClientGroupItemData& item( itemData_[index] );
if( item.closeButton_ ) { item.closeButton_.data()->reset(0); }
}
// copy current caption to old
updateOldCaption();
// reset tab geometry
itemData_.setDirty( true );
// handle size grip
if( configuration_.drawSizeGrip() )
{
if( !hasSizeGrip() ) createSizeGrip();
} else if( hasSizeGrip() ) deleteSizeGrip();
}
//___________________________________________
@ -175,7 +218,9 @@ namespace Oxygen
//_________________________________________________________
KCommonDecorationButton *OxygenClient::createButton(::ButtonType type)
{
switch (type) {
case MenuButton:
return new OxygenButton(*this, i18n("Menu"), ButtonMenu);
@ -203,9 +248,12 @@ namespace Oxygen
case ShadeButton:
return new OxygenButton(*this, i18n("Shade Button"), ButtonShade);
default:
return 0;
default: break;
}
return NULL;
}
//_________________________________________________________
@ -341,11 +389,22 @@ namespace Oxygen
}
//_________________________________________________________
QRect OxygenClient::titleBoundingRect( QPainter* painter, const QRect& rect, const QString& caption ) const
QRect OxygenClient::titleBoundingRect( const QFont& font, QRect rect, const QString& caption ) const
{
// check bounding rect against titleRect
// conflicts can happen only if there is only one item in group
if( itemData_.count() == 1 )
{
QRect titleRect( OxygenClient::titleRect() );
if( titleRect.left() > rect.left() ) { rect.setLeft( titleRect.left() ); }
if( titleRect.right() < rect.right() ) { rect.setRight( titleRect.right() ); }
}
// get title bounding rect
QRect boundingRect = painter->boundingRect( rect, configuration().titleAlignment() | Qt::AlignVCenter, caption );
QRect boundingRect = QFontMetrics( font ).boundingRect( rect, configuration().titleAlignment() | Qt::AlignVCenter, caption );
// adjust to make sure bounding rect
// 1/ has same vertical alignment as original titleRect
@ -353,6 +412,7 @@ namespace Oxygen
boundingRect.setTop( rect.top() );
boundingRect.setBottom( rect.bottom() );
// check bounding rect against input rect
if( rect.left() > boundingRect.left() ) { boundingRect.setLeft( rect.left() ); }
if( rect.right() < boundingRect.right() ) { boundingRect.setRight( rect.right() ); }
@ -360,6 +420,116 @@ namespace Oxygen
}
//_________________________________________________________
void OxygenClient::clearTargetItem( void )
{
if( !itemData_.animated() ) return;
if( itemData_.animationType() == AnimationLeave )
{ itemData_.setDirty( true ); }
}
//_________________________________________________________
void OxygenClient::updateItemBoundingRects( bool alsoUpdate )
{
// make sure items are not animated
itemData_.animate( AnimationNone );
// maximum available space
QRect titleRect( OxygenClient::titleRect() );
// get tabs
int items( clientGroupItems().count() );
// make sure item data have the correct number of items
while( itemData_.count() < items ) itemData_.push_back( ClientGroupItemData() );
while( itemData_.count() > items )
{
if( itemData_.back().closeButton_ ) delete itemData_.back().closeButton_.data();
itemData_.pop_back();
}
assert( !itemData_.isEmpty() );
// create buttons
if( itemData_.count() == 1 )
{
// remove button
if( itemData_.front().closeButton_ )
{ delete itemData_.front().closeButton_.data(); }
// set active rect
itemData_.front().activeRect_ = titleRect.adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
} else {
int left( titleRect.left() );
int width( titleRect.width()/items );
for( int index = 0; index < itemData_.count(); index++ )
{
ClientGroupItemData& item(itemData_[index]);
// make sure button exists
if( !item.closeButton_ )
{
item.closeButton_ = ClientGroupItemData::ButtonPointer( new OxygenButton( *this, "Close this tab", ButtonItemClose ) );
item.closeButton_.data()->show();
item.closeButton_.data()->installEventFilter( this );
}
// set active rect
QRect local( QPoint( left, titleRect.top() ), QSize( width, titleRect.height() ) );
local.adjust( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
item.activeRect_ = local;
left += width;
}
}
if( itemData_.count() == 1 )
{
QRect active( itemData_.front().activeRect_ );
if( configuration().drawTitleOutline() && isActive() )
{
QRect textRect( titleBoundingRect( options()->font( true, false), active, caption() ) );
active.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) );
active.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) );
} else {
active.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) );
active.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) );
}
// assign to item
itemData_.front().reset( active );
} else {
for( int index = 0; index < itemData_.count(); index++ )
{ itemData_[index].reset( itemData_[index].activeRect_ ); }
}
// button activity
itemData_.updateButtonActivity( visibleClientGroupItem() );
// reset buttons location
itemData_.updateButtons( alsoUpdate );
itemData_.setDirty( false );
return;
}
//_________________________________________________________
QColor OxygenClient::titlebarTextColor(const QPalette &palette)
{
@ -391,11 +561,6 @@ namespace Oxygen
}
//_________________________________________________________
QColor OxygenClient::titlebarContrastColor(const QPalette &palette)
{ return helper().calcLightColor( palette.color( widget()->window()->backgroundRole() ) ); }
//_________________________________________________________
void OxygenClient::renderWindowBackground( QPainter* painter, const QRect& rect, const QWidget* widget, const QPalette& palette ) const
{
@ -421,7 +586,7 @@ namespace Oxygen
{
// check if outline is needed
if( !( isActive() && configuration().drawTitleOutline() ) ) return;
if( clientGroupItems().count() < 2 && !itemData_.animated() && !( isActive() && configuration().drawTitleOutline() ) ) return;
// get coordinates relative to the client area
// this is annoying. One could use mapTo if this was taking const QWidget* and not
@ -453,6 +618,22 @@ namespace Oxygen
// title height
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_.animated() ) && !(configuration().drawTitleOutline() && isActive() ) )
{
QPoint topLeft( r.topLeft()-position );
QRect rect( topLeft, QSize( r.width(), titleHeight ) );
QLinearGradient lg( rect.topLeft(), rect.bottomLeft() );
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) );
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) );
painter->setBrush( lg );
painter->setPen( Qt::NoPen );
painter->drawRect( rect );
}
// horizontal line
{
int shadowSize = 7;
@ -470,7 +651,7 @@ namespace Oxygen
}
if( configuration().drawTitleOutline() )
if( configuration().drawTitleOutline() && isActive() )
{
// save mask and frame to where
@ -592,7 +773,7 @@ namespace Oxygen
// center (for active windows only)
{
painter->save();
int offset = 2;
int offset = 1;
int voffset = 1;
QRect adjustedRect( rect.adjusted( offset, voffset, -offset, 1 ) );
@ -608,7 +789,7 @@ namespace Oxygen
// shadow
int shadowSize = 7;
int offset = -2;
int offset = -3;
int voffset = 5-shadowSize;
QRect adjustedRect( rect.adjusted(offset, voffset, -offset, shadowSize) );
helper().slab( palette.color( widget()->backgroundRole() ), 0, shadowSize )->render( adjustedRect, painter, TileSet::Top|TileSet::Left|TileSet::Right );
@ -668,6 +849,135 @@ namespace Oxygen
}
//_______________________________________________________________________
void OxygenClient::renderItem( QPainter* painter, int index, const QPalette& palette )
{
const ClientGroupItemData& item( itemData_[index] );
// see if tag is active
int itemCount( itemData_.count() );
//
if( !item.boundingRect_.isValid() ) return;
// create rect in which text is to be drawn
QRect textRect( item.boundingRect_.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
// add extra space needed for title outline
if( itemCount > 1 || itemData_.animated() )
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
// add extra space for the button
if( itemCount > 1 )
{ textRect.adjust( 0, 0, - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); }
// check if current item is active
bool active( index == visibleClientGroupItem() );
// get current item caption and update text rect
const QList< ClientGroupItem >& items( clientGroupItems() );
QString caption( itemCount == 1 ? OxygenClient::caption() : items[index].title() );
// title outline
if( itemCount == 1 ) {
textRect = titleBoundingRect( painter->font(), textRect, caption );
if( itemData_.animated() ) {
renderTitleOutline( painter, item.boundingRect_, palette );
} else if( isActive() && configuration().drawTitleOutline() ) {
// adjusts boundingRect accordingly
QRect boundingRect( item.boundingRect_ );
boundingRect.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) );
boundingRect.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) );
// render bounding rect around it with extra margins
renderTitleOutline( painter, boundingRect, palette );
}
} else if( active ) {
// in multiple tabs render title outline in all cases
renderTitleOutline( painter, item.boundingRect_, palette );
}
// render text
if( active || itemCount == 1 )
{
// for active tab, current caption is "merged" with old caption, if any
renderTitleText(
painter, textRect,
titlebarTextColor( palette ),
titlebarContrastColor( palette ) );
} else {
QColor background( backgroundPalette( widget(), palette ).color( widget()->window()->backgroundRole() ) );
// add extra shade (as used in renderWindowBorder
if( !( isActive() && configuration().drawTitleOutline() ) )
{ background = KColorUtils::mix( background, Qt::black, 0.10 ); }
// otherwise current caption is rendered directly
renderTitleText(
painter, textRect, caption,
titlebarTextColor( backgroundPalette( widget(), palette ), false ),
titlebarContrastColor( background ) );
}
// render separators between inactive tabs
if( !( active || itemCount == 1 ) )
{
// separators
// draw Left separator
QColor color( backgroundPalette( widget(), palette ).window().color() );
if( index != visibleClientGroupItem() && ( ( index == 0 && buttonsLeftWidth() > 0 ) || itemData_.isTarget( index ) ) )
{
QRect local( item.boundingRect_.topLeft()+QPoint(0,2), QSize( 2, item.boundingRect_.height()-3 ) );
helper().drawSeparator( painter, local, color, Qt::Vertical);
}
// draw right separator
if(
( index == itemCount-1 && buttonsRightWidth() > 0 ) ||
( index+1 < itemCount && ( itemData_.isTarget( index+1 ) ||
index+1 != visibleClientGroupItem() ) ) )
{
QRect local( item.boundingRect_.topRight()+QPoint(0,2), QSize( 2, item.boundingRect_.height()-3 ) );
helper().drawSeparator( painter, local, color, Qt::Vertical);
}
}
}
//_______________________________________________________________________
void OxygenClient::renderTargetRect( QPainter* p, const QPalette& palette )
{
if( itemData_.targetRect().isNull() || itemData_.timeLineIsRunning() ) return;
p->save();
QColor color = palette.color(QPalette::Highlight);
p->setPen(KColorUtils::mix(color, palette.color(QPalette::Active, QPalette::WindowText)));
p->setBrush( helper().alphaColor( color, 0.5 ) );
p->drawRect( itemData_.targetRect().adjusted( 4, 2, -4, -2 ) );
p->restore();
}
//_______________________________________________________________________
void OxygenClient::renderFloatFrame( QPainter* painter, const QRect& frame, const QPalette& palette ) const
{
@ -812,7 +1122,7 @@ namespace Oxygen
if( configuration().drawTitleOutline() )
{
if( timeLineIsRunning() )
if( timeLineIsRunning() && !isForcedActive() )
{
QColor inactiveColor( backgroundColor( widget, palette, false ) );
@ -821,7 +1131,7 @@ namespace Oxygen
palette.setColor( widget->window()->backgroundRole(), mixed );
palette.setColor( QPalette::Button, mixed );
} else if( isActive() ) {
} else if( isActive() || isForcedActive() ) {
QColor color = options()->color( KDecorationDefines::ColorTitleBar, true );
palette.setColor( widget->window()->backgroundRole(), color );
@ -863,30 +1173,75 @@ namespace Oxygen
}
//___________________________________________
void OxygenClient::resetConfiguration( void )
//______________________________________________________________________________
bool OxygenClient::eventFilter( QObject* object, QEvent* event )
{
if( !initialized_ ) return;
// all dedicated event filtering is here to handle multiple tabs.
// if tabs are disabled, do nothing
if( !configuration().tabsEnabled() )
{ return KCommonDecorationUnstable::eventFilter( object, event ); }
configuration_ = factory_->configuration( *this );
// animations duration
timeLine_.setDuration( configuration_.animationsDuration() );
titleTimeLine_.setDuration( configuration_.animationsDuration() );
// need to update old caption
updateOldCaption();
// handle size grip
if( configuration_.drawSizeGrip() )
bool state = false;
switch( event->type() )
{
if( !hasSizeGrip() ) createSizeGrip();
case QEvent::Show:
if( widget() == object )
{ itemData_.setDirty( true ); }
break;
} else if( hasSizeGrip() ) deleteSizeGrip();
case QEvent::MouseButtonPress:
if( widget() == object )
{ state = mousePressEvent( static_cast< QMouseEvent* >( event ) ); }
break;
case QEvent::MouseButtonRelease:
if( widget() == object ) state = mouseReleaseEvent( static_cast< QMouseEvent* >( event ) );
else if( OxygenButton *btn = qobject_cast< OxygenButton* >( object ) )
{
if( static_cast< QMouseEvent* >( event )->button() == Qt::LeftButton )
{ state = closeItem( btn ); }
}
break;
case QEvent::MouseMove:
state = mouseMoveEvent( static_cast< QMouseEvent* >( event ) );
break;
case QEvent::DragEnter:
if( widget() == object )
{ state = dragEnterEvent( static_cast< QDragEnterEvent* >( event ) ); }
break;
case QEvent::DragMove:
if( widget() == object )
{ state = dragMoveEvent( static_cast< QDragMoveEvent* >( event ) ); }
break;
case QEvent::DragLeave:
if( widget() == object )
{ state = dragLeaveEvent( static_cast< QDragLeaveEvent* >( event ) ); }
break;
case QEvent::Drop:
if( widget() == object )
{ state = dropEvent( static_cast< QDropEvent* >( event ) ); }
break;
default: break;
}
return state || KCommonDecorationUnstable::eventFilter( object, event );
}
//_________________________________________________________
void OxygenClient::resizeEvent( QResizeEvent* event )
{
itemData_.setDirty( true );
KCommonDecorationUnstable::resizeEvent( event );
}
//_________________________________________________________
@ -915,7 +1270,7 @@ namespace Oxygen
{
TileSet *tileSet( 0 );
if( configuration().useOxygenShadows() && timeLineIsRunning() )
if( configuration().useOxygenShadows() && timeLineIsRunning() && !isForcedActive() )
{
int frame = timeLine_.currentFrame();
@ -969,6 +1324,14 @@ namespace Oxygen
}
// make sure ItemData and clientGroupItems 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() )
{ updateItemBoundingRects( false ); }
// window background
renderWindowBackground( &painter, frame, widget(), backgroundPalette( widget(), palette ) );
renderWindowBorder( &painter, frame, widget(), palette );
@ -985,13 +1348,9 @@ namespace Oxygen
painter.save();
painter.setRenderHint(QPainter::Antialiasing);
// float frame
renderFloatFrame( &painter, frame, palette );
// clipping
if( compositingActive() ) painter.setClipping(false);
// resize handles
renderDots( &painter, frame, QColor(0, 0, 0, 66) );
painter.restore();
@ -1002,27 +1361,335 @@ namespace Oxygen
// title bounding rect
painter.setFont( options()->font(isActive(), false) );
QRect boundingRect( titleBoundingRect( &painter, caption() ) );
if( isActive() && configuration().drawTitleOutline() )
{
renderTitleOutline( &painter, boundingRect.adjusted(
-layoutMetric( LM_TitleBorderLeft ),
-layoutMetric( LM_TitleEdgeTop ),
layoutMetric( LM_TitleBorderRight ), 0 ), palette );
}
// title text
renderTitleText( &painter, boundingRect,
titlebarTextColor( palette ),
titlebarContrastColor( palette ) );
// draw ClientGroupItems
int itemCount( itemData_.count() );
for( int i = 0; i < itemCount; i++ ) renderItem( &painter, i, palette );
// draw target rect
renderTargetRect( &painter, widget()->palette() );
// separator
if( drawSeparator() ) renderSeparator(&painter, frame, widget(), color );
if( itemCount == 1 && !itemData_.animated() && drawSeparator() )
{ renderSeparator(&painter, frame, widget(), color ); }
}
}
//_____________________________________________________________
bool OxygenClient::mousePressEvent( QMouseEvent* event )
{
QPoint point = event->pos();
if( itemClicked( point ) < 0 ) return false;
bool accepted( false );
if( event->button() == Qt::MidButton )
{
dragPoint_ = point;
mouseButton_ = Qt::MidButton;
accepted = true;
} else if( event->button() == Qt::LeftButton ) {
mouseButton_ = Qt::LeftButton;
} else if( event->button() == Qt::RightButton ) {
mouseButton_ = Qt::RightButton;
accepted = true;
}
return accepted;
}
//_____________________________________________________________
bool OxygenClient::mouseReleaseEvent( QMouseEvent* event )
{
bool accepted( false );
if( ( mouseButton_ == Qt::LeftButton && event->button() == Qt::LeftButton ) ||
( mouseButton_ == Qt::MidButton && event->button() == Qt::MidButton ) )
{
QPoint point = event->pos();
int visibleItem = visibleClientGroupItem();
int itemClicked( OxygenClient::itemClicked( point ) );
if( itemClicked >= 0 && visibleItem != itemClicked )
{
setVisibleClientGroupItem( itemClicked );
setForceActive( true );
accepted = true;
}
} else if( mouseButton_ == Qt::RightButton && event->button() == Qt::RightButton ) {
QPoint point = event->pos();
int itemClicked( OxygenClient::itemClicked( point ) );
displayClientMenu( itemClicked, widget()->mapToGlobal( event->pos() ) );
}
mouseButton_ = Qt::NoButton;
return false;
}
//_____________________________________________________________
bool OxygenClient::mouseMoveEvent( QMouseEvent* event )
{
// check button and distance to drag point
if( configuration().hideTitleBar() || mouseButton_ == Qt::NoButton || ( event->pos() - dragPoint_ ).manhattanLength() <= QApplication::startDragDistance() )
{ return false; }
bool accepted( false );
if( mouseButton_ == Qt::MidButton )
{
QPoint point = event->pos();
int itemClicked( OxygenClient::itemClicked( point ) );
if( itemClicked < 0 ) return false;
QDrag *drag = new QDrag( widget() );
QMimeData *groupData = new QMimeData();
groupData->setData( clientGroupItemDragMimeType(), QString().setNum( itemId( itemClicked )).toAscii() );
drag->setMimeData( groupData );
sourceItem_ = OxygenClient::itemClicked( dragPoint_ );
// get tab geometry
QRect geometry;
geometry = itemData_[itemClicked].boundingRect_;
// remove space used for buttons
if( itemData_.count() >= 0 )
{ geometry.adjust( 0, 0, - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); }
drag->setPixmap( itemDragPixmap( itemClicked, geometry ) );
// note: the pixmap is moved just above the pointer on purpose
// because overlapping pixmap and pointer slows down the pixmap alot.
//drag->setHotSpot( QPoint( event->pos().x() - geometry.left(), geometry.height() ) );
drag->setHotSpot( QPoint( event->pos().x() - geometry.left(), -1 ) );
drag->exec( Qt::MoveAction );
// detach tab from window
if( drag->target() == 0 && itemData_.count() > 1 )
{
removeFromClientGroup( sourceItem_,
widget()->frameGeometry().adjusted(
layoutMetric( LM_OuterPaddingLeft ),
layoutMetric( LM_OuterPaddingTop ),
-layoutMetric( LM_OuterPaddingRight ),
-layoutMetric( LM_OuterPaddingBottom )
).translated( QCursor::pos() - event->pos() +
QPoint( layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop )))
);
}
accepted = true;
}
// reset button
mouseButton_ = Qt::NoButton;
return accepted;
}
//_____________________________________________________________
bool OxygenClient::dragEnterEvent( QDragEnterEvent* event )
{
// check if drag enter is allowed
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) || configuration().hideTitleBar() ) return false;
//
event->acceptProposedAction();
if( event->source() != widget() )
{
QPoint position( event->pos() );
itemData_.animate( AnimationEnter, itemClicked( position, true ) );
} else if( itemData_.count() > 1 ) {
QPoint position( event->pos() );
int itemClicked( OxygenClient::itemClicked( position, false ) );
itemData_.animate( AnimationTypes( AnimationEnter|AnimationSameTarget ), itemClicked );
}
return true;
}
//_____________________________________________________________
bool OxygenClient::dragLeaveEvent( QDragLeaveEvent* )
{
if( itemData_.animationType() & AnimationSameTarget )
{
itemData_.animate( Oxygen::AnimationTypes(AnimationLeave|AnimationSameTarget), sourceItem_ );
} else if( itemData_.animated() ) {
itemData_.animate( AnimationLeave );
}
return true;
}
//_____________________________________________________________
bool OxygenClient::dragMoveEvent( QDragMoveEvent* event )
{
// check format
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) ) return false;
if( event->source() != widget() )
{
QPoint position( event->pos() );
itemData_.animate( AnimationMove, itemClicked( position, true ) );
} else if( itemData_.count() > 1 ) {
QPoint position( event->pos() );
int itemClicked( OxygenClient::itemClicked( position, false ) );
itemData_.animate( AnimationTypes( AnimationMove|AnimationSameTarget ), itemClicked );
}
return false;
}
//_____________________________________________________________
bool OxygenClient::dropEvent( QDropEvent* event )
{
QPoint point = event->pos();
itemData_.animate( AnimationNone );
const QMimeData *groupData = event->mimeData();
if( !groupData->hasFormat( clientGroupItemDragMimeType() ) ) return false;
if( widget() == event->source() )
{
int from = OxygenClient::itemClicked( dragPoint_ );
int itemClicked( OxygenClient::itemClicked( point, false ) );
if( itemClicked > from )
{
itemClicked++;
if( itemClicked >= clientGroupItems().count() )
{ itemClicked = -1; }
}
moveItemInClientGroup( from, itemClicked );
itemData_.setDirty( true );
widget()->update();
} else {
setForceActive( true );
int itemClicked( OxygenClient::itemClicked( point, true ) );
int source = QString( groupData->data( clientGroupItemDragMimeType() ) ).toInt();
moveItemToClientGroup( source, itemClicked );
}
return true;
}
//_____________________________________________________________
bool OxygenClient::closeItem( const OxygenButton* button )
{
for( int i=0; i < itemData_.count(); i++ )
{
if( button == itemData_[i].closeButton_.data() )
{
closeClientGroupItem( i );
return true;
}
}
return false;
}
//________________________________________________________________
QPixmap OxygenClient::itemDragPixmap( int index, const QRect& geometry )
{
bool itemValid( index >= 0 && index < clientGroupItems().count() );
QPixmap pixmap( geometry.size() );
QPainter painter( &pixmap );
painter.setRenderHints(QPainter::SmoothPixmapTransform|QPainter::Antialiasing);
painter.translate( -geometry.topLeft() );
// render window background
renderWindowBackground( &painter, geometry, widget(), widget()->palette() );
// darken background if item is inactive
bool itemActive = !( itemValid && index != visibleClientGroupItem() );
if( !itemActive )
{
QLinearGradient lg( geometry.topLeft(), geometry.bottomLeft() );
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) );
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) );
painter.setBrush( lg );
painter.setPen( Qt::NoPen );
painter.drawRect( geometry );
}
// render title text
painter.setFont( options()->font(isActive(), false) );
QRect textRect( geometry.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
if( itemValid )
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
QString caption( itemValid ? clientGroupItems()[index].title() : OxygenClient::caption() );
renderTitleText( &painter, textRect, caption, titlebarTextColor( widget()->palette(), isActive() && itemActive ) );
// floating frame
helper().drawFloatFrame(
&painter, geometry, widget()->palette().window().color(),
true, false,
KDecoration::options()->color(ColorTitleBar)
);
painter.end();
// create pixmap mask
QBitmap bitmap( geometry.size() );
{
bitmap.clear();
QPainter painter( &bitmap );
QPainterPath path;
path.addRegion( helper().roundedMask( geometry.translated( -geometry.topLeft() ) ) );
painter.fillPath( path, Qt::color1 );
}
pixmap.setMask( bitmap );
return pixmap;
}
//_________________________________________________________________
void OxygenClient::createSizeGrip( void )
{

View File

@ -28,13 +28,15 @@
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include <kcommondecoration.h>
#include <QtCore/QTimeLine>
#include "oxygen.h"
#include "oxygenclientgroupitemdata.h"
#include "oxygenconfiguration.h"
#include "lib/helper.h"
#include <kcommondecoration.h>
#include <QtCore/QTimeLine>
#include <QtCore/QSharedPointer>
namespace Oxygen
{
@ -72,6 +74,10 @@ namespace Oxygen
bool timeLineIsRunning( void ) const
{ return timeLine_.state() == QTimeLine::Running; }
//! true when decoration is forced active
bool isForcedActive( void ) const
{ return forceActive_ && clientGroupItems().count() > 1; }
//! true when separator is to be drawn
bool drawSeparator( void ) const
{
@ -124,18 +130,18 @@ namespace Oxygen
virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const;
//! get title bounding rect
virtual QRect titleBoundingRect( QPainter* painter, const QString& caption ) const
{ return titleBoundingRect( painter, titleRect(), caption ); }
virtual QRect titleBoundingRect( const QFont& font, const QString& caption ) const
{ return titleBoundingRect( font, titleRect(), caption ); }
//! get title bounding rect
virtual QRect titleBoundingRect( QPainter*, const QRect&, const QString& ) const;
virtual QRect titleBoundingRect( const QFont&, QRect, const QString& ) const;
//! palette background
QPalette backgroundPalette( const QWidget*, QPalette ) const;
//! background
QColor backgroundColor( const QWidget* widget, QPalette palette ) const
{ return backgroundColor( widget, palette, isActive() ); }
{ return backgroundColor( widget, palette, isActive() || isForcedActive() ); }
//! background
QColor backgroundColor( const QWidget*, QPalette, bool ) const;
@ -165,16 +171,48 @@ namespace Oxygen
//@}
public slots:
//! event filter
virtual bool eventFilter( QObject*, QEvent* );
//! reset configuration
void resetConfiguration( void );
//! resize event
virtual void resizeEvent(QResizeEvent *e);
protected:
//! true when decoration is forced active
void setForceActive( bool value )
{ forceActive_ = value; }
//!@name event filters
//@{
//! paint
virtual void paintEvent( QPaintEvent* );
//! mouse press event
virtual bool mousePressEvent( QMouseEvent* );
//! mouse release event
virtual bool mouseReleaseEvent( QMouseEvent* );
//! mouse move event
virtual bool mouseMoveEvent( QMouseEvent* );
//! drag enter event
virtual bool dragEnterEvent( QDragEnterEvent* );
//! drag move event
virtual bool dragMoveEvent( QDragMoveEvent* );
//! drag leave event
virtual bool dragLeaveEvent( QDragLeaveEvent* );
//! drop event
virtual bool dropEvent( QDropEvent* );
//@}
//!@name rendering methods (called in paintEvent)
//@{
@ -199,6 +237,12 @@ namespace Oxygen
/*! second color, if valid, is for contrast pixel */
virtual void renderTitleText( QPainter*, const QRect&, const QString&, const QColor&, const QColor& = QColor() ) const;
//! GroupItem
virtual void renderItem( QPainter*, int, const QPalette& );
//! tabbing target rect
virtual void renderTargetRect( QPainter*, const QPalette& );
//! render float frame
virtual void renderFloatFrame( QPainter*, const QRect&, const QPalette& ) const;
@ -207,6 +251,16 @@ namespace Oxygen
//@}
//! close tab matching give button
virtual bool closeItem( const OxygenButton* );
//! index of item matching point
int itemClicked( const QPoint& position, bool between = false ) const
{ return itemData_.itemAt( position , between ); }
//! return pixmap corresponding to a given tab, for dragging
QPixmap itemDragPixmap( int, const QRect& );
//! title timeline
bool titleTimeLineIsRunning( void ) const
{ return titleTimeLine_.state() == QTimeLine::Running; }
@ -247,7 +301,12 @@ namespace Oxygen
QColor titlebarTextColor(const QPalette&, bool active);
//! text color
QColor titlebarContrastColor(const QPalette&);
QColor titlebarContrastColor(const QPalette& palette ) const
{ return titlebarContrastColor( palette.color( widget()->window()->backgroundRole() ) ); }
//! text color
QColor titlebarContrastColor(const QColor& color ) const
{ return helper().calcLightColor( color ); }
//!@name size grip
//@{
@ -274,6 +333,17 @@ namespace Oxygen
void updateOldCaption( void )
{ setOldCaption( caption() ); }
//! set target item to -1
void clearTargetItem( void );
//! clear force active flag
void clearForceActive( void )
{ if( isActive() ) setForceActive( false ); }
//! title bounding rects
/*! calculate and return title bounding rects in case of tabbed window */
void updateItemBoundingRects( bool alsoUpdate = true );
private:
//! factory
@ -297,6 +367,21 @@ namespace Oxygen
//! true when initialized
bool initialized_;
//! true when decoration is forced active
bool forceActive_;
//! mouse button
Qt::MouseButton mouseButton_;
//! tab bounding rects
ClientGroupItemDataList itemData_;
//! index of tab being dragged if any, -1 otherwise
int sourceItem_;
//! drag start point
QPoint dragPoint_;
};
//!@name utility functions

View File

@ -0,0 +1,320 @@
//////////////////////////////////////////////////////////////////////////////
// oxygenclientgroupitemdata.cpp
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
// Copyright (c) 2003, 2004 David Johnson <david@usermode.org>
// Copyright (c) 2006, 2007 Riccardo Iaconelli <ruphy@fsfe.org>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "oxygenclientgroupitemdata.h"
#include "oxygenclientgroupitemdata.moc"
#include "oxygenclient.h"
#include "oxygen.h"
namespace Oxygen
{
//____________________________________________________________________________
ClientGroupItemDataList::ClientGroupItemDataList( OxygenClient* parent ):
QObject( parent ),
QList<ClientGroupItemData>(),
client_( *parent ),
timeLine_( 150, this ),
animationType_( AnimationNone ),
draggedItem_( NoItem ),
targetItem_( NoItem )
{
timeLine_.setFrameRange( 0, maxAnimationIndex );
timeLine_.setCurveShape( QTimeLine::EaseInOutCurve );
connect( &timeLine_, SIGNAL( frameChanged( int ) ), this, SLOT( updateBoundingRects() ) );
connect( &timeLine_, SIGNAL( finished() ), this, SLOT( updateBoundingRects() ) );
}
//________________________________________________________________
int ClientGroupItemDataList::itemAt( const QPoint& point, bool between ) const
{
for( int i=0; i < count(); i++ )
{
QRect rect = at(i).activeRect_;
if( between ) rect.translate( -rect.width() / 2, 0 );
if( rect.adjusted(0,0,0,2).contains( point ) )
{ return i; }
}
return NoItem;
}
//____________________________________________________________________________
void ClientGroupItemDataList::animate( AnimationTypes type, int target )
{
// store animation type
animationType_ = type;
if( type == AnimationNone )
{
if( timeLineIsRunning() ) timeLine().stop();
targetItem_ = NoItem;
draggedItem_ = NoItem;
targetRect_ = QRect();
} else if( type & (AnimationEnter|AnimationMove ) ) {
// store dragged item
bool animate( true );
if( (type&AnimationSameTarget) && draggedItem_ == NoItem )
{
animate = false;
draggedItem_ = target;
} else if( (type&AnimationMove) && targetItem_ == target ) return;
// check timeLine
if( timeLineIsRunning() ) timeLine().stop();
targetItem_ = target;
targetRect_ = QRect();
QRect titleRect( client_.titleRect() );
int left( titleRect.left() );
int width = (type&AnimationSameTarget) ?
titleRect.width()/count():
titleRect.width()/(count()+1);
if( (type&AnimationSameTarget) && draggedItem_ < target )
{
target++;
if( target >= count() ) target = NoItem;
}
// loop over items and update bounding rects
for( int index = 0; index < count(); index++ )
{
ClientGroupItemData& item( ClientGroupItemDataList::operator[](index) );
if( index == target )
{
targetRect_ = item.refBoundingRect_;
targetRect_.setLeft( left );
targetRect_.setWidth( width );
left+=width;
}
item.startBoundingRect_ = item.boundingRect_;
item.endBoundingRect_ = item.refBoundingRect_;
item.endBoundingRect_.setLeft( left );
if( !( (type&AnimationSameTarget) && index == draggedItem_ ) )
{
item.endBoundingRect_.setWidth( width );
left+=width;
} else {
item.endBoundingRect_.setWidth( 0 );
}
}
if( targetRect_.isNull() )
{
targetRect_ = back().refBoundingRect_;
targetRect_.setLeft( left );
targetRect_.setWidth( width );
}
if( animate ) timeLine().start();
else {
for( int index = 0; index < count(); index++ )
{
ClientGroupItemData& item( ClientGroupItemDataList::operator[](index) );
item.boundingRect_ = item.endBoundingRect_;
}
updateButtons( true );
}
} else if( type & AnimationLeave ) {
// stop timeLine
if( timeLineIsRunning() ) timeLine().stop();
// reset target
targetItem_ = NoItem;
targetRect_ = QRect();
if( type & AnimationSameTarget )
{
// store dragged item
draggedItem_ = target;
// do nothing if only one item
if( count() <= 1 ) return;
QRect titleRect( client_.titleRect() );
int left( titleRect.left() );
int width = titleRect.width()/(count()-1);
// loop over items and update bounding rects
for( int index = 0; index < count(); index++ )
{
ClientGroupItemData& item( ClientGroupItemDataList::operator[](index) );
item.startBoundingRect_ = item.boundingRect_;
item.endBoundingRect_ = item.refBoundingRect_;
item.endBoundingRect_.setLeft( left );
if( index != target )
{
item.endBoundingRect_.setWidth( width );
left+=width;
} else {
item.endBoundingRect_.setWidth( 0 );
}
}
} else {
// loop over items and update bounding rects
for( int index = 0; index < count(); index++ )
{
ClientGroupItemData& item( ClientGroupItemDataList::operator[](index) );
item.startBoundingRect_ = item.boundingRect_;
item.endBoundingRect_ = item.refBoundingRect_;
}
}
timeLine().start();
}
return;
}
//____________________________________________________________________________
void ClientGroupItemDataList::updateButtonActivity( int visibleItem ) const
{
for( int index = 0; index < count(); index++ )
{
const ClientGroupItemData& item( at(index) );
if( item.closeButton_ )
{ item.closeButton_.data()->setForceInactive( index != visibleItem ); }
}
}
//____________________________________________________________________________
void ClientGroupItemDataList::updateButtons( bool alsoUpdate ) const
{
// move close buttons
// this should move to ClientGroupItemDataList
if( alsoUpdate ) client_.widget()->setUpdatesEnabled( false );
for( int index = 0; index < count(); index++ )
{
const ClientGroupItemData& item( at(index) );
if( !item.closeButton_ ) continue;
if( !item.boundingRect_.isValid() ) {
item.closeButton_.data()->hide();
} else {
QPoint position(
item.boundingRect_.right() - client_.configuration().buttonSize() - client_.layoutMetric(KCommonDecoration::LM_TitleEdgeRight),
item.boundingRect_.top() + client_.layoutMetric( KCommonDecoration::LM_TitleEdgeTop ) );
if( item.closeButton_.data()->isHidden() ) item.closeButton_.data()->show();
item.closeButton_.data()->move( position );
}
}
if( alsoUpdate )
{
client_.widget()->setUpdatesEnabled( true );
client_.widget()->update();
}
}
//____________________________________________________________________________
void ClientGroupItemDataList::updateBoundingRects( bool alsoUpdate )
{
qreal ratio( ClientGroupItemDataList::ratio() );
for( iterator iter = begin(); iter != end(); iter++ )
{
// left
if( iter->endBoundingRect_.left() == iter->startBoundingRect_.left() )
{
iter->boundingRect_.setLeft( iter->startBoundingRect_.left() );
} else {
iter->boundingRect_.setLeft( (1.0-ratio)*iter->startBoundingRect_.left() + ratio*iter->endBoundingRect_.left() );
}
// right
if( iter->endBoundingRect_.right() == iter->startBoundingRect_.right() )
{
iter->boundingRect_.setRight( iter->startBoundingRect_.right() );
} else {
iter->boundingRect_.setRight( (1.0-ratio)*iter->startBoundingRect_.right() + ratio*iter->endBoundingRect_.right() );
}
}
// update button position
updateButtons( alsoUpdate );
}
}

View File

@ -0,0 +1,201 @@
#ifndef oxygenclientgroupitemdata_h
#define oxygenclientgroupitemdata_h
//////////////////////////////////////////////////////////////////////////////
// oxygenclientgroupitemdata.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
// Copyright (c) 2003, 2004 David Johnson <david@usermode.org>
// Copyright (c) 2006, 2007 Riccardo Iaconelli <ruphy@fsfe.org>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////
#include "oxygenbutton.h"
#include <QtCore/QList>
#include <QtCore/QWeakPointer>
#include <QtCore/QRect>
#include <QtCore/QTimeLine>
namespace Oxygen
{
class OxygenClient;
//! animation type
enum AnimationType
{
AnimationNone = 0,
AnimationEnter = 1<<0,
AnimationMove = 1<<1,
AnimationLeave = 1<<2,
AnimationSameTarget = 1<<3
};
Q_DECLARE_FLAGS(AnimationTypes, AnimationType)
//! tab data
class ClientGroupItemData
{
public:
//! constructor
explicit ClientGroupItemData( void )
{}
//! destructor
virtual ~ClientGroupItemData( void )
{}
//! reset all rects to argument
void reset( const QRect& rect )
{
refBoundingRect_ = rect;
startBoundingRect_ = rect;
endBoundingRect_ = rect;
boundingRect_ = rect;
}
//! tab active rect
QRect activeRect_;
//! reference bounding rect
/*! it is usually identical to activeRect unless there is only one tab in window */
QRect refBoundingRect_;
//! tab drawing rect
QRect startBoundingRect_;
//! tab drawing rect
QRect endBoundingRect_;
//! tab drawing rect
QRect boundingRect_;
//! tab button
typedef QWeakPointer<OxygenButton> ButtonPointer;
ButtonPointer closeButton_;
};
class ClientGroupItemDataList: public QObject, public QList<ClientGroupItemData>
{
Q_OBJECT
public:
//! invalid item index
enum { NoItem = -1 };
//! constructor
ClientGroupItemDataList( OxygenClient* parent );
//! dirty state
void setDirty( const bool& value )
{ dirty_ = value; }
//! dirty state
bool isDirty( void ) const
{ return dirty_; }
//! true if being animated
bool animated( void ) const
{ return animationType_ != AnimationNone; }
//! animation type
AnimationTypes animationType( void ) const
{ return animationType_; }
//! return item index matching QPoint, or -1 if none
int itemAt( const QPoint&, bool ) const;
//! returns true if index is target
bool isTarget( int index ) const
{ return index == targetItem_; }
//! start animation
/* might need to add the side of the target here */
void animate( AnimationTypes, int = NoItem );
//! update button activity
void updateButtonActivity( int visibleItem ) const;
//! update buttons
void updateButtons( bool alsoUpdate ) const;
//! get timeLine
const QTimeLine& timeLine( void ) const
{ return timeLine_; }
//! get timeLine
QTimeLine& timeLine( void )
{ return timeLine_; }
//! true if timeLine is running
bool timeLineIsRunning( void ) const
{ return timeLine().state() == QTimeLine::Running; }
//! target rect
const QRect& targetRect( void ) const
{ return targetRect_; }
protected slots:
//! update bounding rects
void updateBoundingRects( bool alsoUpdate = true );
protected:
//! timeLine ratio
qreal ratio( void )
{ return qreal( timeLine().currentFrame() ) / qreal( timeLine().endFrame() ); }
private:
//! client
OxygenClient& client_;
//! dirty flag
/* used to trigger update at next paintEvent */
bool dirty_;
//! animation timeline
QTimeLine timeLine_;
//! last animation
AnimationTypes animationType_;
//! dragged item
int draggedItem_;
//! target item
int targetItem_;
//! target rect
QRect targetRect_;
};
}
#endif

View File

@ -43,6 +43,7 @@ namespace Oxygen
useOxygenShadows_( true ),
useAnimations_( true ),
animationsDuration_( 150 ),
tabsEnabled_( true ),
useNarrowButtonSpacing_( false )
{}
@ -108,10 +109,16 @@ namespace Oxygen
OxygenConfig::ANIMATIONS_DURATION,
defaultConfiguration.animationsDuration() ) );
// tabbing
setTabsEnabled( group.readEntry(
OxygenConfig::TABS_ENABLED,
defaultConfiguration.tabsEnabled() ) );
// buttonSpacing
setUseNarrowButtonSpacing( group.readEntry(
OxygenConfig::NARROW_BUTTON_SPACING,
defaultConfiguration.useNarrowButtonSpacing() ) );
}
//__________________________________________________
@ -130,7 +137,9 @@ namespace Oxygen
group.writeEntry( OxygenConfig::USE_OXYGEN_SHADOWS, useOxygenShadows() );
group.writeEntry( OxygenConfig::USE_ANIMATIONS, useAnimations() );
group.writeEntry( OxygenConfig::ANIMATIONS_DURATION, animationsDuration() );
group.writeEntry( OxygenConfig::TABS_ENABLED, tabsEnabled() );
group.writeEntry( OxygenConfig::NARROW_BUTTON_SPACING, useNarrowButtonSpacing() );
}
//__________________________________________________
@ -298,6 +307,7 @@ namespace Oxygen
useOxygenShadows() == other.useOxygenShadows() &&
useAnimations() == other.useAnimations() &&
animationsDuration() == other.animationsDuration() &&
tabsEnabled() == other.tabsEnabled() &&
useNarrowButtonSpacing() == other.useNarrowButtonSpacing();
}

View File

@ -42,6 +42,7 @@ namespace OxygenConfig
static const QString HIDE_TITLEBAR = "HideTitleBar";
static const QString USE_ANIMATIONS = "UseAnimations";
static const QString ANIMATIONS_DURATION = "AnimationsDuration";
static const QString TABS_ENABLED = "TabsEnabled";
static const QString NARROW_BUTTON_SPACING = "UseNarrowButtonSpacing";
}
@ -265,6 +266,14 @@ namespace Oxygen
virtual void setAnimationsDuration( int value )
{ animationsDuration_ = value; }
//! tabbing
virtual bool tabsEnabled( void ) const
{ return tabsEnabled_; }
//! tabbing
virtual void setTabsEnabled( bool value )
{ tabsEnabled_ = value; }
private:
//! title alignment
@ -300,6 +309,9 @@ namespace Oxygen
//! animations
int animationsDuration_;
//! tabbing
bool tabsEnabled_;
//! narrow button spacing
bool useNarrowButtonSpacing_;

View File

@ -31,7 +31,6 @@
#include <cassert>
#include <KColorUtils>
#include <KDebug>
#include <QtGui/QPainter>
namespace Oxygen
@ -44,7 +43,6 @@ namespace Oxygen
activeShadowConfiguration_( OxygenShadowConfiguration( QPalette::Active ) ),
inactiveShadowConfiguration_( OxygenShadowConfiguration( QPalette::Inactive ) )
{
kDebug(1212) << endl;
shadowCache_.setMaxCost( 1<<6 );
animatedShadowCache_.setMaxCost( maxIndex_<<6 );
}
@ -393,7 +391,7 @@ namespace Oxygen
index(0)
{
active = client->isActive();
active = client->isActive() || client->isForcedActive();
useOxygenShadows = client->configuration().useOxygenShadows();
isShade = client->isShade();
hasTitleOutline = client->configuration().drawTitleOutline();

View File

@ -33,8 +33,6 @@
#include <QtGui/QPolygon>
#include <QtCore/QTimer>
#include <KDebug>
#include <QtGui/QX11Info>
#include <X11/Xlib.h>
@ -92,12 +90,10 @@ namespace Oxygen
WId window_id = client().windowId();
if( client().isPreview() ) {
kDebug(1212) << "Using kcommondecoration::widget()" << endl;
setParent( client().widget() );
} else if( window_id ) {
kDebug(1212) << "Using Window ID" << endl;
WId current = window_id;
while( true )
{
@ -113,7 +109,6 @@ namespace Oxygen
XReparentWindow( QX11Info::display(), winId(), current, 0, 0 );
} else {
kDebug(1212) << "Unable to find valid parent. Hiding" << endl;
hide();
}

View File

@ -0,0 +1,15 @@
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

@ -0,0 +1,10 @@
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

@ -0,0 +1,91 @@
/********************************************************************
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::TabstripConfig( conf, parent );
}
}
TabstripConfig::TabstripConfig( KConfig *c, QWidget *parent )
{
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() ) );
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 ) );
}
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() );
config->sync();
}
void TabstripConfig::defaults()
{
ui->left->setChecked( false );
ui->center->setChecked( true );
ui->right->setChecked( false );
ui->showIcon->setChecked( true );
emit changed();
}

View File

@ -0,0 +1,56 @@
/********************************************************************
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

@ -0,0 +1,58 @@
<?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>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,4 @@
[Desktop Entry]
Name=Tabstrip
Name[x-test]=xxTabstripxx
X-KDE-Library=kwin3_tabstrip

View File

@ -0,0 +1,317 @@
/********************************************************************
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

@ -0,0 +1,55 @@
/********************************************************************
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

@ -0,0 +1,457 @@
/********************************************************************
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;
}
TabstripDecoration::~TabstripDecoration()
{
for( int i = 0; i < closeButtons.size(); ++i )
{
TabstripButton *btn = closeButtons.front();
closeButtons.removeFirst();
delete btn;
}
}
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("AllDesktops") ) );
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( Qt::white );
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;
QFontMetrics metrics( font );
QString string = metrics.elidedText( item.title(), Qt::ElideRight, rect.width() );
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;
QFontMetrics metrics( font );
QString string = metrics.elidedText( item.title(), Qt::ElideRight, rect.width() );
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( Qt::white );
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.front();
closeButtons.removeFirst();
delete btn;
}
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( Qt::white );
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( Qt::white );
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;
QFontMetrics metrics( font );
QString string = metrics.elidedText( caption(), Qt::ElideRight, rect.width() );
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 )
{
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 ) );
else if( TabstripButton *btn = dynamic_cast< TabstripButton* >( o ) )
if( e->type() == QEvent::MouseButtonRelease )
{
if( static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton )
{ closeClientGroupItem( closeButtons.indexOf( btn ) ); }
return true;
}
if( TabstripButton *btn = dynamic_cast< TabstripButton* >( o ) )
if( e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseMove )
{
QMouseEvent *past = static_cast< QMouseEvent* >( e );
QPoint point = btn->mapToParent( past->pos() );
QMouseEvent *ev = new QMouseEvent( past->type(), point, past->button(), past->buttons(), past->modifiers() );
e = dynamic_cast< QEvent* >( ev );
o = widget();
}
return state || KCommonDecorationUnstable::eventFilter( o, e );
}
bool TabstripDecoration::mouseButtonPressEvent( QMouseEvent* e )
{
click = widget()->mapToParent( e->pos() );
int item = itemClicked( click );
if( item >= 0 &&
( e->button() == Qt::MidButton || e->button() == Qt::LeftButton || e->button() == Qt::RightButton ))
{
click_in_progress = true;
button = e->button();
return button != Qt::LeftButton;
}
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;
if( e->button() == Qt::LeftButton || e->button() == Qt::MidButton )
setVisibleClientGroupItem( item );
else if( e->button() == Qt::RightButton )
displayClientMenu( item, widget()->mapToGlobal( release ) );
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 && button == Qt::MidButton && ( 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
{
int source = QString( group_data->data( clientGroupItemDragMimeType() ) ).toInt();
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

@ -0,0 +1,61 @@
/********************************************************************
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 );
~TabstripDecoration();
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

@ -0,0 +1,111 @@
/********************************************************************
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::TabstripFactory();
}
}
Qt::AlignmentFlag TabstripFactory::titlealign = Qt::AlignCenter;
bool TabstripFactory::show_icon = true;
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 );
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

@ -0,0 +1,57 @@
/********************************************************************
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();
private:
bool readConfig();
bool initialized;
static Qt::AlignmentFlag titlealign;
static bool show_icon;
};
inline Qt::AlignmentFlag TabstripFactory::titleAlign()
{
return titlealign;
}
inline bool TabstripFactory::showIcon()
{
return show_icon;
}
#endif

View File

@ -261,6 +261,24 @@ void EffectsHandlerImpl::windowUnminimized( EffectWindow* c )
ep.second->windowUnminimized( c );
}
void EffectsHandlerImpl::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
{
foreach( const EffectPair &ep, loaded_effects )
ep.second->clientGroupItemSwitched( from, to );
}
void EffectsHandlerImpl::clientGroupItemAdded( EffectWindow* from, EffectWindow* to )
{
foreach( const EffectPair &ep, loaded_effects )
ep.second->clientGroupItemAdded( from, to );
}
void EffectsHandlerImpl::clientGroupItemRemoved( EffectWindow* c, EffectWindow* group )
{
foreach( const EffectPair &ep, loaded_effects )
ep.second->clientGroupItemRemoved( c, group );
}
void EffectsHandlerImpl::desktopChanged( int old )
{
foreach( const EffectPair &ep, loaded_effects )
@ -1495,6 +1513,13 @@ void EffectWindowImpl::closeWindow() const
}
}
bool EffectWindowImpl::visibleInClientGroup() const
{
if( Client* c = dynamic_cast< Client* >( toplevel ))
return c == c->clientGroup()->visible();
return false;
}
EffectWindow* effectWindow( Toplevel* w )
{
EffectWindowImpl* ret = w->effectWindow();

View File

@ -147,6 +147,9 @@ class EffectsHandlerImpl : public EffectsHandler
void windowActivated( EffectWindow* c );
void windowMinimized( EffectWindow* c );
void windowUnminimized( EffectWindow* c );
void clientGroupItemSwitched( EffectWindow* from, EffectWindow* to );
void clientGroupItemAdded( EffectWindow* from, EffectWindow* to );
void clientGroupItemRemoved( EffectWindow* c, EffectWindow* group );
void desktopChanged( int old );
void windowDamaged( EffectWindow* w, const QRect& r );
void windowGeometryShapeChanged( EffectWindow* w, const QRect& old );
@ -264,6 +267,8 @@ class EffectWindowImpl : public EffectWindow
virtual void unminimize() const;
virtual void closeWindow() const;
virtual bool visibleInClientGroup() const;
const Toplevel* window() const;
Toplevel* window();

View File

@ -0,0 +1,26 @@
#######################################
# Effect
# Source files
set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
slidetabs/slidetabs.cpp
)
# .desktop files
install( FILES
slidetabs/slidetabs.desktop
DESTINATION ${SERVICES_INSTALL_DIR}/kwin )
#######################################
# Config
# Source files
set( kwin4_effect_builtins_config_sources ${kwin4_effect_builtins_config_sources}
slidetabs/slidetabs_config.cpp
slidetabs/slidetabs_config.ui
)
# .desktop files
install( FILES
slidetabs/slidetabs_config.desktop
DESTINATION ${SERVICES_INSTALL_DIR}/kwin )

View File

@ -0,0 +1,194 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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 "slidetabs.h"
#include <cmath>
#include <QPoint>
namespace KWin
{
KWIN_EFFECT( slidetabs, SlideTabsEffect )
SlideTabsEffect::SlideTabsEffect()
{
reconfigure( ReconfigureAll );
}
void SlideTabsEffect::reconfigure( ReconfigureFlags )
{
KConfigGroup conf = EffectsHandler::effectConfig("SlideTabs");
switching = conf.readEntry("SlideSwitching", true );
grouping = conf.readEntry("SlideGrouping", true );
totalTime = conf.readEntry("SlideDuration", 500 );
}
void SlideTabsEffect::prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time )
{
if( motionManager.isManaging( w ) )
{
data.setTransformed();
w->enablePainting( EffectWindow::PAINT_DISABLED );
timeLine.addTime( time );
}
effects->prePaintWindow( w, data, time );
}
void SlideTabsEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if( motionManager.isManaging( w ) && w == inMove )
motionManager.apply( w, data );
effects->paintWindow( w, mask, region, data );
}
void SlideTabsEffect::postPaintWindow( EffectWindow* w )
{
if( motionManager.isManaging( w ) )
{
if( w == inMove )
{
QRect moving = calculateNextMove();
motionManager.moveWindow( w, moving );
if( direction && timeLine.progress() >= 0.5 )
{
moving = target;
target = source;
source = moving;
direction = false;
effects->setElevatedWindow( notMoving, false );
effects->setElevatedWindow( w, true );
}
else if( timeLine.progress() >= 1.0 )
{
effects->setElevatedWindow( notMoving, false );
effects->setElevatedWindow( inMove, false );
motionManager.unmanage( notMoving );
motionManager.unmanage( inMove );
notMoving = NULL;
inMove = NULL;
wasD = false;
effects->addRepaintFull();
}
}
else if( w == notMoving && !direction && target != w->geometry() && !wasD )
{
target = w->geometry();
}
w->addRepaintFull();
}
effects->postPaintWindow( w );
}
void SlideTabsEffect::prePaintScreen( ScreenPrePaintData &data, int time )
{
if( motionManager.managingWindows() )
{
motionManager.calculate( time );
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
}
effects->prePaintScreen( data, time );
}
void SlideTabsEffect::postPaintScreen()
{
if( motionManager.managingWindows() )
effects->addRepaintFull();
effects->postPaintScreen();
}
void SlideTabsEffect::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
{
if( !switching )
return;
inMove = to;
notMoving = from;
source = notMoving->geometry();
QRect window = notMoving->geometry();
int leftSpace = window.x(), rightSpace = displayWidth() - ( window.x() + window.width() ),
upSpace = window.y(), downSpace = displayHeight() - ( window.y() + window.height() );
if( leftSpace >= rightSpace && leftSpace >= upSpace && leftSpace >= downSpace )
target = QRect( source.x() - ( 1.2 * source.width() ), source.y(), source.width(), source.height() );
else if( rightSpace >= leftSpace && rightSpace >= upSpace && rightSpace >= downSpace )
target = QRect( source.x() + ( 1.2 * source.width() ), source.y(), source.width(), source.height() );
else if( upSpace >= leftSpace && upSpace >= rightSpace && upSpace >= downSpace )
target = QRect( source.x(), source.y() - ( 1.2 * source.height() ), source.width(), source.height() );
else
target = QRect( source.x(), source.y() + ( 1.2 * source.height() ), source.width(), source.height() );
timeLine.setCurveShape( TimeLine::LinearCurve );
timeLine.setDuration( animationTime( totalTime ) );
timeLine.setProgress( 0.0f );
motionManager.manage( inMove );
motionManager.manage( notMoving );
distance = sqrt( ( ( source.x()-target.x() ) * ( source.x()-target.x() ) ) + ( ( source.y()-target.y() ) * ( source.y()-target.y() ) ) );
effects->setElevatedWindow( notMoving, true );
direction = wasD = true;
QRect moving = calculateNextMove();
motionManager.moveWindow( inMove, moving );
}
void SlideTabsEffect::clientGroupItemAdded( EffectWindow* from, EffectWindow* to )
{
if( !grouping || from->desktop() != to->desktop() || from->isMinimized() || to->isMinimized() )
return;
timeLine.setCurveShape( TimeLine::LinearCurve );
timeLine.setDuration( animationTime( totalTime ) );
timeLine.setProgress( 0.0f );
inMove = from;
notMoving = to;
source = inMove->geometry();
target = notMoving->geometry();
distance = sqrt( ( ( source.x()-target.x() ) * ( source.x()-target.x() ) ) + ( ( source.y()-target.y() ) * ( source.y()-target.y() ) ) );
motionManager.manage( inMove );
motionManager.manage( notMoving );
QRect moving = calculateNextMove();
motionManager.moveWindow( inMove, moving );
effects->setElevatedWindow( notMoving, true );
direction = wasD = false;
}
QPoint SlideTabsEffect::calculatePointTarget( const QPoint &a, const QPoint &b )
{
double dy, dx, x, y, k;
k = direction? (2.0*timeLine.progress()):(wasD?((timeLine.progress()-0.5)*2):timeLine.progress());
dx = fabs( a.x() - b.x() );
dy = fabs( a.y() - b.y() );
y = k*dy;
x = k*dx;
if( a.x() > b.x() )
x = -x;
if( a.y() > b.y() )
y = -y;
return QPoint( a.x() + x, a.y() + y );
}
QRect SlideTabsEffect::calculateNextMove()
{
QPoint topLeft, bottomRight;
int mw = source.width(), mh = source.height(), tw = target.width(), th = target.height();
topLeft = calculatePointTarget( QPoint( source.x(), source.y() ), QPoint( target.x(), target.y() ) );
bottomRight = calculatePointTarget( QPoint( source.x() + mw, source.y() + mh ), QPoint( target.x() + tw, target.y() + th ) );
return QRect( topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y() );
}
}

View File

@ -0,0 +1,21 @@
[Desktop Entry]
Name=Slide Tabs
Name[x-test]=xxSlide Tabsxx
Icon=preferences-system-windows-effect-slidetabs
Type=Service
Comment=Slide windows when switching or groupping tabs.
Type=Service
X-KDE-ServiceTypes=KWin/Effect
X-KDE-PluginInfo-Author=Jorge Mata
X-KDE-PluginInfo-Email=matamax123@gmail.com
X-KDE-PluginInfo-Name=kwin4_effect_slidetabs
X-KDE-PluginInfo-Version=0.1.0
X-KDE-PluginInfo-Category=Focus
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=false
X-KDE-Library=kwin4_effect_builtins
X-KDE-Ordering=50

View File

@ -0,0 +1,56 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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_SLIDETABS_H
#define KWIN_SLIDETABS_H
#include <kwineffects.h>
namespace KWin
{
class SlideTabsEffect : public Effect
{
public:
SlideTabsEffect();
virtual void reconfigure( ReconfigureFlags );
virtual void prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time );
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
virtual void postPaintWindow( EffectWindow* w );
virtual void prePaintScreen( ScreenPrePaintData &data, int time );
virtual void postPaintScreen();
virtual void clientGroupItemSwitched( EffectWindow* from, EffectWindow* to );
virtual void clientGroupItemAdded( EffectWindow* from, EffectWindow* to );
private:
QRect calculateNextMove();
QPoint calculatePointTarget( const QPoint &a, const QPoint &b );
WindowMotionManager motionManager;
EffectWindow* inMove;
EffectWindow* notMoving;
TimeLine timeLine;
QRect target, source;
bool direction, wasD, grouping, switching;
int totalTime, distance;
};
}
#endif

View File

@ -0,0 +1,95 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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 "slidetabs_config.h"
#include <kwineffects.h>
#include <klocale.h>
#include <kconfiggroup.h>
#include <QWidget>
#include <QVBoxLayout>
namespace KWin
{
KWIN_EFFECT_CONFIG_FACTORY
SlideTabsEffectConfigForm::SlideTabsEffectConfigForm( QWidget* parent ) : QWidget( parent )
{
setupUi( this );
}
SlideTabsEffectConfig::SlideTabsEffectConfig(QWidget* parent, const QVariantList& args)
: KCModule( EffectFactory::componentData(), parent, args )
{
m_ui = new SlideTabsEffectConfigForm( this );
QVBoxLayout* layout = new QVBoxLayout( this );
layout->addWidget( m_ui );
connect( m_ui->grouping, SIGNAL( toggled( bool )), this, SLOT( changed() ) );
connect( m_ui->switching, SIGNAL( toggled( bool )), this, SLOT( changed() ) );
connect( m_ui->duration, SIGNAL( valueChanged( int ) ), this, SLOT( changed() ) );
load();
}
void SlideTabsEffectConfig::save()
{
KConfigGroup conf = EffectsHandler::effectConfig( "SlideTabs" );
conf.writeEntry( "SlideGrouping", m_ui->grouping->isChecked() );
conf.writeEntry( "SlideSwitching", m_ui->switching->isChecked() );
conf.writeEntry( "SlideDuration", m_ui->duration->value() );
conf.sync();
KCModule::save();
emit changed( false );
EffectsHandler::sendReloadMessage( "slidetabs" );
}
void SlideTabsEffectConfig::load()
{
KCModule::load();
KConfigGroup conf = EffectsHandler::effectConfig( "SlideTabs" );
bool switching = conf.readEntry( "SlideSwitching", true );
bool grouping = conf.readEntry( "SlideGrouping", true );
int duration = conf.readEntry("SlideDuration", 500 );
m_ui->switching->setChecked( switching );
m_ui->grouping->setChecked( grouping );
m_ui->duration->setValue( duration );
emit changed( false );
}
void SlideTabsEffectConfig::defaults()
{
m_ui->grouping->setChecked( true );
m_ui->switching->setChecked( true );
m_ui->duration->setValue( 500 );
emit changed( true );
}
}
#include "slidetabs_config.moc"

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=KCModule
X-KDE-Library=kcm_kwin4_effect_builtins
X-KDE-ParentComponents=kwin4_effect_slidetabs
X-KDE-PluginKeyword=slidetabs
Name=Slide Tabs
Name[x-test]=xxSlide Tabsxx

View File

@ -0,0 +1,53 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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_SLIDETABS_CONFIG_H
#define KWIN_SLIDETABS_CONFIG_H
#include <kcmodule.h>
#include "ui_slidetabs_config.h"
namespace KWin
{
class SlideTabsEffectConfigForm : public QWidget, public Ui::SlideTabsEffectConfigForm
{
Q_OBJECT
public:
explicit SlideTabsEffectConfigForm( QWidget* parent );
};
class SlideTabsEffectConfig : public KCModule
{
Q_OBJECT
public:
explicit SlideTabsEffectConfig( QWidget* parent = 0, const QVariantList& args = QVariantList() );
public slots:
virtual void save();
virtual void load();
virtual void defaults();
private:
SlideTabsEffectConfigForm* m_ui;
};
}
#endif

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KWin::SlideTabsEffectConfigForm</class>
<widget class="QWidget" name="KWin::SlideTabsEffectConfigForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>238</width>
<height>90</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="grouping">
<property name="text">
<string>Slide when grouping</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="switching">
<property name="text">
<string>slide when switching tabs</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="duration">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,26 @@
#######################################
# Effect
# Source files
set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
swiveltabs/swiveltabs.cpp
)
# .desktop files
install( FILES
swiveltabs/swiveltabs.desktop
DESTINATION ${SERVICES_INSTALL_DIR}/kwin )
#######################################
# Config
# Source files
set( kwin4_effect_builtins_config_sources ${kwin4_effect_builtins_config_sources}
swiveltabs/swiveltabs_config.cpp
swiveltabs/swiveltabs_config.ui
)
# .desktop files
install( FILES
swiveltabs/swiveltabs_config.desktop
DESTINATION ${SERVICES_INSTALL_DIR}/kwin )

View File

@ -0,0 +1,157 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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 "swiveltabs.h"
#include <cmath>
namespace KWin
{
KWIN_EFFECT( swiveltabs, SwivelTabsEffect )
KWIN_EFFECT_SUPPORTED( swiveltabs, SwivelTabsEffect::supported() )
SwivelTabsEffect::SwivelTabsEffect()
{
isActive = false;
PI = 2.0 * acos( 0.0 );
reconfigure( ReconfigureAll );
}
bool SwivelTabsEffect::supported()
{
return effects->compositingType() == OpenGLCompositing;
}
void SwivelTabsEffect::reconfigure( ReconfigureFlags )
{
KConfigGroup conf = EffectsHandler::effectConfig("SwivelTabs");
vertical = conf.readEntry("SwivelVertical", true );
horizontal = conf.readEntry("SwivelHorizontal", true );
totalTime = conf.readEntry("SwivelDuration", 500 );
}
void SwivelTabsEffect::prePaintScreen( ScreenPrePaintData& data, int time )
{
if( isActive )
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
effects->prePaintScreen( data, time );
}
void SwivelTabsEffect::prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time )
{
if( isActive && ( w == windows.show || w == windows.hide ) )
{
data.quads = data.quads.makeGrid( 40 );
data.setTransformed();
w->enablePainting( EffectWindow::PAINT_DISABLED );
windows.time.addTime( time );
}
effects->prePaintWindow( w, data, time );
}
void SwivelTabsEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if( isActive && ( w == windows.show || w == windows.hide ) )
{
for( int i = 0; i < data.quads.count(); ++i )
transformQuad( data.quads[i] );
}
effects->paintWindow( w, mask, region, data );
}
void SwivelTabsEffect::postPaintWindow( EffectWindow* w )
{
if( isActive && ( w == windows.show || w == windows.hide ) )
w->addRepaintFull();
effects->postPaintWindow( w );
}
void SwivelTabsEffect::transformQuad( WindowQuad &quad )
{
double F = windows.time.progress();
int width = quad[1].x()-quad[0].x();
int height = quad[3].y() - quad[0].y();
int cx = quad[0].x() + ( width/2 );
int cy = quad[0].y() + ( height/2 );
if( lastF < 0.5 && F > 0.5 )
{
effects->setElevatedWindow( windows.hide, false );
effects->setElevatedWindow( windows.show, true );
}
lastF = F;
if( F >= 1.0 )
{
isActive = false;
effects->setElevatedWindow( windows.hide, false );
effects->setElevatedWindow( windows.show, false );
}
if( F < 0.5 )
{
if( horizontal )
{
quad[0].setX( quad[0].x() + ( width * F ) );
quad[3].setX( quad[3].x() + ( width * F ) );
quad[1].setX( quad[1].x() - ( width * F ) );
quad[2].setX( quad[2].x() - ( width * F ) );
}
if( vertical )
{
quad[0].setY( quad[0].y() + ( height * F ) );
quad[3].setY( quad[3].y() - ( height * F ) );
quad[1].setY( quad[1].y() + ( height * F ) );
quad[2].setY( quad[2].y() - ( height * F ) );
}
}
else
{
F -= 0.5;
if( horizontal )
{
quad[0].setX( cx - ( width * F ) );
quad[3].setX( cx - ( width * F ) );
quad[1].setX( cx + ( width * F ) );
quad[2].setX( cx + ( width * F ) );
}
if( vertical )
{
quad[0].setY( cy - ( height * F ) );
quad[3].setY( cy + ( height * F ) );
quad[1].setY( cy - ( height * F ) );
quad[2].setY( cy + ( height * F ) );
}
}
}
void SwivelTabsEffect::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
{
if( isActive || from->isMinimized() )
return;
windows.show = to;
windows.hide = from;
windows.time.setCurveShape( TimeLine::LinearCurve );
windows.time.setDuration( animationTime( totalTime ) );
windows.time.setProgress( 0.0f );
lastF = 0.0;
isActive = true;
effects->setElevatedWindow( to, false );
effects->setElevatedWindow( from, true );
}
}

View File

@ -0,0 +1,18 @@
[Desktop Entry]
Name=Swivel Tabs
Name[x-test]=xxSwivel Tabsxx
Icon=preferences-system-windows-effect-swiveltabs
Comment=Make the windows turn when switching tabs
Comment[x-test]=xxMake the windows turn when switching tabsxx
Type=Service
X-KDE-ServiceTypes=KWin/Effect
X-KDE-PluginInfo-Author=Jorge Mata
X-KDE-PluginInfo-Email=matamax123@gmail.com
X-KDE-PluginInfo-Name=kwin4_effect_swiveltabs
X-KDE-PluginInfo-Version=0.1.0
X-KDE-PluginInfo-Category=Focus
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=false
X-KDE-Library=kwin4_effect_builtins

View File

@ -0,0 +1,62 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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_SWIVELTABS_H
#define KWIN_SWIVELTABS_H
#include <kwineffects.h>
namespace KWin
{
class SwivelTabsEffect : public Effect
{
public:
SwivelTabsEffect();
virtual void reconfigure( ReconfigureFlags );
virtual void prePaintScreen( ScreenPrePaintData& data, int time );
virtual void prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time );
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
virtual void postPaintWindow( EffectWindow* w );
virtual void clientGroupItemSwitched( EffectWindow* from, EffectWindow* to );
static bool supported();
private:
struct swivel
{
EffectWindow* hide;
EffectWindow* show;
TimeLine time;
// The points of the transformed window
QPoint topLeft, topRight, bottomLeft, bottomRight;
// The size of the transformed window
double left, top, right, bottom;
};
swivel windows;
bool isActive, horizontal, vertical;
int totalTime, kal;
double PI, lastF;
void nextStep( EffectWindow *w );
void transformQuad( WindowQuad &quad );
double calculateCoords( double original, double space, double newSpace );
};
}
#endif

View File

@ -0,0 +1,91 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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 "swiveltabs_config.h"
#include <kwineffects.h>
#include <QVBoxLayout>
namespace KWin
{
KWIN_EFFECT_CONFIG_FACTORY
SwivelTabsEffectConfigForm::SwivelTabsEffectConfigForm( QWidget* parent ) : QWidget( parent )
{
setupUi( this );
}
SwivelTabsEffectConfig::SwivelTabsEffectConfig(QWidget* parent, const QVariantList& args)
: KCModule( EffectFactory::componentData(), parent, args )
{
m_ui = new SwivelTabsEffectConfigForm( this );
QVBoxLayout* layout = new QVBoxLayout( this );
layout->addWidget( m_ui );
connect( m_ui->vertical, SIGNAL( toggled( bool )), this, SLOT( changed() ) );
connect( m_ui->horizontal, SIGNAL( toggled( bool )), this, SLOT( changed() ) );
connect( m_ui->duration, SIGNAL( valueChanged( int ) ), this, SLOT( changed() ) );
load();
}
SwivelTabsEffectConfig::~SwivelTabsEffectConfig()
{
delete m_ui;
}
void SwivelTabsEffectConfig::save()
{
KConfigGroup conf = EffectsHandler::effectConfig( "SwivelTabs" );
conf.writeEntry( "SwivelVertical", m_ui->vertical->isChecked() );
conf.writeEntry( "SwivelHorizontal", m_ui->horizontal->isChecked() );
conf.writeEntry( "SwivelDuration", m_ui->duration->value() );
conf.sync();
KCModule::save();
emit changed( false );
EffectsHandler::sendReloadMessage( "swiveltabs" );
}
void SwivelTabsEffectConfig::load()
{
KCModule::load();
KConfigGroup conf = EffectsHandler::effectConfig( "SwivelTabs" );
bool vertical = conf.readEntry( "SwivelVertical", true );
bool horizontal = conf.readEntry( "SwivelHorizontal", true );
int duration = conf.readEntry("SwivelDuration", 500 );
m_ui->vertical->setChecked( vertical );
m_ui->horizontal->setChecked( horizontal );
m_ui->duration->setValue( duration );
emit changed( false );
}
void SwivelTabsEffectConfig::defaults()
{
m_ui->vertical->setChecked( true );
m_ui->horizontal->setChecked( true );
m_ui->duration->setValue( 500 );
emit changed( true );
}
}

View File

@ -0,0 +1,10 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=KCModule
X-KDE-Library=kcm_kwin4_effect_builtins
X-KDE-ParentComponents=kwin4_effect_swiveltabs
X-KDE-PluginKeyword=swiveltabs
Name=Swivel Tabs
Name[x-test]=xxSwivel Tabsxx

View File

@ -0,0 +1,53 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Jorge Mata <matamax123@gmail.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_SWIVELTABS_CONFIG_H
#define KWIN_SWIVELTABS_CONFIG_H
#include <kcmodule.h>
#include "ui_swiveltabs_config.h"
namespace KWin
{
class SwivelTabsEffectConfigForm : public QWidget, public Ui::SwivelTabsEffectConfigForm
{
Q_OBJECT
public:
explicit SwivelTabsEffectConfigForm( QWidget* parent );
};
class SwivelTabsEffectConfig : public KCModule
{
Q_OBJECT
public:
explicit SwivelTabsEffectConfig(QWidget* parent = 0, const QVariantList& args = QVariantList());
~SwivelTabsEffectConfig();
public slots:
virtual void save();
virtual void load();
virtual void defaults();
private:
SwivelTabsEffectConfigForm *m_ui;
};
}
#endif

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KWin::SwivelTabsEffectConfigForm</class>
<widget class="QWidget" name="KWin::SwivelTabsEffectConfigForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>234</width>
<height>102</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="vertical">
<property name="text">
<string>Vertical</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="horizontal">
<property name="text">
<string>Horizontal</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Duration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="duration">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>250</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1614,6 +1614,8 @@ bool PresentWindowsEffect::isSelectableWindow( EffectWindow *w )
return false;
if( !w->acceptsFocus() )
return false;
if( !w->visibleInClientGroup() )
return false;
switch( m_mode )
{
case ModeAllDesktops:

View File

@ -61,6 +61,18 @@ void SlideBackEffect::windowActivated( EffectWindow* w )
return;
}
if( clientItemShown == w )
{
clientItemShown = NULL;
updateStackingOrder();
return;
}
if( clientItemHidden == w )
{
clientItemHidden = NULL;
updateStackingOrder();
return;
}
// Determine all windows on top of the activated one
bool currentFound = false;
foreach( EffectWindow *tmp, oldStackingOrder )
@ -351,6 +363,12 @@ void SlideBackEffect::windowUnminimized( EffectWindow* w)
}
}
void SlideBackEffect::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
{
clientItemShown = to;
clientItemHidden = from;
}
void SlideBackEffect::tabBoxClosed()
{
disabled = true;
@ -368,7 +386,8 @@ bool SlideBackEffect::isWindowOnTop( EffectWindow* w )
bool SlideBackEffect::isWindowUsable( EffectWindow* w )
{
return w && ( w->isNormalWindow() || w->isDialog() ) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized();
return w && ( w->isNormalWindow() || w->isDialog() ) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized()
&& w->visibleInClientGroup();
}
bool SlideBackEffect::intersects( EffectWindow* windowUnder, const QRect &windowOverGeometry )

View File

@ -45,6 +45,7 @@ class SlideBackEffect
virtual void windowDeleted( EffectWindow* w );
virtual void windowAdded( EffectWindow* w );
virtual void windowUnminimized( EffectWindow* w );
virtual void clientGroupItemSwitched( EffectWindow* from, EffectWindow* to );
virtual void tabBoxClosed();
@ -56,6 +57,8 @@ class SlideBackEffect
EffectWindowList coveringWindows;
EffectWindowList elevatedList;
EffectWindow* unminimizedWindow;
EffectWindow* clientItemShown;
EffectWindow* clientItemHidden;
QHash<EffectWindow *, QRect> destinationList;
bool disabled;
QList <QRegion> clippedRegions;

View File

@ -1367,6 +1367,8 @@ bool Client::buttonReleaseEvent( Window w, int /*button*/, int state, int x, int
QPoint mousepos( x_root - x, y_root - y );
mode = mousePosition( mousepos );
}
else if( workspace()->decorationSupportsClientGrouping() )
return false;
updateCursor();
}
return true;

View File

@ -402,6 +402,7 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos, bool unrestricted
{
if ((*l)->isOnDesktop(c->desktop()) &&
!(*l)->isMinimized()
&& (!(*l)->clientGroup() || (*l) != (*l)->clientGroup()->visible())
&& (*l) != c )
{
lx = (*l)->x();
@ -1345,8 +1346,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 = minSize();
QSize max_size = maxSize();
QSize min_size = clientGroup() ? clientGroup()->minSize() : minSize();
QSize max_size = clientGroup() ? clientGroup()->maxSize() : maxSize();
if( decoration != NULL )
{
QSize decominsize = decoration->minimumSize();
@ -1902,8 +1903,8 @@ bool Client::isResizable() const
if( rules()->checkSize( QSize()).isValid()) // forced size
return false;
QSize min = minSize();
QSize max = maxSize();
QSize min = clientGroup() ? clientGroup()->minSize() : minSize();
QSize max = clientGroup() ? clientGroup()->maxSize() : maxSize();
return min.width() < max.width() || min.height() < max.height();
}
@ -2025,6 +2026,10 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
addWorkspaceRepaint( deco_rect );
geom_before_block = geom;
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::plainResize( int w, int h, ForceGeometry_t force )
@ -2097,6 +2102,10 @@ void Client::plainResize( int w, int h, ForceGeometry_t force )
addWorkspaceRepaint( deco_rect );
geom_before_block = geom;
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
/*!
@ -2138,6 +2147,10 @@ void Client::move( int x, int y, ForceGeometry_t force )
addWorkspaceRepaint( deco_rect ); // trigger repaint of window's new location
geom_before_block = geom;
deco_rect_before_block = deco_rect;
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::blockGeometryUpdates( bool block )
@ -2181,6 +2194,10 @@ void Client::setMaximize( bool vertically, bool horizontally )
false );
if( effects )
static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), true, true );
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
@ -3005,7 +3022,6 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
abort();
break;
}
// adjust new size to snap to other windows/borders
moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );

View File

@ -467,6 +467,58 @@ bool KDecorationPreviewBridge::compositingActive() const
return KWindowSystem::compositingActive();
}
// Window tabbing
bool KDecorationPreviewBridge::isClientGroupActive()
{
return active;
}
QList< ClientGroupItem > KDecorationPreviewBridge::clientGroupItems() const
{
return QList< ClientGroupItem >() << ClientGroupItem(
active ? "Active Window" : "Inactive Window", icon() );
}
int KDecorationPreviewBridge::itemId( int )
{
return 0;
}
int KDecorationPreviewBridge::visibleClientGroupItem()
{
return 0;
}
void KDecorationPreviewBridge::setVisibleClientGroupItem( int )
{
}
void KDecorationPreviewBridge::moveItemInClientGroup( int, int )
{
}
void KDecorationPreviewBridge::moveItemToClientGroup( int, int )
{
}
void KDecorationPreviewBridge::removeFromClientGroup( int, const QRect& )
{
}
void KDecorationPreviewBridge::closeClientGroupItem( int )
{
}
void KDecorationPreviewBridge::closeAllInClientGroup()
{
}
void KDecorationPreviewBridge::displayClientMenu( int, const QPoint& )
{
}
KDecorationPreviewOptions::KDecorationPreviewOptions()
{
customBorderSize = BordersCount; // invalid

View File

@ -115,6 +115,20 @@ class KDecorationPreviewBridge
virtual void grabXServer( bool grab );
virtual bool compositingActive() const;
// Window tabbing
virtual bool isClientGroupActive();
virtual QList< ClientGroupItem > clientGroupItems() const;
virtual int itemId( int index );
virtual int visibleClientGroupItem();
virtual void setVisibleClientGroupItem( int index );
virtual void moveItemInClientGroup( int index, int before );
virtual void moveItemToClientGroup( int 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 );
private:
KDecorationPreview* preview;
bool active;

View File

@ -54,6 +54,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
a->setText( i18n("Navigation") );
DEF( I18N_NOOP("Walk Through Windows"), Qt::ALT+Qt::Key_Tab, slotWalkThroughWindows() );
DEF( I18N_NOOP("Walk Through Windows (Reverse)"), Qt::ALT+Qt::SHIFT+Qt::Key_Backtab, slotWalkBackThroughWindows() );
DEF( I18N_NOOP("Walk Through Grouped Windows"), 0, slotSwitchToTabRight() );
DEF( I18N_NOOP("Walk Through Grouped Windows (Reverse)"), 0, slotSwitchToTabLeft() );
DEF( I18N_NOOP("Remove Window From Group"), 0, slotRemoveFromGroup() );
DEF( I18N_NOOP("Walk Through Windows Alternative"), 0, slotWalkThroughWindowsAlternative() );
DEF( I18N_NOOP("Walk Through Windows Alternative (Reverse)"), 0, slotWalkBackThroughWindowsAlternative() );
DEF( I18N_NOOP("Walk Through Desktops"), 0, slotWalkThroughDesktops() );

View File

@ -820,6 +820,10 @@ void Client::setKeepAbove( bool b )
decoration->emitKeepAboveChanged( keepAbove());
workspace()->updateClientLayer( this );
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
void Client::setKeepBelow( bool b )
@ -839,6 +843,10 @@ void Client::setKeepBelow( bool b )
decoration->emitKeepBelowChanged( keepBelow());
workspace()->updateClientLayer( this );
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
Layer Client::layer() const

View File

@ -1279,5 +1279,67 @@ bool KCommonDecorationUnstable::compositingActive() const
return static_cast<const KDecorationUnstable*>( decoration() )->compositingActive();
}
// Window tabbing
bool KCommonDecorationUnstable::isClientGroupActive()
{
return static_cast<const KDecorationUnstable*>( decoration() )->isClientGroupActive();
}
QList< ClientGroupItem > KCommonDecorationUnstable::clientGroupItems() const
{
return static_cast<const KDecorationUnstable*>( decoration() )->clientGroupItems();
}
int KCommonDecorationUnstable::itemId( int index )
{
return static_cast<const KDecorationUnstable*>( decoration() )->itemId( index );
}
int KCommonDecorationUnstable::visibleClientGroupItem()
{
return static_cast<const KDecorationUnstable*>( decoration() )->visibleClientGroupItem();
}
void KCommonDecorationUnstable::setVisibleClientGroupItem( int index )
{
static_cast<const KDecorationUnstable*>( decoration() )->setVisibleClientGroupItem( index );
}
void KCommonDecorationUnstable::moveItemInClientGroup( int index, int before )
{
static_cast<const KDecorationUnstable*>( decoration() )->moveItemInClientGroup( index, before );
}
void KCommonDecorationUnstable::moveItemToClientGroup( int itemId, int before )
{
static_cast<const KDecorationUnstable*>( decoration() )->moveItemToClientGroup( itemId, before );
}
void KCommonDecorationUnstable::removeFromClientGroup( int index, const QRect& newGeom )
{
static_cast<const KDecorationUnstable*>( decoration() )->removeFromClientGroup( index, newGeom );
}
void KCommonDecorationUnstable::closeClientGroupItem( int index )
{
static_cast<const KDecorationUnstable*>( decoration() )->closeClientGroupItem( index );
}
void KCommonDecorationUnstable::closeAllInClientGroup()
{
static_cast<const KDecorationUnstable*>( decoration() )->closeAllInClientGroup();
}
void KCommonDecorationUnstable::displayClientMenu( int index, const QPoint& pos )
{
static_cast<const KDecorationUnstable*>( decoration() )->displayClientMenu( index, pos );
}
bool KCommonDecorationUnstable::eventFilter( QObject* o, QEvent* e )
{
return KCommonDecoration::eventFilter( o, e );
}
// kate: space-indent on; indent-width 4; mixedindent off; indent-mode cstyle;

View File

@ -44,7 +44,9 @@ enum ButtonType {
AboveButton,
BelowButton,
ShadeButton,
NumButtons
NumButtons,
ItemCloseButton=100, // Close only one tab
ItemMenuButton // shows the window menu for one tab
};
class KCommonDecorationButton;
@ -378,6 +380,21 @@ class KWIN_EXPORT KCommonDecorationUnstable
KCommonDecorationUnstable(KDecorationBridge* bridge, KDecorationFactory* factory);
virtual ~KCommonDecorationUnstable();
bool compositingActive() const;
// Window tabbing
bool isClientGroupActive();
QList< ClientGroupItem > clientGroupItems() const;
int itemId( int index );
int visibleClientGroupItem();
void setVisibleClientGroupItem( int index );
void moveItemInClientGroup( int index, int before );
void moveItemToClientGroup( int 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 );
virtual bool eventFilter( QObject* o, QEvent* e );
};
/**

View File

@ -99,7 +99,7 @@ QWidget* KDecoration::initialParentWidget() const
{
return bridge_->initialParentWidget();
}
Qt::WFlags KDecoration::initialWFlags() const
{
return bridge_->initialWFlags();
@ -109,7 +109,7 @@ bool KDecoration::isActive() const
{
return bridge_->isActive();
}
bool KDecoration::isCloseable() const
{
return bridge_->isCloseable();
@ -119,12 +119,12 @@ bool KDecoration::isMaximizable() const
{
return bridge_->isMaximizable();
}
KDecoration::MaximizeMode KDecoration::maximizeMode() const
{
return bridge_->maximizeMode();
}
bool KDecoration::isMinimizable() const
{
return bridge_->isMinimizable();
@ -134,32 +134,32 @@ bool KDecoration::providesContextHelp() const
{
return bridge_->providesContextHelp();
}
int KDecoration::desktop() const
{
return bridge_->desktop();
}
bool KDecoration::isModal() const
{
return bridge_->isModal();
}
bool KDecoration::isShadeable() const
{
return bridge_->isShadeable();
}
bool KDecoration::isShade() const
{
return bridge_->isShade();
}
bool KDecoration::isSetShade() const
{
return bridge_->isSetShade();
}
bool KDecoration::keepAbove() const
{
return bridge_->keepAbove();
@ -189,7 +189,7 @@ QIcon KDecoration::icon() const
{
return bridge_->icon();
}
QString KDecoration::caption() const
{
return bridge_->caption();
@ -204,7 +204,7 @@ void KDecoration::showWindowMenu( const QRect &pos )
{
bridge_->showWindowMenu( pos );
}
void KDecoration::showWindowMenu( QPoint pos )
{
bridge_->showWindowMenu( pos );
@ -219,22 +219,22 @@ void KDecoration::setMask( const QRegion& reg, int mode )
{
bridge_->setMask( reg, mode );
}
void KDecoration::clearMask()
{
bridge_->setMask( QRegion(), 0 );
}
bool KDecoration::isPreview() const
{
return bridge_->isPreview();
}
QRect KDecoration::geometry() const
{
return bridge_->geometry();
}
QRect KDecoration::iconGeometry() const
{
return bridge_->iconGeometry();
@ -249,7 +249,7 @@ WId KDecoration::windowId() const
{
return bridge_->windowId();
}
void KDecoration::closeWindow()
{
bridge_->closeWindow();
@ -264,7 +264,7 @@ void KDecoration::maximize( MaximizeMode mode )
{
bridge_->maximize( mode );
}
void KDecoration::minimize()
{
bridge_->minimize();
@ -274,7 +274,7 @@ void KDecoration::showContextHelp()
{
bridge_->showContextHelp();
}
void KDecoration::setDesktop( int desktop )
{
bridge_->setDesktop( desktop );
@ -302,12 +302,12 @@ void KDecoration::setShade( bool set )
{
bridge_->setShade( set );
}
void KDecoration::setKeepAbove( bool set )
{
bridge_->setKeepAbove( set );
}
void KDecoration::setKeepBelow( bool set )
{
bridge_->setKeepBelow( set );
@ -341,7 +341,7 @@ void KDecoration::grabXServer()
{
bridge_->grabXServer( true );
}
void KDecoration::ungrabXServer()
{
bridge_->grabXServer( false );
@ -403,6 +403,69 @@ void KDecorationUnstable::padding(int &left, int &right, int &top, int &bottom)
left = right = top = bottom = 0;
}
// Window tabbing
bool KDecorationUnstable::isClientGroupActive()
{
return static_cast< KDecorationBridgeUnstable* >( bridge_ )->isClientGroupActive();
}
QList< ClientGroupItem > KDecorationUnstable::clientGroupItems() const
{
return static_cast< KDecorationBridgeUnstable* >( bridge_ )->clientGroupItems();
}
int KDecorationUnstable::itemId( int index )
{
return static_cast< KDecorationBridgeUnstable* >( bridge_ )->itemId( index );
}
int KDecorationUnstable::visibleClientGroupItem()
{
return static_cast< KDecorationBridgeUnstable* >( bridge_ )->visibleClientGroupItem();
}
void KDecorationUnstable::setVisibleClientGroupItem( int index )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->setVisibleClientGroupItem( index );
}
void KDecorationUnstable::moveItemInClientGroup( int index, int before )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->moveItemInClientGroup( index, before );
}
void KDecorationUnstable::moveItemToClientGroup( int itemId, int before )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->moveItemToClientGroup( itemId, before );
}
void KDecorationUnstable::removeFromClientGroup( int index, const QRect& newGeom )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->removeFromClientGroup( index, newGeom );
}
void KDecorationUnstable::closeClientGroupItem( int index )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->closeClientGroupItem( index );
}
void KDecorationUnstable::closeAllInClientGroup()
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->closeAllInClientGroup();
}
void KDecorationUnstable::displayClientMenu( int index, const QPoint& pos )
{
static_cast< KDecorationBridgeUnstable* >( bridge_ )->displayClientMenu( index, pos );
}
QString KDecorationDefines::clientGroupItemDragMimeType()
{
return "text/ClientGroupItem";
}
KDecorationOptions::KDecorationOptions()
: d( new KDecorationOptionsPrivate )
{
@ -495,7 +558,7 @@ bool KDecorationOptions::moveResizeMaximizedWindows() const
KDecorationDefines::WindowOperation KDecorationOptions::operationMaxButtonClick( Qt::MouseButtons button ) const
{
return button == Qt::RightButton? d->opMaxButtonRightClick :
return button == Qt::RightButton? d->opMaxButtonRightClick :
button == Qt::MidButton? d->opMaxButtonMiddleClick :
d->opMaxButtonLeftClick;
}

View File

@ -105,7 +105,11 @@ public:
NoBorderOp,
NoOp,
SetupWindowShortcutOp,
ApplicationRulesOp
ApplicationRulesOp,
RemoveClientFromGroupOp, // Remove from group
CloseClientGroupOp, // Close the group
MoveClientInGroupLeftOp, // Move left in the group
MoveClientInGroupRightOp // Move right in the group
};
/**
* Basic color types that should be recognized by all decoration styles.
@ -192,13 +196,44 @@ public:
/// The mask is still used to define the input region and the blurred
/// region, when the blur plugin is enabled.
/// @since 4.3
// Tabbing
AbilityClientGrouping = 4000, ///< The decoration supports tabbing
// TODO colors for individual button types
ABILITY_DUMMY = 10000000
};
enum Requirement { REQUIREMENT_DUMMY = 1000000 };
/**
*
* Returns mimeType used to drag and drop clientGroupItems
*/
static QString clientGroupItemDragMimeType();
};
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
{
@ -672,6 +707,7 @@ class KWIN_EXPORT KDecoration
* isShade() to get the current state.
*/
virtual void shadeChange() = 0;
Q_SIGNALS:
/**
* This signal is emitted whenever the window's keep-above state changes.
@ -681,6 +717,7 @@ class KWIN_EXPORT KDecoration
* This signal is emitted whenever the window's keep-below state changes.
*/
void keepBelowChanged( bool );
public:
/**
* This function may be reimplemented to provide custom bound drawing
@ -848,6 +885,7 @@ class KWIN_EXPORT KDecoration
* @internal
*/
void emitKeepBelowChanged( bool below );
private:
KDecorationBridge* bridge_;
QWidget* w_;
@ -856,6 +894,7 @@ class KWIN_EXPORT KDecoration
friend class KDecorationUnstable; // for bridge_
static KDecorationOptions* options_;
KDecorationPrivate* d;
};
/**
@ -884,6 +923,62 @@ class KWIN_EXPORT KDecorationUnstable
* Returns @a true if compositing--and therefore ARGB--is enabled.
*/
bool compositingActive() const;
// Window tabbing
/**
* Returns whether or not this client group contains the active client.
*/
bool isClientGroupActive();
/**
* Return a list of all the clients in the group that contains the client that this
* decoration is attached to.
*/
QList< ClientGroupItem > clientGroupItems() const;
/**
* Returns a unique identifier for the client at index \p index of the client group list.
* \see moveItemToClientGroup()
*/
int itemId( int index );
/**
* Returns the list index of the currently visible client in this group.
*/
int visibleClientGroupItem();
/**
* Switch the currently visible client to the one at list index \p index.
*/
void setVisibleClientGroupItem( int index );
/**
* Move the client at index \p index to the position before the client at index \p before.
*/
void moveItemInClientGroup( int index, int before );
/**
* 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()
*/
void moveItemToClientGroup( int itemId, int before = -1 );
/**
* 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.
*/
void removeFromClientGroup( int index, const QRect& newGeom = QRect() );
/**
* Close the client at index \p index.
*/
void closeClientGroupItem( int index );
/**
* Close all windows in this group.
*/
void closeAllInClientGroup();
/**
* 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 );
};
inline

View File

@ -94,6 +94,19 @@ class KWIN_EXPORT KDecorationBridgeUnstable
{
public:
virtual bool compositingActive() const = 0;
// Window tabbing
virtual bool isClientGroupActive() = 0;
virtual QList< ClientGroupItem > clientGroupItems() const = 0;
virtual int 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( int 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;
};
/** @} */

View File

@ -155,6 +155,18 @@ void Effect::windowUnminimized( EffectWindow* )
{
}
void Effect::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
{
}
void Effect::clientGroupItemAdded( EffectWindow* from, EffectWindow* to )
{
}
void Effect::clientGroupItemRemoved( EffectWindow* c, EffectWindow* group )
{
}
void Effect::windowInputMouseEvent( Window, QEvent* )
{
}

View File

@ -170,7 +170,7 @@ X-KDE-Library=kwin4_effect_cooleffect
#define KWIN_EFFECT_API_MAKE_VERSION( major, minor ) (( major ) << 8 | ( minor ))
#define KWIN_EFFECT_API_VERSION_MAJOR 0
#define KWIN_EFFECT_API_VERSION_MINOR 106
#define KWIN_EFFECT_API_VERSION_MINOR 107
#define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \
KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR )
@ -393,6 +393,9 @@ class KWIN_EXPORT Effect
virtual void windowActivated( EffectWindow* c );
virtual void windowMinimized( EffectWindow* c );
virtual void windowUnminimized( EffectWindow* c );
virtual void clientGroupItemSwitched( EffectWindow* from, EffectWindow* to );
virtual void clientGroupItemAdded( EffectWindow* from, EffectWindow* to ); // from merged with to
virtual void clientGroupItemRemoved( EffectWindow* c, EffectWindow* group ); // c removed from group
virtual void windowInputMouseEvent( Window w, QEvent* e );
virtual void desktopChanged( int old );
virtual void windowDamaged( EffectWindow* w, const QRect& r );
@ -956,6 +959,8 @@ class KWIN_EXPORT EffectWindow
virtual void minimize() const = 0;
virtual void unminimize() const = 0;
virtual void closeWindow() const = 0;
virtual bool visibleInClientGroup() const = 0;
};
class KWIN_EXPORT EffectWindowGroup

View File

@ -296,6 +296,26 @@ bool Client::manage( Window w, bool isMapped )
if( placementDone )
move( geom.x(), geom.y() ); // Before gravitating
// Create client group if the window will have a decoration
if( !noBorder() )
{
client_group = NULL;
// Automatically add to previous groups on session restore
if( session && session->clientGroupClient && session->clientGroupClient != this )
session->clientGroupClient->clientGroup()->add( this, -1, true );
else if( isMapped )
// 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() ))
{
group->add( this, -1, true );
break;
}
if( !client_group )
client_group = new ClientGroup( this );
}
updateDecoration( false ); // Also gravitates
// TODO: Is CentralGravity right here, when resizing is done after gravitating?
plainResize( rules()->checkSize( sizeForClientSize( geom.size() ), !isMapped ));

13
sm.cpp
View File

@ -127,6 +127,10 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase )
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 ? (int) c->clientGroup() : 0;
cg.writeEntry( QString("clientGroup")+n, group );
}
}
if( phase == SMSavePhase0 )
@ -193,6 +197,8 @@ void Workspace::loadSessionInfo()
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;
}
}
@ -266,6 +272,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;
return realInfo;
}

5
sm.h
View File

@ -34,6 +34,8 @@ class QSocketNotifier;
namespace KWin
{
class Client;
struct SessionInfo
{
QByteArray sessionId;
@ -62,6 +64,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
Client* clientGroupClient; // The first client created that has an identical identifier
};

View File

@ -859,6 +859,15 @@ void Workspace::slotWalkBackThroughWindowsKeyChanged( const QKeySequence& seq )
cutWalkThroughWindowsReverse = KShortcut( seq );
}
void Workspace::slotMoveToTabLeftKeyChanged( const QKeySequence& seq )
{
cutWalkThroughGroupWindows = KShortcut( seq );
}
void Workspace::slotMoveToTabRightKeyChanged( const QKeySequence& seq )
{
cutWalkThroughGroupWindowsReverse = KShortcut( seq );
}
void Workspace::slotWalkThroughWindowsAlternativeKeyChanged( const QKeySequence& seq )
{
cutWalkThroughWindowsAlternative = KShortcut( seq );

View File

@ -167,6 +167,27 @@ QMenu* Workspace::clientPopup()
mShadeOpAction->setCheckable( true );
mShadeOpAction->setData( Options::ShadeOp );
popup->addSeparator();
// Actions for window tabbing
if( decorationSupportsClientGrouping() )
{
mRemoveTabGroup = popup->addAction( i18n("Remove &from group") );
kaction = qobject_cast<KAction*>( keys->action("Remove TabGroup") );
if( kaction!=0 )
mRemoveTabGroup->setShortcut( kaction->globalShortcut().primary() );
mRemoveTabGroup->setData( Options::RemoveClientFromGroupOp );
mCloseGroup = popup->addAction( i18n("Close entire &group") );
mCloseGroup->setIcon( KIcon( "window-close" ) );
kaction = qobject_cast<KAction*>( keys->action("Close TabGroup") );
if( kaction!=0 )
mCloseGroup->setShortcut( kaction->globalShortcut().primary() );
mCloseGroup->setData( Options::CloseClientGroupOp );
popup->addSeparator();
}
action = popup->addMenu( advanced_popup );
action->setText( i18n("Ad&vanced") );
@ -196,6 +217,8 @@ void Workspace::discardPopup()
delete popup;
popup = NULL;
desk_popup = NULL;
switch_to_tab_popup = NULL;
add_tabs_popup = NULL;
}
void Workspace::setPopupClientOpacity( QAction* action )
@ -240,6 +263,22 @@ void Workspace::clientPopupAboutToShow()
mNoBorderOpAction->setChecked( active_popup_client->noBorder() );
mMinimizeOpAction->setEnabled( active_popup_client->isMinimizable() );
mCloseOpAction->setEnabled( active_popup_client->isCloseable() );
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()->items().count();
if( tabGroupSize > 1 )
initSwitchToTab();
initAddToTabGroup();
mRemoveTabGroup->setVisible( tabGroupSize > 1 );
mCloseGroup->setVisible( tabGroupSize > 1 );
}
if( trans_popup != NULL )
{
foreach( QAction* action, trans_popup->actions())
@ -252,6 +291,118 @@ void Workspace::clientPopupAboutToShow()
}
}
void Workspace::initSwitchToTab()
{
if( switch_to_tab_popup )
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 group window") );
}
void Workspace::slotSwitchToTab( QAction* action )
{
int side = action->data().toInt();
int c_id = active_popup_client->clientGroup()->indexOfClient( active_popup_client );
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.begin(); i != clientGroups.end(); ++i )
{
if( (*i)->contains( active_popup_client ))
{
(*i)->setVisible( side );
break;
}
}
}
}
void Workspace::switchToTabPopupAboutToShow()
{
if( !switch_to_tab_popup )
return;
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 );
switch_to_tab_popup->addSeparator();
int index = 2;
foreach( Client* c, active_popup_client->clientGroup()->clients() )
{
if( c != active_popup_client )
{
action = switch_to_tab_popup->addAction( c->caption() );
action->setData( index );
}
index++;
}
}
void Workspace::initAddToTabGroup()
{
if( add_tabs_popup )
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") );
}
void Workspace::slotAddToTabGroup( QAction* action )
{
if( !action->data().isValid() )
return;
moveItemToClientGroup( active_popup_client->clientGroup(),
active_popup_client->clientGroup()->indexOfClient( active_popup_client ),
clientGroups[action->data().toInt()], -1 );
}
void Workspace::groupTabPopupAboutToShow()
{
if( !add_tabs_popup )
return;
add_tabs_popup->clear();
int index = 0;
for( QList<ClientGroup*>::const_iterator i = clientGroups.begin(); i != clientGroups.end(); i++, index++ )
{
if( !(*i)->contains( active_popup_client ))
{
QAction* action = add_tabs_popup->addAction( (*i)->visible()->caption() );
action->setData( index );
}
}
}
void Workspace::initDesktopPopup()
{
@ -382,6 +533,22 @@ void Workspace::readShortcuts()
connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughWindowsKeyChanged(QKeySequence)));
}
kaction = qobject_cast<KAction*>( keys->action("Walk Through Group Windows") );
if( kaction != 0 )
{
cutWalkThroughGroupWindows = kaction->globalShortcut();
connect( kaction, SIGNAL( globalShortcutChanged( QKeySequence ) ), this,
SLOT( slotMoveToTabRightKeyChanged( QKeySequence ) ) );
}
kaction = qobject_cast<KAction*>( keys->action("Walk Through Group Windows (Reverse)") );
if( kaction != 0 )
{
cutWalkThroughGroupWindowsReverse = kaction->globalShortcut();
connect( kaction, SIGNAL( globalShortcutChanged( QKeySequence ) ), this,
SLOT( slotMoveToTabLeftKeyChanged( QKeySequence ) ) );
}
kaction = qobject_cast<KAction*>( keys->action("Walk Through Windows Alternative") );
if ( kaction!=0 )
{
@ -582,6 +749,31 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op )
break;
case Options::NoOp:
break;
case Options::RemoveClientFromGroupOp:
c->clientGroup()->remove( c );
break;
case Options::MoveClientInGroupLeftOp:
{
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 );
break;
}
case Options::MoveClientInGroupRightOp:
{
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 );
break;
}
case Options::CloseClientGroupOp:
c->clientGroup()->closeAll();
}
}
@ -1154,6 +1346,36 @@ void Workspace::slotWindowToDesktopDown()
}
}
void Workspace::slotSwitchToTabRight()
{
if( !active_client )
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 );
}
void Workspace::slotSwitchToTabLeft()
{
if( !active_client )
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 );
}
void Workspace::slotRemoveFromGroup()
{
if( !active_client )
return;
active_client->clientGroup()->remove( active_client );
}
/*!
Kill Window feature, similar to xkill

View File

@ -122,6 +122,8 @@ Workspace::Workspace( bool restore )
, advanced_popup( 0 )
, trans_popup( 0 )
, desk_popup( 0 )
, add_tabs_popup( 0 )
, switch_to_tab_popup( 0 )
, keys( 0 )
, client_keys( NULL )
, client_keys_dialog( NULL )
@ -1070,6 +1072,13 @@ void Workspace::slotReconfigure()
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.begin(); i != tmpGroups.end(); i++ )
(*i)->removeAll();
}
mgr->destroyPreviousPlugin();
}
else
@ -2855,6 +2864,19 @@ void Workspace::checkCursorPos()
x11ToQtKeyboardModifiers( last_buttons ), x11ToQtKeyboardModifiers( lastb ));
}
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 );
oldGroup->remove( c );
group->add( c, index, true );
}
} // namespace
#include "workspace.moc"

View File

@ -60,6 +60,7 @@ class TabBox;
}
class Client;
class ClientGroup;
class DesktopChangeOSD;
class RootInfo;
class PluginMgr;
@ -314,6 +315,15 @@ class Workspace : public QObject, public KDecorationDefines
void unrefTabBox();
void closeTabBox();
// 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 );
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
@ -371,6 +381,7 @@ class Workspace : public QObject, public KDecorationDefines
bool hasDecorationShadows() const;
bool decorationHasAlpha() const;
bool decorationSupportsClientGrouping() const; // Returns true if the decoration supports tabs.
// D-Bus interface
void cascadeDesktop();
@ -588,6 +599,8 @@ class Workspace : public QObject, public KDecorationDefines
void slotWalkBackThroughDesktopListKeyChanged( const QKeySequence& seq );
void slotWalkThroughWindowsKeyChanged( const QKeySequence& seq );
void slotWalkBackThroughWindowsKeyChanged( const QKeySequence& seq );
void slotMoveToTabLeftKeyChanged( const QKeySequence& seq );
void slotMoveToTabRightKeyChanged( const QKeySequence& seq );
void slotWalkThroughWindowsAlternativeKeyChanged( const QKeySequence& seq );
void slotWalkBackThroughWindowsAlternativeKeyChanged( const QKeySequence& seq );
@ -636,7 +649,15 @@ class Workspace : public QObject, public KDecorationDefines
void suspendCompositing();
void suspendCompositing( bool suspend );
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 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 desktopPopupAboutToShow();
void clientPopupAboutToShow();
void slotSendToDesktop( QAction* );
@ -820,6 +841,7 @@ class Workspace : public QObject, public KDecorationDefines
KShortcut cutWalkThroughDesktops, cutWalkThroughDesktopsReverse;
KShortcut cutWalkThroughDesktopList, cutWalkThroughDesktopListReverse;
KShortcut cutWalkThroughWindows, cutWalkThroughWindowsReverse;
KShortcut cutWalkThroughGroupWindows, cutWalkThroughGroupWindowsReverse;
KShortcut cutWalkThroughWindowsAlternative, cutWalkThroughWindowsAlternativeReverse;
bool mouse_emulation;
unsigned int mouse_emulation_state;
@ -833,6 +855,8 @@ class Workspace : public QObject, public KDecorationDefines
QMenu* advanced_popup;
QMenu* trans_popup;
QMenu* desk_popup;
QMenu* add_tabs_popup; // Menu to add the group to other group
QMenu* switch_to_tab_popup; // Menu to change tab
void modalActionsSwitch( bool enabled );
@ -848,12 +872,17 @@ class Workspace : public QObject, public KDecorationDefines
QAction* mNoBorderOpAction;
QAction* mMinimizeOpAction;
QAction* mCloseOpAction;
QAction* mRemoveTabGroup; // Remove client from group
QAction* mCloseGroup; // Close all clients in the group
ShortcutDialog* client_keys_dialog;
Client* client_keys_client;
KActionCollection* disable_shortcuts_keys;
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;
@ -1222,6 +1251,21 @@ inline bool Workspace::decorationHasAlpha() const
return mgr->factory()->supports( AbilityUsesAlphaChannel );
}
inline bool Workspace::decorationSupportsClientGrouping() const
{
return mgr->factory()->supports( AbilityClientGrouping );
}
inline void Workspace::addClientGroup( ClientGroup* group )
{
clientGroups.append( group );
}
inline void Workspace::removeClientGroup( ClientGroup* group )
{
clientGroups.removeAll( group );
}
} // namespace
#endif