diff options
Diffstat (limited to 'sfx2/source/dialog/splitwin.cxx')
-rw-r--r-- | sfx2/source/dialog/splitwin.cxx | 1155 |
1 files changed, 1155 insertions, 0 deletions
diff --git a/sfx2/source/dialog/splitwin.cxx b/sfx2/source/dialog/splitwin.cxx new file mode 100644 index 000000000..2abedce11 --- /dev/null +++ b/sfx2/source/dialog/splitwin.cxx @@ -0,0 +1,1155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifdef __sun +#include <ctime> +#endif + +#include <unotools/viewoptions.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <vcl/dialoghelper.hxx> +#include <vcl/event.hxx> +#include <vcl/timer.hxx> +#include <vcl/svapp.hxx> + +#include <splitwin.hxx> +#include <workwin.hxx> +#include <sfx2/dockwin.hxx> +#include <o3tl/string_view.hxx> + +#include <memory> +#include <vector> +#include <utility> + +using namespace ::com::sun::star::uno; + +#define VERSION 1 +#define nPixel 30L +constexpr OUStringLiteral USERITEM_NAME = u"UserItem"; + +namespace { + // helper class to deactivate UpdateMode, if needed, for the life time of an instance + class DeactivateUpdateMode + { + public: + explicit DeactivateUpdateMode( SfxSplitWindow& rSplitWindow ) + : mrSplitWindow( rSplitWindow ) + , mbUpdateMode( rSplitWindow.IsUpdateMode() ) + { + if ( mbUpdateMode ) + { + mrSplitWindow.SetUpdateMode( false ); + } + } + + ~DeactivateUpdateMode() + { + if ( mbUpdateMode ) + { + mrSplitWindow.SetUpdateMode( true ); + } + } + + private: + SfxSplitWindow& mrSplitWindow; + const bool mbUpdateMode; + }; +} + +class SfxEmptySplitWin_Impl : public SplitWindow +{ +/* [Description] + + The SfxEmptySplitWin_Impldow is an empty SplitWindow, that replaces the + SfxSplitWindow AutoHide mode. It only serves as a placeholder to receive + mouse moves and if possible blend in the true SplitWindow display. +*/ +friend class SfxSplitWindow; + + VclPtr<SfxSplitWindow> pOwner; + bool bFadeIn; + bool bAutoHide; + bool bSplit; + bool bEndAutoHide; + Timer aTimer; + Point aLastPos; + sal_uInt16 nState; + +public: + explicit SfxEmptySplitWin_Impl( SfxSplitWindow *pParent ) + : SplitWindow( pParent->GetParent(), WinBits( WB_BORDER | WB_3DLOOK ) ) + , pOwner( pParent ) + , bFadeIn( false ) + , bAutoHide( false ) + , bSplit( false ) + , bEndAutoHide( false ) + , aTimer("sfx2 SfxEmptySplitWin_Impl aTimer") + , nState( 1 ) + { + aTimer.SetInvokeHandler( + LINK(pOwner, SfxSplitWindow, TimerHdl ) ); + aTimer.SetTimeout( 200 ); + SetAlign( pOwner->GetAlign() ); + Actualize(); + ShowFadeInHideButton(); + } + + virtual ~SfxEmptySplitWin_Impl() override + { disposeOnce(); } + virtual void dispose() override + { + aTimer.Stop(); + pOwner.clear(); + SplitWindow::dispose(); + } + + virtual void FadeIn() override; + void Actualize(); +}; + +void SfxEmptySplitWin_Impl::Actualize() +{ + Size aSize( pOwner->GetSizePixel() ); + switch ( pOwner->GetAlign() ) + { + case WindowAlign::Left: + case WindowAlign::Right: + aSize.setWidth( GetFadeInSize() ); + break; + case WindowAlign::Top: + case WindowAlign::Bottom: + aSize.setHeight( GetFadeInSize() ); + break; + } + + SetSizePixel( aSize ); +} + +void SfxEmptySplitWin_Impl::FadeIn() +{ + if (!bAutoHide ) + bAutoHide = IsFadeNoButtonMode(); + pOwner->SetFadeIn_Impl( true ); + if ( bAutoHide ) + { + // Set Timer to close; the caller has to ensure themselves that the + // Window is not closed instantly (eg by setting the focus or a modal + // mode. + aLastPos = GetPointerPosPixel(); + aTimer.Start(); + } + else + pOwner->SaveConfig_Impl(); +} + + +void SfxSplitWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( rMEvt.GetClicks() != 2 ) + SplitWindow::MouseButtonDown( rMEvt ); +} + +SfxSplitWindow::SfxSplitWindow( vcl::Window* pParent, SfxChildAlignment eAl, + SfxWorkWindow *pW, bool bWithButtons ) + +/* [Description] + + A SfxSplitWindow brings the recursive structure of the SV-SplitWindows to + the outside by simulating a table-like structure with rows and columns + (maximum recursion depth 2). Furthermore, it ensures the persistence of + the arrangement of the SfxDockingWindows. +*/ + +: SplitWindow ( pParent, WB_BORDER | WB_SIZEABLE | WB_3DLOOK | WB_HIDE ), + eAlign(eAl), + pWorkWin(pW), + bPinned(true), + pEmptyWin(nullptr), + pActive(nullptr) +{ + if (bWithButtons) + { + ShowFadeOutButton(); + } + + // Set SV-Alignment + WindowAlign eTbxAlign; + switch ( eAlign ) + { + case SfxChildAlignment::LEFT: + eTbxAlign = WindowAlign::Left; + break; + case SfxChildAlignment::RIGHT: + eTbxAlign = WindowAlign::Right; + break; + case SfxChildAlignment::TOP: + eTbxAlign = WindowAlign::Top; + break; + case SfxChildAlignment::BOTTOM: + eTbxAlign = WindowAlign::Bottom; + bPinned = true; + break; + default: + eTbxAlign = WindowAlign::Top; // some sort of default... + break; // -Wall lots not handled... + } + + SetAlign (eTbxAlign); + pEmptyWin = VclPtr<SfxEmptySplitWin_Impl>::Create( this ); + if ( bPinned ) + { + pEmptyWin->bFadeIn = true; + pEmptyWin->nState = 2; + } + + if ( bWithButtons ) + { + // Read Configuration + const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(eTbxAlign)) }; + SvtViewOptions aWinOpt( EViewType::Window, aWindowId ); + OUString aWinData; + Any aUserItem = aWinOpt.GetUserItem( USERITEM_NAME ); + OUString aTemp; + if ( aUserItem >>= aTemp ) + aWinData = aTemp; + if ( aWinData.startsWith("V") ) + { + sal_Int32 nIdx{ 0 }; + pEmptyWin->nState = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 1, ',', nIdx ))); + if ( pEmptyWin->nState & 2 ) + pEmptyWin->bFadeIn = true; + bPinned = true; // always assume pinned - floating mode not used anymore + + const sal_Int32 nCount{ o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)) }; + for ( sal_Int32 n=0; n<nCount; ++n ) + { + std::unique_ptr<SfxDock_Impl> pDock(new SfxDock_Impl); + pDock->pWin = nullptr; + pDock->bNewLine = false; + pDock->bHide = true; + pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx))); + if ( !pDock->nType ) + { + // could mean NewLine + pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx))); + if ( !pDock->nType ) + { + // Read error + break; + } + else + pDock->bNewLine = true; + } + + maDockArr.insert(maDockArr.begin() + n, std::move(pDock)); + } + } + } + else + { + bPinned = true; + pEmptyWin->bFadeIn = true; + pEmptyWin->nState = 2; + } +} + + +SfxSplitWindow::~SfxSplitWindow() +{ + disposeOnce(); +} + +void SfxSplitWindow::dispose() +{ + SaveConfig_Impl(); + + if ( pEmptyWin ) + { + // Set pOwner to NULL, otherwise try to delete pEmptyWin once more. The + // window that is just being docked is always deleted from the outside. + pEmptyWin->pOwner = nullptr; + } + pEmptyWin.disposeAndClear(); + + maDockArr.clear(); + pActive.clear(); + SplitWindow::dispose(); +} + +void SfxSplitWindow::SaveConfig_Impl() +{ + // Save configuration + OUStringBuffer aWinData; + aWinData.append('V'); + aWinData.append(static_cast<sal_Int32>(VERSION)); + aWinData.append(','); + aWinData.append(static_cast<sal_Int32>(pEmptyWin->nState)); + aWinData.append(','); + + sal_uInt16 nCount = 0; + for ( auto const & rDock: maDockArr ) + { + if ( rDock->bHide || rDock->pWin ) + nCount++; + } + + aWinData.append(static_cast<sal_Int32>(nCount)); + + for ( auto const & rDock: maDockArr ) + { + if ( !rDock->bHide && !rDock->pWin ) + continue; + if ( rDock->bNewLine ) + aWinData.append(",0"); + aWinData.append(','); + aWinData.append(static_cast<sal_Int32>(rDock->nType)); + } + + const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(GetAlign())) }; + SvtViewOptions aWinOpt( EViewType::Window, aWindowId ); + aWinOpt.SetUserItem( USERITEM_NAME, Any( aWinData.makeStringAndClear() ) ); +} + + +void SfxSplitWindow::StartSplit() +{ + tools::Long nSize = 0; + Size aSize = GetSizePixel(); + + if ( pEmptyWin ) + { + pEmptyWin->bFadeIn = true; + pEmptyWin->bSplit = true; + } + + tools::Rectangle aRect = pWorkWin->GetFreeArea( !bPinned ); + switch ( GetAlign() ) + { + case WindowAlign::Left: + case WindowAlign::Right: + nSize = aSize.Width() + aRect.GetWidth(); + break; + case WindowAlign::Top: + case WindowAlign::Bottom: + nSize = aSize.Height() + aRect.GetHeight(); + break; + } + + SetMaxSizePixel( nSize ); +} + + +void SfxSplitWindow::SplitResize() +{ + if ( bPinned ) + { + pWorkWin->ArrangeChildren_Impl(); + pWorkWin->ShowChildren_Impl(); + } + else + pWorkWin->ArrangeAutoHideWindows( this ); +} + + +void SfxSplitWindow::Split() +{ + if ( pEmptyWin ) + pEmptyWin->bSplit = false; + + SplitWindow::Split(); + + std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes; + + sal_uInt16 nCount = maDockArr.size(); + for ( sal_uInt16 n=0; n<nCount; n++ ) + { + const SfxDock_Impl& rD = *maDockArr[n]; + if ( rD.pWin ) + { + const sal_uInt16 nId = rD.nType; + const tools::Long nSize = GetItemSize( nId, SplitWindowItemFlags::Fixed ); + const tools::Long nSetSize = GetItemSize( GetSet( nId ) ); + Size aSize; + + if ( IsHorizontal() ) + { + aSize.setWidth( nSize ); + aSize.setHeight( nSetSize ); + } + else + { + aSize.setWidth( nSetSize ); + aSize.setHeight( nSize ); + } + + rD.pWin->SetItemSize_Impl( aSize ); + + aNewOrgSizes.emplace_back( nId, nSize ); + } + } + + // workaround insufficiency of <SplitWindow> regarding dock layouting: + // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window + { + DeactivateUpdateMode aDeactivateUpdateMode( *this ); + for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes) + { + SetItemSize( rNewOrgSize.first, rNewOrgSize.second ); + } + } + + SaveConfig_Impl(); +} + + +void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize) + +/* + To insert SfxDockingWindows just pass no position. The SfxSplitWindow + searches the last marked one to the passed SfxDockingWindow or appends a + new one at the end. +*/ +{ + short nLine = -1; // so that the first window cab set nline to 0 + sal_uInt16 nL; + sal_uInt16 nPos = 0; + bool bNewLine = true; + bool bSaveConfig = false; + SfxDock_Impl *pFoundDock=nullptr; + sal_uInt16 nCount = maDockArr.size(); + for ( sal_uInt16 n=0; n<nCount; n++ ) + { + SfxDock_Impl& rDock = *maDockArr[n]; + if ( rDock.bNewLine ) + { + // The window opens a new line + if ( pFoundDock ) + // But after the just inserted window + break; + + // New line + nPos = 0; + bNewLine = true; + } + + if ( rDock.pWin ) + { + // Does there exist a window now at this position + if ( bNewLine && !pFoundDock ) + { + // Not known until now in which real line it is located + GetWindowPos( rDock.pWin, nL, nPos ); + nLine = static_cast<short>(nL); + } + + if ( !pFoundDock ) + { + // The window is located before the inserted one + nPos++; + } + + // Line is opened + bNewLine = false; + if ( pFoundDock ) + break; + } + + if ( rDock.nType == pDockWin->GetType() ) + { + DBG_ASSERT( !pFoundDock && !rDock.pWin, "Window already exists!"); + pFoundDock = &rDock; + if ( !bNewLine ) + break; + else + { + // A new line has been created but no window was found there; + // continue searching for a window in this line in-order to set + // bNewLine correctly. While doing so nline or nPos are not + // to be changed! + nLine++; + } + } + } + + if ( !pFoundDock ) + { + // Not found, insert at end + pFoundDock = new SfxDock_Impl; + pFoundDock->bHide = true; + maDockArr.push_back( std::unique_ptr<SfxDock_Impl>(pFoundDock) ); + pFoundDock->nType = pDockWin->GetType(); + nLine++; + nPos = 0; + bNewLine = true; + pFoundDock->bNewLine = bNewLine; + bSaveConfig = true; + } + + pFoundDock->pWin = pDockWin; + pFoundDock->bHide = false; + InsertWindow_Impl( pFoundDock, rSize, nLine, nPos, bNewLine ); + if ( bSaveConfig ) + SaveConfig_Impl(); +} + + +void SfxSplitWindow::ReleaseWindow_Impl(SfxDockingWindow const *pDockWin, bool bSave) +{ +// The docking window is no longer stored in the internal data. + sal_uInt16 nCount = maDockArr.size(); + for ( sal_uInt16 n=0; n<nCount; n++ ) + { + const SfxDock_Impl& rDock = *maDockArr[n]; + if ( rDock.nType == pDockWin->GetType() ) + { + if ( rDock.bNewLine && n<nCount-1 ) + maDockArr[n+1]->bNewLine = true; + + // Window has a position, this we forget + maDockArr.erase(maDockArr.begin() + n); + break; + } + } + + if ( bSave ) + SaveConfig_Impl(); +} + + +void SfxSplitWindow::MoveWindow( SfxDockingWindow* pDockWin, const Size& rSize, + sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine) + +/* [Description] + + The docking window is moved within the SplitWindows. +*/ + +{ + sal_uInt16 nL, nP; + GetWindowPos( pDockWin, nL, nP ); + + if ( nLine > nL && GetItemCount( GetItemId( nL ) ) == 1 ) + { + // If the last window is removed from its line, then everything slips + // one line to the front! + nLine--; + } + RemoveWindow( pDockWin ); + InsertWindow( pDockWin, rSize, nLine, nPos, bNewLine ); +} + + +void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize, + sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine) + +/* [Description] + + The DockingWindow that is pushed on this SplitWindow and shall hold the + given position and size. +*/ +{ + ReleaseWindow_Impl( pDockWin, false ); + SfxDock_Impl *pDock = new SfxDock_Impl; + pDock->bHide = false; + pDock->nType = pDockWin->GetType(); + pDock->bNewLine = bNewLine; + pDock->pWin = pDockWin; + + DBG_ASSERT( nPos==0 || !bNewLine, "Wrong Parameter!"); + if ( bNewLine ) + nPos = 0; + + // The window must be inserted before the first window so that it has the + // same or a greater position than pDockWin. + sal_uInt16 nCount = maDockArr.size(); + sal_uInt16 nLastWindowIdx(0); + + // If no window is found, a first window is inserted + sal_uInt16 nInsertPos = 0; + for ( sal_uInt16 n=0; n<nCount; n++ ) + { + SfxDock_Impl& rD = *maDockArr[n]; + + if (rD.pWin) + { + // A docked window has been found. If no suitable window behind + // the desired insertion point s found, then insertion is done at + // the end. + nInsertPos = nCount; + nLastWindowIdx = n; + sal_uInt16 nL=0, nP=0; + GetWindowPos( rD.pWin, nL, nP ); + + if ( (nL == nLine && nP == nPos) || nL > nLine ) + { + DBG_ASSERT( nL == nLine || bNewLine || nPos > 0, "Wrong Parameter!" ); + if ( nL == nLine && nPos == 0 && !bNewLine ) + { + DBG_ASSERT(rD.bNewLine, "No new line?"); + + // The position is pushed to nPos==0 + rD.bNewLine = false; + pDock->bNewLine = true; + } + + nInsertPos = n != 0 ? nLastWindowIdx + 1 : 0; // ignore all non-windows after the last window + break; + } + } + } + if (nCount != 0 && nInsertPos == nCount && nLastWindowIdx != nCount - 1) + { + nInsertPos = nLastWindowIdx + 1; // ignore all non-windows after the last window + } + + maDockArr.insert(maDockArr.begin() + nInsertPos, std::unique_ptr<SfxDock_Impl>(pDock)); + InsertWindow_Impl( pDock, rSize, nLine, nPos, bNewLine ); + SaveConfig_Impl(); +} + + +void SfxSplitWindow::InsertWindow_Impl( SfxDock_Impl const * pDock, + const Size& rSize, + sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine) + +/* [Description] + + Adds a DockingWindow, and causes the recalculation of the size of + the SplitWindows. +*/ + +{ + SfxDockingWindow* pDockWin = pDock->pWin; + + SplitWindowItemFlags nItemBits = SplitWindowItemFlags::NONE; + + tools::Long nWinSize, nSetSize; + if ( IsHorizontal() ) + { + nWinSize = rSize.Width(); + nSetSize = rSize.Height(); + } + else + { + nSetSize = rSize.Width(); + nWinSize = rSize.Height(); + } + + std::unique_ptr<DeactivateUpdateMode> pDeactivateUpdateMode(new DeactivateUpdateMode( *this )); + + if ( bNewLine || nLine == GetItemCount() ) + { + // An existing row should not be inserted, instead a new one + // will be created + + sal_uInt16 nId = 1; + for ( sal_uInt16 n=0; n<GetItemCount(); n++ ) + { + if ( GetItemId(n) >= nId ) + nId = GetItemId(n)+1; + } + + // Create a new nLine:th line + SplitWindowItemFlags nBits = nItemBits; + if ( GetAlign() == WindowAlign::Top || GetAlign() == WindowAlign::Bottom ) + nBits |= SplitWindowItemFlags::ColSet; + InsertItem( nId, nSetSize, nLine, 0, nBits ); + } + + // Insert the window at line with the position nline. ItemWindowSize set to + // "percentage" share since the SV then does the re-sizing as expected, + // "pixel" actually only makes sense if also items with percentage or + // relative sizes are present. + nItemBits |= SplitWindowItemFlags::PercentSize; + sal_uInt16 nSet = GetItemId( nLine ); + InsertItem( pDockWin->GetType(), pDockWin, nWinSize, nPos, nSet, nItemBits ); + + // SplitWindows are once created in SFX and when inserting the first + // DockingWindows is made visible. + if ( GetItemCount() == 1 && GetItemCount( 1 ) == 1 ) + { + // The Rearranging in WorkWindow and a Show() on the SplitWindow is + // caused by SfxDockingwindow (->SfxWorkWindow::ConfigChild_Impl) + if ( !bPinned && !IsFloatingMode() ) + { + bPinned = true; + bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0; + pEmptyWin->bFadeIn = false; + SetPinned_Impl( false ); + pEmptyWin->Actualize(); + SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" ); + pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE; + // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that + // can make a different to load times because it avoids extra accessibility calcs + if ( bFadeIn ) + FadeIn(); + else + pWorkWin->ArrangeChildren_Impl(); + } + else + { + bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0; + pEmptyWin->bFadeIn = false; + pEmptyWin->Actualize(); + if ( !bPinned || !pEmptyWin->bFadeIn ) + { + SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" ); + } + else + { + SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering real Splitwindow" ); + } + pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE; + // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that + // can make a different to load times because it avoids extra accessibility calcs + if ( bFadeIn ) + FadeIn(); + else + pWorkWin->ArrangeChildren_Impl(); + } + + pWorkWin->ShowChildren_Impl(); + } + + pDeactivateUpdateMode.reset(); + + // workaround insufficiency of <SplitWindow> regarding dock layouting: + // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window + { + std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes; + // get FIXED item sizes + sal_uInt16 nCount = maDockArr.size(); + for ( sal_uInt16 n=0; n<nCount; ++n ) + { + const SfxDock_Impl& rD = *maDockArr[n]; + if ( rD.pWin ) + { + const sal_uInt16 nId = rD.nType; + const tools::Long nSize = GetItemSize( nId, SplitWindowItemFlags::Fixed ); + aNewOrgSizes.emplace_back( nId, nSize ); + } + } + // apply new item sizes + DeactivateUpdateMode aDeactivateUpdateMode( *this ); + for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes) + { + SetItemSize( rNewOrgSize.first, rNewOrgSize.second ); + } + } +} + + +void SfxSplitWindow::RemoveWindow( SfxDockingWindow const * pDockWin, bool bHide ) + +/* [Description] + + Removes a DockingWindow. If it was the last one, then the SplitWindow is + being hidden. +*/ +{ + sal_uInt16 nSet = GetSet( pDockWin->GetType() ); + + // SplitWindows are once created in SFX and is made invisible after + // removing the last DockingWindows. + if ( GetItemCount( nSet ) == 1 && GetItemCount() == 1 ) + { + // The Rearranging in WorkWindow is caused by SfxDockingwindow + Hide(); + pEmptyWin->aTimer.Stop(); + sal_uInt16 nRealState = pEmptyWin->nState; + FadeOut_Impl(); + pEmptyWin->Hide(); +#ifdef DBG_UTIL + if ( !bPinned || !pEmptyWin->bFadeIn ) + { + SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing empty Splitwindow" ); + } + else + { + SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing real Splitwindow" ); + } +#endif + pWorkWin->ReleaseChild_Impl( *GetSplitWindow() ); + pEmptyWin->nState = nRealState; + pWorkWin->ArrangeAutoHideWindows( this ); + } + + sal_uInt16 nCount = maDockArr.size(); + for ( sal_uInt16 n=0; n<nCount; n++ ) + { + SfxDock_Impl& rDock = *maDockArr[n]; + if ( rDock.nType == pDockWin->GetType() ) + { + rDock.pWin = nullptr; + rDock.bHide = bHide; + break; + } + } + + // Remove Windows, and if it was the last of the line, then also remove + // the line (line = itemset) + DeactivateUpdateMode aDeactivateUpdateMode( *this ); + + RemoveItem( pDockWin->GetType() ); + + if ( nSet && !GetItemCount( nSet ) ) + RemoveItem( nSet ); +}; + + +bool SfxSplitWindow::GetWindowPos( const SfxDockingWindow* pWindow, + sal_uInt16& rLine, sal_uInt16& rPos ) const +/* [Description] + + Returns the ID of the item sets and items for the DockingWindow in + the position passed on the old row / column-name. +*/ + +{ + sal_uInt16 nSet = GetSet ( pWindow->GetType() ); + if ( nSet == SPLITWINDOW_ITEM_NOTFOUND ) + return false; + + rPos = GetItemPos( pWindow->GetType(), nSet ); + rLine = GetItemPos( nSet ); + return true; +} + + +bool SfxSplitWindow::GetWindowPos( const Point& rTestPos, + sal_uInt16& rLine, sal_uInt16& rPos ) const +/* [Description] + + Returns the ID of the item sets and items for the DockingWindow in + the position passed on the old row / column-name. +*/ + +{ + sal_uInt16 nId = GetItemId( rTestPos ); + if ( nId == 0 ) + return false; + + sal_uInt16 nSet = GetSet ( nId ); + rPos = GetItemPos( nId, nSet ); + rLine = GetItemPos( nSet ); + return true; +} + + +sal_uInt16 SfxSplitWindow::GetLineCount() const + +/* [Description] + + Returns the number of rows = number of sub-itemsets in the root set. +*/ +{ + return GetItemCount(); +} + + +tools::Long SfxSplitWindow::GetLineSize( sal_uInt16 nLine ) const + +/* [Description] + + Returns the Row Height of nline itemset. +*/ +{ + sal_uInt16 nId = GetItemId( nLine ); + return GetItemSize( nId ); +} + + +sal_uInt16 SfxSplitWindow::GetWindowCount( sal_uInt16 nLine ) const + +/* [Description] + + Returns the total number of windows +*/ +{ + sal_uInt16 nId = GetItemId( nLine ); + return GetItemCount( nId ); +} + + +sal_uInt16 SfxSplitWindow::GetWindowCount() const + +/* [Description] + + Returns the total number of windows +*/ +{ + return GetItemCount(); +} + + +IMPL_LINK( SfxSplitWindow, TimerHdl, Timer*, pTimer, void) +{ + if ( pTimer ) + pTimer->Stop(); + + if ( CursorIsOverRect() || !pTimer ) + { + // If the cursor is within the window, display the SplitWindow and set + // up the timer for close + pEmptyWin->bAutoHide = true; + if ( !IsVisible() ) + pEmptyWin->FadeIn(); + + pEmptyWin->aLastPos = GetPointerPosPixel(); + pEmptyWin->aTimer.Start(); + } + else if ( pEmptyWin->bAutoHide ) + { + if ( GetPointerPosPixel() != pEmptyWin->aLastPos ) + { + // The mouse has moved within the running time of the timer, thus + // do nothing + pEmptyWin->aLastPos = GetPointerPosPixel(); + pEmptyWin->aTimer.Start(); + return; + } + + // Especially for TF_AUTOSHOW_ON_MOUSEMOVE : + // If the window is not visible, there is nothing to do + // (user has simply moved the mouse over pEmptyWin) + if ( IsVisible() ) + { + pEmptyWin->bEndAutoHide = false; + if ( !Application::IsInModalMode() && + !vcl::IsInPopupMenuExecute() && + !pEmptyWin->bSplit && !HasChildPathFocus( true ) ) + { + // While a modal dialog or a popup menu is open or while the + // Splitting is done, in any case, do not close. Even as long + // as one of the Children has the focus, the window remains + // open. + pEmptyWin->bEndAutoHide = true; + } + + if ( pEmptyWin->bEndAutoHide ) + { + // As far as I am concerned this can be the end of AutoShow + // But maybe some other SfxSplitWindow will remain open, + // then all others remain open too. + if ( !pWorkWin->IsAutoHideMode( this ) ) + { + FadeOut_Impl(); + pWorkWin->ArrangeAutoHideWindows( this ); + } + else + { + pEmptyWin->aLastPos = GetPointerPosPixel(); + pEmptyWin->aTimer.Start(); + } + } + else + { + pEmptyWin->aLastPos = GetPointerPosPixel(); + pEmptyWin->aTimer.Start(); + } + } + } +} + + +bool SfxSplitWindow::CursorIsOverRect() const +{ + bool bVisible = IsVisible(); + + // Also, take the collapsed SplitWindow into account + Point aPos = pEmptyWin->GetParent()->OutputToScreenPixel( pEmptyWin->GetPosPixel() ); + Size aSize = pEmptyWin->GetSizePixel(); + + tools::Rectangle aRect( aPos, aSize ); + + if ( bVisible ) + { + Point aVisPos = GetPosPixel(); + Size aVisSize = GetSizePixel(); + + // Extend with +/- a few pixels, otherwise it is too nervous + aVisPos.AdjustX( -(nPixel) ); + aVisPos.AdjustY( -(nPixel) ); + aVisSize.AdjustWidth(2 * nPixel ); + aVisSize.AdjustHeight(2 * nPixel ); + + tools::Rectangle aVisRect( aVisPos, aVisSize ); + aRect = aRect.GetUnion( aVisRect ); + } + + return aRect.Contains( OutputToScreenPixel( static_cast<vcl::Window*>(const_cast<SfxSplitWindow *>(this))->GetPointerPosPixel() ) ); +} + + +SplitWindow* SfxSplitWindow::GetSplitWindow() +{ + if ( !bPinned || !pEmptyWin->bFadeIn ) + return pEmptyWin; + return this; +} + + +bool SfxSplitWindow::IsFadeIn() const +{ + return pEmptyWin->bFadeIn; +} + +bool SfxSplitWindow::IsAutoHide( bool bSelf ) const +{ + return bSelf ? pEmptyWin->bAutoHide && !pEmptyWin->bEndAutoHide : pEmptyWin->bAutoHide; +} + + +void SfxSplitWindow::SetPinned_Impl( bool bOn ) +{ + if ( bPinned == bOn ) + return; + + bPinned = bOn; + if ( GetItemCount() == 0 ) + return; + + if ( !bOn ) + { + pEmptyWin->nState |= 1; + if ( pEmptyWin->bFadeIn ) + { + // Unregister replacement windows + SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing real Splitwindow" ); + pWorkWin->ReleaseChild_Impl( *this ); + Hide(); + pEmptyWin->Actualize(); + SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering empty Splitwindow" ); + pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE; + } + + Point aPos( GetPosPixel() ); + aPos = GetParent()->OutputToScreenPixel( aPos ); + SetFloatingPos( aPos ); + SetFloatingMode( true ); + GetFloatingWindow()->SetOutputSizePixel( GetOutputSizePixel() ); + + if ( pEmptyWin->bFadeIn ) + Show(); + } + else + { + pEmptyWin->nState &= ~1; + SetOutputSizePixel( GetFloatingWindow()->GetOutputSizePixel() ); + SetFloatingMode(false); + + if ( pEmptyWin->bFadeIn ) + { + // Unregister replacement windows + SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing empty Splitwindow" ); + pWorkWin->ReleaseChild_Impl( *pEmptyWin ); + pEmptyWin->Hide(); + SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering real Splitwindow" ); + pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE; + } + } +} + +void SfxSplitWindow::SetFadeIn_Impl( bool bOn ) +{ + if ( bOn == pEmptyWin->bFadeIn ) + return; + + if ( GetItemCount() == 0 ) + return; + + pEmptyWin->bFadeIn = bOn; + if ( bOn ) + { + pEmptyWin->nState |= 2; + if ( IsFloatingMode() ) + { + // FloatingWindow is not visible, thus display it + pWorkWin->ArrangeAutoHideWindows( this ); + Show(); + } + else + { + SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing empty Splitwindow" ); + pWorkWin->ReleaseChild_Impl( *pEmptyWin ); + pEmptyWin->Hide(); + SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering real Splitwindow" ); + pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE; + pWorkWin->ArrangeChildren_Impl(); + pWorkWin->ShowChildren_Impl(); + } + } + else + { + pEmptyWin->bAutoHide = false; + pEmptyWin->nState &= ~2; + if ( !IsFloatingMode() ) + { + // The window is not "floating", should be hidden + SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing real Splitwindow" ); + pWorkWin->ReleaseChild_Impl( *this ); + Hide(); + pEmptyWin->Actualize(); + SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering empty Splitwindow" ); + pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE; + pWorkWin->ArrangeChildren_Impl(); + pWorkWin->ShowChildren_Impl(); + pWorkWin->ArrangeAutoHideWindows( this ); + } + else + { + Hide(); + pWorkWin->ArrangeAutoHideWindows( this ); + } + } +} + +void SfxSplitWindow::FadeOut_Impl() +{ + if ( pEmptyWin->aTimer.IsActive() ) + { + pEmptyWin->bAutoHide = false; + pEmptyWin->aTimer.Stop(); + } + + SetFadeIn_Impl( false ); +} + +void SfxSplitWindow::FadeOut() +{ + FadeOut_Impl(); + SaveConfig_Impl(); +} + +void SfxSplitWindow::FadeIn() +{ + SetFadeIn_Impl( true ); +} + +void SfxSplitWindow::SetActiveWindow_Impl( SfxDockingWindow* pWin ) +{ + pActive = pWin; + pWorkWin->SetActiveChild_Impl( this ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |