commit 120d718da524a537913ea80f9b7c1ddc43c28e17 Author: Simon Rozman Date: Thu Feb 4 12:17:25 2016 +0100 wxExtend library added diff --git a/build/wxExtend.vcxproj b/build/wxExtend.vcxproj new file mode 100644 index 0000000..0845e5b --- /dev/null +++ b/build/wxExtend.vcxproj @@ -0,0 +1,90 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + Create + Create + Create + Create + + + + + + + + + {A3A36689-AC35-4026-93DA-A3BA0C0E767C} + wxExtend + + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/wxExtend.vcxproj.filters b/build/wxExtend.vcxproj.filters new file mode 100644 index 0000000..ec75aac --- /dev/null +++ b/build/wxExtend.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/include/wxex/appbar.h b/include/wxex/appbar.h new file mode 100644 index 0000000..fce993e --- /dev/null +++ b/include/wxex/appbar.h @@ -0,0 +1,663 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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 3 of the License, or + (at your option) any later version. + + wxExtend 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 wxExtend. If not, see . +*/ + +#pragma once + +#include +#include + +#include + + +#define wxABT_AUTOHIDETIMERID 1 +#define wxABT_AUTOHIDETIME 300 +#define wxABT_AUTOHIDETIMERINTERVAL 3000 + + +/// +/// Dockable application bar +/// +class wxAppBar : public wxFrame +{ +public: + /// + /// Application bar states + /// + enum wxState { + wxSTATE_UNKNOWN = -2, + wxSTATE_FLOAT = -1, + wxSTATE_LEFT = ABE_LEFT, + wxSTATE_TOP = ABE_TOP, + wxSTATE_RIGHT = ABE_RIGHT, + wxSTATE_BOTTOM = ABE_BOTTOM, + }; + + + /// + /// Application bar flags + /// + enum wxFlags { + wxFLAG_ALLOWFLOAT = 1 << 0, ///< Is floating application bar allowed? + wxFLAG_ALLOWDOCKTOP = 1 << 1, ///< Is docking on top of the screen allowed? + wxFLAG_ALLOWDOCKBOTTOM = 1 << 2, ///< Is docking on bottom of the screen allowed? + wxFLAG_ALLOWDOCKLEFT = 1 << 3, ///< Is docking left of the screen allowed? + wxFLAG_ALLOWDOCKRIGHT = 1 << 4, ///< Is docking right of the screen allowed? + wxFLAG_ALWAYSONTOP = 1 << 5, ///< Is application bar always on top? + wxFLAG_ALWAYSONTOPTASKBAR = 1 << 6, ///< Does application bar follow always on top state of the system taskbar? + wxFLAG_AUTOHIDE = 1 << 7, ///< Is application bar setup for auto-hide? + wxFLAG_AUTOHIDETASKBAR = 1 << 8, ///< Does application bar follow auto-hide state of the system taskbar? + wxFLAG_ALLOWSIZING = 1 << 9, ///< Is application bar's sizing allowed? + wxFLAG_HIDETASKBARTABWHENFLOATING = 1 << 10, ///< Should application bar's tab on the system taskbar hide when floating? + wxFLAG_HIDETASKBARTABWHENDOCKED = 1 << 11, ///< Should application bar's tab on the system taskbar hide when docked? + + // Internal flags + wxFLAG_FULLSCREENAPPOPEN = 1 << 12, ///< Is full-screen application open? + wxFLAG_AUTOHIDDEN = 1 << 13, ///< Is application bar auto-hidden right now? + wxFLAG_POSITIONSET = 1 << 14, ///< Is application bar's position correct? + + wxFLAG_ALLOWDOCKANY = (wxFLAG_ALLOWDOCKTOP | wxFLAG_ALLOWDOCKBOTTOM | wxFLAG_ALLOWDOCKLEFT | wxFLAG_ALLOWDOCKRIGHT), ///< Is docking at any edge of the screen allowed? + wxFLAG_ALLOWDOCKHORIZONTAL = (wxFLAG_ALLOWDOCKTOP | wxFLAG_ALLOWDOCKBOTTOM), ///< Is docking on top or bottom of the screen allowed? + wxFLAG_ALLOWDOCKVERTICAL = (wxFLAG_ALLOWDOCKLEFT | wxFLAG_ALLOWDOCKRIGHT), ///< Is docking left or right of the screen allowed? + wxFLAG_ALLOWANY = (wxFLAG_ALLOWFLOAT | wxFLAG_ALLOWDOCKANY), ///< Is floating and docking at any edge of the screen allowed? + wxFLAG_ALWAYSHIDETASKBARTAB = (wxFLAG_HIDETASKBARTABWHENFLOATING | wxFLAG_HIDETASKBARTABWHENDOCKED), ///< Should application bar's tab on the system taskbar hide always? + }; + +public: + wxAppBar(); + wxAppBar(wxWindow *parent, + wxWindowID id, + const wxString& title, + wxState state = wxSTATE_FLOAT, + int flags = wxFLAG_ALLOWANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxDEFAULT_FRAME_STYLE, + const wxString& name = wxFrameNameStr); + + bool Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + wxState state = wxSTATE_FLOAT, + int flags = wxFLAG_ALLOWANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxDEFAULT_FRAME_STYLE, + const wxString& name = wxFrameNameStr); + + virtual ~wxAppBar(); + +public: + /// \name Application bar general management + /// @{ + + /// + /// Returns current state of the application bar. + /// + inline wxState GetState() const; + + /// + /// Returns whether application bar is displayed always on top. + /// + /// \returns + /// - true when application bar is always on top + /// - false otherwise + /// + inline bool IsAlwaysOnTop() const; + + /// + /// Sets whether application bar is displayed always on top. + /// + /// \param[in] alwaysOnTop Logical variable to control whether always-on-top behaviour is to be enabled (true) or disabled (false). + /// + inline bool SetAlwaysOnTop(bool alwaysOnTop = true); + + /// + /// Returns if application bar is configured for auto-hide. + /// + /// \returns + /// - true if application bar is configured for auto-hide + /// - false otherwise + /// + inline bool IsAutoHide() const; + + /// + /// Sets whether application bar is displayed always on top. + /// + /// \param[in] autoHide Logical variable to control whether auto-hide behaviour is to be enabled (true) or disabled (false). + /// + inline bool SetAutoHide(bool autoHide = true); + + /// + /// Returns if sizing of the application bar is allowed. + /// + /// \returns + /// - true if sizing of the application bar is allowed + /// - false otherwise + /// + inline bool GetAllowSizing() const; + + /// + /// Sets whether resizing of the application bar is allowed. + /// + /// \param[in] bAllowSizing Logical variable to control whether resizing is to be enabled (true) or disabled (false). + /// + inline bool SetAllowSizing(bool bAllowSizing = true); + + /// + /// Returns if application bar is auto-hidden right now. + /// + /// \returns + /// - true if application bar is auto-hidden right now + /// - false otherwise + inline bool GetAutoHidden() const; + + //bool GetAppBarPlacement(LPAPPBARPLACEMENT lpPlacement) const; + //bool SetAppBarPlacement(LPCAPPBARPLACEMENT lpPlacement); + + /// + /// Minimize application bar to the edge of the desktop. + /// + /// \param[in] state The edge at which to dock. Must be either of: wxSTATE_LEFT, wxSTATE_TOP, wxSTATE_RIGHT, or wxSTATE_BOTTOM. + /// \param[in] wnd When the undocked and docked window is different, this parameter denotes the undocked version. + /// + void MinimiseToEdge(wxState state, wxWindow* wnd = NULL); + + /// + /// Restore application bar from the edge of the desktop. + /// + /// \param[in] rect The desired coordinates of the restored window. If NULL internally saved coordinates are used. + /// \param[in] wnd When the undocked and docked window is different, this parameter denotes the undocked version. + /// + void MaximiseFromEdge(const RECT* rect = NULL); + void MaximiseFromEdge(wxWindow *wnd); + + /// + /// Shows or hides auto-hide application bar. + /// + /// \param[in] bShow Logical variable to control whether application bar is be shown (true) or hidden (false). + /// + void ShowAutoHideAppBar(bool bShow = true); + + /// + /// Hides auto-hide application bar. + /// + inline void HideAutoHideAppBar() { ShowAutoHideAppBar(false); } + + /// @} + +protected: + /// \name Notification handlers + /// @{ + + /// + /// Notification handler when the new state of the application bar is proposed. + /// + /// Derived classes can implement their own. However, overriden virtual method should call the parent's implementation too. + /// + /// \param[in] stateNew New state about to take effect. + /// + virtual void OnChangeState(wxState stateNew); + + /// + /// Notification handler when the Windows' task bar state changes. + /// + /// Derived classes can implement their own. However, overriden virtual method should call the parent's implementation too. + /// + /// \param[in] state New task bar state. + /// + virtual void OnChangeTaskBarState(UINT_PTR state); + + /// + /// Notification handler when desktop windows are being arranged (cascaded, tiled, ...) + /// + /// Derived classes can implement their own. However, overriden virtual method should call the parent's implementation too. + /// + /// \param[in] bBeginning true when windows arrangement starts and false when arrangement ends. + /// + virtual void OnWindowsArrange(bool bBeginning); + + /// + /// Called when application bar was forced from auto-hide to normal docking. + /// + /// Default implementation displays a warning dialog. + /// + virtual void OnAutoHideDenied(); + + /// @} + +protected: + virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam); + +private: + inline bool DockAppBar(wxState state); + inline bool UndockAppBar(); + inline bool RegisterAutoHide(wxState state); + inline bool UnregisterAutoHide(wxState state); + inline bool GetDockedRect(wxState state, LPRECT rect) const; + inline bool GetAutoHideRect(wxState state, bool bAutoHidden, LPRECT rect) const; + inline static bool IsDocked(wxState state); + inline static bool IsDockedVert(wxState state); + inline static bool IsDockedHoriz(wxState state); + inline static WXHWND GetZWnd(wxState state, int flags); + inline static UINT_PTR GetTaskBarState(); + +protected: + wxState m_state; ///< Current state of the application bar + wxState m_stateDesired; ///< Desired state of the application bar while moving/resizing + int m_flags; ///< Flags describing application bar's behaviour + + RECT m_rect; ///< Window rectangle + SIZE m_sizeFloat; ///< Window size when floating (we need it to restore floating size, when we undock) + SIZE m_sizeDocked; ///< Size of the window when docked (height when wxSTATE_TOP or wxSTATE_BOTTOM, width when wxSTATE_LEFT or wxSTATE_RIGHT) + SIZE m_sizeMin; ///< Minimum window size + + UINT_PTR m_stateTaskBar; ///< TaskBar's current state + UINT_PTR m_timerID; ///< Application bar's timer id. + + ITaskbarList *m_taskbarList; ///< Windows's taskbar list interface + +private: + static const UINT WM_AB_NOTIFY; ///< Application bar notification message +}; + + +inline wxAppBar::wxState wxAppBar::GetState() const +{ + return m_stateDesired != wxSTATE_UNKNOWN ? m_stateDesired : m_state; +} + + +inline bool wxAppBar::IsAlwaysOnTop() const +{ + return (m_flags & wxFLAG_ALWAYSONTOP) ? true : false; +} + + +inline bool wxAppBar::SetAlwaysOnTop(bool alwaysOnTop) +{ + if (alwaysOnTop) + m_flags |= wxFLAG_ALWAYSONTOP; + else + m_flags &= ~wxFLAG_ALWAYSONTOP; + + if (::IsWindowVisible(m_hWnd)) { + // Set the Z-order. SWP_NOSENDCHANGING flag prevents our OnWindowPosChanging() method to be called, since moving is not necessary. + wxCHECK(::SetWindowPos(m_hWnd, GetZWnd(m_state, m_flags), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOSENDCHANGING), false); + } + + return true; +} + + +inline bool wxAppBar::IsAutoHide() const +{ + return (m_flags & wxFLAG_AUTOHIDE) ? true : false; +} + + +inline bool wxAppBar::SetAutoHide(bool autoHide) +{ + bool autoHidePrev = IsAutoHide(); + + if (autoHide) + m_flags |= wxFLAG_AUTOHIDE; + else + m_flags &= ~wxFLAG_AUTOHIDE; + + if (autoHidePrev != autoHide && IsDocked(m_state) && ::IsWindowVisible(m_hWnd)) { + if (autoHide) { + // Application bar should be auto-hidden. Try to setup it so. + + // We undock to free the space before registering auto-hide, otherwise system will leave application bar's space reserved. + UndockAppBar(); + + // Register auto-hide application bar. + if (!RegisterAutoHide(m_state)) { + // Auto-hide failed; some other application bar must be hidden on this edge already. Dock back instead. + DockAppBar(m_state); + return false; + } + } else { + // Unregister the auto-hidden application bar and dock. + UnregisterAutoHide(m_state); + DockAppBar(m_state); + } + } + + return true; +} + + +inline bool wxAppBar::GetAllowSizing() const +{ + return (m_flags & wxFLAG_ALLOWSIZING) ? true : false; +} + + +inline bool wxAppBar::SetAllowSizing(bool bAllowSizing) +{ + if (bAllowSizing) + m_flags |= wxFLAG_ALLOWSIZING; + else + m_flags &= ~wxFLAG_ALLOWSIZING; + + return true; +} + + +inline bool wxAppBar::GetAutoHidden() const +{ + return (m_flags & wxFLAG_AUTOHIDDEN) ? true : false; +} + + +inline bool wxAppBar::DockAppBar(wxState state) +{ + wxASSERT(IsDocked(state)); + + APPBARDATA abd = { + sizeof(abd), + m_hWnd, + 0, + state + }; + + // Calculate docked window rect and dock the window there. + GetDockedRect(state, &(abd.rc)); + wxCHECK(::SHAppBarMessage(ABM_SETPOS, &abd), false); + m_flags |= wxFLAG_POSITIONSET; + m_rect = abd.rc; + wxCHECK(::SetWindowPos(m_hWnd, GetZWnd(state, m_flags), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_FRAMECHANGED), false); + + return true; +} + + +inline bool wxAppBar::UndockAppBar() +{ + APPBARDATA abd = { + sizeof(abd), + m_hWnd, + 0, + wxSTATE_LEFT + }; + + // Free application bar's space to undock. + wxASSERT(!abd.rc.left && !abd.rc.top && !abd.rc.right && !abd.rc.bottom); + wxCHECK(::SHAppBarMessage(ABM_SETPOS, &abd), false); + + return true; +} + + +inline bool wxAppBar::RegisterAutoHide(wxState state) +{ + wxASSERT(IsDocked(state)); + + APPBARDATA abd = { + sizeof(abd), + m_hWnd, + 0, + state, + {}, + (LPARAM)true + }; + + // Register application bar as auto-hide. + if (::SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd)) { + // Auto-hide succeeded. + m_flags |= wxFLAG_AUTOHIDE; + m_flags &= ~wxFLAG_AUTOHIDDEN; + + // Calculate auto-hidden window rect and move the window there. + GetAutoHideRect(state, false, &(abd.rc)); + m_flags |= wxFLAG_POSITIONSET; + m_rect = abd.rc; + wxCHECK(::SetWindowPos(m_hWnd, GetZWnd(state, m_flags), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_FRAMECHANGED), false); + + // Reset auto-hide timer. + m_timerID = ::SetTimer(m_hWnd, wxABT_AUTOHIDETIMERID, wxABT_AUTOHIDETIMERINTERVAL, NULL); + + return true; + } else { + // Auto-hide failed; some other application bar must be hidden on this edge already. + m_flags &= ~(wxFLAG_AUTOHIDE | wxFLAG_AUTOHIDDEN); + + // Post a message to user. + OnAutoHideDenied(); + + return false; + } +} + + +inline bool wxAppBar::UnregisterAutoHide(wxState state) +{ + wxASSERT(IsDocked(state)); + + APPBARDATA abd = { + sizeof(abd), + m_hWnd, + 0, + state, + {}, + (LPARAM)false + }; + + // Unregister application bar as auto-hide. + wxCHECK(::SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd), false); + m_flags &= ~wxFLAG_AUTOHIDDEN; + + // Cancel the timer. + if (m_timerID) { + ::KillTimer(m_hWnd, m_timerID); + m_timerID = 0; + } + + return true; +} + + +inline bool wxAppBar::GetDockedRect(wxState state, LPRECT rect) const +{ + wxASSERT(rect); + wxASSERT(IsDocked(state)); + + APPBARDATA abd = { + sizeof(abd), + m_hWnd, + 0, + state, + { + // Set dimensions to full screen. + 0, + 0, + ::GetSystemMetrics(SM_CXSCREEN), + ::GetSystemMetrics(SM_CYSCREEN) + } + }; + + wxCHECK(::SHAppBarMessage(ABM_QUERYPOS, &abd), false); + + // Correct our dimensions accordingly. + switch (state) { + case wxSTATE_LEFT: + rect->left = abd.rc.left; + rect->top = abd.rc.top; + rect->right = abd.rc.left + m_sizeDocked.cx; + rect->bottom = abd.rc.bottom; + break; + + case wxSTATE_TOP: + rect->left = abd.rc.left; + rect->top = abd.rc.top; + rect->right = abd.rc.right; + rect->bottom = abd.rc.top + m_sizeDocked.cy; + break; + + case wxSTATE_RIGHT: + rect->left = abd.rc.right - m_sizeDocked.cx; + rect->top = abd.rc.top; + rect->right = abd.rc.right; + rect->bottom = abd.rc.bottom; + break; + + case wxSTATE_BOTTOM: + rect->left = abd.rc.left; + rect->top = abd.rc.bottom - m_sizeDocked.cy; + rect->right = abd.rc.right; + rect->bottom = abd.rc.bottom; + break; + + default: + // Unknown state. + wxFAIL; + return false; + } + + return true; +} + + +inline bool wxAppBar::GetAutoHideRect(wxState state, bool bAutoHidden, LPRECT rect) const +{ + wxASSERT(rect); + wxASSERT(IsDocked(state)); + + // Keep a part of the application bar visible at all times + const int iBorder = ::GetSystemMetrics(IsDockedVert(state) ? SM_CXBORDER : SM_CYBORDER) * 2; + const RECT rcScreen = { + 0, + 0, + ::GetSystemMetrics(SM_CXSCREEN), + ::GetSystemMetrics(SM_CYSCREEN) + }; + + // Correct our dimensions accordingly. + switch (state) { + case wxSTATE_LEFT: + rect->top = rcScreen.top; + rect->bottom = rcScreen.bottom; + rect->right = rcScreen.left + (bAutoHidden ? iBorder : m_sizeDocked.cx); + rect->left = rect->right - m_sizeDocked.cx; + break; + + case wxSTATE_TOP: + rect->left = rcScreen.left; + rect->right = rcScreen.right; + rect->bottom = rcScreen.top + (bAutoHidden ? iBorder : m_sizeDocked.cy); + rect->top = rect->bottom - m_sizeDocked.cy; + break; + + case wxSTATE_RIGHT: + rect->left = rcScreen.right - (bAutoHidden ? iBorder : m_sizeDocked.cx); + rect->top = rcScreen.top; + rect->right = rect->left + m_sizeDocked.cx; + rect->bottom = rcScreen.bottom; + break; + + case wxSTATE_BOTTOM: + rect->left = rcScreen.left; + rect->top = rcScreen.bottom - (bAutoHidden ? iBorder : m_sizeDocked.cy); + rect->right = rcScreen.right; + rect->bottom = rect->top + m_sizeDocked.cy; + break; + + default: + // Unknown state. + wxFAIL; + return false; + } + + return true; +} + + +inline bool wxAppBar::IsDocked(wxState state) +{ + return wxSTATE_LEFT <= state && state <= wxSTATE_BOTTOM; +} + + +inline bool wxAppBar::IsDockedVert(wxState state) +{ + return state == wxSTATE_LEFT || state == wxSTATE_RIGHT; +} + + +inline bool wxAppBar::IsDockedHoriz(wxState state) +{ + return state == wxSTATE_TOP || state == wxSTATE_BOTTOM; +} + + +inline WXHWND wxAppBar::GetZWnd(wxState state, int flags) +{ + if (flags & wxFLAG_FULLSCREENAPPOPEN) { + // If full screen app is open, clear the always-on-top flag. + return HWND_NOTOPMOST; + } + + //_AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); + //if (::IsWindow(pThreadState->m_hTrackingWindow)) { + // // We're tracking another window/menu, position behind it. + // return pThreadState->m_hTrackingWindow; + // } + + if (state == wxSTATE_FLOAT) { + // When floating, decide according to the flags. + return (flags & wxFLAG_ALWAYSONTOP) ? HWND_TOPMOST : HWND_NOTOPMOST; + } + + if (IsDocked(state)) { + if (flags & wxFLAG_AUTOHIDE) { + // Auto-hidden docked application bar is always on top. + return HWND_TOPMOST; + } + + if (flags & wxFLAG_ALWAYSONTOP) { + // Regular docked always-on-top application bar. + WXHWND hWndTaskbar = ::FindWindow(_T("Shell_TrayWnd"), _T("")); + if (hWndTaskbar) { + // We found the taskbar window. Position directly after it. Otherwise our window overlaps Vista's Start button orb. + return hWndTaskbar; + } else { + // Only always-on-top windows are allowed to resize properly outside the desktop rect. + // If window is docked and not always on top, resizing a window to smaller size is obstructed by desktop rect limit. + return HWND_TOPMOST; + } + } + + + // Regular docked non-always-on-top application bar. + return HWND_NOTOPMOST; + } else { + // Unknown state. Do nothing. + wxFAIL; + return HWND_TOP; + } +} + + +inline UINT_PTR wxAppBar::GetTaskBarState() +{ + APPBARDATA abd = { sizeof(abd) }; + + return ::SHAppBarMessage(ABM_GETSTATE, &abd); +} diff --git a/include/wxex/common.h b/include/wxex/common.h new file mode 100644 index 0000000..aa98f2f --- /dev/null +++ b/include/wxex/common.h @@ -0,0 +1,77 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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 3 of the License, or + (at your option) any later version. + + wxExtend 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 wxExtend. If not, see . +*/ + +#pragma once + +#include +#include +#include + + +//--------------------------------------------------------------------------- +// Usual debugging macros +//--------------------------------------------------------------------------- +#if wxDEBUG_LEVEL +#define wxVERIFY(x) wxASSERT((x)) +#else +#define wxVERIFY(x) (x) +#endif + + +/// +/// Returns whether windows animation is enabled. +/// +/// \returns +/// - true when window animation is enabled +/// - false otherwise +/// +inline bool wxGetDoWndAnimation() +{ + ANIMATIONINFO ai = { sizeof(ai) }; + wxCHECK(SystemParametersInfo(SPI_GETANIMATION, sizeof(ai), &ai, 0), false); + return ai.iMinAnimate ? true : false; +} + + +/// +/// Modifies window extended style. +/// +/// \param[in] hWnd Handle of the window to modify. +/// \param[in] dwRemove Set of extended styles to remove. +/// \param[in] dwAdd Set of extended styles to add. +/// \param[in] nFlags Additional SWP_ flags to pass to SetWindowPos(). If zero, SetWindowPos() is not called. +/// \returns +/// - true when the window extended style was modified +/// - false if the window extended style was not neccessary +/// +inline bool wxModifyStyleEx(_In_ WXHWND hWnd, _In_ DWORD dwRemove, _In_ DWORD dwAdd, _In_ UINT nFlags = 0) +{ + wxASSERT(IsWindow(hWnd)); + + DWORD dwStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + DWORD dwNewStyle = (dwStyle & ~dwRemove) | dwAdd; + if(dwStyle == dwNewStyle) + return false; + + SetWindowLong(hWnd, GWL_EXSTYLE, dwNewStyle); + if(nFlags != 0) + SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | nFlags); + + return true; +} diff --git a/src/appbar.cpp b/src/appbar.cpp new file mode 100644 index 0000000..a663dcf --- /dev/null +++ b/src/appbar.cpp @@ -0,0 +1,847 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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 3 of the License, or + (at your option) any later version. + + wxExtend 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 wxExtend. If not, see . +*/ + +#include "stdafx.h" + + +////////////////////////////////////////////////////////////////////////// +// wxAppBar +////////////////////////////////////////////////////////////////////////// + +const UINT wxAppBar::WM_AB_NOTIFY = ::RegisterWindowMessage(_T("AppBarNotify")); + + +wxAppBar::wxAppBar() : + m_timerID(0), + m_taskbarList(NULL), + wxFrame() +{ +} + + +wxAppBar::wxAppBar(wxWindow *parent, wxWindowID id, const wxString& title, wxState state, int flags, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : + m_timerID(0), + m_taskbarList(NULL), + wxFrame() +{ + Create(parent, id, title, state, flags, pos, size, style, name); +} + + +bool wxAppBar::Create(wxWindow *parent, wxWindowID id, const wxString& title, wxState state, int flags, const wxPoint& pos, const wxSize& size, long style, const wxString& name) +{ + // Save initial window rectangle. + m_rect.left = pos.x; + m_rect.top = pos.y; + m_rect.right = pos.x + size.x; + m_rect.bottom = pos.y + size.y; + + // Save initial floating window size. + m_sizeFloat.cx = size.x; + m_sizeFloat.cy = size.y; + + // Save initial docked window width/height. + m_sizeDocked.cx = size.x; + m_sizeDocked.cy = size.y; + + // Save initial state and flags. + m_state = state; + m_stateDesired = wxSTATE_UNKNOWN; + m_flags = flags; + + // Find the taskbar list's interface. + wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList, (LPVOID*)&m_taskbarList)), false); + wxCHECK(SUCCEEDED(m_taskbarList->HrInit()), false); + + // If any taskbar list tab's hiding is in affect, set application bar's style as tool window to avoid being displayed on the taskbar initially. + if (m_flags & wxFLAG_ALWAYSHIDETASKBARTAB) + style |= wxFRAME_TOOL_WINDOW; + + // Create frame. + wxCHECK(wxFrame::Create(parent, id, title, pos, size, style, name), false); + + // Register our application bar. + APPBARDATA abd = { sizeof(abd), m_hWnd, WM_AB_NOTIFY, m_state, }; + wxCHECK(::SHAppBarMessage(ABM_NEW, &abd), false); + + // Get the state of the Windows taskbar. + m_stateTaskBar = GetTaskBarState(); + + if (m_flags & wxFLAG_AUTOHIDETASKBAR) { + // Mimic the auto-hide state of taskbar. + SetAutoHide((m_stateTaskBar & ABS_AUTOHIDE) ? true : false); + } + + if (m_flags & wxFLAG_ALWAYSONTOPTASKBAR) { + // Mimic the always-on-top state of taskbar. + SetAlwaysOnTop((m_stateTaskBar & ABS_ALWAYSONTOP) ? true : false); + } + + return true; +} + + +wxAppBar::~wxAppBar() +{ + wxASSERT(m_timerID == 0); + + // Remove the application bar. + APPBARDATA abd = { + sizeof(abd), + m_hWnd + }; + wxVERIFY(::SHAppBarMessage(ABM_REMOVE, &abd)); + + if (m_taskbarList) + m_taskbarList->Release(); +} + + +//bool wxAppBar::GetAppBarPlacement(LPAPPBARPLACEMENT lpPlacement) const +//{ +// wxASSERT(lpPlacement); +// +// if (lpPlacement->dwSize >= sizeof(APPBARPLACEMENT1)) { +// lpPlacement->dwState = (DWORD)m_state; +// lpPlacement->dwFlags = m_flags; +// if (m_state == wxSTATE_FLOAT) { +// // Current application bar's rect is floating rect. +// lpPlacement->rcFloat = m_rect; +// } else if (IsDocked(m_state)) { +// // Application bar is docked. Calc floating rect from m_sizeFloat. +// lpPlacement->rcFloat.left = 0; +// lpPlacement->rcFloat.top = 0; +// lpPlacement->rcFloat.right = m_sizeFloat.cx; +// lpPlacement->rcFloat.bottom = m_sizeFloat.cy; +// } else { +// // Application bar's state is unknown. +// return false; +// } +// lpPlacement->szDocked = m_sizeDocked; +// } +// +// lpPlacement->dwSize = sizeof(*lpPlacement); +// +// return true; +//} +// +// +//bool wxAppBar::SetAppBarPlacement(LPCAPPBARPLACEMENT lpPlacement) +//{ +// wxASSERT(lpPlacement); +// +// if (lpPlacement->dwSize >= sizeof(APPBARPLACEMENT1)) { +// if (lpPlacement->dwState != wxSTATE_FLOAT && !IsDocked(lpPlacement->dwState)) { +// // The state is unknown. Abort, before we mix up internal state. +// return false; +// } +// +// // Clean previous docking/auto-hide settings if required. +// if (IsDocked(m_state) && +// (lpPlacement->dwState == wxSTATE_FLOAT || lpPlacement->dwState != m_state && IsDocked(lpPlacement->dwState))) +// { +// if (IsAutoHide()) +// UnregisterAutoHide(m_state); +// else if (lpPlacement->dwState == wxSTATE_FLOAT) +// UndockAppBar(); +// } +// +// // Update application bar's flags, size and position. +// m_flags |= wxFLAG_POSITIONSET; +// m_sizeDocked = lpPlacement->szDocked; +// if (lpPlacement->dwState == wxSTATE_FLOAT) { +// m_rect = lpPlacement->rcFloat; +// } else if (IsDocked(lpPlacement->dwState)) { +// m_sizeFloat.cx = lpPlacement->rcFloat.right - lpPlacement->rcFloat.left; +// m_sizeFloat.cy = lpPlacement->rcFloat.bottom - lpPlacement->rcFloat.top; +// GetDockedRect(lpPlacement->dwState, &m_rect); +// } else { +// // Application bar's state is unknown. +// wxFAIL; +// return false; +// } +// +// // Notify about the change of state. +// OnChangeState(lpPlacement->dwState); +// m_state = lpPlacement->dwState; +// +// SetAutoHide(lpPlacement->dwFlags & wxFLAG_AUTOHIDE); +// SetAlwaysOnTop(lpPlacement->dwFlags & wxFLAG_ALWAYSONTOP); +// } +// +// return true; +//} + + +void wxAppBar::MinimiseToEdge(wxState state, wxWindow *wnd) +{ + wxASSERT(IsDocked(state)); + + if (!wnd) { + // No other window was specified. Minimize ourself. + wnd = this; + } + + WXHWND hWnd = wnd->GetHWND(); + + // If our window is hidden, there's nothing we can do. + if (hWnd == m_hWnd && !::IsWindowVisible(m_hWnd)) + return; + + if (m_state == wxSTATE_FLOAT) { + // Remember the last floating size. + m_sizeFloat.cx = m_rect.right - m_rect.left; + m_sizeFloat.cy = m_rect.bottom - m_rect.top; + } + + RECT rectTo; + if (IsAutoHide()) + GetAutoHideRect(state, false, &rectTo); + else + GetDockedRect(state, &rectTo); + + if (::wxGetDoWndAnimation()) { + RECT rectFrom; + + // Calculate source and destination rectangles. + if (hWnd != m_hWnd) + ::GetWindowRect(hWnd, &rectFrom); + else + rectFrom = m_rect; + + // Do the animation. + wxVERIFY(::DrawAnimatedRects(hWnd, IDANI_CAPTION, &rectFrom, &rectTo)); + } + + // Set the window rect. + m_flags |= wxFLAG_POSITIONSET; + m_rect = rectTo; + + // Notify about the change of state. + OnChangeState(state); + m_state = state; + + if (hWnd != m_hWnd) { + // Hide the source window. + wnd->Hide(); + // ::SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) &~ WS_VISIBLE); + + // Show the application bar. + Show(); + } else { + if (IsAutoHide()) { + // Register auto-hide application bar. + RegisterAutoHide(state); + } else { + // Auto-hide failed or wasn't desired at all. + DockAppBar(state); + } + } + + ::SetActiveWindow(m_hWnd); +} + + +void wxAppBar::MaximiseFromEdge(const RECT* rect) +{ + wxASSERT(::IsWindowVisible(m_hWnd)); + + RECT rc; + + if (!rect) { + // Calculate the destination rect. + rc.left = (::GetSystemMetrics(SM_CXSCREEN) - m_sizeFloat.cx) / 2; + rc.top = (::GetSystemMetrics(SM_CYSCREEN) - m_sizeFloat.cy) / 2; + rc.right = rc.left + m_sizeFloat.cx; + rc.bottom = rc.top + m_sizeFloat.cy; + + rect = &rc; + } else { + m_sizeFloat.cx = rect->right - rect->left; + m_sizeFloat.cy = rect->bottom - rect->top; + } + + if (::wxGetDoWndAnimation()) { + // Do the animation. + wxVERIFY(::DrawAnimatedRects(m_hWnd, IDANI_CAPTION, &m_rect, rect)); + } + + // Clean previous docking/auto-hide settings if required. + if (IsDocked(m_state)) { + if (IsAutoHide()) + UnregisterAutoHide(m_state); + else + UndockAppBar(); + } + + // Set the window rect. + m_flags |= wxFLAG_POSITIONSET; + m_rect = *rect; + + // Notify about the change of state. + OnChangeState(wxSTATE_FLOAT); + m_state = wxSTATE_FLOAT; + + ::SetWindowPos(m_hWnd, GetZWnd(wxSTATE_FLOAT, m_flags), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_FRAMECHANGED); +} + + +void wxAppBar::MaximiseFromEdge(wxWindow *wnd) +{ + wxASSERT(wnd); + wxASSERT(::IsWindowVisible(m_hWnd)); + + WXHWND hWnd = wnd->GetHWND(); + RECT rectTo; + + ::GetWindowRect(hWnd, &rectTo); + m_sizeFloat.cx = rectTo.right - rectTo.left; + m_sizeFloat.cy = rectTo.bottom - rectTo.top; + + if (::wxGetDoWndAnimation()) { + // Do the animation. + wxVERIFY(::DrawAnimatedRects(hWnd, IDANI_CAPTION, &m_rect, &rectTo)); + } + + Hide(); + + // Show the destination window. + wnd->Show(); + // ::SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) | WS_VISIBLE); + wxVERIFY(::SetForegroundWindow(hWnd)); + ::SetActiveWindow(hWnd); + // wxVERIFY(::RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME | RDW_INVALIDATE | RDW_ERASE)); +} + + +void wxAppBar::ShowAutoHideAppBar(bool bShow) +{ + RECT rcEnd; + bool + bFullDragOn, + bHidden = GetAutoHidden(), + bVisible = ::IsWindowVisible(m_hWnd) ? true : false; + + if (!IsAutoHide() || bHidden != bShow) { + // We're not setup for auto-hide or already in the desired state. + return; + } + + // Get our end window location. + GetAutoHideRect(m_state, !bHidden, &rcEnd); + + if (bVisible) { + RECT rcStart; + + // Only slide the window if the user has FullDrag turned on. + if (!::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bFullDragOn, 0)) + bFullDragOn = false; + + // Get our start window location. + GetAutoHideRect(m_state, bHidden, &rcStart); + + if (bFullDragOn && (rcStart.left != rcEnd.left || rcStart.top != rcEnd.top || rcStart.right != rcEnd.right || rcStart.bottom != rcEnd.bottom)) { + // Get our starting and ending time. + DWORD dwTimeStart = ::GetTickCount(); + DWORD dwTimeElapsed; + RECT rcDelta; + + // Calculate difference between ending and starting rectangle. + rcDelta.left = rcEnd.left - rcStart.left; + rcDelta.top = rcEnd.top - rcStart.top; + rcDelta.right = rcEnd.right - rcStart.right; + rcDelta.bottom = rcEnd.bottom - rcStart.bottom; + + m_flags |= wxFLAG_POSITIONSET; + + while ((dwTimeElapsed = ::GetTickCount() - dwTimeStart) < wxABT_AUTOHIDETIME) { + // Do the linear interpolation. + m_rect.left = rcStart.left + ::MulDiv(rcDelta.left, dwTimeElapsed, wxABT_AUTOHIDETIME); + m_rect.top = rcStart.top + ::MulDiv(rcDelta.top, dwTimeElapsed, wxABT_AUTOHIDETIME); + m_rect.right = rcStart.right + ::MulDiv(rcDelta.right, dwTimeElapsed, wxABT_AUTOHIDETIME); + m_rect.bottom = rcStart.bottom + ::MulDiv(rcDelta.bottom, dwTimeElapsed, wxABT_AUTOHIDETIME); + + // Show the window at its changed position + ::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE); + ::UpdateWindow(m_hWnd); + } + } + } + + // Make sure that the window is at its final position + m_flags |= wxFLAG_POSITIONSET; + m_rect = rcEnd; + + if (bShow) + m_flags &= ~wxFLAG_AUTOHIDDEN; + else + m_flags |= wxFLAG_AUTOHIDDEN; + + if (bVisible) { + ::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE); + ::UpdateWindow(m_hWnd); + // Reset auto-hide timer. + m_timerID = ::SetTimer(m_hWnd, wxABT_AUTOHIDETIMERID, wxABT_AUTOHIDETIMERINTERVAL, NULL); + } +} + + +void wxAppBar::OnChangeState(wxState stateNew) +{ + if (stateNew == wxSTATE_FLOAT) { + if (m_flags & wxFLAG_HIDETASKBARTABWHENFLOATING) { + // Hide our application bar's entry on the Windows's taskbar. + wxModifyStyleEx(m_hWnd, 0, WS_EX_TOOLWINDOW); + if (m_taskbarList) + wxVERIFY(SUCCEEDED(m_taskbarList->DeleteTab(m_hWnd))); + } else if (m_flags & wxFLAG_HIDETASKBARTABWHENDOCKED) { + // Show our application bar's entry on the Windows's taskbar. + wxModifyStyleEx(m_hWnd, WS_EX_TOOLWINDOW, 0); + if (m_taskbarList) + wxVERIFY(SUCCEEDED(m_taskbarList->AddTab(m_hWnd))); + } + } else if (IsDocked(stateNew)) { + if (m_flags & wxFLAG_HIDETASKBARTABWHENDOCKED) { + // Hide our application bar's entry on the Windows's taskbar. + wxModifyStyleEx(m_hWnd, 0, WS_EX_TOOLWINDOW); + if (m_taskbarList) + wxVERIFY(SUCCEEDED(m_taskbarList->DeleteTab(m_hWnd))); + } else if (m_flags & wxFLAG_HIDETASKBARTABWHENFLOATING) { + // Show our application bar's entry on the Windows's taskbar. + wxModifyStyleEx(m_hWnd, WS_EX_TOOLWINDOW, 0); + if (m_taskbarList) + wxVERIFY(SUCCEEDED(m_taskbarList->AddTab(m_hWnd))); + } + } else { + // Unknown state. + wxFAIL; + } +} + + +void wxAppBar::OnChangeTaskBarState(UINT_PTR state) +{ + if (state != m_stateTaskBar) { + // No change, no fun. + return; + } + + if ((m_flags & wxFLAG_ALWAYSONTOPTASKBAR) && + (m_stateTaskBar & ABS_ALWAYSONTOP) != (state & ABS_ALWAYSONTOP)) { + // Always-on-top state of the taskbar changed and we're supposed to mimic it. + SetAlwaysOnTop((state & ABS_ALWAYSONTOP) ? true : false); + } + + if ((m_flags & wxFLAG_AUTOHIDETASKBAR) && + (m_stateTaskBar & ABS_AUTOHIDE) != (state & ABS_AUTOHIDE)) { + // Auto-hide state of the taskbar changed and we're supposed to mimic it. + SetAutoHide((state & ABS_AUTOHIDE) ? true : false); + } + + // Remember the state. + m_stateTaskBar = state; +} + + +void wxAppBar::OnWindowsArrange(bool WXUNUSED(bBeginning)) +{ +} + + +void wxAppBar::OnAutoHideDenied() +{ + wxMessageBox(wxT("There is already an auto hidden bar on this edge.\nOnly one auto hidden bar is allowed on each edge.\nAuto-hide feature is now off."), wxT("Warning"), wxOK | wxICON_WARNING, this); +} + + +WXLRESULT wxAppBar::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) +{ + switch (message) { + case WM_SHOWWINDOW: + if (wParam) { + // Do the false change of state notification, to allow initialization. + OnChangeState(m_state); + + if (IsDocked(m_state)) { + if (IsAutoHide()) { + // Register auto-hide application bar. + RegisterAutoHide(m_state); + } else { + // Auto-hide failed or wasn't desired at all. + DockAppBar(m_state); + } + } + + // We're all set. Show the window now. + return wxFrame::MSWWindowProc(message, wParam, lParam); + } else { + // Hide the window first, to avoid flicker. + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + // Clean previous docking/auto-hide settings if required. + if (IsDocked(m_state)) { + if (IsAutoHide()) + UnregisterAutoHide(m_state); + else + UndockAppBar(); + } + + return lResult; + } + + case WM_WINDOWPOSCHANGING: { + wxASSERT(lParam); + LPWINDOWPOS lpwndpos = (LPWINDOWPOS)lParam; + + // Sync to our window rect. + if (!(m_flags & wxFLAG_POSITIONSET) && !(lpwndpos->flags & SWP_NOMOVE)) { + // Get the window position first time the SetWindowPos() is called. + m_rect.right = lpwndpos->x + m_rect.right - m_rect.left; + m_rect.bottom = lpwndpos->y + m_rect.bottom - m_rect.top; + m_rect.left = lpwndpos->x; + m_rect.top = lpwndpos->y; + m_flags |= wxFLAG_POSITIONSET; + } else { + lpwndpos->x = m_rect.left; + lpwndpos->y = m_rect.top; + } + lpwndpos->cx = m_rect.right - m_rect.left; + lpwndpos->cy = m_rect.bottom - m_rect.top; + lpwndpos->flags &= ~(SWP_NOMOVE | SWP_NOSIZE); + return wxFrame::MSWWindowProc(message, wParam, lParam); + } + + case WM_WINDOWPOSCHANGED: { + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + if (IsDocked(m_state)) { + APPBARDATA abd = { + sizeof(abd), + m_hWnd + }; + + // When our window changes position, tell the Shell so that any + // auto-hidden application bar on our edge stays on top of our window making it + // always accessible to the user. + wxVERIFY(::SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd)); + } + + return lResult; + } + + case WM_ENTERSIZEMOVE: + m_stateDesired = m_state; + + return 0; + + case WM_EXITSIZEMOVE: + // Clean previous docking/auto-hide settings if required. + if (IsDocked(m_state) && + (m_stateDesired == wxSTATE_FLOAT || m_stateDesired != m_state && IsDocked(m_stateDesired))) + { + if (IsAutoHide()) + UnregisterAutoHide(m_state); + else if (m_stateDesired == wxSTATE_FLOAT) + UndockAppBar(); + } + + // Setup new docking/auto-hide settings. + if (IsDocked(m_stateDesired)) { + if (IsAutoHide()) { + // Application bar should be auto-hide. Try to setup it as so. + // Register auto-hide application bar. + RegisterAutoHide(m_stateDesired); + } else { + // Auto-hide failed or wasn't desired at all. + DockAppBar(m_stateDesired); + } + } + + // Notify about the change of state. + OnChangeState(m_stateDesired); + m_state = m_stateDesired; + m_stateDesired = wxSTATE_UNKNOWN; + + return 0; + + case WM_MOVING: { + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + wxASSERT(lParam); + LPRECT lpRect = (LPRECT)lParam; + + // Phase 1. - Determine the desired state (which screen edge or floating) according to the mouse position. + + wxState uStateDesiredPrev = m_stateDesired; + DWORD dwPoint = ::GetMessagePos(); + wxPoint ptMouse(GET_X_LPARAM(dwPoint), GET_Y_LPARAM(dwPoint)); + + m_stateDesired = wxSTATE_UNKNOWN; + + if ((m_flags & wxFLAG_ALLOWFLOAT) && (::GetKeyState(VK_CONTROL) < 0 || !(m_flags & wxFLAG_ALLOWDOCKANY))) { + // The application bar's style allows floating and user is holding down the Ctrl key, or docking is disabled anyway, so we'll force float. + m_stateDesired = wxSTATE_FLOAT; + } else { + RECT rcWorkArea = {}; + INT iDist, iDistMin = INT_MAX; + + // Get the rectangle that bounds the size of the screen + // minus any docked (but not-autohidden) AppBars. + wxVERIFY(::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0)); + + // Test all four edges, to find the closest one. + if ((m_flags & wxFLAG_ALLOWDOCKTOP) && (iDist = ptMouse.y - rcWorkArea.top) < iDistMin) { + m_stateDesired = wxSTATE_TOP; + iDistMin = iDist; + } + if ((m_flags & wxFLAG_ALLOWDOCKBOTTOM) && (iDist = rcWorkArea.bottom - ptMouse.y) < iDistMin) { + m_stateDesired = wxSTATE_BOTTOM; + iDistMin = iDist; + } + if ((m_flags & wxFLAG_ALLOWDOCKLEFT) && (iDist = ptMouse.x - rcWorkArea.left) < iDistMin) { + m_stateDesired = wxSTATE_LEFT; + iDistMin = iDist; + } + if ((m_flags & wxFLAG_ALLOWDOCKRIGHT) && (iDist = rcWorkArea.right - ptMouse.x) < iDistMin) { + m_stateDesired = wxSTATE_RIGHT; + iDistMin = iDist; + } + if ((m_flags & wxFLAG_ALLOWFLOAT) && (iDist = wxMax(::GetSystemMetrics(SM_CXVSCROLL), ::GetSystemMetrics(SM_CYHSCROLL))) < iDistMin) { + m_stateDesired = wxSTATE_FLOAT; + iDistMin = iDist; + } + } + + // We must had come up with some useful state. + wxASSERT(m_stateDesired != wxSTATE_UNKNOWN); + + // Phase 2. - Calculate the desired rectangle, according to the desired state. + + if (m_stateDesired == wxSTATE_FLOAT) { + if (IsDocked(uStateDesiredPrev)) { + // We just switched from docked to floating state. Align the window according to the mouse pointer. + lpRect->left = ptMouse.x - m_sizeFloat.cx / 2; + lpRect->top = ptMouse.y; + + // We also resize back to floating window size. + lpRect->right = lpRect->left + m_sizeFloat.cx; + lpRect->bottom = lpRect->top + m_sizeFloat.cy; + } + } else if (IsDocked(m_stateDesired)) { + if (IsAutoHide()) { + // Calculate auto-hide window rect. + GetAutoHideRect(m_stateDesired, false, lpRect); + } else { + // Calculate docked window rect. + GetDockedRect(m_stateDesired, lpRect); + } + } else { + // Window is not floating. It's not docked either. Then what? + wxFAIL; + } + + // Set the window rect. + m_flags |= wxFLAG_POSITIONSET; + m_rect = *lpRect; + + if (m_stateDesired != uStateDesiredPrev) { + // Notify about the proposed change of state, but don't change the state yet. + OnChangeState(m_stateDesired); + } + + return lResult; + } + + case WM_SIZING: { + wxASSERT(lParam); + + LPRECT lpRect = (LPRECT)lParam; + + m_flags |= wxFLAG_POSITIONSET; + m_rect = *lpRect; + + if (m_stateDesired == wxSTATE_FLOAT) { + // Remember the floating window size. + m_sizeFloat.cx = lpRect->right - lpRect->left; + m_sizeFloat.cy = lpRect->bottom - lpRect->top; + } else if (IsDocked(m_stateDesired)) { + // Remember the docked window size. + if (IsDockedVert(m_stateDesired)) + m_sizeDocked.cx = lpRect->right - lpRect->left; + else + m_sizeDocked.cy = lpRect->bottom - lpRect->top; + } else { + // Unknown state. + wxFAIL; + } + + return wxFrame::MSWWindowProc(message, wParam, lParam); + } + + case WM_GETMINMAXINFO: { + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + wxASSERT(lParam); + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + + // Convert client size to window size. + RECT rc = {}; + wxVERIFY(::GetClientRect(m_hWnd, &rc)); + if (rc.right - rc.left && rc.bottom - rc.top) { + lpMMI->ptMinTrackSize.x = m_sizeMin.cx + (m_rect.right - m_rect.left) - (rc.right - rc.left); + lpMMI->ptMinTrackSize.y = m_sizeMin.cy + (m_rect.bottom - m_rect.top ) - (rc.bottom - rc.top ); + } + + return lResult; + } + + case WM_NCMOUSEMOVE: + // If we are a docked, auto-hidden application bar, shown us + // when the user moves over our non-client area + ShowAutoHideAppBar(true); + + return wxFrame::MSWWindowProc(message, wParam, lParam); + + case WM_NCHITTEST: { + // Find out what the base class thinks is the hit test code. + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + if (HTSIZEFIRST <= lResult && lResult <= HTSIZELAST) { + if (GetAllowSizing()) { + if (IsDocked(m_state)) { + // When the application bar is docked, the user can resize only one edge. + // This next section determines which edge the user can resize. + // To allow resizing, the application bar window must have the WS_THICKFRAME style. + + RECT rcClient = {}; + wxPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + // Resizing IS allowed for the edge that the application bar is docked on. + // Get the location of the appbar's client area in screen coordinates. + wxVERIFY(::GetClientRect(m_hWnd, &rcClient)); + wxVERIFY(::ClientToScreen(m_hWnd, (LPPOINT)&rcClient)); + wxVERIFY(::ClientToScreen(m_hWnd, ((LPPOINT)&rcClient) + 1)); + + // Assume that we can't resize + lResult = HTBORDER; + + switch (m_state) { + case wxSTATE_LEFT: if (point.x > rcClient.right) lResult = HTRIGHT; break; + case wxSTATE_TOP: if (point.y > rcClient.bottom) lResult = HTBOTTOM; break; + case wxSTATE_RIGHT: if (point.x < rcClient.left) lResult = HTLEFT; break; + case wxSTATE_BOTTOM: if (point.y < rcClient.top) lResult = HTTOP; break; + } + } + } else { + // The sizing is locked. Pretend that the mouse is not on a resize border. + lResult = HTBORDER; + } + } + + return lResult; + } + + case WM_ACTIVATE: { + WXLRESULT lResult = wxFrame::MSWWindowProc(message, wParam, lParam); + + if (IsDocked(m_state)) { + APPBARDATA abd = { + sizeof(abd), + m_hWnd + }; + + // When our window changes activation state, tell the Shell so that any + // auto-hidden application bar on our edge stays on top of our window making it + // always accessible to the user. + wxVERIFY(::SHAppBarMessage(ABM_ACTIVATE, &abd)); + + if (LOWORD(wParam) == WA_INACTIVE && IsAutoHide()) { + // Hide the application bar if we are docked and auto-hidden. + ShowAutoHideAppBar(false); + } + } + + return lResult; + } + + case WM_TIMER: + if (wParam == wxABT_AUTOHIDETIMERID) { + if (!GetAutoHidden() && + IsDocked(m_state) && + GetActiveWindow() != m_hWnd) + { + // Get the position of the mouse and the application bar's position + // Everything must be in screen coordinates. + DWORD dwPoint = ::GetMessagePos(); + wxPoint pt(GET_X_LPARAM(dwPoint), GET_Y_LPARAM(dwPoint)); + wxRect rc(m_rect.left, m_rect.top, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top); + + // Add a little margin around the application bar + rc.Inflate(2 * ::GetSystemMetrics(SM_CXDOUBLECLK), 2 * ::GetSystemMetrics(SM_CYDOUBLECLK)); + if (!rc.Contains(pt)) { + // If the mouse is NOT over or near the application bar, hide it. + ShowAutoHideAppBar(false); + } + } + + return 0; + } else + return wxFrame::MSWWindowProc(message, wParam, lParam); + + default: + if (message == WM_AB_NOTIFY) { + switch (wParam) { + case ABN_FULLSCREENAPP: + if (lParam) + m_flags |= wxFLAG_FULLSCREENAPPOPEN; + else + m_flags &= ~wxFLAG_FULLSCREENAPPOPEN; + OnChangeTaskBarState(GetTaskBarState()); + + if (::IsWindowVisible(m_hWnd)) { + // Set the Z-order. SWP_NOSENDCHANGING flag prevents our OnWindowPosChanging() method to be called, since moving is not desired. + wxVERIFY(::SetWindowPos(m_hWnd, GetZWnd(m_state, m_flags), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOSENDCHANGING)); + } + break; + + case ABN_POSCHANGED: + if (IsDocked(m_state) && !IsAutoHide()) + DockAppBar(m_state); + break; + + case ABN_STATECHANGE: + // Taskbar's state changed. + OnChangeTaskBarState(GetTaskBarState()); + break; + + case ABN_WINDOWARRANGE: + OnWindowsArrange(lParam ? true : false); + break; + + default: + // Unknown message. + wxFAIL; + break; + } + + return 0; + } else + return wxFrame::MSWWindowProc(message, wParam, lParam); + } +} diff --git a/src/stdafx.cpp b/src/stdafx.cpp new file mode 100644 index 0000000..2ff81d0 --- /dev/null +++ b/src/stdafx.cpp @@ -0,0 +1,20 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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 3 of the License, or + (at your option) any later version. + + wxExtend 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 wxExtend. If not, see . +*/ + +#include "stdafx.h" diff --git a/src/stdafx.h b/src/stdafx.h new file mode 100644 index 0000000..e2693c8 --- /dev/null +++ b/src/stdafx.h @@ -0,0 +1,27 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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 3 of the License, or + (at your option) any later version. + + wxExtend 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 wxExtend. If not, see . +*/ + +#pragma once + +#include "../include/wxex/appbar.h" + +#include "../include/wxex/common.h" + +#include +#include