/* -*- 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "viewfunc.hxx" #include // The SetVisArea of the DocShell must not be called from InnerResizePixel. // But our adjustments must take place. static bool bProtectDocShellVisArea = false; static sal_uInt16 nPgNum = 0; bool SwView::IsDocumentBorder() { if (GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) return true; if (!m_pWrtShell) return false; return m_pWrtShell->GetViewOptions()->getBrowseMode() || SvxZoomType::PAGEWIDTH_NOBORDER == m_pWrtShell->GetViewOptions()->GetZoomType(); } static tools::Long GetLeftMargin( SwView const &rView ) { SvxZoomType eType = rView.GetWrtShell().GetViewOptions()->GetZoomType(); tools::Long lRet = rView.GetWrtShell().GetAnyCurRect(CurRectType::PagePrt).Left(); return eType == SvxZoomType::PERCENT ? lRet + DOCUMENTBORDER : eType == SvxZoomType::PAGEWIDTH || eType == SvxZoomType::PAGEWIDTH_NOBORDER ? 0 : lRet + DOCUMENTBORDER + nLeftOfst; } static void lcl_GetPos(SwView const * pView, Point& rPos, const weld::Scrollbar& rScrollbar, bool bHori, bool bBorder) { SwWrtShell &rSh = pView->GetWrtShell(); const Size aDocSz( rSh.GetDocSize() ); const tools::Long lBorder = bBorder ? DOCUMENTBORDER : DOCUMENTBORDER * 2; const tools::Long lPos = rScrollbar.adjustment_get_value() + (bBorder ? DOCUMENTBORDER : 0); tools::Long lDelta = lPos - (bHori ? rSh.VisArea().Pos().X() : rSh.VisArea().Pos().Y()); const tools::Long lSize = (bHori ? aDocSz.Width() : aDocSz.Height()) + lBorder; // Should right or below are too much space, // then they must be subtracted out of the VisArea! tools::Long nTmp = pView->GetVisArea().Right()+lDelta; if ( bHori && nTmp > lSize ) lDelta -= nTmp - lSize; nTmp = pView->GetVisArea().Bottom()+lDelta; if ( !bHori && nTmp > lSize ) lDelta -= nTmp - lSize; bHori ? rPos.AdjustX(lDelta) : rPos.AdjustY(lDelta); if ( bBorder && (bHori ? rPos.X() : rPos.Y()) < DOCUMENTBORDER ) bHori ? rPos.setX(DOCUMENTBORDER) : rPos.setY(DOCUMENTBORDER); } // Set zero ruler void SwView::InvalidateRulerPos() { static sal_uInt16 aInval[] = { SID_ATTR_PARA_LRSPACE, SID_RULER_BORDERS, SID_RULER_PAGE_POS, SID_RULER_LR_MIN_MAX, SID_ATTR_LONG_ULSPACE, SID_ATTR_LONG_LRSPACE, SID_RULER_BORDER_DISTANCE, SID_ATTR_PARA_LRSPACE_VERTICAL, SID_RULER_BORDERS_VERTICAL, SID_RULER_TEXT_RIGHT_TO_LEFT, SID_RULER_ROWS, SID_RULER_ROWS_VERTICAL, FN_STAT_PAGE, 0 }; GetViewFrame().GetBindings().Invalidate(aInval); assert(m_pHRuler && "Why is the ruler not there?"); m_pHRuler->ForceUpdate(); m_pVRuler->ForceUpdate(); } // Limits the scrolling so far that only a quarter of the // screen can be scrolled up before the end of the document. tools::Long SwView::SetHScrollMax( tools::Long lMax ) { const tools::Long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; const tools::Long lSize = GetDocSz().Width() + lBorder - m_aVisArea.GetWidth(); // At negative values the document is completely visible. // In this case, no scrolling. return std::clamp( lSize, tools::Long(0), lMax ); } tools::Long SwView::SetVScrollMax( tools::Long lMax ) { const tools::Long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; tools::Long lSize = GetDocSz().Height() + lBorder - m_aVisArea.GetHeight(); return std::clamp( lSize, tools::Long(0), lMax ); // see horizontal } Point SwView::AlignToPixel(const Point &rPt) const { return GetEditWin().PixelToLogic( GetEditWin().LogicToPixel( rPt ) ); } // Document size has changed. void SwView::DocSzChgd(const Size &rSz) { m_aDocSz = rSz; if( !m_pWrtShell || m_aVisArea.IsEmpty() ) // no shell -> no change { bDocSzUpdated = false; return; } //If text has been deleted, it may be that the VisArea points behind the visible range. tools::Rectangle aNewVisArea( m_aVisArea ); bool bModified = false; SwTwips lGreenOffset = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; SwTwips lTmp = m_aDocSz.Width() + lGreenOffset; if ( aNewVisArea.Right() >= lTmp ) { lTmp = aNewVisArea.Right() - lTmp; aNewVisArea.AdjustRight( -lTmp ); aNewVisArea.AdjustLeft( -lTmp ); bModified = true; } lTmp = m_aDocSz.Height() + lGreenOffset; if ( aNewVisArea.Bottom() >= lTmp ) { lTmp = aNewVisArea.Bottom() - lTmp; aNewVisArea.AdjustBottom( -lTmp ); aNewVisArea.AdjustTop( -lTmp ); bModified = true; } if ( bModified ) SetVisArea( aNewVisArea, false ); if ( UpdateScrollbars() && !m_bInOuterResizePixel && !m_bInInnerResizePixel && !GetViewFrame().GetFrame().IsInPlace()) OuterResizePixel( Point(), GetViewFrame().GetWindow().GetOutputSizePixel() ); } // Set VisArea newly void SwView::SetVisArea( const tools::Rectangle &rRect, bool bUpdateScrollbar ) { Size aOldSz( m_aVisArea.GetSize() ); if (comphelper::LibreOfficeKit::isActive() && m_pWrtShell) // If m_pWrtShell's visible area is the whole document, do the same here. aOldSz = m_pWrtShell->VisArea().SSize(); if( rRect == m_aVisArea ) return; const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; // No negative position, no negative size tools::Rectangle aLR = rRect; if( aLR.Top() < lMin ) { aLR.AdjustBottom(lMin - aLR.Top() ); aLR.SetTop( lMin ); } if( aLR.Left() < lMin ) { aLR.AdjustRight(lMin - aLR.Left() ); aLR.SetLeft( lMin ); } if( aLR.Right() < 0 ) aLR.SetRight( 0 ); if( aLR.Bottom() < 0 ) aLR.SetBottom( 0 ); if( aLR == m_aVisArea ) return; const Size aSize( aLR.GetSize() ); if( aSize.IsEmpty() ) return; // Before the data can be changed, call an update if necessary. This // ensures that adjacent Paints in document coordinates are converted // correctly. // As a precaution, we do this only when an action is running in the // shell, because then it is not really drawn but the rectangles will // be only marked (in document coordinates). if ( m_pWrtShell && m_pWrtShell->ActionPend() ) m_pWrtShell->GetWin()->PaintImmediately(); m_aVisArea = aLR; const bool bOuterResize = bUpdateScrollbar && UpdateScrollbars(); if ( m_pWrtShell ) { m_pWrtShell->VisPortChgd( SwRect(m_aVisArea) ); if ( aOldSz != m_pWrtShell->VisArea().SSize() && ( std::abs(aOldSz.Width() - m_pWrtShell->VisArea().Width()) > 2 || std::abs(aOldSz.Height() - m_pWrtShell->VisArea().Height()) > 2 ) ) m_pWrtShell->InvalidateLayout( false ); } if ( !bProtectDocShellVisArea ) { // If the size of VisArea is unchanged, we extend the size of the VisArea // InternalObject on. By that the transport of errors shall be avoided. tools::Rectangle aVis( m_aVisArea ); if ( aVis.GetSize() == aOldSz ) aVis.SetSize( GetDocShell()->SfxObjectShell::GetVisArea(ASPECT_CONTENT).GetSize() ); // TODO/LATER: why casting?! //GetDocShell()->SfxInPlaceObject::GetVisArea().GetSize() ); // With embedded always with modify... // TODO/LATER: why casting?! GetDocShell()->SfxObjectShell::SetVisArea( aVis ); /* if ( GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) GetDocShell()->SfxInPlaceObject::SetVisArea( aVis ); else GetDocShell()->SvEmbeddedObject::SetVisArea( aVis );*/ } SfxViewShell::VisAreaChanged(); InvalidateRulerPos(); if ( bOuterResize && !m_bInOuterResizePixel && !m_bInInnerResizePixel) OuterResizePixel( Point(), GetViewFrame().GetWindow().GetOutputSizePixel() ); } // Set Pos VisArea void SwView::SetVisArea( const Point &rPt, bool bUpdateScrollbar ) { // Align once, so brushes will be inserted correctly. // This goes wrong in the BrowseView, because the entire document may // not be visible. Since the content in frames is fitting exactly, // align is not possible (better idea?!?!) // (fix: Bild.de, 200%) It does not work completely without alignment // Let's see how far we get with half BrushSize. Point aPt = GetEditWin().LogicToPixel( rPt ); #if HAVE_FEATURE_DESKTOP const tools::Long nTmp = 8; aPt.AdjustX( -(aPt.X() % nTmp) ); aPt.AdjustY( -(aPt.Y() % nTmp) ); #endif aPt = GetEditWin().PixelToLogic( aPt ); if ( aPt == m_aVisArea.TopLeft() ) return; if (GetWrtShell().GetViewOptions()->IsShowOutlineContentVisibilityButton()) GetEditWin().GetFrameControlsManager().HideControls(FrameControlType::Outline); const tools::Long lXDiff = m_aVisArea.Left() - aPt.X(); const tools::Long lYDiff = m_aVisArea.Top() - aPt.Y(); SetVisArea( tools::Rectangle( aPt, Point( m_aVisArea.Right() - lXDiff, m_aVisArea.Bottom() - lYDiff ) ), bUpdateScrollbar); } void SwView::CheckVisArea() { if (m_pHScrollbar) m_pHScrollbar->SetAuto( m_pWrtShell->GetViewOptions()->getBrowseMode() && !GetViewFrame().GetFrame().IsInPlace() ); if ( IsDocumentBorder() ) { if ( m_aVisArea.Left() != DOCUMENTBORDER || m_aVisArea.Top() != DOCUMENTBORDER ) { tools::Rectangle aNewVisArea( m_aVisArea ); aNewVisArea.Move( DOCUMENTBORDER - m_aVisArea.Left(), DOCUMENTBORDER - m_aVisArea.Top() ); SetVisArea( aNewVisArea ); } } } /// Calculate the visible range. // OUT Point *pPt: new position of the visible area // IN Rectangle &rRect: Rectangle, which should be located // within the new visible area. // sal_uInt16 nRange optional accurate indication of the // range by which to scroll if necessary. void SwView::CalcPt( Point *pPt, const tools::Rectangle &rRect, sal_uInt16 nRangeX, sal_uInt16 nRangeY) { const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; tools::Long nYScroll = GetYScroll(); tools::Long nDesHeight = rRect.GetHeight(); tools::Long nCurHeight = m_aVisArea.GetHeight(); nYScroll = std::min(nYScroll, nCurHeight - nDesHeight); // If it is scarce, then scroll not too much. if(nDesHeight > nCurHeight) // the height is not sufficient, then nYScroll is no longer of interest { pPt->setY( rRect.Top() ); pPt->setY( std::max( lMin, SwTwips(pPt->Y()) ) ); } else if ( rRect.Top() < m_aVisArea.Top() ) // Upward shift { pPt->setY( rRect.Top() - (nRangeY != USHRT_MAX ? nRangeY : nYScroll) ); pPt->setY( std::max( lMin, SwTwips(pPt->Y()) ) ); } else if( rRect.Bottom() > m_aVisArea.Bottom() ) // Downward shift { pPt->setY( rRect.Bottom() - (m_aVisArea.GetHeight()) + ( nRangeY != USHRT_MAX ? nRangeY : nYScroll ) ); pPt->setY( SetVScrollMax( pPt->Y() ) ); } tools::Long nXScroll = GetXScroll(); if ( rRect.Right() > m_aVisArea.Right() ) // Shift right { pPt->setX( rRect.Right() - (m_aVisArea.GetWidth()) + (nRangeX != USHRT_MAX ? nRangeX : nXScroll) ); pPt->setX( SetHScrollMax( pPt->X() ) ); } else if ( rRect.Left() < m_aVisArea.Left() ) // Shift left { pPt->setX( rRect.Left() - (nRangeX != USHRT_MAX ? nRangeX : nXScroll) ); pPt->setX( std::max( ::GetLeftMargin( *this ) + nLeftOfst, pPt->X() ) ); pPt->setX( std::min( rRect.Left() - nScrollX, pPt->X() ) ); pPt->setX( std::max( tools::Long(0), pPt->X() ) ); } } // Scrolling bool SwView::IsScroll( const tools::Rectangle &rRect ) const { return m_bCenterCursor || m_bTopCursor || !m_aVisArea.Contains(rRect); } void SwView::Scroll( const tools::Rectangle &rRect, sal_uInt16 nRangeX, sal_uInt16 nRangeY ) { if ( m_aVisArea.IsEmpty() ) return; tools::Rectangle aOldVisArea( m_aVisArea ); tools::Long nDiffY = 0; weld::Window* pCareDialog = SwViewShell::GetCareDialog(GetWrtShell()); if (pCareDialog) { int x, y, width, height; tools::Rectangle aDlgRect; if (pCareDialog->get_extents_relative_to(*GetEditWin().GetFrameWeld(), x, y, width, height)) { AbsoluteScreenPixelPoint aTopLeftAbs(GetEditWin().GetSystemWindow()->OutputToAbsoluteScreenPixel(Point(x, y))); Point aTopLeft = GetEditWin().AbsoluteScreenToOutputPixel(aTopLeftAbs); aDlgRect = GetEditWin().PixelToLogic(tools::Rectangle(aTopLeft, Size(width, height))); } // Only if the dialogue is not the VisArea right or left: if ( aDlgRect.Left() < m_aVisArea.Right() && aDlgRect.Right() > m_aVisArea.Left() ) { // If we are not supposed to be centered, lying in the VisArea // and are not covered by the dialogue ... if ( !m_bCenterCursor && aOldVisArea.Contains( rRect ) && ( rRect.Left() > aDlgRect.Right() || rRect.Right() < aDlgRect.Left() || rRect.Top() > aDlgRect.Bottom() || rRect.Bottom() < aDlgRect.Top() ) ) return; // Is above or below the dialogue more space? tools::Long nTopDiff = aDlgRect.Top() - m_aVisArea.Top(); tools::Long nBottomDiff = m_aVisArea.Bottom() - aDlgRect.Bottom(); if ( nTopDiff < nBottomDiff ) { if ( nBottomDiff > 0 ) // Is there room below at all? { // then we move the upper edge and we remember this nDiffY = aDlgRect.Bottom() - m_aVisArea.Top(); m_aVisArea.AdjustTop(nDiffY ); } } else { if ( nTopDiff > 0 ) // Is there room below at all? m_aVisArea.SetBottom( aDlgRect.Top() ); // Modify the lower edge } } } //s.o. !IsScroll() if( !(m_bCenterCursor || m_bTopCursor) && m_aVisArea.Contains( rRect ) ) { m_aVisArea = aOldVisArea; return; } // If the rectangle is larger than the visible area --> // upper left corner Size aSize( rRect.GetSize() ); const Size aVisSize( m_aVisArea.GetSize() ); if( !m_aVisArea.IsEmpty() && ( aSize.Width() + GetXScroll() > aVisSize.Width() || aSize.Height()+ GetYScroll() > aVisSize.Height() )) { Point aPt( m_aVisArea.TopLeft() ); aSize.setWidth( std::min( aSize.Width(), aVisSize.Width() ) ); aSize.setHeight( std::min( aSize.Height(),aVisSize.Height()) ); CalcPt( &aPt, tools::Rectangle( rRect.TopLeft(), aSize ), static_cast< sal_uInt16 >((aVisSize.Width() - aSize.Width()) / 2), static_cast< sal_uInt16 >((aVisSize.Height()- aSize.Height())/ 2) ); if( m_bTopCursor ) { const tools::Long nBorder = IsDocumentBorder() ? DOCUMENTBORDER : 0; aPt.setY( std::min( std::max( nBorder, rRect.Top() ), m_aDocSz.Height() + nBorder - m_aVisArea.GetHeight() ) ); } aPt.AdjustY( -nDiffY ); m_aVisArea = aOldVisArea; SetVisArea( aPt ); return; } if( !m_bCenterCursor ) { Point aPt( m_aVisArea.TopLeft() ); CalcPt( &aPt, rRect, nRangeX, nRangeY ); if( m_bTopCursor ) { const tools::Long nBorder = IsDocumentBorder() ? DOCUMENTBORDER : 0; aPt.setY( std::min( std::max( nBorder, rRect.Top() ), m_aDocSz.Height() + nBorder - m_aVisArea.GetHeight() ) ); } aPt.AdjustY( -nDiffY ); m_aVisArea = aOldVisArea; SetVisArea( aPt ); return; } //Center cursor Point aPnt( m_aVisArea.TopLeft() ); // ... in Y-direction in any case aPnt.AdjustY(( rRect.Top() + rRect.Bottom() - m_aVisArea.Top() - m_aVisArea.Bottom() ) / 2 - nDiffY ); // ... in X-direction, only if the rectangle protrudes over the right or left of the VisArea. if ( rRect.Right() > m_aVisArea.Right() || rRect.Left() < m_aVisArea.Left() ) { aPnt.AdjustX(( rRect.Left() + rRect.Right() - m_aVisArea.Left() - m_aVisArea.Right() ) / 2 ); aPnt.setX( SetHScrollMax( aPnt.X() ) ); const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; aPnt.setX( std::max( (GetLeftMargin( *this ) - lMin) + nLeftOfst, aPnt.X() ) ); } m_aVisArea = aOldVisArea; if (pCareDialog) { // If we want to avoid only a dialogue, we do // not want to go beyond the end of the document. aPnt.setY( SetVScrollMax( aPnt.Y() ) ); } SetVisArea( aPnt ); } /// Scroll page by page // Returns the value by which to be scrolled with PageUp / Down bool SwView::GetPageScrollUpOffset( SwTwips &rOff ) const { // in the LOK case, force the value set by the API if (comphelper::LibreOfficeKit::isActive() && m_nLOKPageUpDownOffset > 0) { rOff = -m_nLOKPageUpDownOffset; return true; } if ( !m_aVisArea.Top() || !m_aVisArea.GetHeight() ) return false; tools::Long nYScrl = GetYScroll() / 2; rOff = -(m_aVisArea.GetHeight() - nYScrl); // Do not scroll before the beginning of the document. if( m_aVisArea.Top() - rOff < 0 ) rOff = rOff - m_aVisArea.Top(); else if( GetWrtShell().GetCharRect().Top() < (m_aVisArea.Top() + nYScrl)) rOff += nYScrl; return true; } bool SwView::GetPageScrollDownOffset( SwTwips &rOff ) const { // in the LOK case, force the value set by the API if (comphelper::LibreOfficeKit::isActive() && m_nLOKPageUpDownOffset > 0) { rOff = m_nLOKPageUpDownOffset; return true; } if ( !m_aVisArea.GetHeight() || (m_aVisArea.GetHeight() > m_aDocSz.Height()) ) return false; tools::Long nYScrl = GetYScroll() / 2; rOff = m_aVisArea.GetHeight() - nYScrl; // Do not scroll past the end of the document. if ( m_aVisArea.Top() + rOff > m_aDocSz.Height() ) rOff = m_aDocSz.Height() - m_aVisArea.Bottom(); else if( GetWrtShell().GetCharRect().Bottom() > ( m_aVisArea.Bottom() - nYScrl )) rOff -= nYScrl; return rOff > 0; } // Scroll page by page bool SwView::PageUp() { if (!m_aVisArea.GetHeight()) return false; Point aPos(m_aVisArea.TopLeft()); aPos.AdjustY( -(m_aVisArea.GetHeight() - (GetYScroll() / 2)) ); aPos.setY( std::max(tools::Long(0), aPos.Y()) ); SetVisArea( aPos ); return true; } bool SwView::PageDown() { if ( !m_aVisArea.GetHeight() ) return false; Point aPos( m_aVisArea.TopLeft() ); aPos.AdjustY(m_aVisArea.GetHeight() - (GetYScroll() / 2) ); aPos.setY( SetVScrollMax( aPos.Y() ) ); SetVisArea( aPos ); return true; } void SwView::PhyPageUp() { // Check for the currently visible page, do not format sal_uInt16 nActPage = m_pWrtShell->GetNextPrevPageNum( false ); if( USHRT_MAX != nActPage ) { const Point aPt( m_aVisArea.Left(), m_pWrtShell->GetPagePos( nActPage ).Y() ); Point aAlPt( AlignToPixel( aPt ) ); // If there is a difference, has been truncated --> then add one pixel, // so that no residue of the previous page is visible. if( aPt.Y() != aAlPt.Y() ) aAlPt.AdjustY(3 * GetEditWin().PixelToLogic( Size( 0, 1 ) ).Height() ); SetVisArea( aAlPt ); } } void SwView::PhyPageDown() { // Check for the currently visible page, do not format sal_uInt16 nActPage = m_pWrtShell->GetNextPrevPageNum(); // If the last page of the document is visible, do nothing. if( USHRT_MAX != nActPage ) { const Point aPt( m_aVisArea.Left(), m_pWrtShell->GetPagePos( nActPage ).Y() ); Point aAlPt( AlignToPixel( aPt ) ); // If there is a difference, has been truncated --> then add one pixel, // so that no residue of the previous page is visible. if( aPt.Y() != aAlPt.Y() ) aAlPt.AdjustY(3 * GetEditWin().PixelToLogic( Size( 0, 1 ) ).Height() ); SetVisArea( aAlPt ); } } bool SwView::PageUpCursor( bool bSelect ) { if ( !bSelect ) { const FrameTypeFlags eType = m_pWrtShell->GetFrameType(nullptr,true); if ( eType & FrameTypeFlags::FOOTNOTE ) { m_pWrtShell->MoveCursor(); m_pWrtShell->GotoFootnoteAnchor(); m_pWrtShell->Right(SwCursorSkipMode::Chars, false, 1, false ); return true; } } SwTwips lOff = 0; if ( GetPageScrollUpOffset( lOff ) && (m_pWrtShell->IsCursorReadonly() || !m_pWrtShell->PageCursor( lOff, bSelect )) && PageUp() ) { m_pWrtShell->ResetCursorStack(); return true; } return false; } bool SwView::PageDownCursor(bool bSelect) { SwTwips lOff = 0; if ( GetPageScrollDownOffset( lOff ) && (m_pWrtShell->IsCursorReadonly() || !m_pWrtShell->PageCursor( lOff, bSelect )) && PageDown() ) { m_pWrtShell->ResetCursorStack(); return true; } return false; } static void HideQuickHelp(vcl::Window* pParent) { Help::ShowQuickHelp(pParent, {}, {}); } // Handler of the scrollbars IMPL_LINK(SwView, VertScrollHdl, weld::Scrollbar&, rScrollbar, void) { if ( GetWrtShell().ActionPend() ) return; if (rScrollbar.get_scroll_type() == ScrollType::Drag) m_pWrtShell->EnableSmooth( false ); bool bHidePending = nPgNum != 0; nPgNum = 0; // avoid flicker from hiding then showing help window again EndScrollHdl(rScrollbar, false); if (!m_pWrtShell->GetViewOptions()->getBrowseMode() && rScrollbar.get_scroll_type() == ScrollType::Drag) { if ( !m_bWheelScrollInProgress && Help::IsQuickHelpEnabled() && m_pWrtShell->GetViewOptions()->IsShowScrollBarTips()) { Point aPos( m_aVisArea.TopLeft() ); lcl_GetPos(this, aPos, rScrollbar, false, IsDocumentBorder()); sal_uInt16 nPhNum = 1; sal_uInt16 nVirtNum = 1; OUString sDisplay; if(m_pWrtShell->GetPageNumber( aPos.Y(), false, nPhNum, nVirtNum, sDisplay )) { //QuickHelp: if( m_pWrtShell->GetPageCnt() > 1 ) { tools::Rectangle aRect; aRect.SetLeft( m_pVScrollbar->GetParent()->OutputToScreenPixel( m_pVScrollbar->GetPosPixel() ).X() -8 ); aRect.SetTop( m_pVScrollbar->OutputToScreenPixel( m_pVScrollbar->GetPointerPosPixel() ).Y() ); aRect.SetRight( aRect.Left() ); aRect.SetBottom( aRect.Top() ); OUString sPageStr( GetPageStr( nPhNum, nVirtNum, sDisplay )); SwContentAtPos aCnt(IsAttrAtPos::Outline | IsAttrAtPos::AllowContaining); bool bSuccess = m_pWrtShell->GetContentAtPos(aPos, aCnt); if (bSuccess && !aCnt.sStr.isEmpty()) { sal_Int32 nChunkLen = std::min(aCnt.sStr.getLength(), 80); std::u16string_view sChunk = aCnt.sStr.subView(0, nChunkLen); sPageStr = sPageStr + " - " + sChunk; sPageStr = sPageStr.replace('\t', ' ').replace(0x0a, ' '); } Help::ShowQuickHelp(m_pVScrollbar, aRect, sPageStr, QuickHelpFlags::Right|QuickHelpFlags::VCenter); bHidePending = false; nPgNum = nPhNum; } } } } if (bHidePending) HideQuickHelp(m_pVScrollbar); if (rScrollbar.get_scroll_type() == ScrollType::Drag) m_pWrtShell->EnableSmooth( true ); } // Handler of the scrollbars void SwView::EndScrollHdl(weld::Scrollbar& rScrollbar, bool bHorizontal) { if ( GetWrtShell().ActionPend() ) return; if(nPgNum) { nPgNum = 0; HideQuickHelp(bHorizontal ? m_pHScrollbar : m_pVScrollbar); } Point aPos( m_aVisArea.TopLeft() ); bool bBorder = IsDocumentBorder(); lcl_GetPos(this, aPos, rScrollbar, bHorizontal, bBorder); if ( bBorder && aPos == m_aVisArea.TopLeft() ) UpdateScrollbars(); else SetVisArea( aPos, false ); GetViewFrame().GetBindings().Update(FN_STAT_PAGE); } IMPL_LINK(SwView, HoriScrollHdl, weld::Scrollbar&, rScrollBar, void) { EndScrollHdl(rScrollBar, true); } // Calculates the size of the m_aVisArea in dependency of the size of // EditWin on the screen. void SwView::CalcVisArea( const Size &rOutPixel ) { Point aTopLeft; tools::Rectangle aRect( aTopLeft, rOutPixel ); aRect = GetEditWin().PixelToLogic(aRect); // The shifts to the right and/or below can now be incorrect // (e.g. change zoom level, change view size). const tools::Long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER*2; if ( aRect.Left() ) { const tools::Long lWidth = GetWrtShell().GetDocSize().Width() + lBorder; if ( aRect.Right() > lWidth ) { tools::Long lDelta = aRect.Right() - lWidth; aRect.AdjustLeft( -lDelta ); aRect.AdjustRight( -lDelta ); } } if ( aRect.Top() ) { const tools::Long lHeight = GetWrtShell().GetDocSize().Height() + lBorder; if ( aRect.Bottom() > lHeight ) { tools::Long lDelta = aRect.Bottom() - lHeight; aRect.AdjustTop( -lDelta ); aRect.AdjustBottom( -lDelta ); } } SetVisArea( aRect ); GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM ); GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); // for snapping points } // Rearrange control elements void SwView::CalcAndSetBorderPixel( SvBorder &rToFill ) { bool bRightVRuler = m_pWrtShell->GetViewOptions()->IsVRulerRight(); if ( m_pVRuler->IsVisible() ) { tools::Long nWidth = m_pVRuler->GetSizePixel().Width(); if(bRightVRuler) rToFill.Right() = nWidth; else rToFill.Left() = nWidth; } OSL_ENSURE(m_pHRuler, "Why is the ruler not present?"); if ( m_pHRuler->IsVisible() ) rToFill.Top() = m_pHRuler->GetSizePixel().Height(); const StyleSettings &rSet = GetEditWin().GetSettings().GetStyleSettings(); const tools::Long nTmp = rSet.GetScrollBarSize(); if( m_pVScrollbar->IsScrollbarVisible(true) ) { if(bRightVRuler) rToFill.Left() = nTmp; else rToFill.Right() = nTmp; } if ( m_pHScrollbar->IsScrollbarVisible(true) ) rToFill.Bottom() = nTmp; SetBorderPixel( rToFill ); } void ViewResizePixel( const vcl::RenderContext &rRef, const Point &rOfst, const Size &rSize, const Size &rEditSz, SwScrollbar& rVScrollbar, SwScrollbar& rHScrollbar, SvxRuler* pVRuler, SvxRuler* pHRuler, bool bVRulerRight ) { // ViewResizePixel is also used by Preview!!! const bool bHRuler = pHRuler && pHRuler->IsVisible(); const tools::Long nHLinSzHeight = bHRuler ? pHRuler->GetSizePixel().Height() : 0; const bool bVRuler = pVRuler && pVRuler->IsVisible(); const tools::Long nVLinSzWidth = bVRuler ? pVRuler->GetSizePixel().Width() : 0; const tools::Long nScrollBarSize = rRef.GetSettings().GetStyleSettings().GetScrollBarSize(); const tools::Long nHBSzHeight = rHScrollbar.IsScrollbarVisible(true) ? nScrollBarSize : 0; const tools::Long nVBSzWidth = rVScrollbar.IsScrollbarVisible(true) ? nScrollBarSize : 0; if(pVRuler) { WinBits nStyle = pVRuler->GetStyle()&~WB_RIGHT_ALIGNED; Point aPos( rOfst.X(), rOfst.Y()+nHLinSzHeight ); if(bVRulerRight) { aPos.AdjustX(rSize.Width() - nVLinSzWidth ); nStyle |= WB_RIGHT_ALIGNED; } Size aSize( nVLinSzWidth, rEditSz.Height() ); if(!aSize.Width()) aSize.setWidth( pVRuler->GetSizePixel().Width() ); pVRuler->SetStyle(nStyle); pVRuler->SetPosSizePixel( aPos, aSize ); if(!pVRuler->IsVisible()) pVRuler->Resize(); } // Ruler needs a resize, otherwise it will not work in the invisible condition if(pHRuler) { Size aSize( rSize.Width(), nHLinSzHeight ); if ( nVBSzWidth && !bVRulerRight) aSize.AdjustWidth( -nVBSzWidth ); if(!aSize.Height()) aSize.setHeight( pHRuler->GetSizePixel().Height() ); pHRuler->SetPosSizePixel( rOfst, aSize ); // VCL calls no resize on invisible windows // but that is not a good idea for the ruler if(!pHRuler->IsVisible()) pHRuler->Resize(); } // Arrange scrollbars and SizeBox Point aScrollFillPos; { Point aPos( rOfst.X(), rOfst.Y()+rSize.Height()-nHBSzHeight ); if(bVRulerRight) { aPos.AdjustX(nVBSzWidth ); } Size aSize( rSize.Width(), nHBSzHeight ); if ( nVBSzWidth ) aSize.AdjustWidth( -nVBSzWidth ); rHScrollbar.SetPosSizePixel( aPos, aSize ); aScrollFillPos.setY( aPos.Y() ); } { Point aPos( rOfst.X()+rSize.Width()-nVBSzWidth, rOfst.Y() ); Size aSize( nVBSzWidth, rSize.Height() ); if(bVRulerRight) { aPos.setX( rOfst.X() ); if(bHRuler) { aPos.AdjustY(nHLinSzHeight ); aSize.AdjustHeight( -nHLinSzHeight ); } } if ( nHBSzHeight ) aSize.AdjustHeight( -nHBSzHeight ); rVScrollbar.SetPosSizePixel( aPos, aSize ); aPos.AdjustY(aSize.Height() ); aScrollFillPos.setX( aPos.X() ); } } void SwView::ShowAtResize() { m_bShowAtResize = false; if ( m_pWrtShell->GetViewOptions()->IsViewHRuler() ) m_pHRuler->Show(); } void SwView::InnerResizePixel( const Point &rOfst, const Size &rSize, bool ) { Size aObjSize = GetObjectShell()->GetVisArea().GetSize(); if ( !aObjSize.IsEmpty() ) { SvBorder aBorder( GetBorderPixel() ); Size aSize( rSize ); aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); Size aObjSizePixel = GetWindow()->LogicToPixel(aObjSize, MapMode(MapUnit::MapTwip)); SfxViewShell::SetZoomFactor( Fraction( aSize.Width(), aObjSizePixel.Width() ), Fraction( aSize.Height(), aObjSizePixel.Height() ) ); } m_bInInnerResizePixel = true; const bool bHScrollVisible = m_pHScrollbar->IsScrollbarVisible(true); const bool bVScrollVisible = m_pVScrollbar->IsScrollbarVisible(true); bool bRepeat = false; do { Size aSz( rSize ); SvBorder aBorder; CalcAndSetBorderPixel( aBorder ); if ( GetViewFrame().GetFrame().IsInPlace() ) { Size aViewSize( aSz ); Point aViewPos( rOfst ); aViewSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); aViewSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); aViewPos.AdjustX(aBorder.Left() ); aViewPos.AdjustY(aBorder.Top() ); GetEditWin().SetPosSizePixel( aViewPos, aViewSize ); } else { aSz.AdjustHeight(aBorder.Top() + aBorder.Bottom() ); aSz.AdjustWidth(aBorder.Left() + aBorder.Right() ); } Size aEditSz( GetEditWin().GetOutputSizePixel() ); ViewResizePixel( *GetEditWin().GetOutDev(), rOfst, aSz, aEditSz, *m_pVScrollbar, *m_pHScrollbar, m_pVRuler, m_pHRuler, m_pWrtShell->GetViewOptions()->IsVRulerRight()); if ( m_bShowAtResize ) ShowAtResize(); if( m_pHRuler->IsVisible() || m_pVRuler->IsVisible() ) { const Fraction& rFrac = GetEditWin().GetMapMode().GetScaleX(); tools::Long nZoom = 100; if (rFrac.IsValid()) nZoom = tools::Long(rFrac * 100); const Fraction aFrac( nZoom, 100 ); m_pVRuler->SetZoom( aFrac ); m_pHRuler->SetZoom( aFrac ); InvalidateRulerPos(); // Invalidate content. } // Reset the cursor stack because the cursor positions for PageUp/Down // no longer fit the currently visible area. m_pWrtShell->ResetCursorStack(); // EditWin never set! // Set VisArea, but do not call the SetVisArea of the Docshell there! bProtectDocShellVisArea = true; CalcVisArea( aEditSz ); // Visibility changes of the automatic horizontal scrollbar // require to repeat the ViewResizePixel() call - but only once! if(bRepeat) bRepeat = false; else if(bHScrollVisible != m_pHScrollbar->IsScrollbarVisible(true) || bVScrollVisible != m_pVScrollbar->IsScrollbarVisible(true)) bRepeat = true; }while( bRepeat ); bProtectDocShellVisArea = false; m_bInInnerResizePixel = false; } void SwView::OuterResizePixel( const Point &rOfst, const Size &rSize ) { // #i16909# return, if no size (caused by minimize window). if ( m_bInOuterResizePixel || ( !rSize.Width() && !rSize.Height() ) ) return; m_bInOuterResizePixel = true; // Determine whether scroll bars may be displayed. bool bShowH = true, bShowV = true, bAuto = true, bHAuto = true; const SwViewOption *pVOpt = m_pWrtShell->GetViewOptions(); if ( !pVOpt->IsReadonly() || pVOpt->IsStarOneSetting() ) { bShowH = pVOpt->IsViewHScrollBar(); bShowV = pVOpt->IsViewVScrollBar(); } if (!m_bHScrollbarEnabled) { bHAuto = bShowH = false; } if (!m_bVScrollbarEnabled) { bAuto = bShowV = false; } SwDocShell* pDocSh = GetDocShell(); bool bIsPreview = pDocSh->IsPreview(); if( bIsPreview ) { bShowH = bShowV = bHAuto = bAuto = false; } if(m_pHScrollbar->IsScrollbarVisible(false) != bShowH && !bHAuto) ShowHScrollbar(bShowH); m_pHScrollbar->SetAuto( bHAuto ); if(m_pVScrollbar->IsScrollbarVisible(false) != bShowV && !bAuto) ShowVScrollbar(bShowV); m_pVScrollbar->SetAuto(bAuto); CurrShell aCurr( m_pWrtShell.get() ); bool bRepeat = false; tools::Long nCnt = 0; bool bUnLockView = !m_pWrtShell->IsViewLocked(); m_pWrtShell->LockView( true ); m_pWrtShell->LockPaint(LockPaintReason::OuterResize); do { ++nCnt; const bool bScroll1 = m_pVScrollbar->IsScrollbarVisible(true); const bool bScroll2 = m_pHScrollbar->IsScrollbarVisible(true); SvBorder aBorder; CalcAndSetBorderPixel( aBorder ); const Size aEditSz( GetEditWin().GetOutputSizePixel() ); ViewResizePixel( *GetEditWin().GetOutDev(), rOfst, rSize, aEditSz, *m_pVScrollbar, *m_pHScrollbar, m_pVRuler, m_pHRuler, m_pWrtShell->GetViewOptions()->IsVRulerRight() ); if ( m_bShowAtResize ) ShowAtResize(); if( m_pHRuler->IsVisible() || m_pVRuler->IsVisible() ) InvalidateRulerPos(); // Invalidate content. // Reset the cursor stack because the cursor positions for PageUp/Down // no longer fit the currently visible area. m_pWrtShell->ResetCursorStack(); OSL_ENSURE( !GetEditWin().IsVisible() || !aEditSz.IsEmpty() || !m_aVisArea.IsEmpty(), "Small world, isn't it?" ); // Never set EditWin! // Of course the VisArea must also be set. // Now is the right time to re-calculate the zoom if it is not a simple factor. m_pWrtShell->StartAction(); CalcVisArea( aEditSz ); //Thus also in the outplace editing the page width will be adjusted immediately. //TODO/LATER: is that still necessary?! /* if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) pDocSh->SetVisArea( pDocSh->SfxInPlaceObject::GetVisArea() );*/ if ( m_pWrtShell->GetViewOptions()->GetZoomType() != SvxZoomType::PERCENT && !m_pWrtShell->GetViewOptions()->getBrowseMode() ) SetZoom_( aEditSz, m_pWrtShell->GetViewOptions()->GetZoomType(), 100, true ); m_pWrtShell->EndAction(); bRepeat = bScroll1 != m_pVScrollbar->IsScrollbarVisible(true); if ( !bRepeat ) bRepeat = bScroll2 != m_pHScrollbar->IsScrollbarVisible(true); // Do no infinite loops. // If possible stop when the (auto-) scroll bars are visible. if ( bRepeat && ( nCnt > 10 || ( nCnt > 3 && bHAuto && bAuto ) ) ) { bRepeat = false; } } while ( bRepeat ); m_pWrtShell->UnlockPaint(); if( bUnLockView ) m_pWrtShell->LockView( false ); m_bInOuterResizePixel = false; if ( m_pPostItMgr ) { m_pPostItMgr->CalcRects(); m_pPostItMgr->LayoutPostIts(); } } void SwView::SetZoomFactor( const Fraction &rX, const Fraction &rY ) { const Fraction &rFrac = rX < rY ? rX : rY; SetZoom( SvxZoomType::PERCENT, static_cast(tools::Long(rFrac * Fraction( 100, 1 ))) ); // To minimize rounding errors we also adjust the odd values // of the base class if necessary. SfxViewShell::SetZoomFactor( rX, rY ); } bool SwView::UpdateScrollbars() { bool bRet = false; if ( !m_aVisArea.IsEmpty() ) { const bool bBorder = IsDocumentBorder(); tools::Rectangle aTmpRect( m_aVisArea ); if ( bBorder ) { Point aPt( DOCUMENTBORDER, DOCUMENTBORDER ); aPt = AlignToPixel( aPt ); aTmpRect.Move( -aPt.X(), -aPt.Y() ); } Size aTmpSz( m_aDocSz ); const tools::Long lOfst = bBorder ? 0 : DOCUMENTBORDER * 2; aTmpSz.AdjustWidth(lOfst ); aTmpSz.AdjustHeight(lOfst ); { const bool bVScrollVisible = m_pVScrollbar->IsScrollbarVisible(true); m_pVScrollbar->DocSzChgd( aTmpSz ); m_pVScrollbar->ViewPortChgd( aTmpRect ); if ( bVScrollVisible != m_pVScrollbar->IsScrollbarVisible(true) ) bRet = true; } { const bool bHScrollVisible = m_pHScrollbar->IsScrollbarVisible(true); m_pHScrollbar->DocSzChgd( aTmpSz ); m_pHScrollbar->ViewPortChgd( aTmpRect ); if ( bHScrollVisible != m_pHScrollbar->IsScrollbarVisible(true) ) bRet = true; } } return bRet; } void SwView::Move() { if ( GetWrtShell().IsInSelect() ) GetWrtShell().EndSelect(); SfxViewShell::Move(); } bool SwView::HandleWheelCommands( const CommandEvent& rCEvt ) { bool bOk = false; const CommandWheelData* pWData = rCEvt.GetWheelData(); if (pWData && CommandWheelMode::ZOOM == pWData->GetMode()) { sal_uInt16 nFact = m_pWrtShell->GetViewOptions()->GetZoom(); if( 0L > pWData->GetDelta() ) nFact = std::max(MIN_ZOOM_PERCENT, basegfx::zoomtools::zoomOut( nFact )); else nFact = std::min(MAX_ZOOM_PERCENT, basegfx::zoomtools::zoomIn( nFact )); SetZoom( SvxZoomType::PERCENT, nFact ); bOk = true; } else { if (pWData && pWData->GetMode()==CommandWheelMode::SCROLL) { // This influences whether quick help is shown m_bWheelScrollInProgress=true; } if (pWData && (CommandWheelMode::SCROLL==pWData->GetMode()) && (COMMAND_WHEEL_PAGESCROLL == pWData->GetScrollLines())) { if (pWData->GetDelta()<0) PhyPageDown(); else PhyPageUp(); bOk = true; } else bOk = m_pEditWin->HandleScrollCommand(rCEvt, m_pHScrollbar, m_pVScrollbar); // Restore default state for case when scroll command comes from dragging scrollbar handle m_bWheelScrollInProgress=false; } return bOk; } bool SwView::HandleGestureZoomCommand(const CommandEvent& rCEvt) { const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData(); if (pData->meEventType == GestureEventZoomType::Begin) { m_fLastZoomScale = pData->mfScaleDelta; return true; } if (pData->meEventType == GestureEventZoomType::Update) { double deltaBetweenEvents = (pData->mfScaleDelta - m_fLastZoomScale) / m_fLastZoomScale; m_fLastZoomScale = pData->mfScaleDelta; // Accumulate fractional zoom to avoid small zoom changes from being ignored m_fAccumulatedZoom += deltaBetweenEvents; int nZoomChangePercent = m_fAccumulatedZoom * 100; m_fAccumulatedZoom -= nZoomChangePercent / 100.0; sal_uInt16 nFact = m_pWrtShell->GetViewOptions()->GetZoom(); nFact += nZoomChangePercent; nFact = std::clamp(nFact, MIN_ZOOM_PERCENT, MAX_ZOOM_PERCENT); SetZoom(SvxZoomType::PERCENT, nFact); return true; } return true; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */