2009-11-15 03:24:04 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
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;
|
|
|
|
|
2009-12-19 10:13:43 +00:00
|
|
|
// Remove the Client->ClientGroup reference if the client is already in another group so we
|
|
|
|
// don't change the geometry of other clients in their current group by accident. However
|
|
|
|
// don't REMOVE them from the actual group until we are certain that the client will be moved.
|
|
|
|
ClientGroup* oldGroup = NULL;
|
|
|
|
if( c->clientGroup() )
|
|
|
|
{
|
|
|
|
oldGroup = c->clientGroup();
|
|
|
|
c->setClientGroup( NULL );
|
|
|
|
}
|
|
|
|
|
2009-12-14 12:39:33 +00:00
|
|
|
// If it's not possible to have the same states then ungroup them, TODO: Check all states
|
2009-12-14 12:29:48 +00:00
|
|
|
// We do this here as the ungroup code in updateStates() cannot be called until add() completes
|
2010-01-28 03:23:09 +00:00
|
|
|
ShadeMode oldShadeMode = c->shadeMode();
|
|
|
|
if( c->shadeMode() != clients_[visible_]->shadeMode() )
|
|
|
|
c->setShade( clients_[visible_]->shadeMode() );
|
|
|
|
if( c->shadeMode() != clients_[visible_]->shadeMode() )
|
|
|
|
{
|
|
|
|
if( oldGroup ) // Re-add to old group if required
|
|
|
|
c->setClientGroup( oldGroup );
|
|
|
|
// One need to trigger decoration repaint on the group to
|
|
|
|
// make sure hover animations are properly reset.
|
|
|
|
clients_[visible_]->triggerDecorationRepaint();
|
|
|
|
return;
|
|
|
|
}
|
2009-12-14 12:39:33 +00:00
|
|
|
QRect oldGeom = c->geometry();
|
|
|
|
if( c->geometry() != clients_[visible_]->geometry() )
|
|
|
|
c->setGeometry( clients_[visible_]->geometry() );
|
|
|
|
if( c->geometry() != clients_[visible_]->geometry() )
|
2009-12-19 10:13:43 +00:00
|
|
|
{
|
2010-01-28 03:23:09 +00:00
|
|
|
if( c->shadeMode() != oldShadeMode )
|
|
|
|
c->setShade( oldShadeMode ); // Restore old shade mode
|
2009-12-19 10:13:43 +00:00
|
|
|
if( oldGroup ) // Re-add to old group if required
|
|
|
|
c->setClientGroup( oldGroup );
|
2010-01-28 03:23:09 +00:00
|
|
|
clients_[visible_]->triggerDecorationRepaint();
|
2009-12-14 12:39:33 +00:00
|
|
|
return;
|
2009-12-19 10:13:43 +00:00
|
|
|
}
|
2009-12-14 12:29:48 +00:00
|
|
|
if( c->desktop() != clients_[visible_]->desktop() )
|
|
|
|
c->setDesktop( clients_[visible_]->desktop() );
|
|
|
|
if( c->desktop() != clients_[visible_]->desktop() )
|
2009-12-14 12:39:33 +00:00
|
|
|
{
|
|
|
|
if( c->geometry() != oldGeom )
|
|
|
|
c->setGeometry( oldGeom ); // Restore old geometry
|
2010-01-28 03:23:09 +00:00
|
|
|
if( c->shadeMode() != oldShadeMode )
|
|
|
|
c->setShade( oldShadeMode ); // Restore old shade mode
|
2009-12-19 10:13:43 +00:00
|
|
|
if( oldGroup ) // Re-add to old group if required
|
|
|
|
c->setClientGroup( oldGroup );
|
2010-01-28 03:23:09 +00:00
|
|
|
clients_[visible_]->triggerDecorationRepaint();
|
2009-12-14 12:29:48 +00:00
|
|
|
return;
|
2009-12-14 12:39:33 +00:00
|
|
|
}
|
2009-11-15 03:24:04 +00:00
|
|
|
|
|
|
|
// Tabbed windows MUST have a decoration
|
|
|
|
if( c->noBorder() )
|
|
|
|
c->setNoBorder( false );
|
|
|
|
if( clients_[visible_]->noBorder() )
|
|
|
|
clients_[visible_]->setNoBorder( false );
|
|
|
|
|
2009-12-19 10:13:43 +00:00
|
|
|
// Re-add to old group if required for the effect hook
|
|
|
|
if( oldGroup )
|
|
|
|
c->setClientGroup( oldGroup );
|
|
|
|
|
2009-11-15 03:24:04 +00:00
|
|
|
// Notify effects of merge
|
|
|
|
if( effects != NULL )
|
|
|
|
static_cast<EffectsHandlerImpl*>(effects)->clientGroupItemAdded(
|
|
|
|
c->effectWindow(), clients_[visible_]->effectWindow() );
|
|
|
|
|
2009-12-19 10:13:43 +00:00
|
|
|
// Actually remove from old group if required and update
|
2009-12-14 12:29:48 +00:00
|
|
|
if( c->clientGroup() )
|
|
|
|
c->clientGroup()->remove( c );
|
2009-11-15 03:24:04 +00:00
|
|
|
c->setClientGroup( this ); // Let the client know which group it belongs to
|
|
|
|
|
|
|
|
// Actually add to new group
|
|
|
|
if( before >= 0 )
|
|
|
|
{
|
|
|
|
if( visible_ >= before )
|
|
|
|
visible_++;
|
|
|
|
clients_.insert( before, c );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
clients_.append( c );
|
|
|
|
if( !becomeVisible ) // Hide before adding
|
|
|
|
c->setClientShown( false );
|
|
|
|
updateItems();
|
|
|
|
updateMinMaxSize();
|
|
|
|
updateStates( clients_[visible_], c );
|
|
|
|
|
|
|
|
if( becomeVisible ) // Set visible after settings geometry
|
|
|
|
setVisible( c );
|
|
|
|
|
|
|
|
// Activate the new visible window
|
|
|
|
clients_[visible_]->setActive( true );
|
|
|
|
//clients_[visible_]->takeFocus( Allowed );
|
|
|
|
//clients_[visible_]->workspace()->raiseClient( clients_[visible_] );
|
|
|
|
|
|
|
|
clients_[visible_]->triggerDecorationRepaint();
|
|
|
|
}
|
|
|
|
|
2009-11-16 02:39:16 +00:00
|
|
|
void ClientGroup::remove( int index, const QRect& newGeom, bool toNullGroup )
|
2009-11-15 03:24:04 +00:00
|
|
|
{
|
2009-11-16 02:39:16 +00:00
|
|
|
remove( clients_[index], newGeom, toNullGroup );
|
2009-11-15 03:24:04 +00:00
|
|
|
}
|
|
|
|
|
2009-11-16 02:39:16 +00:00
|
|
|
void ClientGroup::remove( Client* c, const QRect& newGeom, bool toNullGroup )
|
2009-11-15 03:24:04 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
|
2009-11-16 02:39:16 +00:00
|
|
|
c->setClientGroup( toNullGroup ? NULL : new ClientGroup( c ));
|
2009-11-15 03:24:04 +00:00
|
|
|
if( newGeom.isValid() )
|
2010-01-23 03:41:39 +00:00
|
|
|
{
|
|
|
|
// HACK: if the group was maximized, one needs to make some checks on the future client maximize mode
|
|
|
|
// because the transition from maximized to MaximizeRestore is not handled properly in setGeometry when
|
|
|
|
// the new geometry size is unchanged.
|
|
|
|
// since newGeom has the same size as the old client geometry, one just needs to check the topLeft position of newGeom
|
|
|
|
// and compare that to the group maximize mode.
|
|
|
|
// when the new mode is predicted to be MaximizeRestore, one must set it manually, in order to avoid decoration artifacts
|
|
|
|
Client::MaximizeMode groupMaxMode( newVisible->maximizeMode() );
|
|
|
|
if( ( ( groupMaxMode & Client::MaximizeHorizontal ) && newGeom.left() != newVisible->geometry().left() ) ||
|
|
|
|
( ( groupMaxMode & Client::MaximizeVertical ) && newGeom.top() != newVisible->geometry().top() ) )
|
|
|
|
c->maximize( Client::MaximizeRestore );
|
2009-11-15 03:24:04 +00:00
|
|
|
c->setGeometry( newGeom );
|
2010-01-23 03:41:39 +00:00
|
|
|
}
|
2009-11-15 03:24:04 +00:00
|
|
|
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 )
|
|
|
|
{
|
2010-01-10 03:36:51 +00:00
|
|
|
if( index == -1 )
|
|
|
|
index = visible_;
|
2009-11-15 03:24:04 +00:00
|
|
|
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 );
|
2009-12-17 21:35:37 +00:00
|
|
|
for( ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i )
|
2009-11-15 03:24:04 +00:00
|
|
|
if( (*i) != c )
|
|
|
|
(*i)->setClientShown( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientGroup::updateStates( Client* main, Client* only )
|
|
|
|
{
|
2009-11-16 08:22:50 +00:00
|
|
|
for( ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); i++ )
|
2009-11-15 03:24:04 +00:00
|
|
|
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() );
|
2010-05-19 22:32:57 +00:00
|
|
|
if( (*i)->activities() != main->activities() )
|
|
|
|
(*i)->setOnActivities( main->activities() );
|
2009-11-15 03:24:04 +00:00
|
|
|
if( (*i)->keepAbove() != main->keepAbove() )
|
|
|
|
(*i)->setKeepAbove( main->keepAbove() );
|
|
|
|
if( (*i)->keepBelow() != main->keepBelow() )
|
|
|
|
(*i)->setKeepBelow( main->keepBelow() );
|
2009-12-14 12:29:48 +00:00
|
|
|
|
2009-12-14 12:39:33 +00:00
|
|
|
// If it's not possible to have the same states then ungroup them, TODO: Check all states
|
|
|
|
if( (*i)->geometry() != main->geometry() )
|
|
|
|
remove( *i );
|
2009-12-14 12:29:48 +00:00
|
|
|
if( (*i)->desktop() != main->desktop() )
|
|
|
|
remove( *i );
|
2009-11-15 03:24:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientGroup::updateItems()
|
|
|
|
{
|
|
|
|
items_.clear();
|
2009-12-17 21:35:37 +00:00
|
|
|
for( ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i )
|
2009-11-15 03:24:04 +00:00
|
|
|
{
|
|
|
|
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 );
|
2009-12-17 21:35:37 +00:00
|
|
|
for( ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i )
|
2009-11-15 03:24:04 +00:00
|
|
|
{
|
|
|
|
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() )
|
|
|
|
{
|
2009-11-15 15:48:59 +00:00
|
|
|
//kWarning(1212) << "ClientGroup's min size is greater than its max size. Setting max to min.";
|
2009-11-15 03:24:04 +00:00
|
|
|
maxSize_ = minSize_;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure all windows are within these sizes
|
2009-11-16 02:53:12 +00:00
|
|
|
const QSize size = clients_[visible_]->clientSize();
|
2009-11-15 03:24:04 +00:00
|
|
|
QSize newSize(
|
|
|
|
qBound( minSize_.width(), size.width(), maxSize_.width() ),
|
|
|
|
qBound( minSize_.height(), size.height(), maxSize_.height() ));
|
|
|
|
if( newSize != size )
|
2009-12-17 21:35:37 +00:00
|
|
|
for( ClientList::const_iterator i = clients_.constBegin(); i != clients_.constEnd(); ++i )
|
2009-11-15 03:24:04 +00:00
|
|
|
// 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.
|
2009-11-16 02:53:12 +00:00
|
|
|
(*i)->setGeometry( QRect( clients_[visible_]->pos(), (*i)->sizeForClientSize( newSize )));
|
2009-11-15 03:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|