Activity association for windows.

ok'd by fredrikh.
this code is buggy right now, but I promise to squash the bugs by the 19th :)


svn path=/trunk/KDE/kdebase/workspace/; revision=1125614
icc-effect-5.14.5
Chani Armitage 2010-05-11 20:30:20 +00:00
parent b8637bf4fb
commit a9ad071575
12 changed files with 409 additions and 4 deletions

View File

@ -45,6 +45,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/effects
${CMAKE_CURRENT_SOURCE_DIR}/tabbox
${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kephal
${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kworkspace
)
@ -129,7 +130,7 @@ qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWin::Works
kde4_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS})
target_link_libraries(kdeinit_kwin ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${QT_QTXML_LIBRARY} kephal kdecorations kwineffects ${X11_LIBRARIES})
target_link_libraries(kdeinit_kwin ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${QT_QTXML_LIBRARY} kephal kworkspace kdecorations kwineffects ${X11_LIBRARIES})
if(OPENGL_FOUND)
add_subdirectory(opengltest)

View File

@ -400,7 +400,7 @@ void Workspace::handleTakeActivity( Client* c, Time /*timestamp*/, int flags )
*/
void Workspace::clientHidden( Client* c )
{
assert( !c->isShown( true ) || !c->isOnCurrentDesktop());
assert( !c->isShown( true ) || !c->isOnCurrentDesktop() || !c->isOnCurrentActivity());
activateNextClient( c );
}
@ -431,7 +431,7 @@ bool Workspace::activateNextClient( Client* c )
{
Client* ci = focus_chain[ currentDesktop() ].at( i );
if( c == ci || !ci->isShown( false )
|| !ci->isOnCurrentDesktop())
|| !ci->isOnCurrentDesktop() || !ci->isOnCurrentActivity())
continue;
if( options->separateScreenFocus )
{
@ -478,7 +478,7 @@ void Workspace::setCurrentScreen( int new_screen )
--i )
{
Client* ci = focus_chain[ currentDesktop() ].at( i );
if( !ci->isShown( false ) || !ci->isOnCurrentDesktop())
if( !ci->isShown( false ) || !ci->isOnCurrentDesktop() || !ci->isOnCurrentActivity())
continue;
if( !ci->screen() == new_screen )
continue;

View File

@ -1090,6 +1090,14 @@ void Client::updateVisibility()
internalHide( Allowed );
return;
}
if( !isOnCurrentActivity())
{
if( compositing() && options->hiddenPreviews != HiddenPreviewsNever )
internalKeep( Allowed );
else
internalHide( Allowed );
return;
}
bool belongs_to_desktop = false;
for( ClientList::ConstIterator it = group()->members().constBegin();
it != group()->members().constEnd();
@ -1476,6 +1484,52 @@ void Client::setDesktop( int desktop )
clientGroup()->updateStates( this );
}
/**
* Sets whether the client is on @p activity.
* If you remove it from its last activity, then it's on all activities.
*
* Note: If it was on all activities and you try to remove it from one, nothing will happen;
* I don't think that's an important enough use case to handle here.
*/
void Client::setOnActivity( const QString &activity, bool enable )
{
if( activityList.contains(activity) == enable ) //nothing to do
return;
KActivityConsumer c;
QStringList allActivities = c.availableActivities();
if( !allActivities.contains(activity) ) //bogus ID
return;
//check whether we should set it to all activities
QStringList newActivitiesList = activityList;
if (enable)
newActivitiesList.append(activity);
else
newActivitiesList.removeOne(activity);
if( newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty() )
{
setOnAllActivities(true);
return;
}
activityList = newActivitiesList;
/* FIXME I don't think I need the transients but what about the rest?
if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
{ // onAllDesktops changed
if( isShown( true ))
Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
workspace()->updateOnAllDesktopsOfTransients( this );
}
if( decoration != NULL )
decoration->desktopChange();
*/
workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
updateVisibility();
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
/**
* Returns the virtual desktop within the workspace() the client window
* is located in, 0 if it isn't located on any special desktop (not mapped yet),
@ -1487,6 +1541,17 @@ int Client::desktop() const
return desk;
}
/**
* Returns the list of activities the client window is on.
* if it's on all activities, the list will be empty.
* Don't use this, use isOnActivity() and friends (from class Toplevel)
* FIXME do I need to consider if it's not mapped yet?
*/
QStringList Client::activities() const
{
return activityList;
}
void Client::setOnAllDesktops( bool b )
{
if(( b && isOnAllDesktops() ) ||
@ -1502,6 +1567,33 @@ void Client::setOnAllDesktops( bool b )
clientGroup()->updateStates( this );
}
/**
* if @p b is true, sets on all activities.
* if it's false, sets it to only be on the current activity
*/
void Client::setOnAllActivities( bool b )
{
if( b == isOnAllActivities() )
return;
if( b ) {
activityList.clear();
//FIXME update transients
} else {
KActivityConsumer c;
setOnActivity(c.currentActivity(), true);
//FIXME update transients
return;
}
//FIXME c&p'd from setOnActivity, I probably need more code and should probably factor it out
workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
updateVisibility();
updateWindowRules();
// Update states of all other windows in this group
if( clientGroup() )
clientGroup()->updateStates( this );
}
/**
* Performs activation and/or raising of the window
*/

View File

@ -143,6 +143,10 @@ class Client
void setDesktop( int );
void setOnAllDesktops( bool set );
virtual QStringList activities() const;
void setOnActivity( const QString &activity, bool enable );
void setOnAllActivities( bool set );
/// Is not minimized and not hidden. I.e. normally visible on some virtual desktop.
bool isShown( bool shaded_is_shown ) const;
bool isHiddenInternal() const; // For compositing
@ -506,6 +510,7 @@ class Client
KDecoration* decoration;
Bridge* bridge;
int desk;
QStringList activityList;
bool buttonDown;
bool moveResizeMode;
bool move_faked_activity;

View File

@ -67,6 +67,7 @@ void Deleted::copyToDeleted( Toplevel* c )
assert( dynamic_cast< Deleted* >( c ) == NULL );
Toplevel::copyToDeleted( c );
desk = c->desktop();
activityList = c->activities();
contentsRect = QRect( c->clientPos(), c->clientSize());
transparent_rect = c->transparentRect();
if( WinInfo* cinfo = dynamic_cast< WinInfo* >( info ))
@ -111,6 +112,11 @@ int Deleted::desktop() const
return desk;
}
QStringList Deleted::activities() const
{
return activityList;
}
QPoint Deleted::clientPos() const
{
return contentsRect.topLeft();

View File

@ -37,6 +37,7 @@ class Deleted
void unrefWindow( bool delay = false );
void discard( allowed_t );
virtual int desktop() const;
virtual QStringList activities() const;
virtual QPoint clientPos() const;
virtual QSize clientSize() const;
virtual QRect transparentRect() const;
@ -58,6 +59,7 @@ class Deleted
int delete_refcount;
double window_opacity;
int desk;
QStringList activityList;
QRect contentsRect; // for clientPos()/clientSize()
QRect transparent_rect;

View File

@ -28,6 +28,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kdecoration.h>
#include <kdebug.h>
#include "kactivityconsumer.h"
#include "utils.h"
#include "workspace.h"
@ -90,9 +92,13 @@ class Toplevel
bool isDNDIcon() const;
virtual int desktop() const = 0;
virtual QStringList activities() const = 0;
bool isOnDesktop( int d ) const;
bool isOnActivity( const QString &activity ) const;
bool isOnCurrentDesktop() const;
bool isOnCurrentActivity() const;
bool isOnAllDesktops() const;
bool isOnAllActivities() const;
QByteArray windowRole() const;
QByteArray sessionId();
@ -386,16 +392,31 @@ inline bool Toplevel::isOnAllDesktops() const
return desktop() == NET::OnAllDesktops;
}
inline bool Toplevel::isOnAllActivities() const
{
return activities().isEmpty();
}
inline bool Toplevel::isOnDesktop( int d ) const
{
return desktop() == d || /*desk == 0 ||*/ isOnAllDesktops();
}
inline bool Toplevel::isOnActivity( const QString &activity ) const
{
return activities().isEmpty() || activities().contains(activity);
}
inline bool Toplevel::isOnCurrentDesktop() const
{
return isOnDesktop( workspace()->currentDesktop());
}
inline bool Toplevel::isOnCurrentActivity() const
{
return isOnActivity( KActivityConsumer().currentActivity());
}
inline QByteArray Toplevel::resourceName() const
{
return resource_name; // it is always lowercase

View File

@ -112,6 +112,11 @@ int Unmanaged::desktop() const
return NET::OnAllDesktops; // TODO for some window types should be the current desktop?
}
QStringList Unmanaged::activities() const
{
return QStringList();
}
QPoint Unmanaged::clientPos() const
{
return QPoint( 0, 0 ); // unmanaged windows don't have decorations

View File

@ -39,6 +39,7 @@ class Unmanaged
bool track( Window w );
static void deleteUnmanaged( Unmanaged* c, allowed_t );
virtual int desktop() const;
virtual QStringList activities() const;
virtual QPoint clientPos() const;
virtual QSize clientSize() const;
virtual QRect transparentRect() const;

View File

@ -33,6 +33,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "tile.h"
#include "tilinglayout.h"
#include "kactivityinfo.h"
#include <fixx11h.h>
#include <QPushButton>
#include <QSlider>
@ -237,6 +239,7 @@ void Workspace::discardPopup()
delete popup;
popup = NULL;
desk_popup = NULL;
activity_popup = NULL;
switch_to_tab_popup = NULL;
add_tabs_popup = NULL;
}
@ -268,6 +271,17 @@ void Workspace::clientPopupAboutToShow()
{
initDesktopPopup();
}
QStringList act = activityController_.availableActivities();
kDebug() << "activities:" << act.size();
if ( act.size() < 2 )
{
delete activity_popup;
activity_popup = 0;
}
else
{
initActivityPopup();
}
mResizeOpAction->setEnabled( active_popup_client->isResizable() );
mMoveOpAction->setEnabled( active_popup_client->isMovableAcrossScreens() );
@ -455,6 +469,29 @@ void Workspace::initDesktopPopup()
action->setText( i18n("To &Desktop") );
}
/*!
Creates activity popup.
I'm going with checkable ones instead of "copy to" and "move to" menus; I *think* it's an easier way.
Oh, and an 'all' option too of course
*/
void Workspace::initActivityPopup()
{
if (activity_popup)
return;
activity_popup = new QMenu( popup );
activity_popup->setFont(KGlobalSettings::menuFont());
connect( activity_popup, SIGNAL( triggered(QAction*) ),
this, SLOT( slotToggleOnActivity(QAction*) ) );
connect( activity_popup, SIGNAL( aboutToShow() ),
this, SLOT( activityPopupAboutToShow() ) );
QAction *action = activity_popup->menuAction();
// set it as the first item
popup->insertAction( trans_popup ? trans_popup->menuAction() : mMoveOpAction, action);
action->setText( i18n("Ac&tivities") ); //FIXME is that a good string?
}
/*!
Adjusts the desktop popup to the current values and the location of
the popup client.
@ -489,6 +526,37 @@ void Workspace::desktopPopupAboutToShow()
}
}
/*!
Adjusts the activity popup to the current values and the location of
the popup client.
*/
void Workspace::activityPopupAboutToShow()
{
if ( !activity_popup )
return;
activity_popup->clear();
QAction *action = activity_popup->addAction( i18n("&All Activities") );
action->setData( 0 );
action->setCheckable( true );
if ( active_popup_client && active_popup_client->isOnAllActivities() )
action->setChecked( true );
activity_popup->addSeparator();
foreach (const QString &activity, activityController_.availableActivities()) {
QString name = KActivityInfo::name(activity);
name.replace('&', "&&");
action = activity_popup->addAction( name );
action->setData( activity );
action->setCheckable( true );
if ( active_popup_client &&
!active_popup_client->isOnAllActivities() && active_popup_client->isOnActivity(activity) )
action->setChecked( true );
}
}
void Workspace::closeActivePopup()
{
if( active_popup )
@ -1505,6 +1573,26 @@ void Workspace::slotSendToDesktop( QAction *action )
}
/*!
Toggles whether the popup client is on the \a activity
Internal slot for the window operation menu
*/
void Workspace::slotToggleOnActivity( QAction *action )
{
QString activity = action->data().toString();
if ( !active_popup_client )
return;
if ( activity.isEmpty() )
{ // the 'on_all_activities' menu entry
active_popup_client->setOnAllActivities( !active_popup_client->isOnAllActivities());
return;
}
toggleClientOnActivity( active_popup_client, activity, false );
}
/*!
Switches to the nearest window in given direction
*/

View File

@ -125,6 +125,7 @@ Workspace::Workspace( bool restore )
, advanced_popup( 0 )
, trans_popup( 0 )
, desk_popup( 0 )
, activity_popup( 0 )
, add_tabs_popup( 0 )
, switch_to_tab_popup( 0 )
, keys( 0 )
@ -240,6 +241,8 @@ Workspace::Workspace( bool restore )
connect( Kephal::Screens::self(), SIGNAL( screenRemoved(int) ), SLOT( desktopResized() ));
connect( Kephal::Screens::self(), SIGNAL( screenResized(Kephal::Screen*, QSize, QSize) ), SLOT( desktopResized() ));
connect( Kephal::Screens::self(), SIGNAL( screenMoved(Kephal::Screen*, QPoint, QPoint) ), SLOT( desktopResized() ));
connect( &activityController_, SIGNAL( currentActivityChanged(QString) ), SLOT( updateCurrentActivity(QString) ));
}
void Workspace::init()
@ -1514,6 +1517,135 @@ bool Workspace::setCurrentDesktop( int new_desktop )
return true;
}
/**
* Updates the current activity when it changes
* do *not* call this directly; it does not set the activity.
*
* Shows/Hides windows according to the stacking order
*/
void Workspace::updateCurrentActivity(const QString &new_activity)
{
//closeActivePopup();
++block_focus;
// TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
StackingUpdatesBlocker blocker( this );
if ( new_activity != activity_ )
{
++block_showing_desktop; //FIXME should I be using that?
// Optimized Desktop switching: unmapping done from back to front
// mapping done from front to back => less exposure events
//Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
ObscuringWindows obs_wins;
QString old_activity = activity_;
activity_ = new_activity;
for( ClientList::ConstIterator it = stacking_order.constBegin();
it != stacking_order.constEnd();
++it )
if( !(*it)->isOnActivity( new_activity ) && (*it) != movingClient )
{
if( (*it)->isShown( true ) && (*it)->isOnActivity( old_activity ))
obs_wins.create( *it );
(*it)->updateVisibility();
}
// Now propagate the change, after hiding, before showing
//rootInfo->setCurrentDesktop( currentDesktop() );
/* TODO someday enable dragging windows to other activities
if( movingClient && !movingClient->isOnDesktop( new_desktop ))
{
int old_desktop = movingClient->desktop();
movingClient->setDesktop( new_desktop );
if( tilingEnabled() )
{
notifyWindowDesktopChanged( movingClient, old_desktop );
}
}
*/
for( int i = stacking_order.size() - 1; i >= 0 ; --i )
if( stacking_order.at( i )->isOnActivity( new_activity ))
stacking_order.at( i )->updateVisibility();
--block_showing_desktop;
//FIXME not sure if I should do this either
if( showingDesktop() ) // Do this only after desktop change to avoid flicker
resetShowingDesktop( false );
}
// Restore the focus on this desktop
--block_focus;
Client* c = 0;
//FIXME below here is a lot of focuschain stuff, probably all wrong now
if( options->focusPolicyIsReasonable() )
{ // Search in focus chain
if( movingClient != NULL && active_client == movingClient &&
focus_chain[currentDesktop()].contains( active_client ) &&
active_client->isShown( true ) && active_client->isOnCurrentDesktop())
c = active_client; // The requestFocus below will fail, as the client is already active
if( !c )
{
for( int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i )
{
if( focus_chain[currentDesktop()].at( i )->isShown( false ) &&
focus_chain[currentDesktop()].at( i )->isOnCurrentDesktop() )
{
c = focus_chain[currentDesktop()].at( i );
break;
}
}
}
}
// If "unreasonable focus policy" and active_client is on_all_desktops and
// under mouse (Hence == old_active_client), conserve focus.
// (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop() )
c = active_client;
if( c == NULL && !desktops.isEmpty() )
c = findDesktop( true, currentDesktop() );
if( c != active_client )
setActiveClient( NULL, Allowed );
if ( c )
requestFocus( c );
else if( !desktops.isEmpty() )
requestFocus( findDesktop( true, currentDesktop() ));
else
focusToNull();
updateCurrentTopMenu();
// Update focus chain:
// If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3,
// Output: chain = { 3, 1, 2, 4 }.
//kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
// .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
for( int i = desktop_focus_chain.indexOf( currentDesktop() ); i > 0; i-- )
desktop_focus_chain[i] = desktop_focus_chain[i-1];
desktop_focus_chain[0] = currentDesktop();
//QString s = "desktop_focus_chain[] = { ";
//for( uint i = 0; i < desktop_focus_chain.size(); i++ )
// s += QString::number( desktop_focus_chain[i] ) + ", ";
//kDebug( 1212 ) << s << "}\n";
// Not for the very first time, only if something changed and there are more than 1 desktops
//if( effects != NULL && old_desktop != 0 && old_desktop != new_desktop )
// static_cast<EffectsHandlerImpl*>( effects )->desktopChanged( old_desktop );
if( compositing())
addRepaintFull();
}
/**
* Called only from D-Bus
*/
@ -1631,6 +1763,46 @@ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
updateClientArea();
}
/**
* Adds/removes client \a c to/from \a desk.
*
* Takes care of transients as well.
*/
void Workspace::toggleClientOnActivity( Client* c, const QString &activity, bool dont_activate )
{
//int old_desktop = c->desktop();
bool was_on_activity = c->isOnActivity(activity);
bool was_on_all = c->isOnAllActivities();
//note: all activities === no activities
bool enable = was_on_all || !was_on_activity;
c->setOnActivity( activity, enable );
if( c->isOnActivity(activity) == was_on_activity && c->isOnAllActivities() == was_on_all ) // No change
return;
if( c->isOnActivity( activityController_.currentActivity() ))
{
if( c->wantsTabFocus() && options->focusPolicyIsReasonable() &&
!was_on_activity && // for stickyness changes
//FIXME not sure if the line above refers to the correct activity
!dont_activate )
requestFocus( c );
else
restackClientUnderActive( c );
}
else
raiseClient( c );
//notifyWindowDesktopChanged( c, old_desktop );
//FIXME does tiling break?
ClientList transients_stacking_order = ensureStackingOrder( c->transients() );
for( ClientList::ConstIterator it = transients_stacking_order.constBegin();
it != transients_stacking_order.constEnd();
++it )
toggleClientOnActivity( *it, activity, dont_activate );
updateClientArea();
}
int Workspace::numScreens() const
{
if( !options->xineramaEnabled )

View File

@ -32,6 +32,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDateTime>
#include <kmanagerselection.h>
#include "kactivitycontroller.h"
#include "plugins.h"
#include "utils.h"
#include "kdecoration.h"
@ -312,8 +314,11 @@ class Workspace : public QObject, public KDecorationDefines
QSize desktopGridSize_;
int* desktopGrid_;
int currentDesktop_;
QString activity_;
bool desktopLayoutDynamicity_;
KActivityController activityController_;
bool tilingEnabled_;
// Each tilingLayout is for one virtual desktop.
// The length is always one more than the number of
@ -373,6 +378,7 @@ class Workspace : public QObject, public KDecorationDefines
bool only_normal = true ) const;
Client* findDesktop( bool topmost, int desktop ) const;
void sendClientToDesktop( Client* c, int desktop, bool dont_activate );
void toggleClientOnActivity( Client* c, const QString &activity, bool dont_activate );
void windowToPreviousDesktop( Client* c );
void windowToNextDesktop( Client* c );
void sendClientToScreen( Client* c, int screen );
@ -722,8 +728,10 @@ class Workspace : public QObject, public KDecorationDefines
void slotAddToTabGroup( QAction* ); // Add client to a group
void slotSwitchToTab( QAction* ); // Change the tab
void desktopPopupAboutToShow();
void activityPopupAboutToShow();
void clientPopupAboutToShow();
void slotSendToDesktop( QAction* );
void slotToggleOnActivity( QAction* );
void clientPopupActivated( QAction* );
void configureWM();
void desktopResized();
@ -747,6 +755,8 @@ class Workspace : public QObject, public KDecorationDefines
void resetCursorPosTime();
void delayedCheckUnredirect();
void updateCurrentActivity(const QString &activity);
protected:
bool keyPressMouseEmulation( XKeyEvent& ev );
@ -758,6 +768,7 @@ class Workspace : public QObject, public KDecorationDefines
void initShortcuts();
void readShortcuts();
void initDesktopPopup();
void initActivityPopup();
void discardPopup();
void setupWindowShortcut( Client* c );
void checkCursorPos();
@ -930,6 +941,7 @@ class Workspace : public QObject, public KDecorationDefines
QMenu* trans_popup;
QActionGroup* trans_popup_group;
QMenu* desk_popup;
QMenu* activity_popup;
QMenu* add_tabs_popup; // Menu to add the group to other group
QMenu* switch_to_tab_popup; // Menu to change tab