/* -*- 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/. */ #include #include #include #include #include #include #include #include #include // This method positions the cursor to the position rPos. void SwNavigationMgr::GotoSwPosition(const SwPosition &rPos) { // EnterStdMode() prevents the cursor to 'block' the current // shell when it should move from the image back to the normal shell m_rMyShell.EnterStdMode(); m_rMyShell.StartAllAction(); // cursor consists of two SwPositions: Point and Mark. // Such a pair is called a PaM. SwPaM is derived from SwRing. // The Ring contains the single regions of a multi-selection. SwPaM* pPaM = m_rMyShell.GetCursor(); if(pPaM->HasMark()) pPaM->DeleteMark(); // If there was a selection, get rid of it *pPaM->GetPoint() = rPos; // Position Cursor m_rMyShell.EndAllAction(); } // Ctor for the SwNavigationMgr class // Sets the shell to the current shell // and the index of the current position to 0 SwNavigationMgr::SwNavigationMgr(SwWrtShell & rShell) : m_nCurrent(0), m_rMyShell(rShell) { } SwNavigationMgr::~SwNavigationMgr() { SolarMutexGuard g; for (auto & it : m_entries) { EndListening(it->m_aNotifier); } m_entries.clear(); } void SwNavigationMgr::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) { // our cursors may now spontaneously self-destruct: remove from // m_entries if that happens if (typeid(rHint) == typeid(sw::UnoCursorHint)) { auto it = std::find_if(m_entries.begin(), m_entries.end(), [&rBC](const sw::UnoCursorPointer& rItem) { return !rItem || &rBC == &rItem.get()->m_aNotifier; }); if (it != m_entries.end()) { EndListening(rBC); m_entries.erase(it); } } } // This method is used by the navigation shell - defined in sw/source/uibase/inc/navsh.hxx // and implemented in sw/source/uibase/shells/navsh.cxx // It is called when we want to check if the back button should be enabled or not. // The back button should be enabled only if there are some entries in the navigation history bool SwNavigationMgr::backEnabled() { return (m_nCurrent > 0); } // Similar to backEnabled() method. // The forward button should be enabled if we ever clicked back // Due to the implementation of the navigation class, this is when the // current position within the navigation history entries in not the last one // i.e. when the m_nCurrent index is not at the end of the m_entries vector bool SwNavigationMgr::forwardEnabled() { return m_nCurrent+1 < m_entries.size(); } // The goBack() method positions the cursor to the previous entry in the navigation history // If there was no history to go forward to, it adds the current position of the cursor // to the history so we could go forward to where we came from void SwNavigationMgr::goBack() { // Although the button should be disabled whenever the backEnabled() returns false, // the UI is sometimes not as responsive as we would like it to be :) // this check prevents segmentation faults and in this way the class is not relying on the UI if (backEnabled()) { /* Trying to get the current cursor */ SwPaM* pPaM = m_rMyShell.GetCursor(); if (!pPaM) { return; } // This flag will be used to manually refresh the buttons bool bForwardWasDisabled = !forwardEnabled(); // If we're going backwards in our history, but the current location is not // in the history then we need to add *here* to it so that we can "go // forward" to here again. if (bForwardWasDisabled) { // the cursor consists of two SwPositions: Point and Mark. // We are adding the current Point to the navigation history // so we could later navigate forward to it // The addEntry() method returns true iff we should decrement // the index before navigating back if (addEntry(*pPaM->GetPoint()) ) { m_nCurrent--; } } m_nCurrent--; // Position cursor to appropriate navigation history entry GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); // Refresh the buttons if (bForwardWasDisabled) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); if (!backEnabled()) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); } } // The goForward() method positions the cursor to the next entry in the navigation history void SwNavigationMgr::goForward() { // Although the button should be disabled whenever the backForward() returns false, // the UI is sometimes not as responsive as we would like it to be :) // this check prevents segmentation faults and in this way the class is not relying on the UI if (forwardEnabled()) { // This flag will be used to manually refresh the buttons bool bBackWasDisabled = !backEnabled(); // The current index is positioned at the current entry in the navigation history // We have to increment it to go to the next entry m_nCurrent++; GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); // Refresh the buttons if (bBackWasDisabled) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); if (!forwardEnabled()) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); } } // This method adds the SwPosition rPos to the navigation history // rPos is usually the current position of the cursor in the document bool SwNavigationMgr::addEntry(const SwPosition& rPos) { // Flags that will be used for refreshing the buttons bool bBackWasDisabled = !backEnabled(); bool bForwardWasEnabled = forwardEnabled(); bool bRet = false; // return value of the function. // Indicates whether the index should be decremented before // jumping back or not // The navigation history has recency with temporal ordering enhancement, // as described on http://zing.ncsl.nist.gov/hfweb/proceedings/greenberg/ // If any forward history exists, twist the tail of the // list from the current position to the end if (bForwardWasEnabled) { size_t number_ofm_entries = m_entries.size(); // To avoid calling m_entries.size() multiple times int curr = m_nCurrent; // Index from which we'll twist the tail. int n = (number_ofm_entries - curr) / 2; // Number of entries that will swap places for (int i = 0; i < n; i++) { std::swap(m_entries[curr + i], m_entries[number_ofm_entries -1 - i]); } if (*m_entries.back()->GetPoint() != rPos) { sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); StartListening(pCursor->m_aNotifier); m_entries.push_back(pCursor); } bRet = true; } else { if ( (!m_entries.empty() && *m_entries.back()->GetPoint() != rPos) || m_entries.empty() ) { sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); StartListening(pCursor->m_aNotifier); m_entries.push_back(pCursor); bRet = true; } if (m_entries.size() > 1 && *m_entries.back()->GetPoint() == rPos) bRet = true; if (m_entries.size() == 1 && *m_entries.back()->GetPoint() == rPos) bRet = false; } m_nCurrent = m_entries.size(); // Refresh buttons if (bBackWasDisabled) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); if (bForwardWasEnabled) m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); // show the Navigation toolbar css::uno::Reference< css::frame::XFrame > xFrame = m_rMyShell.GetView().GetViewFrame()->GetFrame().GetFrameInterface(); if (xFrame.is()) { css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); if (xPropSet.is()) { css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager"); aValue >>= xLayoutManager; if (xLayoutManager.is()) { const OUString sResourceURL( "private:resource/toolbar/navigationobjectbar" ); css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL); if (!xUIElement.is()) { xLayoutManager->createElement( sResourceURL ); xLayoutManager->showElement( sResourceURL ); } } } } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */