From a9ad07157518683172c0529cdd86521518c6341a Mon Sep 17 00:00:00 2001 From: Chani Armitage Date: Tue, 11 May 2010 20:30:20 +0000 Subject: [PATCH] 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 --- CMakeLists.txt | 3 +- activation.cpp | 6 +- client.cpp | 92 ++++++++++++++++++++++++++ client.h | 5 ++ deleted.cpp | 6 ++ deleted.h | 2 + toplevel.h | 21 ++++++ unmanaged.cpp | 5 ++ unmanaged.h | 1 + useractions.cpp | 88 +++++++++++++++++++++++++ workspace.cpp | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ workspace.h | 12 ++++ 12 files changed, 409 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d426a7f203..04d057755c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/activation.cpp b/activation.cpp index 03988dda1d..eea49369e0 100644 --- a/activation.cpp +++ b/activation.cpp @@ -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; diff --git a/client.cpp b/client.cpp index a415007d29..b815f1bf84 100644 --- a/client.cpp +++ b/client.cpp @@ -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 */ diff --git a/client.h b/client.h index 4bd480085e..ade0f411f8 100644 --- a/client.h +++ b/client.h @@ -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; diff --git a/deleted.cpp b/deleted.cpp index cde2c34bc2..6c1297ff00 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -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(); diff --git a/deleted.h b/deleted.h index 94fda09b3f..e7f70b1511 100644 --- a/deleted.h +++ b/deleted.h @@ -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; diff --git a/toplevel.h b/toplevel.h index b8ebfc268e..1348b8e595 100644 --- a/toplevel.h +++ b/toplevel.h @@ -28,6 +28,8 @@ along with this program. If not, see . #include #include +#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 diff --git a/unmanaged.cpp b/unmanaged.cpp index 31ceb7187f..5c13cf0a6d 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -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 diff --git a/unmanaged.h b/unmanaged.h index 708d339d0b..e92a6afd90 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -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; diff --git a/useractions.cpp b/useractions.cpp index 0a6672e832..7de07d89a0 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -33,6 +33,8 @@ along with this program. If not, see . #include "tile.h" #include "tilinglayout.h" +#include "kactivityinfo.h" + #include #include #include @@ -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 */ diff --git a/workspace.cpp b/workspace.cpp index bdc7863a1b..1509af6ea3 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -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 ) + 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( 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 ) diff --git a/workspace.h b/workspace.h index 65e6516286..095e24eb6f 100644 --- a/workspace.h +++ b/workspace.h @@ -32,6 +32,8 @@ along with this program. If not, see . #include #include +#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