    //------------------------------------------------------------------------------
    // emWindow.h
    //
    // Copyright (C) 2005-2010,2016-2017 Oliver Hamann.
    //
    // Homepage: http://eaglemode.sourceforge.net/
    //
    // This program is free software: you can redistribute it and/or modify it under
    // the terms of the GNU General Public License version 3 as published by the
    // Free Software Foundation.
    //
    // 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 version 3 for
    // more details.
    //
    // You should have received a copy of the GNU General Public License version 3
    // along with this program. If not, see <http://www.gnu.org/licenses/>.
    //------------------------------------------------------------------------------

    #ifndef emWindow_h
    #define emWindow_h

    #ifndef emScreen_h
    #include <emCore/emScreen.h>
    #endif

    #ifndef emView_h
    #include <emCore/emView.h>
    #endif

    class emWindowPort;

    //==============================================================================
    //================================== emWindow ==================================
    //==============================================================================

    class emWindow : public emView {

    public:

            // An emWindow is an emView which is shown as a window on a screen. See
            // also class emScreen. Such a screen interface has to be installed in a
            // context before windows can be constructed. This should be done at
            // program start, maybe with the help of an emGUIFramework.

            typedef int WindowFlags;
                    // Data type for the feature flags of a window. Possible flags
                    // are:
            enum {
                    WF_MODAL       = (1<<0),
                            // Ancestor windows of same screen cannot get input
                            // events or close signal. This flag has no effect if
                            // the are no such ancestors.
                    WF_UNDECORATED = (1<<1),
                            // The window has no border and not title bar.
                    WF_POPUP       = (1<<2),
                            // Like WF_UNDECORATED, but the close signal is
                            // generated as soon as the focus is lost.
                    WF_MAXIMIZED   = (1<<3),
                            // The window covers the whole work area of the desktop.
                            // This flag may have no effect when combined with
                            // WF_UNDECORATED, WF_POPUP or WF_FULLSCREEN.
                    WF_FULLSCREEN  = (1<<4),
                            // The window covers the whole screen. This flag may
                            // have no effect when combined with WF_UNDECORATED or
                            // WF_POPUP.
                    WF_AUTO_DELETE = (1<<5)
                            // The window deletes itself automatically three time
                            // slices after the close signal is signaled.
            };

            emWindow(
                    emContext & parentContext,
                    ViewFlags viewFlags=0,
                    WindowFlags windowFlags=0,
                    const emString & wmResName="emWindow"
            );
                    // Construct a window. If the parent context or a higher
                    // ancestor context is a window (without another emScreen in
                    // between), this new window will be a slave of that ancestor
                    // window ("Transient Window" in X11 speaking, "Owned Window" in
                    // Windows speaking). The new window is not shown before the end
                    // of the current time slice (by typical emWindowPort
                    // derivatives). This gives a chance to prepare things like the
                    // size and position of the window without any flicker on the
                    // screen. If the position or size is not set before the window
                    // is shown, a default is invented at the moment of showing the
                    // window (the default depends on the emWindowPort derivative,
                    // which may even forward the job to the window manager).
                    // Arguments:
                    //   parentContext - Parent context for this new context.
                    //   viewFlags     - Initial view features.
                    //   windowFlags   - Initial window features.
                    //   wmResName     - A resource name for the window. This is
                    //                   reported to the window manager.

            virtual ~emWindow();
                    // Destructor.

            void LinkCrossPtr(emCrossPtrPrivate & crossPtr);
                    // This means emCrossPtr<emWindow> is possible.

            emScreen & GetScreen() const;
                    // Get the screen where this window is shown.

            WindowFlags GetWindowFlags() const;
            void SetWindowFlags(WindowFlags windowFlags);
                    // Get or set the features of this window.

            const emSignal & GetWindowFlagsSignal() const;
                    // This signal is signaled when the features of this window have
                    // changed.

            const emString & GetWMResName() const;
                    // Get the resource name of this window.

            emWindowPort & GetWindowPort() const;
                    // Get the window port of this window.

            const emSignal & GetCloseSignal() const;
                    // Get the close signal. It is signaled when the window should
                    // be deleted.

            void SignalClosing();
                    // Signal the close signal.

            void SetViewPos(double x, double y);
            void SetViewSize(double w, double h);
            void SetViewPosSize(double x, double y, double w, double h);
                    // Set position and size of this window by the coordinates of
                    // the view port. This sets the coordinates you can get with
                    // GetHomeX, GetHomeY, GetHomeWidth and GetHomeHeight from the
                    // base class. On X11, the position may not be exact.

            void SetWinPos(double x, double y);
            void SetWinSize(double w, double h);
            void SetWinPosSize(double x, double y, double w, double h);
                    // Set position and size of this window by the coordinates of
                    // the whole window rectangle including the borders. On X11, the
                    // size may not be exact.

            void SetWinPosViewSize(double x, double y, double w, double h);
                    // Like SetWinPos and SetViewSize.

            bool SetWinPosViewSize(const char * geometry);
                    // Like SetWinPosViewSize, but parse a typical X11 geometry
                    // option string (e.g. "800x600+100-100"). As expected, the
                    // position or size is not set if it is not specified (e.g.
                    // "320x200" sets the size only). On parse error, false is
                    // returned.

            void GetBorderSizes(double * pL, double * pT, double * pR,
                                double * pB) const;
                    // Get the best known size (thickness) of the left, top, right
                    // and bottom edges of the window border.

            int GetMonitorIndex() const;
                    // Get the index of the display monitor on which this window is.

            const emImage & GetWindowIcon() const;
            void SetWindowIcon(const emImage & windowIcon);
                    // Get or set the icon to be shown for this window. The default
                    // is to inherit the icon from an ancestor window at
                    // construction. But if there is no ancestor window, the default
                    // is an empty image which means to have no specific icon.

            void Raise();
                    // Bring this window to the top of the stacking order. The
                    // window is even restored from any iconified state.

            void InhibitScreensaver();
            void AllowScreensaver();
                    // Inhibit or re-allow the screensaver. This should implement an
                    // internal counter for calls to InhibitScreensaver() which have
                    // not yet been taken back by calls to AllowScreensaver().
                    // Inhibiting only works while the window is shown quite large
                    // and not iconified.

    protected:

            virtual void InvalidateTitle();
                    // The title of the view is taken for the title of the window.
                    // This method has been overloaded just to update the window
                    // title.

    private:

            friend class emWindowPort;

            class AutoDeleteEngineClass : public emEngine {
            public:
                    AutoDeleteEngineClass(emWindow * window);
            protected:
                    virtual bool Cycle();
            private:
                    emWindow * Window;
                    int CountDown;
            };

            emRef<emScreen> Screen;
            emCrossPtrList CrossPtrList;
            WindowFlags WFlags;
            emString WMResName;
            emImage WindowIcon;
            emWindowPort * WindowPort;
            emSignal WindowFlagsSignal;
            emSignal CloseSignal;
            AutoDeleteEngineClass AutoDeleteEngine;
    };

    //==============================================================================
    //================================ emWindowPort ================================
    //==============================================================================

    class emWindowPort : public emViewPort {

    public:

            // Abstract base class for the connection between an emWindow and the
            // operating system or the hardware or whatever. When an emWindow is
            // constructed, its constructor creates an emWindowPort by calling
            // CreateWindowPort on the emScreen.

            emWindowPort(emWindow & window);
                    // Only to be called through overloaded versions of
                    // emScreen::CreateWindowPort.

            emWindow & GetWindow() const;

    protected:

            emWindow::WindowFlags GetWindowFlags() const;

            const emString & GetWMResName() const;

            emString GetWindowTitle() const;

            const emImage & GetWindowIcon() const;

            virtual void WindowFlagsChanged() = 0;

            enum PosSizeArgSpec {
                    PSAS_IGNORE,
                    PSAS_VIEW,
                    PSAS_WINDOW
            };
            virtual void SetPosSize(
                    double x, double y, PosSizeArgSpec posSpec,
                    double w, double h, PosSizeArgSpec sizeSpec
            ) = 0;
                    // Should call GetWindow().SetViewGeometry(...) immediately with
                    // appropriate values.

            virtual void GetBorderSizes(
                    double * pL, double * pT, double * pR, double * pB
            ) const = 0;

            virtual void Raise() = 0;

            virtual void InhibitScreensaver() = 0;
            virtual void AllowScreensaver() = 0;

            virtual void InvalidateTitle() = 0;

            virtual void InvalidateIcon() = 0;

            void SignalWindowClosing();

    private:
            friend class emWindow;
            emWindow & Window;
    };

    //==============================================================================
    //============================== Implementations ===============================
    //==============================================================================

    //---------------------------------- emWindow ----------------------------------

    inline void emWindow::LinkCrossPtr(emCrossPtrPrivate & crossPtr)
    {
            CrossPtrList.LinkCrossPtr(crossPtr);
    }

    inline emScreen & emWindow::GetScreen() const
    {
            return *(Screen.Get());
    }

    inline emWindow::WindowFlags emWindow::GetWindowFlags() const
    {
            return WFlags;
    }

    inline const emString & emWindow::GetWMResName() const
    {
            return WMResName;
    }

    inline emWindowPort & emWindow::GetWindowPort() const
    {
            return *WindowPort;
    }

    inline const emSignal & emWindow::GetCloseSignal() const
    {
            return CloseSignal;
    }

    inline void emWindow::SignalClosing()
    {
            Signal(CloseSignal);
    }

    inline void emWindow::SetViewPos(double x, double y)
    {
            WindowPort->SetPosSize(
                    x,y,emWindowPort::PSAS_VIEW,
                    0,0,emWindowPort::PSAS_IGNORE
            );
    }

    inline void emWindow::SetViewSize(double w, double h)
    {
            WindowPort->SetPosSize(
                    0,0,emWindowPort::PSAS_IGNORE,
                    w,h,emWindowPort::PSAS_VIEW
            );
    }

    inline void emWindow::SetViewPosSize(double x, double y, double w, double h)
    {
            WindowPort->SetPosSize(
                    x,y,emWindowPort::PSAS_VIEW,
                    w,h,emWindowPort::PSAS_VIEW
            );
    }

    inline void emWindow::SetWinPos(double x, double y)
    {
            WindowPort->SetPosSize(
                    x,y,emWindowPort::PSAS_WINDOW,
                    0,0,emWindowPort::PSAS_IGNORE
            );
    }

    inline void emWindow::SetWinSize(double w, double h)
    {
            WindowPort->SetPosSize(
                    0,0,emWindowPort::PSAS_IGNORE,
                    w,h,emWindowPort::PSAS_WINDOW
            );
    }

    inline void emWindow::SetWinPosSize(double x, double y, double w, double h)
    {
            WindowPort->SetPosSize(
                    x,y,emWindowPort::PSAS_WINDOW,
                    w,h,emWindowPort::PSAS_WINDOW
            );
    }

    inline void emWindow::SetWinPosViewSize(double x, double y, double w, double h)
    {
            WindowPort->SetPosSize(
                    x,y,emWindowPort::PSAS_WINDOW,
                    w,h,emWindowPort::PSAS_VIEW
            );
    }

    inline void emWindow::GetBorderSizes(
            double * pL, double * pT, double * pR, double * pB
    ) const
    {
            WindowPort->GetBorderSizes(pL,pT,pR,pB);
    }

    inline const emImage & emWindow::GetWindowIcon() const
    {
            return WindowIcon;
    }

    inline void emWindow::Raise()
    {
            WindowPort->Raise();
    }

    inline void emWindow::InhibitScreensaver()
    {
            WindowPort->InhibitScreensaver();
    }

    inline void emWindow::AllowScreensaver()
    {
            WindowPort->AllowScreensaver();
    }

    //-------------------------------- emWindowPort --------------------------------

    inline emWindow & emWindowPort::GetWindow() const
    {
            return Window;
    }

    inline emWindow::WindowFlags emWindowPort::GetWindowFlags() const
    {
            return Window.GetWindowFlags();
    }

    inline const emSignal & emWindow::GetWindowFlagsSignal() const
    {
            return WindowFlagsSignal;
    }

    inline const emString & emWindowPort::GetWMResName() const
    {
            return Window.GetWMResName();
    }

    inline emString emWindowPort::GetWindowTitle() const
    {
            return Window.GetTitle();
    }

    inline const emImage & emWindowPort::GetWindowIcon() const
    {
            return Window.GetWindowIcon();
    }

    inline void emWindowPort::SignalWindowClosing()
    {
            Window.SignalClosing();
    }

    #endif