/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
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_WORKSPACE_H
#define KWIN_WORKSPACE_H

#include <QTimer>
#include <QVector>
#include <kshortcut.h>
#include <QCursor>
#include <netwm.h>
#include <kxmessages.h>
#include <QElapsedTimer>
#include <kmanagerselection.h>

// need to include utils.h before we use the ifdefs
#include "utils.h"
#ifdef KWIN_BUILD_ACTIVITIES
#include <KActivities/Controller>
#endif

#include "plugins.h"
#include "kdecoration.h"
#include "kdecorationfactory.h"
#ifdef KWIN_BUILD_SCREENEDGES
#include "screenedge.h"
#endif
#include "sm.h"

#include <X11/Xlib.h>

// TODO: Cleanup the order of things in this .h file

class QMenu;
class QActionGroup;
class QStringList;
class KConfig;
class KActionCollection;
class KStartupInfo;
class KStartupInfoId;
class KStartupInfoData;
class QSlider;
class QPushButton;

namespace KWin
{

#ifdef KWIN_BUILD_TABBOX
namespace TabBox
{
class TabBox;
}
#endif

class Client;
#ifdef KWIN_BUILD_TILING
class Tile;
class Tiling;
class TilingLayout;
#endif
class Outline;
class RootInfo;
class PluginMgr;
class Placement;
class Rules;
class Scripting;
class WindowRules;

class Workspace : public QObject, public KDecorationDefines
{
    Q_OBJECT
public:
    Workspace(bool restore = false);
    virtual ~Workspace();

    static Workspace* self() {
        return _self;
    }

    bool workspaceEvent(XEvent*);
    bool workspaceEvent(QEvent*);

    KDecoration* createDecoration(KDecorationBridge* bridge);
    bool hasDecorationPlugin() const;

    bool hasClient(const Client*);

    template<typename T> Client* findClient(T predicate) const;
    template<typename T1, typename T2> void forEachClient(T1 procedure, T2 predicate);
    template<typename T> void forEachClient(T procedure);
    template<typename T> Unmanaged* findUnmanaged(T predicate) const;
    template<typename T1, typename T2> void forEachUnmanaged(T1 procedure, T2 predicate);
    template<typename T> void forEachUnmanaged(T procedure);

    QRect clientArea(clientAreaOption, const QPoint& p, int desktop) const;
    QRect clientArea(clientAreaOption, const Client* c) const;
    QRect clientArea(clientAreaOption, int screen, int desktop) const;

    QRegion restrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const;

    /**
     * @internal
     */
    void killWindowId(Window window);

    void killWindow() {
        slotKillWindow();
    }

    bool initializing() const;

    /**
     * Returns the active client, i.e. the client that has the focus (or None
     * if no client has the focus)
     */
    Client* activeClient() const;
    /**
     * Client that was activated, but it's not yet really activeClient(), because
     * we didn't process yet the matching FocusIn event. Used mostly in focus
     * stealing prevention code.
     */
    Client* mostRecentlyActivatedClient() const;

    Client* clientUnderMouse(int screen) const;

    void activateClient(Client*, bool force = false);
    void requestFocus(Client* c, bool force = false);
    void takeActivity(Client* c, int flags, bool handled);   // Flags are ActivityFlags
    void handleTakeActivity(Client* c, Time timestamp, int flags);   // Flags are ActivityFlags
    bool allowClientActivation(const Client* c, Time time = -1U, bool focus_in = false,
                               bool ignore_desktop = false);
    void restoreFocus();
    void gotFocusIn(const Client*);
    void setShouldGetFocus(Client*);
    bool activateNextClient(Client* c);
    bool focusChangeEnabled() {
        return block_focus == 0;
    }

    void updateColormap();

    /**
     * Indicates that the client c is being moved around by the user.
     */
    void setClientIsMoving(Client* c);

    void place(Client* c, QRect& area);
    void placeSmart(Client* c, const QRect& area);

    QPoint adjustClientPosition(Client* c, QPoint pos, bool unrestricted, double snapAdjust = 1.0);
    QRect adjustClientSize(Client* c, QRect moveResizeGeom, int mode);
    void raiseClient(Client* c, bool nogroup = false);
    void lowerClient(Client* c, bool nogroup = false);
    void raiseClientRequest(Client* c, NET::RequestSource src, Time timestamp);
    void lowerClientRequest(Client* c, NET::RequestSource src, Time timestamp);
    void restackClientUnderActive(Client*);
    void restack(Client *c, Client *under);
    void updateClientLayer(Client* c);
    void raiseOrLowerClient(Client*);
    void resetUpdateToolWindowsTimer();
    void restoreSessionStackingOrder(Client* c);
    void updateStackingOrder(bool propagate_new_clients = false);
    void forceRestacking();

    void clientHidden(Client*);
    void clientAttentionChanged(Client* c, bool set);

    /**
     * @return List of clients currently managed by Workspace
     **/
    const ClientList &clientList() const {
        return clients;
    }
    /**
     * @return List of unmanaged "clients" currently registered in Workspace
     **/
    const UnmanagedList &unmanagedList() const {
        return unmanaged;
    }

#ifdef KWIN_BUILD_TILING
    Tiling* tiling();
#endif

    Outline* outline();
#ifdef KWIN_BUILD_SCREENEDGES
    ScreenEdge* screenEdge();
#endif

    //-------------------------------------------------
    // Desktop layout

public:
    /**
     * @returns Total number of desktops currently in existence.
     */
    int numberOfDesktops() const;
    /**
     * @returns The maximum number of desktops that KWin supports.
     */
    int maxNumberOfDesktops() const;
    /**
     * Set the number of available desktops to @a count. This function overrides any previous
     * grid layout.
     */
    void setNumberOfDesktops(int count);
    /**
     * Called from within setNumberOfDesktops() to ensure the desktop layout is still valid.
     */
    void updateDesktopLayout();

    /**
     * @returns The size of desktop layout in grid units.
     */
    QSize desktopGridSize() const;
    /**
     * @returns The width of desktop layout in grid units.
     */
    int desktopGridWidth() const;
    /**
     * @returns The height of desktop layout in grid units.
     */
    int desktopGridHeight() const;
    /**
     * @returns The width of desktop layout in pixels. Equivalent to gridWidth() *
     * ::displayWidth().
     */
    int workspaceWidth() const;
    /**
     * @returns The height of desktop layout in pixels. Equivalent to gridHeight() *
     * ::displayHeight().
     */
    int workspaceHeight() const;

    /**
     * @returns The ID of the current desktop.
     */
    int currentDesktop() const;
    /**
     * Set the current desktop to @a current.
     * @returns True on success, false otherwise.
     */
    bool setCurrentDesktop(int current);

    /**
     * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters.
     */
    void setNETDesktopLayout(Qt::Orientation orientation, int width, int height, int startingCorner);

    /**
     * @returns The ID of the desktop at the point @a coords or 0 if no desktop exists at that
     * point. @a coords is to be in grid units.
     */
    int desktopAtCoords(QPoint coords) const;
    /**
     * @returns The coords of desktop @a id in grid units.
     */
    QPoint desktopGridCoords(int id) const;
    /**
     * @returns The coords of the top-left corner of desktop @a id in pixels.
     */
    QPoint desktopCoords(int id) const;

    /**
     * @returns The ID of the desktop above desktop @a id. Wraps around to the bottom of
     * the layout if @a wrap is set. If @a id is not set use the current one.
     */
    int desktopAbove(int id = 0, bool wrap = true) const;
    /**
     * @returns The ID of the desktop to the right of desktop @a id. Wraps around to the
     * left of the layout if @a wrap is set. If @a id is not set use the current one.
     */
    int desktopToRight(int id = 0, bool wrap = true) const;
    /**
     * @returns The ID of the desktop below desktop @a id. Wraps around to the top of the
     * layout if @a wrap is set. If @a id is not set use the current one.
     */
    int desktopBelow(int id = 0, bool wrap = true) const;
    /**
     * @returns The ID of the desktop to the left of desktop @a id. Wraps around to the
     * right of the layout if @a wrap is set. If @a id is not set use the current one.
     */
    int desktopToLeft(int id = 0, bool wrap = true) const;

    QPoint cascadeOffset(const Client *c) const;

private:
    int desktopCount_;
    QSize desktopGridSize_;
    int* desktopGrid_;
    int currentDesktop_;
    QString activity_;
    QStringList allActivities_, openActivities_;
#ifdef KWIN_BUILD_ACTIVITIES
    KActivities::Controller activityController_;
#endif

#ifdef KWIN_BUILD_TILING
    Tiling* m_tiling;
#endif
    Outline* m_outline;
#ifdef KWIN_BUILD_SCREENEDGES
    ScreenEdge m_screenEdge;
    Qt::Orientations m_screenEdgeOrientation;
#endif

    //-------------------------------------------------
    // Unsorted

public:
    int activeScreen() const;
    int numScreens() const;
    void checkActiveScreen(const Client* c);
    void setActiveScreenMouse(const QPoint& mousepos);
    QRect screenGeometry(int screen) const;
    int screenNumber(const QPoint& pos) const;
    QString currentActivity() const {
        return activity_;
    }
    QStringList activityList() const {
        return allActivities_;
    }
    // True when performing Workspace::updateClientArea().
    // The calls below are valid only in that case.
    bool inUpdateClientArea() const;
    QRegion previousRestrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const;
    QVector< QRect > previousScreenSizes() const;
    int oldDisplayWidth() const;
    int oldDisplayHeight() const;

    // Tab box
#ifdef KWIN_BUILD_TABBOX
    TabBox::TabBox *tabBox() const;
#endif
    bool hasTabBox() const;

    const QVector<int> &desktopFocusChain() const {
        return m_desktopFocusChain.value();
    }
    const ClientList &globalFocusChain() const {
        return global_focus_chain;
    }
    KActionCollection* actionCollection() const {
        return keys;
    }
    KActionCollection* disableShortcutsKeys() const {
        return disable_shortcuts_keys;
    }
    KActionCollection* clientKeys() const {
        return client_keys;
    }

    /**
     * Returns the list of clients sorted in stacking order, with topmost client
     * at the last position
     */
    const ToplevelList& stackingOrder() const;
    ToplevelList xStackingOrder() const;
    ClientList ensureStackingOrder(const ClientList& clients) const;

    Client* topClientOnDesktop(int desktop, int screen, bool unconstrained = false,
                               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);

    // KDE4 remove me - And it's also in the DCOP interface :(
    void showWindowMenuAt(unsigned long id, int x, int y);

    void toggleCompositing();
    void loadEffect(const QString& name);
    void toggleEffect(const QString& name);
    void reconfigureEffect(const QString& name);
    void unloadEffect(const QString& name);
    QString supportInformationForEffect(const QString& name) const;
    void updateCompositeBlocking(Client* c = NULL);

    QStringList loadedEffects() const;
    QStringList listOfEffects() const;


    /**
     * Shows the menu operations menu for the client and makes it active if
     * it's not already.
     */
    void showWindowMenu(const QRect& pos, Client* cl);
    /**
     * Backwards compatibility.
     */
    void showWindowMenu(int x, int y, Client* cl);
    void showWindowMenu(QPoint pos, Client* cl);
    bool windowMenuShown();

    void updateMinimizedOfTransients(Client*);
    void updateOnAllDesktopsOfTransients(Client*);
    void updateOnAllActivitiesOfTransients(Client*);
    void checkTransients(Window w);

    void performWindowOperation(Client* c, WindowOperation op);

    void storeSession(KConfig* config, SMSavePhase phase);
    void storeClient(KConfigGroup &cg, int num, Client *c);
    void storeSubSession(const QString &name, QSet<QByteArray> sessionIds);

    SessionInfo* takeSessionInfo(Client*);
    WindowRules findWindowRules(const Client*, bool);
    void rulesUpdated();
    void discardUsedWindowRules(Client* c, bool withdraw);
    void disableRulesUpdates(bool disable);
    bool rulesUpdatesDisabled() const;

    bool hasDecorationShadows() const;
    Qt::Corner decorationCloseButtonCorner();
    bool decorationHasAlpha() const;
    bool decorationSupportsTabbing() const; // Returns true if the decoration supports tabs.
    bool decorationSupportsFrameOverlap() const;
    bool decorationSupportsBlurBehind() const;

    // D-Bus interface
    void cascadeDesktop();
    void unclutterDesktop();
    void doNotManage(const QString&);
    QList<int> decorationSupportedColors() const;
    void nextDesktop();
    void previousDesktop();
    void circulateDesktopApplications();
    bool compositingActive();
    bool waitForCompositingSetup();
    void toggleTiling();
    void nextTileLayout();
    void previousTileLayout();
    bool stopActivity(const QString &id);
    bool startActivity(const QString &id);
    QStringList activeEffects() const;
    QString supportInformation() const;
    bool compositingPossible() const;
    QString compositingNotPossibleReason() const;
    bool openGLIsBroken() const;
    /**
     * Returns the currently used Compositor (Scene):
     * @li @c none No Compositing
     * @li @c xrender XRender
     * @li @c gl1 OpenGL 1
     * @li @c gl2 OpenGL 2
     * @li @c gles OpenGL ES 2
     **/
    QString compositingType();

    void setCurrentScreen(int new_screen);

    QString desktopName(int desk) const;
    void setShowingDesktop(bool showing);
    void resetShowingDesktop(bool keep_hidden);
    bool showingDesktop() const;

    bool isNotManaged(const QString& title);    // TODO: Setter or getter?

    void sendPingToWindow(Window w, Time timestamp);   // Called from Client::pingWindow()
    void sendTakeActivity(Client* c, Time timestamp, long flags);   // Called from Client::takeActivity()

    void removeClient(Client*, allowed_t);   // Only called from Client::destroyClient() or Client::releaseWindow()
    void setActiveClient(Client*, allowed_t);
    Group* findGroup(Window leader) const;
    void addGroup(Group* group, allowed_t);
    void removeGroup(Group* group, allowed_t);
    Group* findClientLeaderGroup(const Client* c) const;

    void removeUnmanaged(Unmanaged*, allowed_t);   // Only called from Unmanaged::release()
    void removeDeleted(Deleted*, allowed_t);
    void addDeleted(Deleted*, Toplevel*, allowed_t);

    bool checkStartupNotification(Window w, KStartupInfoId& id, KStartupInfoData& data);

    void focusToNull(); // SELI TODO: Public?
    enum FocusChainChange {
        FocusChainMakeFirst,
        FocusChainMakeLast,
        FocusChainUpdate
    };
    void updateFocusChains(Client* c, FocusChainChange change);

    bool forcedGlobalMouseGrab() const;
    void clientShortcutUpdated(Client* c);
    bool shortcutAvailable(const KShortcut& cut, Client* ignore = NULL) const;
    bool globalShortcutsDisabled() const;
    void disableGlobalShortcuts(bool disable);
    void disableGlobalShortcutsForClient(bool disable);
    QPoint cursorPos() const;

    void sessionSaveStarted();
    void sessionSaveDone();
    void setWasUserInteraction();
    bool wasUserInteraction() const;
    bool sessionSaving() const;

    int packPositionLeft(const Client* cl, int oldx, bool left_edge) const;
    int packPositionRight(const Client* cl, int oldx, bool right_edge) const;
    int packPositionUp(const Client* cl, int oldy, bool top_edge) const;
    int packPositionDown(const Client* cl, int oldy, bool bottom_edge) const;

    static QStringList configModules(bool controlCenter);

    void cancelDelayFocus();
    void requestDelayFocus(Client*);
    void updateFocusMousePosition(const QPoint& pos);
    QPoint focusMousePosition() const;

    void toggleTopDockShadows(bool on);

    // when adding repaints caused by a window, you probably want to use
    // either Toplevel::addRepaint() or Toplevel::addWorkspaceRepaint()
    void addRepaint(const QRect& r);
    void addRepaint(const QRegion& r);
    void addRepaint(int x, int y, int w, int h);
    void checkUnredirect(bool force = false);
    void checkCompositeTimer();
    // returns the _estimated_ delay to the next screen update
    // good for having a rough idea to calculate transformations, bad to rely on.
    // might happen few ms earlier, might be an entire frame to short. This is NOT deterministic.
    int nextFrameDelay();

    // Mouse polling
    void startMousePolling();
    void stopMousePolling();

    Client* getMovingClient() {
        return movingClient;
    }

public slots:
    void addRepaintFull();

    // Keybindings
    void slotSwitchDesktopNext();
    void slotSwitchDesktopPrevious();
    void slotSwitchDesktopRight();
    void slotSwitchDesktopLeft();
    void slotSwitchDesktopUp();
    void slotSwitchDesktopDown();

    void slotSwitchToDesktop();
    //void slotSwitchToWindow( int );
    void slotWindowToDesktop();

    //void slotWindowToListPosition( int );
    void slotSwitchToScreen();
    void slotWindowToScreen();
    void slotSwitchToNextScreen();
    void slotWindowToNextScreen();
    void slotToggleShowDesktop();

    void slotWindowMaximize();
    void slotWindowMaximizeVertical();
    void slotWindowMaximizeHorizontal();
    void slotWindowMinimize();
    void slotWindowShade();
    void slotWindowRaise();
    void slotWindowLower();
    void slotWindowRaiseOrLower();
    void slotActivateAttentionWindow();
    void slotWindowPackLeft();
    void slotWindowPackRight();
    void slotWindowPackUp();
    void slotWindowPackDown();
    void slotWindowGrowHorizontal();
    void slotWindowGrowVertical();
    void slotWindowShrinkHorizontal();
    void slotWindowShrinkVertical();
    void slotWindowQuickTileLeft();
    void slotWindowQuickTileRight();
    void slotWindowQuickTileTopLeft();
    void slotWindowQuickTileTopRight();
    void slotWindowQuickTileBottomLeft();
    void slotWindowQuickTileBottomRight();

    void slotSwitchWindowUp();
    void slotSwitchWindowDown();
    void slotSwitchWindowRight();
    void slotSwitchWindowLeft();

    void slotIncreaseWindowOpacity();
    void slotLowerWindowOpacity();

    void slotWindowOperations();
    void slotWindowClose();
    void slotWindowMove();
    void slotWindowResize();
    void slotWindowAbove();
    void slotWindowBelow();
    void slotWindowOnAllDesktops();
    void slotWindowFullScreen();
    void slotWindowNoBorder();

    void slotWindowToNextDesktop();
    void slotWindowToPreviousDesktop();
    void slotWindowToDesktopRight();
    void slotWindowToDesktopLeft();
    void slotWindowToDesktopUp();
    void slotWindowToDesktopDown();

    void slotDisableGlobalShortcuts();

    void slotSettingsChanged(int category);

    void reconfigure();
    void slotReconfigure();
    void slotReinitCompositing();
    void resetCompositing();

    void slotKillWindow();

    void slotSetupWindowShortcut();
    void setupWindowShortcutDone(bool);
    void slotToggleCompositing();
    void slotInvertScreen();

    void updateClientArea();
    void suspendCompositing();
    void suspendCompositing(bool suspend);

    // NOTE: debug method
    void dumpTiles() const;

    void slotActivateNextTab(); // Slot to move left the active Client.
    void slotActivatePrevTab(); // Slot to move right the active Client.
    void slotUntab(); // Slot to remove the active client from its group.

private slots:
    void rebuildTabGroupPopup();
    void rebuildTabListPopup();
    void entabPopupClient(QAction*);
    void selectPopupClientTab(QAction*);
    void desktopPopupAboutToShow();
    void activityPopupAboutToShow();
    void clientPopupAboutToShow();
    void slotSendToDesktop(QAction*);
    void slotToggleOnActivity(QAction*);
    void clientPopupActivated(QAction*);
    void configureWM();
    void desktopResized();
    void screenChangeTimeout();
    void slotUpdateToolWindows();
    void delayFocus();
    void gotTemporaryRulesMessage(const QString&);
    void cleanupTemporaryRules();
    void writeWindowRules();
    void slotBlockShortcuts(int data);
    void slotReloadConfig();
    void setupCompositing();
    /**
     * Called from setupCompositing() when the CompositingPrefs are ready.
     **/
    void slotCompositingOptionsInitialized();
    void finishCompositing();
    void fallbackToXRenderCompositing();
    void performCompositing();
    void performMousePoll();
    void lostCMSelection();
    void resetCursorPosTime();
    void delayedCheckUnredirect();
    void updateCurrentActivity(const QString &new_activity);
    void slotActivityRemoved(const QString &activity);
    void slotActivityAdded(const QString &activity);
    void reallyStopActivity(const QString &id);   //dbus deadlocks suck
    void handleActivityReply();
    void showHideActivityMenu();
protected:
    void timerEvent(QTimerEvent *te);

Q_SIGNALS:
    Q_SCRIPTABLE void compositingToggled(bool active);
    /**
     * Emitted after the Workspace has setup the complete initialization process.
     * This can be used to connect to for performing post-workspace initialization.
     **/
    void workspaceInitialized();

    //Signals required for the scripting interface
signals:
    void desktopPresenceChanged(KWin::Client*, int);
    void currentDesktopChanged(int, KWin::Client*);
    void numberDesktopsChanged(int oldNumberOfDesktops);
    void clientAdded(KWin::Client*);
    void clientRemoved(KWin::Client*);
    void clientActivated(KWin::Client*);
    void clientDemandsAttentionChanged(KWin::Client*, bool);
    void groupAdded(KWin::Group*);
    void unmanagedAdded(KWin::Unmanaged*);
    void deletedRemoved(KWin::Deleted*);
    void mouseChanged(const QPoint& pos, const QPoint& oldpos,
                      Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
                      Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
    void propertyNotify(long a);
    void configChanged();
    /**
     * This signal is emitted when the global
     * activity is changed
     * @param id id of the new current activity
     */
    void currentActivityChanged(const QString &id);
    /**
     * This signal is emitted when a new activity is added
     * @param id id of the new activity
     */
    void activityAdded(const QString &id);
    /**
     * This signal is emitted when the activity
     * is removed
     * @param id id of the removed activity
     */
    void activityRemoved(const QString &id);

private:
    void init();
    void initShortcuts();
    void initDesktopPopup();
    void initActivityPopup();
    void initTabbingPopups();
    void restartKWin(const QString &reason);
    void discardPopup();
    void setupWindowShortcut(Client* c);
    void checkCursorPos();
#ifdef KWIN_BUILD_ACTIVITIES
    void updateActivityList(bool running, bool updateCurrent, QString slot = QString());
#endif
    enum Direction {
        DirectionNorth,
        DirectionEast,
        DirectionSouth,
        DirectionWest
    };
    void switchWindow(Direction direction);

    void propagateClients(bool propagate_new_clients);   // Called only from updateStackingOrder
    ToplevelList constrainedStackingOrder();
    void raiseClientWithinApplication(Client* c);
    void lowerClientWithinApplication(Client* c);
    bool allowFullClientRaising(const Client* c, Time timestamp);
    bool keepTransientAbove(const Client* mainwindow, const Client* transient);
    void blockStackingUpdates(bool block);
    void updateToolWindows(bool also_hide);
    void fixPositionAfterCrash(Window w, const XWindowAttributes& attr);
    void saveOldScreenSizes();

    /// This is the right way to create a new client
    Client* createClient(Window w, bool is_mapped);
    void addClient(Client* c, allowed_t);
    Unmanaged* createUnmanaged(Window w);
    void addUnmanaged(Unmanaged* c, allowed_t);

    Window findSpecialEventWindow(XEvent* e);

    void randomPlacement(Client* c);
    void smartPlacement(Client* c);
    void cascadePlacement(Client* c, bool re_init = false);

    // Desktop names and number of desktops
    void loadDesktopSettings();
    void saveDesktopSettings();

    //---------------------------------------------------------------------

    void helperDialog(const QString& message, const Client* c);

    QMenu* clientPopup();
    void closeActivePopup();
    void updateClientArea(bool force);

    bool windowRepaintsPending() const;
    void setCompositeTimer();
    int m_timeSinceLastVBlank, m_nextFrameDelay;

    typedef QHash< QString, QVector<int> > DesktopFocusChains;
    DesktopFocusChains::Iterator m_desktopFocusChain;
    DesktopFocusChains m_activitiesDesktopFocusChain;

    QWidget* active_popup;
    Client* active_popup_client;

    void loadSessionInfo();
    void addSessionInfo(KConfigGroup &cg);
    void loadSubSessionInfo(const QString &name);

    void loadWindowRules();
    void editWindowRules(Client* c, bool whole_app);

    QList<SessionInfo*> session;
    QList<Rules*> rules;
    KXMessages temporaryRulesMessages;
    QTimer rulesUpdatedTimer;
    QTimer screenChangedTimer;
    bool rules_updates_disabled;
    static const char* windowTypeToTxt(NET::WindowType type);
    static NET::WindowType txtToWindowType(const char* txt);
    static bool sessionInfoWindowTypeMatch(Client* c, SessionInfo* info);

    Client* active_client;
    Client* last_active_client;
    Client* most_recently_raised; // Used ONLY by raiseOrLowerClient()
    Client* movingClient;
    Client* pending_take_activity;
    int active_screen;

    // Delay(ed) window focus timer and client
    QTimer* delayFocusTimer;
    Client* delayfocus_client;
    QPoint focusMousePos;

    ClientList clients;
    ClientList desktops;
    UnmanagedList unmanaged;
    DeletedList deleted;

    ToplevelList unconstrained_stacking_order; // Topmost last
    ToplevelList stacking_order; // Topmost last
    bool force_restacking;
    mutable ToplevelList x_stacking; // From XQueryTree()
    mutable bool x_stacking_dirty;
    QVector< ClientList > focus_chain; // Currently ative last
    ClientList global_focus_chain; // This one is only for things like tabbox's MRU
    ClientList should_get_focus; // Last is most recent
    ClientList attention_chain;

    bool showing_desktop;
    ClientList showing_desktop_clients;
    int block_showing_desktop;

    GroupList groups;

    bool was_user_interaction;
    bool session_saving;
    int session_active_client;
    int session_desktop;

    int block_focus;

#ifdef KWIN_BUILD_TABBOX
    TabBox::TabBox* tab_box;
#endif

    QMenu* popup;
    QMenu* advanced_popup;
    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

    void modalActionsSwitch(bool enabled);

    KActionCollection* keys;
    KActionCollection* client_keys;
    KActionCollection* disable_shortcuts_keys;
    QAction* mResizeOpAction;
    QAction* mMoveOpAction;
    QAction* mMaximizeOpAction;
    QAction* mShadeOpAction;
    QAction* mTilingStateOpAction;
    QAction* mKeepAboveOpAction;
    QAction* mKeepBelowOpAction;
    QAction* mFullScreenOpAction;
    QAction* mNoBorderOpAction;
    QAction* mMinimizeOpAction;
    QAction* mCloseOpAction;
    QAction* mRemoveFromTabGroup; // Remove client from group
    QAction* mCloseTabGroup; // Close all clients in the group
    ShortcutDialog* client_keys_dialog;
    Client* client_keys_client;
    bool global_shortcuts_disabled;
    bool global_shortcuts_disabled_for_client;

    PluginMgr* mgr;

    RootInfo* rootInfo;
    QWidget* supportWindow;

    // Swallowing
    QStringList doNotManageList;

    // Colormap handling
    Colormap default_colormap;
    Colormap installed_colormap;

    // Timer to collect requests for 'reconfigure'
    QTimer reconfigureTimer;

    QTimer updateToolWindowsTimer;

    static Workspace* _self;

    bool workspaceInit;

    KStartupInfo* startup;

    Placement* initPositioning;

    QVector<QRect> workarea; // Array of workareas for virtual desktops
    // Array of restricted areas that window cannot be moved into
    QVector<StrutRects> restrictedmovearea;
    // Array of the previous restricted areas that window cannot be moved into
    QVector<StrutRects> oldrestrictedmovearea;
    QVector< QVector<QRect> > screenarea; // Array of workareas per xinerama screen for all virtual desktops
    QVector< QRect > oldscreensizes; // array of previous sizes of xinerama screens
    QSize olddisplaysize; // previous sizes od displayWidth()/displayHeight()

    int set_active_client_recursion;
    int block_stacking_updates; // When > 0, stacking updates are temporarily disabled
    bool blocked_propagating_new_clients; // Propagate also new clients after enabling stacking updates?
    Window null_focus_window;
    bool forced_global_mouse_grab;
    friend class StackingUpdatesBlocker;

    KSelectionOwner* cm_selection;
    bool compositingSuspended, compositingBlocked;
    QBasicTimer compositeTimer;
    QTimer mousePollingTimer;
    uint vBlankInterval, fpsInterval;
    int xrrRefreshRate; // used only for compositing
    QRegion repaints_region;
    QSlider* transSlider;
    QPushButton* transButton;
    QTimer unredirectTimer;
    bool forceUnredirectCheck;
    QTimer compositeResetTimer; // for compressing composite resets
    bool m_finishingCompositing; // finishCompositing() sets this variable while shutting down

    Scripting *m_scripting;

private:
    friend bool performTransiencyCheck();
};

/**
 * Helper for Workspace::blockStackingUpdates() being called in pairs (True/false)
 */
class StackingUpdatesBlocker
{
public:
    StackingUpdatesBlocker(Workspace* w)
        : ws(w) {
        ws->blockStackingUpdates(true);
    }
    ~StackingUpdatesBlocker() {
        ws->blockStackingUpdates(false);
    }

private:
    Workspace* ws;
};

/**
 * NET WM Protocol handler class
 */
class RootInfo : public NETRootInfo
{
private:
    typedef KWin::Client Client;  // Because of NET::Client

public:
    RootInfo(Workspace* ws, Display* dpy, Window w, const char* name, unsigned long pr[],
             int pr_num, int scr = -1);

protected:
    virtual void changeNumberOfDesktops(int n);
    virtual void changeCurrentDesktop(int d);
    virtual void changeActiveWindow(Window w, NET::RequestSource src, Time timestamp, Window active_window);
    virtual void closeWindow(Window w);
    virtual void moveResize(Window w, int x_root, int y_root, unsigned long direction);
    virtual void moveResizeWindow(Window w, int flags, int x, int y, int width, int height);
    virtual void gotPing(Window w, Time timestamp);
    virtual void restackWindow(Window w, RequestSource source, Window above, int detail, Time timestamp);
    virtual void gotTakeActivity(Window w, Time timestamp, long flags);
    virtual void changeShowingDesktop(bool showing);

private:
    Workspace* workspace;
};

//---------------------------------------------------------
// Desktop layout

inline int Workspace::numberOfDesktops() const
{
    return desktopCount_;
}

inline QSize Workspace::desktopGridSize() const
{
    return desktopGridSize_;
}

inline int Workspace::desktopGridWidth() const
{
    return desktopGridSize_.width();
}

inline int Workspace::desktopGridHeight() const
{
    return desktopGridSize_.height();
}

inline int Workspace::workspaceWidth() const
{
    return desktopGridSize_.width() * displayWidth();
}

inline int Workspace::workspaceHeight() const
{
    return desktopGridSize_.height() * displayHeight();
}

inline int Workspace::currentDesktop() const
{
    return currentDesktop_;
}

inline int Workspace::desktopAtCoords(QPoint coords) const
{
    return desktopGrid_[coords.y() * desktopGridSize_.width() + coords.x()];
}

//---------------------------------------------------------
// Unsorted

inline bool Workspace::initializing() const
{
    return workspaceInit;
}

inline Client* Workspace::activeClient() const
{
    return active_client;
}

inline Client* Workspace::mostRecentlyActivatedClient() const
{
    return should_get_focus.count() > 0 ? should_get_focus.last() : active_client;
}

inline void Workspace::addGroup(Group* group, allowed_t)
{
    emit groupAdded(group);
    groups.append(group);
}

inline void Workspace::removeGroup(Group* group, allowed_t)
{
    groups.removeAll(group);
}

inline const ToplevelList& Workspace::stackingOrder() const
{
    // TODO: Q_ASSERT( block_stacking_updates == 0 );
    return stacking_order;
}

inline void Workspace::showWindowMenu(QPoint pos, Client* cl)
{
    showWindowMenu(QRect(pos, pos), cl);
}

inline void Workspace::showWindowMenu(int x, int y, Client* cl)
{
    showWindowMenu(QRect(QPoint(x, y), QPoint(x, y)), cl);
}

inline bool Workspace::windowMenuShown()
{
    return popup && ((QWidget*)popup)->isVisible();
}

inline void Workspace::setWasUserInteraction()
{
    was_user_interaction = true;
}

inline bool Workspace::wasUserInteraction() const
{
    return was_user_interaction;
}

inline void Workspace::sessionSaveStarted()
{
    session_saving = true;
}

inline bool Workspace::sessionSaving() const
{
    return session_saving;
}

inline bool Workspace::forcedGlobalMouseGrab() const
{
    return forced_global_mouse_grab;
}

inline bool Workspace::showingDesktop() const
{
    return showing_desktop;
}

inline bool Workspace::globalShortcutsDisabled() const
{
    return global_shortcuts_disabled || global_shortcuts_disabled_for_client;
}

inline bool Workspace::rulesUpdatesDisabled() const
{
    return rules_updates_disabled;
}

inline void Workspace::forceRestacking()
{
    force_restacking = true;
    StackingUpdatesBlocker blocker(this);   // Do restacking if not blocked
}

inline void Workspace::updateFocusMousePosition(const QPoint& pos)
{
    focusMousePos = pos;
}

inline QPoint Workspace::focusMousePosition() const
{
    return focusMousePos;
}

template< typename T >
inline Client* Workspace::findClient(T predicate) const
{
    if (Client* ret = findClientInList(clients, predicate))
        return ret;
    if (Client* ret = findClientInList(desktops, predicate))
        return ret;
    return NULL;
}

template< typename T1, typename T2 >
inline void Workspace::forEachClient(T1 procedure, T2 predicate)
{
    for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
        if (predicate(const_cast<const Client*>(*it)))
            procedure(*it);
    for (ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it)
        if (predicate(const_cast<const Client*>(*it)))
            procedure(*it);
}

template< typename T >
inline void Workspace::forEachClient(T procedure)
{
    return forEachClient(procedure, TruePredicate());
}

template< typename T >
inline Unmanaged* Workspace::findUnmanaged(T predicate) const
{
    return findUnmanagedInList(unmanaged, predicate);
}

template< typename T1, typename T2 >
inline void Workspace::forEachUnmanaged(T1 procedure, T2 predicate)
{
    for (UnmanagedList::ConstIterator it = unmanaged.constBegin(); it != unmanaged.constEnd(); ++it)
        if (predicate(const_cast<const Unmanaged*>(*it)))
            procedure(*it);
}

template< typename T >
inline void Workspace::forEachUnmanaged(T procedure)
{
    return forEachUnmanaged(procedure, TruePredicate());
}

KWIN_COMPARE_PREDICATE(ClientMatchPredicate, Client, const Client*, cl == value);

inline bool Workspace::hasClient(const Client* c)
{
    return findClient(ClientMatchPredicate(c));
}

inline void Workspace::checkCompositeTimer()
{
    if (!compositeTimer.isActive())
        setCompositeTimer();
}

inline int Workspace::nextFrameDelay()
{
    return m_nextFrameDelay;
}

inline bool Workspace::hasDecorationPlugin() const
{
    if (!mgr) {
        return false;
    }
    return !mgr->hasNoDecoration();
}

inline bool Workspace::hasDecorationShadows() const
{
    if (!hasDecorationPlugin()) {
        return false;
    }
    return mgr->factory()->supports(AbilityProvidesShadow);
}

inline Qt::Corner Workspace::decorationCloseButtonCorner()
{
    if (!hasDecorationPlugin()) {
        return Qt::TopRightCorner;
    }
    return mgr->factory()->closeButtonCorner();
}

inline bool Workspace::decorationHasAlpha() const
{
    if (!hasDecorationPlugin()) {
        return false;
    }
    return mgr->factory()->supports(AbilityUsesAlphaChannel);
}

inline bool Workspace::decorationSupportsTabbing() const
{
    if (!hasDecorationPlugin()) {
        return false;
    }
    return mgr->factory()->supports(AbilityTabbing);
}

inline bool Workspace::decorationSupportsFrameOverlap() const
{
    if (!hasDecorationPlugin()) {
        return false;
    }
    return mgr->factory()->supports(AbilityExtendIntoClientArea);
}

inline bool Workspace::decorationSupportsBlurBehind() const
{
    if (!hasDecorationPlugin()) {
        return false;
    }
    return mgr->factory()->supports(AbilityUsesBlurBehind);
}

} // namespace

#endif
