oox/source/export/vmlexport.cxx | 7 sal/osl/unx/file.cxx | 54 +++- sc/source/filter/rtf/rtfparse.cxx | 4 sfx2/uiconfig/ui/panel.ui | 3 sw/inc/crsrsh.hxx | 4 sw/inc/ndarr.hxx | 9 sw/inc/node.hxx | 2 sw/qa/core/layout/data/floattable-wrapped-by-table-nested.docx |binary sw/qa/core/layout/tabfrm.cxx | 14 + sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt | 15 + sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt | 90 +++++++ sw/qa/extras/layout/layout3.cxx | 68 ++++- sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt | 20 + sw/qa/extras/uiwriter/data/hiddenSectionsAroundPageBreak.fodt | 21 + sw/qa/extras/uiwriter/uiwriter9.cxx | 37 +++ sw/source/core/crsr/crsrsh.cxx | 25 +- sw/source/core/crsr/pam.cxx | 5 sw/source/core/crsr/swcrsr.cxx | 4 sw/source/core/doc/docedt.cxx | 4 sw/source/core/docnode/ndsect.cxx | 4 sw/source/core/docnode/nodes.cxx | 58 +++- sw/source/core/docnode/section.cxx | 7 sw/source/core/edit/editsh.cxx | 2 sw/source/core/inc/frame.hxx | 2 sw/source/core/inc/sectfrm.hxx | 2 sw/source/core/inc/txtfrm.hxx | 2 sw/source/core/layout/calcmove.cxx | 13 - sw/source/core/layout/findfrm.cxx | 8 sw/source/core/layout/flowfrm.cxx | 119 ++++------ sw/source/core/layout/frmtool.cxx | 23 - sw/source/core/layout/ftnfrm.cxx | 10 sw/source/core/layout/pagechg.cxx | 5 sw/source/core/layout/sectfrm.cxx | 37 +++ sw/source/core/layout/tabfrm.cxx | 16 + sw/source/core/layout/trvlfrm.cxx | 25 +- sw/source/core/text/txtfrm.cxx | 19 - sw/source/ui/fldui/flddok.cxx | 2 unotools/source/ucbhelper/tempfile.cxx | 91 ++++--- vcl/inc/osx/salnsmenu.h | 10 vcl/osx/salmenu.cxx | 13 - vcl/osx/salnsmenu.mm | 80 ++++++ vcl/osx/vclnsapp.mm | 49 ---- writerfilter/source/dmapper/DomainMapper.cxx | 3 writerfilter/source/dmapper/PropertyIds.cxx | 1 writerfilter/source/dmapper/PropertyIds.hxx | 1 45 files changed, 711 insertions(+), 277 deletions(-)
New commits: commit feb680acb5dc25d6789289ce52812b11fa69e0c2 Author: Patrick Luby <[email protected]> AuthorDate: Mon Feb 19 15:11:56 2024 -0500 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#126638 dispatch key shortcut events to modal windows Some modal windows, such as the native Open and Save dialogs, return NO from -[NSWindow performKeyEquivalent:]. Fortunately, the main menu's -[NSMenu performKeyEquivalent:] is then called so we can catch and redirect any modal window's key shortcut events without triggering the modal window's "disallowed action" beep. Change-Id: Ib1fff68ab159361ceb847881e3a4da4736a33f51 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163605 Tested-by: Jenkins Reviewed-by: Patrick Luby <[email protected]> (cherry picked from commit 64ca3756416f0355b2008f39120e68ac42269784) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163618 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/vcl/inc/osx/salnsmenu.h b/vcl/inc/osx/salnsmenu.h index 696abca2fc0b..9e0f9acf68ed 100644 --- a/vcl/inc/osx/salnsmenu.h +++ b/vcl/inc/osx/salnsmenu.h @@ -35,17 +35,25 @@ class AquaSalMenuItem; { AquaSalMenu* mpMenu; } ++ (BOOL)dispatchSpecialKeyEquivalents:(NSEvent*)pEvent; - (id)initWithMenu:(AquaSalMenu*)pMenu; - (void)menuNeedsUpdate:(NSMenu*)pMenu; - (void)setSalMenu:(AquaSalMenu*)pMenu; @end -@interface SalNSMenuItem : NSMenuItem +@interface SalNSMenuItem : NSMenuItem <NSMenuItemValidation> { AquaSalMenuItem* mpMenuItem; } - (id)initWithMenuItem:(AquaSalMenuItem*)pMenuItem; - (void)menuItemTriggered:(id)aSender; +- (BOOL)validateMenuItem:(NSMenuItem*)pMenuItem; +@end + +@interface SalNSMainMenu : NSMenu +{ +} +- (BOOL)performKeyEquivalent:(NSEvent*)pEvent; @end #endif // INCLUDED_VCL_INC_OSX_SALNSMENU_H diff --git a/vcl/osx/salmenu.cxx b/vcl/osx/salmenu.cxx index b3d02587f46b..6ea16a6588ae 100644 --- a/vcl/osx/salmenu.cxx +++ b/vcl/osx/salmenu.cxx @@ -125,7 +125,9 @@ static void initAppMenu() NSMenu* pAppMenu = nil; NSMenuItem* pNewItem = nil; - NSMenu* pMainMenu = [[[NSMenu alloc] initWithTitle: @"Main Menu"] autorelease]; + // Related: tdf#126638 use NSMenu subclass to catch and redirect key + // shortcuts when a modal window is displayed + SalNSMainMenu* pMainMenu = [[[SalNSMainMenu alloc] initWithTitle: @"Main Menu"] autorelease]; pNewItem = [pMainMenu addItemWithTitle: @"Application" action: nil keyEquivalent: @""]; @@ -230,12 +232,19 @@ AquaSalMenu::AquaSalMenu( bool bMenuBar ) : { mpMenu = [[SalNSMenu alloc] initWithMenu: this]; [mpMenu setDelegate: reinterpret_cast< id<NSMenuDelegate> >(mpMenu)]; + + // Related: tdf#126638 enable the menu's "autoenabledItems" property + // Enable the menu's "autoenabledItems" property so that + // -[SalNSMenuItem validateMenuItem:] will be called before handling + // a key shortcut and the menu item can be temporarily disabled if a + // modal window is displayed. + [mpMenu setAutoenablesItems: YES]; } else { mpMenu = [NSApp mainMenu]; + [mpMenu setAutoenablesItems: NO]; } - [mpMenu setAutoenablesItems: NO]; } AquaSalMenu::~AquaSalMenu() diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm index b2df2da7e5f5..31627a35ea87 100644 --- a/vcl/osx/salnsmenu.mm +++ b/vcl/osx/salnsmenu.mm @@ -30,6 +30,53 @@ #include <osx/salnsmenu.h> @implementation SalNSMenu + ++(BOOL)dispatchSpecialKeyEquivalents: (NSEvent*)pEvent +{ + if( pEvent && [pEvent type] == NSEventTypeKeyDown ) + { + unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand)); + if( nModMask == NSEventModifierFlagCommand ) + { + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] ) + { + if( [NSApp sendAction: @selector(paste:) to: nil from: nil] ) + return YES; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] ) + { + if( [NSApp sendAction: @selector(copy:) to: nil from: nil] ) + return YES; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] ) + { + if( [NSApp sendAction: @selector(cut:) to: nil from: nil] ) + return YES; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"a"] ) + { + if( [NSApp sendAction: @selector(selectAll:) to: nil from: nil] ) + return YES; + } + else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"z"] ) + { + if( [NSApp sendAction: @selector(undo:) to: nil from: nil] ) + return YES; + } + } + else if( nModMask == (NSEventModifierFlagCommand|NSEventModifierFlagShift) ) + { + if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"Z"] ) + { + if( [NSApp sendAction: @selector(redo:) to: nil from: nil] ) + return YES; + } + } + } + + return NO; +} + -(id)initWithMenu: (AquaSalMenu*)pMenu { mpMenu = pMenu; @@ -167,6 +214,19 @@ OSL_FAIL( "menubar item without frame !" ); } } + +-(BOOL)validateMenuItem: (NSMenuItem *)pMenuItem +{ + // Related: tdf#126638 disable all menu items when displaying modal windows + // For some unknown reason, key shortcuts are dispatched to the LibreOffice + // menu items instead of the modal window so disable all LibreOffice menu + // items while a native modal dialog such as the native Open, Save, or + // Print dialog is displayed. + if (!pMenuItem || [NSApp modalWindow]) + return NO; + + return [pMenuItem isEnabled]; +} @end @implementation OOStatusItemView @@ -257,5 +317,25 @@ SAL_WNODEPRECATED_DECLARATIONS_POP } @end +@implementation SalNSMainMenu + +- (BOOL)performKeyEquivalent:(NSEvent*)pEvent +{ + BOOL bRet = [super performKeyEquivalent: pEvent]; + + // tdf#126638 dispatch key shortcut events to modal windows + // Some modal windows, such as the native Open and Save dialogs, + // return NO from -[NSWindow performKeyEquivalent:]. Fortunately, + // the main menu's -[NSMenu performKeyEquivalent:] is then called + // so we can catch and redirect any modal window's key shortcut + // events without triggering the modal window's "disallowed + // action" beep. + if( !bRet && [NSApp modalWindow] ) + bRet = [SalNSMenu dispatchSpecialKeyEquivalents: pEvent]; + + return bRet; +} + +@end /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/osx/vclnsapp.mm b/vcl/osx/vclnsapp.mm index 5daf923ce105..cd60cb0b0cec 100644 --- a/vcl/osx/vclnsapp.mm +++ b/vcl/osx/vclnsapp.mm @@ -34,6 +34,7 @@ #include <osx/salframe.h> #include <osx/salframeview.h> #include <osx/salinst.h> +#include <osx/salnsmenu.h> #include <osx/vclnsapp.h> #include <quartz/utils.h> @@ -170,44 +171,8 @@ // precondition: this ONLY works because CMD-V (paste), CMD-C (copy) and CMD-X (cut) are // NOT localized, that is the same in all locales. Should this be // different in any locale, this hack will fail. - unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand)); - if( nModMask == NSEventModifierFlagCommand ) - { - - if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"v"] ) - { - if( [NSApp sendAction: @selector(paste:) to: nil from: nil] ) - return; - } - else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"c"] ) - { - if( [NSApp sendAction: @selector(copy:) to: nil from: nil] ) - return; - } - else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"x"] ) - { - if( [NSApp sendAction: @selector(cut:) to: nil from: nil] ) - return; - } - else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"a"] ) - { - if( [NSApp sendAction: @selector(selectAll:) to: nil from: nil] ) - return; - } - else if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"z"] ) - { - if( [NSApp sendAction: @selector(undo:) to: nil from: nil] ) - return; - } - } - else if( nModMask == (NSEventModifierFlagCommand|NSEventModifierFlagShift) ) - { - if( [[pEvent charactersIgnoringModifiers] isEqualToString: @"Z"] ) - { - if( [NSApp sendAction: @selector(redo:) to: nil from: nil] ) - return; - } - } + if( [SalNSMenu dispatchSpecialKeyEquivalents:pEvent] ) + return; } } [super sendEvent: pEvent]; @@ -315,6 +280,14 @@ -(NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *) app { (void)app; + + // Related: tdf#126638 disable all menu items when displaying modal windows + // Although -[SalNSMenuItem validateMenuItem:] disables almost all menu + // items when a modal window is displayed, the standard Quit menu item + // does not get disabled so disable it here. + if ([NSApp modalWindow]) + return NSTerminateCancel; + NSApplicationTerminateReply aReply = NSTerminateNow; { SolarMutexGuard aGuard; commit fe35a9b83e7d3cc8a7a59f88a0c8d9eee742697e Author: Baole Fang <[email protected]> AuthorDate: Wed Feb 14 20:00:48 2024 -0500 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159678: Fix heading dropdown list Change-Id: I65b6e6e5484f26b27e7851a6615550f527fcfb06 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163416 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit 694b9b6422ee27da13ad455174cde50b1a673ac7) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163406 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/sw/source/ui/fldui/flddok.cxx b/sw/source/ui/fldui/flddok.cxx index 1a2857bedbdd..5cb38a38aca9 100644 --- a/sw/source/ui/fldui/flddok.cxx +++ b/sw/source/ui/fldui/flddok.cxx @@ -601,7 +601,7 @@ bool SwFieldDokPage::FillItemSet(SfxItemSet* ) } case SwFieldTypesEnum::Chapter: - aVal = OUString::number(m_xLevelED->get_active()); + aVal = m_xLevelED->get_active_text(); break; default: commit 414ba26d78f45e5d63671078bef775d32281653d Author: Miklos Vajna <[email protected]> AuthorDate: Tue Feb 20 08:06:08 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159285 sw floattable: fix loop with inner table wrapped by inner table Regression from 868140fcc1311259b9d5f666637b33d226511a53 (tdf#60558 sw floattable: allow wrap of table on the right of a floattable, 2023-12-05), the bugdoc layout looped on load. Somehow the big while() loop in SwTabFrame::MakeAll() never finishes: it always tries again but can't reach a state where all of frame area position, frame area size and frame print area is valid. Fix the problem by going back to the old behavior (floating table is wrapped by text frames, not by table frames) for the nested table case: that keeps the old tdf#60558 use-case working and fixes the new tdf#159285 use-case. At some point it would be useful to support the combination of nested floating tables and the "floating table wrapped by table" combination, but that will be a new layout feature. Change-Id: Ia3fdbd08de87e13ddef086ae1339e79a8833674d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163630 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 2da4acdbf8c5a8ba3ef51e5f5dc3439716e71a91) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163612 Reviewed-by: Michael Stahl <[email protected]> diff --git a/sw/qa/core/layout/data/floattable-wrapped-by-table-nested.docx b/sw/qa/core/layout/data/floattable-wrapped-by-table-nested.docx new file mode 100644 index 000000000000..d6950a6c8e04 Binary files /dev/null and b/sw/qa/core/layout/data/floattable-wrapped-by-table-nested.docx differ diff --git a/sw/qa/core/layout/tabfrm.cxx b/sw/qa/core/layout/tabfrm.cxx index 61b1a25109f5..e0d099c77102 100644 --- a/sw/qa/core/layout/tabfrm.cxx +++ b/sw/qa/core/layout/tabfrm.cxx @@ -221,6 +221,20 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineTableThenSplitFly) // large positive one. CPPUNIT_ASSERT_LESS(static_cast<SwTwips>(0), nInlineLeft); } + +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWrappedByTableNested) +{ + // Given a document with 3 tables, one inline toplevel and two inner ones (one inline, one + // floating): + // When laying out that document: + // Without the accompanying fix in place, this test would have failed here with a layout loop. + createSwDoc("floattable-wrapped-by-table-nested.docx"); + + // Than make sure we have 3 tables, but only one of them is floating: + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pDoc->GetTableFrameFormats()->GetFormatCount()); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDoc->GetSpzFrameFormats()->GetFormatCount()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index a04f9a14a7b3..8c49106364dc 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -3373,7 +3373,15 @@ bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper, } bool bFlyHoriOrientLeft = text::HoriOrientation::LEFT == rHori.GetHoriOrient(); - if (bSplitFly && !bFlyHoriOrientLeft) + + bool bToplevelSplitFly = false; + if (bSplitFly) + { + // Floating table wrapped by table: avoid this in the nested case. + bToplevelSplitFly = !pFly->GetAnchorFrame()->IsInTab(); + } + + if (bToplevelSplitFly && !bFlyHoriOrientLeft) { // Only shift to the right if we don't have enough space on the left. SwTwips nTabWidth = getFramePrintArea().Width(); commit 8a890e5dea2813ddc8aa10a75a9aca494f203d7b Author: Oliver Specht <[email protected]> AuthorDate: Thu Feb 15 10:27:03 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159730 add compatibility option in RTF import Set IgnoreTabsAndBlanksForLineCalculation in RTF import to improve formatting. Change-Id: If0129f748c48400f1dd882672b5779f62e685ecd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163429 Reviewed-by: Michael Stahl <[email protected]> Tested-by: Jenkins (cherry picked from commit 3b04e74503ec6d07dc4befdb756e6abdc3de4e58) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163614 diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 7e4eeda7d3d9..bbeafa942de4 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -129,6 +129,9 @@ DomainMapper::DomainMapper( const uno::Reference< uno::XComponentContext >& xCon m_pImpl->SetDocumentSettingsProperty( getPropertyName(PROP_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING), uno::Any(true)); + m_pImpl->SetDocumentSettingsProperty( + getPropertyName(PROP_TABS_AND_BLANKS_FOR_LINE_CALCULATION), + uno::Any(true)); // Don't load the default style definitions to avoid weird mix m_pImpl->SetDocumentSettingsProperty("StylesNoDefault", uno::Any(true)); diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx index b8b4efc06222..8a83ca0bb064 100644 --- a/writerfilter/source/dmapper/PropertyIds.cxx +++ b/writerfilter/source/dmapper/PropertyIds.cxx @@ -384,6 +384,7 @@ namespace { PROP_PARA_CONNECT_BORDERS, u"ParaIsConnectBorder"}, { PROP_DECORATIVE, u"Decorative"}, { PROP_PAPER_TRAY, u"PrinterPaperTray"}, + { PROP_TABS_AND_BLANKS_FOR_LINE_CALCULATION, u"IgnoreTabsAndBlanksForLineCalculation"}, }); } // end anonymous ns diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx index b39fcd24fa49..bb2fb833516c 100644 --- a/writerfilter/source/dmapper/PropertyIds.hxx +++ b/writerfilter/source/dmapper/PropertyIds.hxx @@ -383,6 +383,7 @@ enum PropertyIds ,PROP_RTL_GUTTER ,PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF ,PROP_PARA_CONNECT_BORDERS + ,PROP_TABS_AND_BLANKS_FOR_LINE_CALCULATION }; //Returns the UNO string equivalent to eId. commit 2b34eb19bf038e24cb9d32d52d0575f1b3d229c0 Author: Mike Kaganski <[email protected]> AuthorDate: Mon Feb 19 11:05:55 2024 +0600 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159769: always make sure that default temporary directory exists This basically undoes commit 4743a32b4f991c57e4b8a66adf035b1808ca793a (only need to create the folder for temp files once, 2022-05-18). To avoid the overhead in TempFileFast, it now uses a new function to get the path, which doesn't check that path exists. Change-Id: I9c910a55dc4992058a7168690561b02d682b5e56 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163576 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit 0b58f1a41bf8d58426f16e47f86d313172ad4585) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163541 Reviewed-by: Michael Stahl <[email protected]> diff --git a/unotools/source/ucbhelper/tempfile.cxx b/unotools/source/ucbhelper/tempfile.cxx index 992ff8814d7f..3003cea9ac0a 100644 --- a/unotools/source/ucbhelper/tempfile.cxx +++ b/unotools/source/ucbhelper/tempfile.cxx @@ -46,13 +46,23 @@ using namespace osl; namespace { - OUString gTempNameBase_Impl; +OUString gTempNameBase_Impl; + +OUString ensureTrailingSlash(const OUString& url) +{ + if (!url.isEmpty() && !url.endsWith("/")) + return url + "/"; + return url; } -namespace utl +OUString stripTrailingSlash(const OUString& url) { + if (url.endsWith("/")) + return url.copy(0, url.getLength() - 1); + return url; +} -static OUString getParentName( std::u16string_view aFileName ) +OUString getParentName( std::u16string_view aFileName ) { size_t lastIndex = aFileName.rfind( '/' ); OUString aParent; @@ -71,17 +81,14 @@ static OUString getParentName( std::u16string_view aFileName ) return aParent; } -static bool ensuredir( const OUString& rUnqPath ) +bool ensuredir( const OUString& rUnqPath ) { OUString aPath; if ( rUnqPath.isEmpty() ) return false; // remove trailing slash - if ( rUnqPath.endsWith("/") ) - aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 ); - else - aPath = rUnqPath; + aPath = stripTrailingSlash(rUnqPath); // HACK: create directory on a mount point with nobrowse option // returns ENOSYS in any case !! @@ -115,7 +122,24 @@ static bool ensuredir( const OUString& rUnqPath ) return bSuccess; } -static OUString ConstructTempDir_Impl( const OUString* pParent, bool bCreateParentDirs ) +const OUString& getTempNameBase_Impl() +{ + if (gTempNameBase_Impl.isEmpty()) + { + OUString ustrTempDirURL; + osl::FileBase::RC rc = osl::File::getTempDirURL(ustrTempDirURL); + if (rc == osl::FileBase::E_None) + { + gTempNameBase_Impl = ensureTrailingSlash(ustrTempDirURL); + ensuredir(gTempNameBase_Impl); + } + } + assert(gTempNameBase_Impl.isEmpty() || gTempNameBase_Impl.endsWith("/")); + DBG_ASSERT(!gTempNameBase_Impl.isEmpty(), "No TempDir!"); + return gTempNameBase_Impl; +} + +OUString ConstructTempDir_Impl( const OUString* pParent, bool bCreateParentDirs ) { OUString aName; @@ -147,29 +171,15 @@ static OUString ConstructTempDir_Impl( const OUString* pParent, bool bCreatePare if ( aName.isEmpty() ) { - if (gTempNameBase_Impl.isEmpty()) - { - OUString ustrTempDirURL; - ::osl::FileBase::RC rc = ::osl::File::getTempDirURL( - ustrTempDirURL ); - if (rc == ::osl::FileBase::E_None) - gTempNameBase_Impl = ustrTempDirURL; - ensuredir( aName ); - } // if no parent or invalid parent : use default directory - DBG_ASSERT( !gTempNameBase_Impl.isEmpty(), "No TempDir!" ); - aName = gTempNameBase_Impl; + aName = getTempNameBase_Impl(); + ensuredir(aName); // tdf#159769: always make sure it exists } // Make sure that directory ends with a separator - if( !aName.isEmpty() && !aName.endsWith("/") ) - aName += "/"; - - return aName; + return ensureTrailingSlash(aName); } -namespace { - class Tokens { public: virtual bool next(OUString *) = 0; @@ -233,12 +243,8 @@ private: sal_uInt32 m_count; }; -} - sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32; -namespace -{ class TempDirCreatedObserver : public DirectoryCreationObserver { public: @@ -248,9 +254,8 @@ namespace osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnExe ); }; }; -}; -static OUString lcl_createName( +OUString lcl_createName( std::u16string_view rLeadingChars, Tokens & tokens, std::u16string_view pExtension, const OUString* pParent, bool bDirectory, bool bKeep, bool bLock, bool bCreateParentDirs ) @@ -326,7 +331,7 @@ static OUString lcl_createName( return OUString(); } -static OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true ) +OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true ) { OUString aEyeCatcher = "lu"; #ifdef UNX @@ -351,7 +356,7 @@ static OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool b false, false); } -static OUString CreateTempNameFast() +OUString CreateTempNameFast() { OUString aEyeCatcher = "lu"; #ifdef UNX @@ -372,12 +377,16 @@ static OUString CreateTempNameFast() aEyeCatcher += aPidString; #endif - OUString aName = ConstructTempDir_Impl( /*pParent*/nullptr, /*bCreateParentDirs*/false ) + aEyeCatcher; + OUString aName = getTempNameBase_Impl() + aEyeCatcher; tools::Guid aGuid(tools::Guid::Generate); return aName + aGuid.getOUString() + ".tmp" ; } +} + +namespace utl +{ OUString CreateTempName() { @@ -538,11 +547,8 @@ OUString SetTempNameBaseDirectory( const OUString &rBaseName ) if( rBaseName.isEmpty() ) return OUString(); - OUString aUnqPath( rBaseName ); - // remove trailing slash - if ( rBaseName.endsWith("/") ) - aUnqPath = rBaseName.copy( 0, rBaseName.getLength() - 1 ); + OUString aUnqPath(stripTrailingSlash(rBaseName)); // try to create the directory bool bRet = false; @@ -558,16 +564,15 @@ OUString SetTempNameBaseDirectory( const OUString &rBaseName ) if ( bRet ) { // append own internal directory - OUString &rTempNameBase_Impl = gTempNameBase_Impl; - rTempNameBase_Impl = rBaseName + "/"; + gTempNameBase_Impl = ensureTrailingSlash(rBaseName); TempFileNamed aBase( {}, true ); if ( aBase.IsValid() ) // use it in case of success - rTempNameBase_Impl = aBase.aName; + gTempNameBase_Impl = ensureTrailingSlash(aBase.aName); // return system path of used directory - FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp ); + FileBase::getSystemPathFromFileURL(gTempNameBase_Impl, aTmp); } return aTmp; commit 0b4bf75ffca6ad9b3c359e973aa8a46d67a66625 Author: Caolán McNamara <[email protected]> AuthorDate: Mon Feb 19 09:59:45 2024 +0000 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#155961 put some spacing to the right of the "more options" toolbar the gtk overlay scrolling is all very pretty, but you can't click things underneath, and there is no discoverable way to dismiss the scrollbar to get "under" it, move this widget to the left a little to try and get it outside the hotzone for the overlay scrolling. Change-Id: Ic9d48be4a29754e9aff0f29dbe32bea762b6cc09 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163543 Tested-by: Jenkins Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sfx2/uiconfig/ui/panel.ui b/sfx2/uiconfig/ui/panel.ui index 981a2f2d6017..fb40097ee6d6 100644 --- a/sfx2/uiconfig/ui/panel.ui +++ b/sfx2/uiconfig/ui/panel.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 --> +<!-- Generated with glade 3.40.0 --> <interface domain="sfx"> <requires lib="gtk+" version="3.20"/> <object class="GtkBox" id="Panel"> @@ -64,6 +64,7 @@ <object class="GtkToolbar" id="toolbar"> <property name="can-focus">True</property> <property name="no-show-all">True</property> + <property name="margin-end">9</property> <property name="toolbar-style">icons</property> <property name="show-arrow">False</property> <property name="icon_size">2</property> commit 95e9ad355237ccc52159a5da5a0efa78af6f3062 Author: Kevin Ottens <[email protected]> AuthorDate: Fri Feb 2 15:39:36 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#55004 Fix backup copy creation for files on mounted samba shares There is an unfortunate interaction between file locking and backup creation at save time. openFilePath has logic to lock a file when opening. This goes through fcntl to set a write lock on the file. Later on, when the user wants to save changes, a backup copy might be created (very likely now since this is the defaults in the settings). To create this backup, the file is opened again for reading. Unfortunately this open call fails due to the lock (even though it is a write lock). This commit changes the behavior. osl_file_adjustLockFlags now checks if the file is on a mounted samba share. If that's the case we force the osl_File_OpenFlag_NoLock flag. No issue is then exhibited at backup creation, allowing the save to proceed properly. Change-Id: Ieab252f9f68598834e13339fc5fcea440f0a4c2f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162935 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> (cherry picked from commit 63efbc8ad8aae12b54e649c1495d1233c1a9b33f) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163549 diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx index 1bfb0c72f089..ada4f29578d9 100644 --- a/sal/osl/unx/file.cxx +++ b/sal/osl/unx/file.cxx @@ -65,6 +65,14 @@ #include <o3tl/string_view.hxx> #endif +#ifdef LINUX +#include <sys/vfs.h> +// As documented by the kernel +#define SMB_SUPER_MAGIC 0x517B +#define CIFS_SUPER_MAGIC 0xFF534D42 +#define SMB2_SUPER_MAGIC 0xFE534D42 +#endif + namespace { enum class State @@ -737,9 +745,11 @@ oslFileHandle osl::detail::createFileHandleFromFD(int fd) return static_cast<oslFileHandle>(pImpl); } -static int osl_file_adjustLockFlags(const OString& path, int flags) +static void osl_file_adjustLockFlags(const OString& path, int *flags, sal_uInt32 *uFlags) { #ifdef MACOSX + (void) uFlags; + /* * The AFP implementation of MacOS X 10.4 treats O_EXLOCK in a way * that makes it impossible for OOo to create a backup copy of the @@ -752,20 +762,50 @@ static int osl_file_adjustLockFlags(const OString& path, int flags) { if(strncmp("afpfs", s.f_fstypename, 5) == 0) { - flags &= ~O_EXLOCK; - flags |= O_SHLOCK; + *flags &= ~O_EXLOCK; + *flags |= O_SHLOCK; } else { /* Needed flags to allow opening a webdav file */ - flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK); + *flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK); + } + } +#elif defined(LINUX) + (void) flags; + + /* get filesystem info */ + struct statfs aFileStatFs; + if (statfs(path.getStr(), &aFileStatFs) < 0) + { + int e = errno; + SAL_INFO("sal.file", "statfs(" << path << "): " << UnixErrnoString(e)); + } + else + { + SAL_INFO("sal.file", "statfs(" << path << "): OK"); + + // We avoid locking if on a Linux CIFS mount otherwise this + // fill fail later on when opening the file for reading + // during backup creation at save time (even though this is a + // write lock and not a read lock). + // Fixes the following bug: + // https://bugs.documentfoundation.org/show_bug.cgi?id=55004 + switch (aFileStatFs.f_type) { + case SMB_SUPER_MAGIC: + case CIFS_SUPER_MAGIC: + case SMB2_SUPER_MAGIC: + *uFlags |= osl_File_OpenFlag_NoLock; + break; + default: + break; } } #else (void) path; + (void) flags; + (void) uFlags; #endif - - return flags; } static bool osl_file_queryLocking(sal_uInt32 uFlags) @@ -1129,7 +1169,7 @@ oslFileError openFilePath(const OString& filePath, oslFileHandle* pHandle, } else { - flags = osl_file_adjustLockFlags (filePath, flags); + osl_file_adjustLockFlags (filePath, &flags, &uFlags); } // O_EXCL can be set only when O_CREAT is set commit 5bab810080b0590926b35ab6f7e7216db1bc1c03 Author: Justin Luth <[email protected]> AuthorDate: Fri Feb 16 14:11:11 2024 -0500 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#153761 vml export: avoid corrupt docx: don't write empty r:id For the benefit of MSO, do not write r:id="", since MSO refuses to open such a document. Change-Id: I21887021c747fc9a9764befc7081e21d99e47545 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163523 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> (cherry picked from commit 5132255021aa61f8a1fa7d8de820cb3528699812) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163542 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx index 1090b0857f5c..8438befa62af 100644 --- a/oox/source/export/vmlexport.cxx +++ b/oox/source/export/vmlexport.cxx @@ -741,8 +741,11 @@ void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& Graphic aGraphic; GraphicConverter::Import(aStream, aGraphic); OUString aImageId = m_pTextExport->GetDrawingML().writeGraphicToStorage(aGraphic, false); - pAttrList->add(FSNS(XML_r, XML_id), aImageId); - imageData = true; + if (!aImageId.isEmpty()) + { + pAttrList->add(FSNS(XML_r, XML_id), aImageId); + imageData = true; + } } if (rProps.GetOpt(ESCHER_Prop_fNoFillHitTest, nValue)) commit 618fb381bce2ccc08fa3cad79fb039b4a675878f Author: Mike Kaganski <[email protected]> AuthorDate: Wed Feb 14 19:22:42 2024 +0600 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159565: make sure to handle leading hidden section correctly Change-Id: I41c7d2b6e765f03c72a968fd05e8de7047f1ce41 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163371 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163478 Reviewed-by: Michael Stahl <[email protected]> diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index bddea2b87cb6..03afe5006ad6 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -334,7 +334,7 @@ public: void ExtendedSelectAll(bool bFootnotes = true); /// If ExtendedSelectAll() was called and selection didn't change since then. ::std::optional<::std::pair<SwNode const*, ::std::vector<SwTableNode*>>> ExtendedSelectedAll() const; - enum class StartsWith { None, Table, HiddenPara }; + enum class StartsWith { None, Table, HiddenPara, HiddenSection }; /// If document body starts with a table or starts/ends with hidden paragraph. StartsWith StartsWith_(); diff --git a/sw/inc/ndarr.hxx b/sw/inc/ndarr.hxx index 7afe8d2bce46..7383c253a2e8 100644 --- a/sw/inc/ndarr.hxx +++ b/sw/inc/ndarr.hxx @@ -131,6 +131,11 @@ class SW_DLLPUBLIC SwNodes final SwNodes(SwDoc& rDoc); + // Returns start of the document section (PostIts/Inserts/Autotext/Redlines/Content), + // or of a specific fly / header / footer / footnote, where this node is, which must not + // be crossed when moving backwards + SwNodeOffset StartOfGlobalSection(const SwNode& node) const; + public: ~SwNodes(); @@ -188,8 +193,8 @@ public: SwContentNode* GoNext(SwNodeIndex *) const; SwContentNode* GoNext(SwPosition *) const; - static SwContentNode* GoPrevious(SwNodeIndex *); - static SwContentNode* GoPrevious(SwPosition *); + static SwContentNode* GoPrevious(SwNodeIndex *, bool canCrossBoundary = false); + static SwContentNode* GoPrevious(SwPosition *, bool canCrossBoundary = false); /** Go to next content-node that is not protected or hidden (Both set FALSE ==> GoNext/GoPrevious!!!). */ diff --git a/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt b/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt new file mode 100644 index 000000000000..2095c7173046 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:body> + <office:text> + <text:section text:name="Section1"> + <text:section text:name="Section2Hidden" text:display="none"> + <text:p><draw:frame text:anchor-type="paragraph" svg:x="1cm" svg:y="1cm" svg:width="1cm"> + <draw:text-box/> + </draw:frame>lorem</text:p> + </text:section> + <text:section text:name="Section3"/> + <text:section text:name="Section4"/> + <text:section text:name="Section5"> + <text:p>ipsum</text:p> + </text:section> + </text:section> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx b/sw/qa/extras/uiwriter/uiwriter9.cxx index f870f5ea480a..58c95f21d03d 100644 --- a/sw/qa/extras/uiwriter/uiwriter9.cxx +++ b/sw/qa/extras/uiwriter/uiwriter9.cxx @@ -16,6 +16,8 @@ #include <com/sun/star/text/XTextTable.hpp> #include <com/sun/star/text/XTextViewCursorSupplier.hpp> #include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + #include <comphelper/propertysequence.hxx> #include <swdtflvr.hxx> #include <o3tl/string_view.hxx> @@ -138,6 +140,25 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testHiddenSectionsAroundPageBreak) CPPUNIT_ASSERT_EQUAL(u"Landscape"_ustr, getProperty<OUString>(xCursor, "PageStyleName")); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159565) +{ + // Given a document with a hidden section in the beginning, additionally containing a frame + createSwDoc("FrameInHiddenSection.fodt"); + + dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {}); + + // Check that the selection covers the whole visible text + auto xModel(mxComponent.queryThrow<css::frame::XModel>()); + auto xSelSupplier(xModel->getCurrentController().queryThrow<css::view::XSelectionSupplier>()); + auto xSelections(xSelSupplier->getSelection().queryThrow<css::container::XIndexAccess>()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelections->getCount()); + auto xSelection(xSelections->getByIndex(0).queryThrow<css::text::XTextRange>()); + + // Without the fix, this would fail - there was no selection + CPPUNIT_ASSERT_EQUAL(u"" SAL_NEWLINE_STRING SAL_NEWLINE_STRING "ipsum"_ustr, + xSelection->getString()); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index d08e4971f556..b7f6962982b5 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -775,6 +775,8 @@ static typename SwCursorShell::StartsWith StartsWith(SwStartNode const& rStart) switch (rNode.GetNodeType()) { case SwNodeType::Section: + if (rNode.GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; continue; case SwNodeType::Table: return SwCursorShell::StartsWith::Table; @@ -799,11 +801,16 @@ static typename SwCursorShell::StartsWith EndsWith(SwStartNode const& rStart) switch (rNode.GetNodeType()) { case SwNodeType::End: - if (rNode.StartOfSectionNode()->IsTableNode()) + if (auto pStartNode = rNode.StartOfSectionNode(); pStartNode->IsTableNode()) { return SwCursorShell::StartsWith::Table; } -//TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode()); + else if (pStartNode->IsSectionNode()) + { + if (pStartNode->GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; + } + //TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode()); break; case SwNodeType::Text: if (rNode.GetTextNode()->IsHidden()) @@ -3470,7 +3477,7 @@ bool SwCursorShell::FindValidContentNode( bool bOnlyText ) GetDoc()->GetDocShell()->IsReadOnlyUI() ) return true; - if( m_pCurrentCursor->HasMark() ) + if( m_pCurrentCursor->HasMark() && !mbSelectAll ) ClearMark(); // first check for frames diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx index 8d0246bed14f..5a2f9afeada5 100644 --- a/sw/source/core/crsr/swcrsr.cxx +++ b/sw/source/core/crsr/swcrsr.cxx @@ -913,7 +913,7 @@ static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd, rPam.SetMark(); rPam.GetPoint()->Assign(rEndNd); - pCNd = SwNodes::GoPrevious( rPam.GetPoint() ); + pCNd = SwNodes::GoPrevious(rPam.GetPoint(), true); if( !pCNd ) return false; rPam.GetPoint()->AssignEndIndex(*pCNd); @@ -933,7 +933,7 @@ static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd, if( !bFirst ) { rPam.GetPoint()->Assign(rSttNd); - pCNd = SwNodes::GoPrevious( rPam.GetPoint() ); + pCNd = SwNodes::GoPrevious(rPam.GetPoint(), true); if( !pCNd ) return false; rPam.GetPoint()->AssignEndIndex(*pCNd); diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 8967833a64b1..3506dff2300b 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1336,34 +1336,68 @@ SwContentNode* SwNodes::GoNext(SwPosition *pIdx) const return static_cast<SwContentNode*>(pNd); } -SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx) +SwNodeOffset SwNodes::StartOfGlobalSection(const SwNode& node) const +{ + const SwNodeOffset pos = node.GetIndex(); + if (GetEndOfExtras().GetIndex() < pos) + // Regular ContentSection + return GetEndOfExtras().GetIndex() + SwNodeOffset(1); + if (GetEndOfAutotext().GetIndex() < pos) + // Redlines + return GetEndOfAutotext().GetIndex() + SwNodeOffset(1); + if (GetEndOfInserts().GetIndex() < pos) + { + // Flys/Headers/Footers + if (auto* p = node.FindFlyStartNode()) + return p->GetIndex(); + if (auto* p = node.FindHeaderStartNode()) + return p->GetIndex(); + if (auto* p = node.FindFooterStartNode()) + return p->GetIndex(); + return GetEndOfInserts().GetIndex() + SwNodeOffset(1); + } + if (GetEndOfPostIts().GetIndex() < pos) + { + // Footnotes + if (auto* p = node.FindFootnoteStartNode()) + return p->GetIndex(); + return GetEndOfPostIts().GetIndex() + SwNodeOffset(1); + } + return SwNodeOffset(0); +} + +SwContentNode* SwNodes::GoPrevious(SwNodeIndex* pIdx, bool canCrossBoundary) { if( !pIdx->GetIndex() ) return nullptr; SwNodeIndex aTmp( *pIdx, -1 ); + SwNodeOffset aGlobalStart( + canCrossBoundary ? SwNodeOffset(0) : aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); SwNode* pNd = nullptr; - while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() ) + while (aTmp > aGlobalStart && !(pNd = &aTmp.GetNode())->IsContentNode()) --aTmp; - if( !aTmp.GetIndex() ) + if (aTmp <= aGlobalStart) pNd = nullptr; else (*pIdx) = aTmp; return static_cast<SwContentNode*>(pNd); } -SwContentNode* SwNodes::GoPrevious(SwPosition *pIdx) +SwContentNode* SwNodes::GoPrevious(SwPosition* pIdx, bool canCrossBoundary) { if( !pIdx->GetNodeIndex() ) return nullptr; SwNodeIndex aTmp( pIdx->GetNode(), -1 ); + SwNodeOffset aGlobalStart( + canCrossBoundary ? SwNodeOffset(0) : aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); SwNode* pNd = nullptr; - while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() ) + while( aTmp > aGlobalStart && !( pNd = &aTmp.GetNode())->IsContentNode() ) --aTmp; - if( !aTmp.GetIndex() ) + if (aTmp <= aGlobalStart) pNd = nullptr; else pIdx->Assign(aTmp); @@ -2072,8 +2106,9 @@ SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx, { bool bFirst = true; SwNodeIndex aTmp( *pIdx ); + SwNodeOffset aGlobalStart(aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); const SwNode* pNd; - while( aTmp > SwNodeOffset(0) ) + while (aTmp > aGlobalStart) { pNd = & aTmp.GetNode(); if (SwNodeType::End == pNd->GetNodeType()) @@ -2129,8 +2164,9 @@ SwContentNode* SwNodes::GoPrevSection( SwPosition * pIdx, { bool bFirst = true; SwNodeIndex aTmp( pIdx->GetNode() ); + SwNodeOffset aGlobalStart(aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); const SwNode* pNd; - while( aTmp > SwNodeOffset(0) ) + while (aTmp > aGlobalStart) { pNd = & aTmp.GetNode(); if (SwNodeType::End == pNd->GetNodeType()) commit 60a253b4b3abf1bc8208a60e31573306894f0c92 Author: Mike Kaganski <[email protected]> AuthorDate: Fri Feb 9 11:56:19 2024 +0600 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#159565 prerequisite: make hidden sections have zero-height frames As mentioned in commit bb733957dd39e6f0b9d80bb59eb0177188794797 (tdf#114973 sw: enable SelectAll with hidden para at start/end, 2023-01-27), the hidden sections didn't have frames. That prevented correct handling of the case when such a frame was in the beginning of the document. This change re-implements the hidden section to use 0-height frames, like hidden paragraphs, as a pre-requisite for a follow-up change. Some layout breakages noticed while working on this are unit-tested now. This change needed to handle the case when the first section is hidden, and then goes a page break with page style. In this case, the page style must apply to the very first page of the document. Implementing this now, when the frame that defines the page style is not the first in the document, I accidentally fixed also the previously broken case when the first paragraph was hidden. Now the page style defined in the second paragraph's page break will apply correctly. This change makes hidden sections break outer section's frames. This means that when text borders are shown, there will be an artifact in the place of the hidden sections (a horizontal line breaking outer frame). I suppose it's not a problem, actually helping to see the layout better, so in line with the "show text borders" helper functionality. If this proves to be problematic, this can be handled specially in a follow-up. Change-Id: I14ebf0559b463186aba28902cd10c5cc978ba456 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163151 Tested-by: Mike Kaganski <[email protected]> Reviewed-by: Mike Kaganski <[email protected]> Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163477 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 20636648c794..bddea2b87cb6 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -267,7 +267,7 @@ private: SAL_DLLPRIVATE bool LRMargin( bool, bool bAPI = false ); SAL_DLLPRIVATE bool IsAtLRMargin( bool, bool bAPI = false ) const; - SAL_DLLPRIVATE bool isInHiddenTextFrame(SwShellCursor* pShellCursor); + SAL_DLLPRIVATE bool isInHiddenFrame(SwShellCursor* pShellCursor); SAL_DLLPRIVATE bool GoStartWordImpl(); SAL_DLLPRIVATE bool GoEndWordImpl(); diff --git a/sw/inc/node.hxx b/sw/inc/node.hxx index de7e24a0b152..b1e3309d596f 100644 --- a/sw/inc/node.hxx +++ b/sw/inc/node.hxx @@ -590,7 +590,7 @@ public: const SwSection& GetSection() const { return *m_pSection; } SwSection& GetSection() { return *m_pSection; } - SwFrame *MakeFrame( SwFrame* ); + SwFrame* MakeFrame(SwFrame* pSib, bool bHidden); /** Creates the frms for the SectionNode (i.e. the SectionFrames). On default the frames are created until the end of the range. diff --git a/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt b/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt new file mode 100644 index 000000000000..fbefc5c48046 --- /dev/null +++ b/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:styles> + <style:style style:name="Standard" style:family="paragraph" style:class="text"> + <style:paragraph-properties fo:margin-top="1in" fo:margin-bottom="0" style:contextual-spacing="false" fo:line-height="12pt"/> + </style:style> + </office:styles> + <office:body> + <office:text> + <text:section text:name="Hidden" text:display="none"/> + <text:section text:name="Shown"/> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt b/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt new file mode 100644 index 000000000000..5fae6a491704 --- /dev/null +++ b/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:master-page-name="Landscape"> + <style:paragraph-properties style:page-number="1"/> + </style:style> + <style:style style:name="P2" style:family="paragraph" style:master-page-name="Landscape"> + <style:paragraph-properties style:page-number="1"/> + <style:text-properties text:display="none"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p>First line</text:p> + <text:section text:name="Hidden" text:display="none"> + <text:p text:style-name="P1"/> + </text:section> + <text:section text:name="Shown"> + <text:p>Before break (still first page)</text:p> + </text:section> + <text:section text:name="HiddenText"> + <text:p text:style-name="P2"/> + </text:section> + <text:p>After break</text:p> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p>Should be together with next</text:p> + <text:section text:name="Hidden2" text:display="none"> + <text:p text:style-name="P2"/> + </text:section> + <text:p>Should be together with previous</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index 9c46d8395a1e..a074a35dc61b 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -94,15 +94,25 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf119875) { createSwDoc("tdf119875.odt"); xmlDocUniquePtr pXmlDoc = parseLayoutDump(); - sal_Int32 nFirstTop - = getXPath(pXmlDoc, "/root/page[2]/body/section[1]/infos/bounds"_ostr, "top"_ostr) - .toInt32(); - sal_Int32 nSecondTop - = getXPath(pXmlDoc, "/root/page[2]/body/section[2]/infos/bounds"_ostr, "top"_ostr) - .toInt32(); - // The first section had the same top value as the second one, so they - // overlapped. - CPPUNIT_ASSERT_LESS(nSecondTop, nFirstTop); + + assertXPath(pXmlDoc, "//page[2]/body/section[1]"_ostr, "formatName"_ostr, u"S10"_ustr); + assertXPath(pXmlDoc, "//page[2]/body/section[2]"_ostr, "formatName"_ostr, u"S11"_ustr); + assertXPath(pXmlDoc, "//page[2]/body/section[3]"_ostr, "formatName"_ostr, u"S13"_ustr); + assertXPath(pXmlDoc, "//page[2]/body/section[4]"_ostr, "formatName"_ostr, u"S14"_ustr); + // Sections "S10" and "S13" are hidden -> their frames are zero-height + assertXPath(pXmlDoc, "//page[2]/body/section[1]/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); + assertXPath(pXmlDoc, "//page[2]/body/section[3]/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); + + OUString S10Top = getXPath(pXmlDoc, "//page[2]/body/section[1]/infos/bounds"_ostr, "top"_ostr); + OUString S11Top = getXPath(pXmlDoc, "//page[2]/body/section[2]/infos/bounds"_ostr, "top"_ostr); + OUString S13Top = getXPath(pXmlDoc, "//page[2]/body/section[3]/infos/bounds"_ostr, "top"_ostr); + OUString S14Top = getXPath(pXmlDoc, "//page[2]/body/section[4]/infos/bounds"_ostr, "top"_ostr); + + CPPUNIT_ASSERT_EQUAL(S10Top, S11Top); + CPPUNIT_ASSERT_EQUAL(S13Top, S14Top); + + // Section "S11" had the same top value as section "S14", so they overlapped. + CPPUNIT_ASSERT_LESS(S14Top.toInt32(), S11Top.toInt32()); } CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf137523) @@ -2303,6 +2313,46 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf159259) CPPUNIT_ASSERT_EQUAL(paraHeight, flyHeight); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testLargeTopParaMarginAfterHiddenSection) +{ + // Given a large top margin in Standard paragraph style, and the first section hidden + createSwDoc("largeTopMarginAndHiddenFirstSection.fodt"); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Make sure there is only one page and two sections, first hidden (zero-height) + assertXPath(pXmlDoc, "//page"_ostr, 1); + assertXPath(pXmlDoc, "//page/body/section"_ostr, 2); + assertXPath(pXmlDoc, "//page/body/section[1]/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); + // Check that the top margin (1 in = 1440 twip) is added to line height (12 pt = 240 twip) + assertXPath(pXmlDoc, "//page/body/section[2]/infos/bounds"_ostr, "height"_ostr, u"1680"_ustr); +} + +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testPageBreakInHiddenSection) +{ + // Given a paragraph with page-break-before with page style and page number + createSwDoc("pageBreakInHiddenSection.fodt"); + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page"_ostr, 4); + assertXPath(pXmlDoc, "//section"_ostr, 4); + assertXPath(pXmlDoc, "//page[1]/body/txt"_ostr, 1); + // The page break inside the hidden section is ignored (otherwise, there would be one section + // on the first page) + assertXPath(pXmlDoc, "//page[1]/body/section"_ostr, 2); + // The first section is hidden + assertXPath(pXmlDoc, "//page[1]/body/section[1]/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); + + // Page 2 is empty even page (generated by the next page's section with page-break-before) + assertXPath(pXmlDoc, "//page[2]/body"_ostr, 0); + + // The section on page 3 is not hidden, only text in it is, therefore its page break works + assertXPath(pXmlDoc, "//page[3]/body/section"_ostr, 1); + assertXPath(pXmlDoc, "//page[3]/body/section/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); + + // The section on page 4 is hidden, thus page break in it is ignored (no further pages, where + // the section would be moved to otherwise) + assertXPath(pXmlDoc, "//page[4]/body/section"_ostr, 1); + assertXPath(pXmlDoc, "//page[4]/body/section/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/uiwriter/data/hiddenSectionsAroundPageBreak.fodt b/sw/qa/extras/uiwriter/data/hiddenSectionsAroundPageBreak.fodt new file mode 100644 index 000000000000..12761847ed75 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/hiddenSectionsAroundPageBreak.fodt @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:master-page-name="Landscape"> + <style:paragraph-properties style:page-number="auto" fo:break-before="page"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:section text:name="Section 1" text:display="none"/> + <text:section text:name="Section 2"> + <text:p text:style-name="P1">A paragraph with a page-break-before</text:p> + </text:section> + <text:section text:name="Section 3" text:display="none"/> + <text:section text:name="Section 4"> + <text:p>Lorem</text:p> + </text:section> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx b/sw/qa/extras/uiwriter/uiwriter9.cxx index 1a3e49c25754..f870f5ea480a 100644 --- a/sw/qa/extras/uiwriter/uiwriter9.cxx +++ b/sw/qa/extras/uiwriter/uiwriter9.cxx @@ -122,6 +122,22 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf135083) CPPUNIT_ASSERT(!getProperty<OUString>(xLastPara, u"ListId"_ustr).isEmpty()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testHiddenSectionsAroundPageBreak) +{ + createSwDoc("hiddenSectionsAroundPageBreak.fodt"); + + CPPUNIT_ASSERT_EQUAL(1, getPages()); + + auto xModel(mxComponent.queryThrow<frame::XModel>()); + auto xTextViewCursorSupplier( + xModel->getCurrentController().queryThrow<text::XTextViewCursorSupplier>()); + auto xCursor(xTextViewCursorSupplier->getViewCursor().queryThrow<text::XPageCursor>()); + + // Make sure that the page style is set correctly + xCursor->jumpToFirstPage(); + CPPUNIT_ASSERT_EQUAL(u"Landscape"_ustr, getProperty<OUString>(xCursor, "PageStyleName")); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index 173414ed4db7..d08e4971f556 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -944,14 +944,14 @@ bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage ) return bRet; } -bool SwCursorShell::isInHiddenTextFrame(SwShellCursor* pShellCursor) +bool SwCursorShell::isInHiddenFrame(SwShellCursor* pShellCursor) { SwContentNode *pCNode = pShellCursor->GetPointContentNode(); std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false); SwContentFrame *const pFrame = pCNode ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp) : nullptr; - return !pFrame || (pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow()); + return !pFrame || pFrame->IsHiddenNow(); } // sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara @@ -992,7 +992,7 @@ bool SwCursorShell::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & //which is what SwCursorShell::UpdateCursorPos will reset //the position to if we pass it a position in an //invisible hidden paragraph field - while (isInHiddenTextFrame(pTmpCursor) + while (isInHiddenFrame(pTmpCursor) || !IsAtStartOrEndOfFrame(this, pTmpCursor, fnPosPara)) { if (!pTmpCursor->MovePara(fnWhichPara, fnPosPara)) @@ -1796,7 +1796,7 @@ void SwCursorShell::UpdateCursorPos() SwShellCursor* pShellCursor = getShellCursor( true ); Size aOldSz( GetDocSize() ); - if (isInHiddenTextFrame(pShellCursor) && !ExtendedSelectedAll()) + if (isInHiddenFrame(pShellCursor) && !ExtendedSelectedAll()) { SwCursorMoveState aTmpState(CursorMoveState::SetOnlyText); aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); @@ -1805,14 +1805,14 @@ void SwCursorShell::UpdateCursorPos() pShellCursor->DeleteMark(); // kde45196-1.html: try to get to a non-hidden paragraph, there must // be one in the document body - while (isInHiddenTextFrame(pShellCursor)) + while (isInHiddenFrame(pShellCursor)) { if (!pShellCursor->MovePara(GoNextPara, fnParaStart)) { break; } } - while (isInHiddenTextFrame(pShellCursor)) + while (isInHiddenFrame(pShellCursor)) { if (!pShellCursor->MovePara(GoPrevPara, fnParaStart)) { diff --git a/sw/source/core/crsr/pam.cxx b/sw/source/core/crsr/pam.cxx index 14552d33a912..c5e2a1cc3557 100644 --- a/sw/source/core/crsr/pam.cxx +++ b/sw/source/core/crsr/pam.cxx @@ -1060,7 +1060,7 @@ SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & ( nullptr == pFrame || ( !bInReadOnly && pFrame->IsProtected() ) || - (pFrame->IsTextFrame() && static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()) + pFrame->IsHiddenNow() ) || ( !bInReadOnly && pNd->FindSectionNode() && pNd->FindSectionNode()->GetSection().IsProtect() @@ -1101,8 +1101,7 @@ SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout)); if (nullptr == pFrame || ( !bInReadOnly && pFrame->IsProtected() ) || - ( pFrame->IsTextFrame() && - static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow())) + pFrame->IsHiddenNow()) { pNd = nullptr; continue; diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx index c78d8e18b633..2a5aad64de21 100644 --- a/sw/source/core/doc/docedt.cxx +++ b/sw/source/core/doc/docedt.cxx @@ -553,7 +553,7 @@ uno::Any SwDoc::Spell( SwPaM& rPaM, { nCurrNd = pNd->EndOfSectionIndex(); } - else if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + else if( !pContentFrame->IsHiddenNow() ) { if( pPageCnt && *pPageCnt && pPageSt ) { @@ -766,7 +766,7 @@ static bool lcl_HyphenateNode( SwNode* pNd, void* pArgs ) // sw_redlinehide: this will be called once per node for merged nodes; // the fully deleted ones won't have frames so are skipped. SwContentFrame* pContentFrame = pNode->getLayoutFrame( pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ); - if( pContentFrame && !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + if( pContentFrame && !pContentFrame->IsHiddenNow() ) { sal_uInt16 *pPageSt = pHyphArgs->GetPageSt(); sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt(); diff --git a/sw/source/core/docnode/ndsect.cxx b/sw/source/core/docnode/ndsect.cxx index cf96e1d5094b..1da9ecb43ed6 100644 --- a/sw/source/core/docnode/ndsect.cxx +++ b/sw/source/core/docnode/ndsect.cxx @@ -1022,9 +1022,9 @@ SwSectionNode::~SwSectionNode() } } -SwFrame *SwSectionNode::MakeFrame( SwFrame *pSib ) +SwFrame* SwSectionNode::MakeFrame(SwFrame* pSib, bool bHidden) { - m_pSection->m_Data.SetHiddenFlag(false); + m_pSection->m_Data.SetHiddenFlag(bHidden); return new SwSectionFrame( *m_pSection, pSib ); } diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index c6ecb9ccf974..8967833a64b1 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1963,7 +1963,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, if (SwNodeType::Section == pNd->GetNodeType()) { const SwSection& rSect = static_cast<const SwSectionNode*>(pNd)->GetSection(); - if( (bSkipHidden && rSect.IsHiddenFlag()) || + if( (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag()) ) // than skip the section aTmp = *pNd->EndOfSectionNode(); @@ -1974,7 +1974,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, { const SwSection& rSect = static_cast<SwSectionNode*>(pNd-> m_pStartOfSection)->GetSection(); - if( (bSkipHidden && rSect.IsHiddenFlag()) || + if( (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag()) ) // than skip the section aTmp = *pNd->EndOfSectionNode(); @@ -1985,7 +1985,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, const SwSectionNode* pSectNd; if( ( bSkipHidden || bSkipProtect ) && nullptr != (pSectNd = pNd->FindSectionNode() ) && - ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) || + ( ( bSkipHidden && pSectNd->GetSection().CalcHiddenFlag() ) || ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) ) { aTmp = *pSectNd->EndOfSectionNode(); diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx index 12d9e281c2f0..53418541eb4c 100644 --- a/sw/source/core/docnode/section.cxx +++ b/sw/source/core/docnode/section.cxx @@ -299,14 +299,11 @@ void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition) // Tell all Children that they are hidden const sw::SectionHidden aHint; pFormat->CallSwClientNotify(aHint); - - // Delete all Frames - pFormat->DelFrames(); } } else if (m_Data.IsHiddenFlag()) // show Nodes again { - // Show all Frames (Child Sections are accounted for by MakeFrames) + // Show all Frames // Only if the Parent Section is not restricting us! SwSection* pParentSect = pFormat->GetParentSection(); if( !pParentSect || !pParentSect->IsHiddenFlag() ) @@ -314,8 +311,6 @@ void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition) // Tell all Children that the Parent is not hidden anymore const sw::SectionHidden aHint(false); pFormat->CallSwClientNotify(aHint); - - pFormat->MakeFrames(); } } } diff --git a/sw/source/core/edit/editsh.cxx b/sw/source/core/edit/editsh.cxx index 333eecd35262..0400dc887169 100644 --- a/sw/source/core/edit/editsh.cxx +++ b/sw/source/core/edit/editsh.cxx @@ -796,7 +796,7 @@ void SwEditShell::SetNumberingRestart() if( nullptr != pContentFrame ) { // skip hidden frames - ignore protection! - if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + if( !pContentFrame->IsHiddenNow() ) { // if the node is numbered and the starting value of the numbering equals the // start value of the numbering rule then set this value as hard starting value diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx index d57654f6a9e7..604488a18c9f 100644 --- a/sw/source/core/inc/frame.hxx +++ b/sw/source/core/inc/frame.hxx @@ -891,6 +891,8 @@ public: // Fly in ... and footnotes bool IsProtected() const; + virtual bool IsHiddenNow() const; + bool IsColLocked() const { return mbColLocked; } virtual bool IsDeleteForbidden() const { return mnForbidDelete > 0; } diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx index c07d78b12ba9..69158b335863 100644 --- a/sw/source/core/inc/sectfrm.hxx +++ b/sw/source/core/inc/sectfrm.hxx @@ -88,6 +88,8 @@ public: virtual void Cut() override; virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override; + virtual bool IsHiddenNow() const override; + inline const SwSectionFrame *GetFollow() const; inline SwSectionFrame *GetFollow(); SwSectionFrame* FindMaster() const; diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index e60dbd20fb97..df11ca589bc6 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -559,7 +559,7 @@ public: #endif /// Hidden - bool IsHiddenNow() const; // bHidden && pOut == pPrt + virtual bool IsHiddenNow() const override; // bHidden && pOut == pPrt void HideHidden(); // Remove appendage if Hidden void HideFootnotes(TextFrameIndex nStart, TextFrameIndex nEnd); diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx index c2bbdd8904f8..e13fdf012143 100644 --- a/sw/source/core/layout/calcmove.cxx +++ b/sw/source/core/layout/calcmove.cxx @@ -177,7 +177,7 @@ bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & ) if ( nMoveAnyway < 3 ) { - if ( nSpace ) + if (nSpace || IsHiddenNow()) { // Do not notify footnotes which are stuck to the paragraph: // This would require extremely confusing code, taking into @@ -209,7 +209,7 @@ bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & ) } // Check for space left in new upper - return nSpace != 0; + return nSpace != 0 || IsHiddenNow(); } } return false; @@ -532,7 +532,7 @@ static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame ) do { pRet = lcl_Prev( pRet ); - } while ( pRet && pRet->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() ); + } while ( pRet && pRet->IsHiddenNow() ); return pRet; } @@ -1083,9 +1083,8 @@ void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs ) setFramePrintAreaValid(true); SwRectFnSet aRectFnSet(this); - const bool bTextFrame = IsTextFrame(); SwTwips nUpper = 0; - if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() ) + if (IsTextFrame() && IsHiddenNow()) { if ( static_cast<SwTextFrame*>(this)->HasFollow() ) static_cast<SwTextFrame*>(this)->JoinFrame(); @@ -1715,7 +1714,7 @@ void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/) const bool bMoveFwdInvalid = nullptr != GetIndNext(); const bool bNxtNew = ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) && - (!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow()); + !pNxt->IsHiddenNow(); pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut()); @@ -2217,7 +2216,7 @@ bool SwContentFrame::WouldFit_( SwTwips nSpace, pTmpPrev = nullptr; else { - if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() ) + if (pFrame->IsHiddenNow()) pTmpPrev = lcl_NotHiddenPrev( pFrame ); else pTmpPrev = pFrame; diff --git a/sw/source/core/layout/findfrm.cxx b/sw/source/core/layout/findfrm.cxx index 5331baacd93e..0dd64c6aecff 100644 --- a/sw/source/core/layout/findfrm.cxx +++ b/sw/source/core/layout/findfrm.cxx @@ -994,7 +994,7 @@ SwFrame *SwFrame::FindNext_() (!bFootnote || pSct->IsInFootnote() ) ) return pSct; } - return pRet; + return pRet == this ? nullptr : pRet; } // #i27138# - add parameter <_bInSameFootnote> @@ -1380,11 +1380,7 @@ void SwFrame::InvalidateNextPrtArea() SwFrame* pNextFrame = FindNext(); // skip empty section frames and hidden text frames { - while ( pNextFrame && - ( ( pNextFrame->IsSctFrame() && - !static_cast<SwSectionFrame*>(pNextFrame)->GetSection() ) || - ( pNextFrame->IsTextFrame() && - static_cast<SwTextFrame*>(pNextFrame)->IsHiddenNow() ) ) ) + while (pNextFrame && pNextFrame->IsHiddenNow()) { pNextFrame = pNextFrame->FindNext(); } diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx index da509e2a6bea..88158161c530 100644 --- a/sw/source/core/layout/flowfrm.cxx +++ b/sw/source/core/layout/flowfrm.cxx @@ -1271,8 +1271,7 @@ bool SwFlowFrame::IsPageBreak( bool bAct ) const // Determine predecessor const SwFrame *pPrev = m_rThis.FindPrev(); - while ( pPrev && ( !pPrev->IsInDocBody() || - ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) ) + while (pPrev && (!pPrev->IsInDocBody() || pPrev->IsHiddenNow())) pPrev = pPrev->FindPrev(); if ( pPrev ) @@ -1333,7 +1332,7 @@ bool SwFlowFrame::IsColBreak( bool bAct ) const // Determine predecessor const SwFrame *pPrev = m_rThis.FindPrev(); while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) || - ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) ) + pPrev->IsHiddenNow() ) ) pPrev = pPrev->FindPrev(); if ( pPrev ) @@ -1364,6 +1363,14 @@ bool SwFlowFrame::IsColBreak( bool bAct ) const return false; } +// Skip hidden paragraphs and empty sections on the same level +static const SwFrame* skipHiddenSiblingFrames_(const SwFrame* pFrame) +{ + while (pFrame && pFrame->IsHiddenNow()) + pFrame = pFrame->GetPrev(); + return pFrame; +} + bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const { if( m_rThis.IsInSct() ) @@ -1379,7 +1386,7 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const return !pTmp->GetPrev() || IsPageBreak(true); if( pTmp->IsColumnFrame() && pTmp->GetPrev() ) return IsColBreak( true ); - if( pTmp->IsSctFrame() && ( !bSct || pTmp->GetPrev() ) ) + if (pTmp->IsSctFrame() && (!bSct || skipHiddenSiblingFrames_(pTmp->GetPrev()))) return false; pTmp = pTmp->GetUpper(); } @@ -1401,6 +1408,31 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const return pTmp && !pTmp->GetPrev(); } +// Skip hidden paragraphs and empty sections +static const SwFrame* skipHiddenFrames_(const SwFrame* pFrame) +{ + do + { + pFrame = skipHiddenSiblingFrames_(pFrame); + if (!pFrame || !pFrame->IsSctFrame()) + return pFrame; + // Special case: found previous frame is a section + // Search for the last content in the section + auto pSectFrame = static_cast<const SwSectionFrame*>(pFrame); + pFrame = pSectFrame->FindLastContent(); + // If the last content is in a table _inside_ the section, + // take the table herself. + // Correction: Check directly, if table is inside table, instead of indirectly + // by checking, if section isn't inside a table + if (pFrame && pFrame->IsInTab()) + { + const SwTabFrame* pTableFrame = pFrame->FindTabFrame(); + if (pSectFrame->IsAnLower(pTableFrame)) + return pTableFrame; + } + } while (true); +} + /** helper method to determine previous frame for calculation of the upper space @@ -1408,73 +1440,21 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const */ const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const { - const SwFrame* pPrevFrame = _pProposedPrevFrame - ? _pProposedPrevFrame - : m_rThis.GetPrev(); - - // Skip hidden paragraphs and empty sections - while ( pPrevFrame && - ( ( pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) || - ( pPrevFrame->IsSctFrame() && - !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } + const SwFrame* pPrevFrame + = skipHiddenFrames_(_pProposedPrevFrame ? _pProposedPrevFrame : m_rThis.GetPrev()); + if (pPrevFrame || !m_rThis.IsInFootnote() + || !(m_rThis.IsSctFrame() || !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote())) + return pPrevFrame; // Special case: no direct previous frame is found but frame is in footnote // Search for a previous frame in previous footnote, // if frame isn't in a section, which is also in the footnote - if ( !pPrevFrame && m_rThis.IsInFootnote() && - ( m_rThis.IsSctFrame() || - !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote() ) ) - { - const SwFootnoteFrame* pPrevFootnoteFrame = - static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev()); - if ( pPrevFootnoteFrame ) - { - pPrevFrame = pPrevFootnoteFrame->GetLastLower(); - - // Skip hidden paragraphs and empty sections - while ( pPrevFrame && - ( ( pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) || - ( pPrevFrame->IsSctFrame() && - !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } - } - } - // Special case: found previous frame is a section - // Search for the last content in the section - if( pPrevFrame && pPrevFrame->IsSctFrame() ) - { - const SwSectionFrame* pPrevSectFrame = - static_cast<const SwSectionFrame*>(pPrevFrame); - pPrevFrame = pPrevSectFrame->FindLastContent(); - // If the last content is in a table _inside_ the section, - // take the table herself. - // Correction: Check directly, if table is inside table, instead of indirectly - // by checking, if section isn't inside a table - if ( pPrevFrame && pPrevFrame->IsInTab() ) - { - const SwTabFrame* pTableFrame = pPrevFrame->FindTabFrame(); - if ( pPrevSectFrame->IsAnLower( pTableFrame ) ) - { - pPrevFrame = pTableFrame; - } - } - // Correction: skip hidden text frames - while ( pPrevFrame && - pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } - } + const SwFootnoteFrame* pPrevFootnoteFrame = + static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev()); + if ( pPrevFootnoteFrame ) + return skipHiddenFrames_(pPrevFootnoteFrame->GetLastLower()); - return pPrevFrame; + return nullptr; } // This should be renamed to something like lcl_UseULSpacing @@ -1912,6 +1892,8 @@ SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell( /// Moves the Frame forward if it seems necessary regarding the current conditions and attributes. bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue ) { + if (m_rThis.IsHiddenNow()) + return false; const SwFrame* pNxt = m_rThis.GetIndNext(); if ( bKeep && //!bMovedBwd && @@ -2299,7 +2281,8 @@ bool SwFlowFrame::MoveBwd( bool &rbReformat ) ) pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false ); } - else if ( IsPageBreak( true ) ) // Do we have to respect a PageBreak? + // Do we have to respect a PageBreak? + else if (IsPageBreak(true) && (!m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsHiddenNow())) { // If the previous page doesn't have a Frame in the body, // flowing back makes sense despite the PageBreak (otherwise, @@ -2366,7 +2349,7 @@ bool SwFlowFrame::MoveBwd( bool &rbReformat ) } } } - else if ( IsColBreak( true ) ) + else if (IsColBreak(true)) { // If the previous column doesn't contain a ContentFrame, flowing back // makes sense despite the ColumnBreak, as otherwise we'd get diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index eeab5c60c1ce..baf632d6ebff 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -844,11 +844,7 @@ void SwContentNotify::ImplDestroy() SwFrame* pPrevFrame = pCnt->FindPrev(); // skip empty section frames and hidden text frames { - while ( pPrevFrame && - ( ( pPrevFrame->IsSctFrame() && - !static_cast<SwSectionFrame*>(pPrevFrame)->GetSection() ) || - ( pPrevFrame->IsTextFrame() && - static_cast<SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ) ) + while (pPrevFrame && pPrevFrame->IsHiddenNow()) { pPrevFrame = pPrevFrame->FindPrev(); } @@ -1608,7 +1604,7 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, pFrame = pNode->IsTextNode() ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode) : pNode->MakeFrame(pLay); - if( pPageMaker ) + if (pPageMaker && !pLay->IsHiddenNow()) pPageMaker->CheckInsert( nIndex ); pFrame->InsertBehind( pLay, pPrv ); @@ -1766,15 +1762,11 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, continue; // skip it } SwSectionNode *pNode = static_cast<SwSectionNode*>(pNd); - if( pNode->GetSection().CalcHiddenFlag() ) - // is hidden, skip the area - nIndex = pNode->EndOfSectionIndex(); - else { if (pActualSection) pActualSection->SetLastPos(pPrv); - pFrame = pNode->MakeFrame( pLay ); + pFrame = pNode->MakeFrame(pLay, pNode->GetSection().CalcHiddenFlag()); pActualSection.reset( new SwActualSection( pActualSection.release(), static_cast<SwSectionFrame*>(pFrame), pNode ) ); if ( pActualSection->GetUpper() ) @@ -1945,7 +1937,8 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, } else { - pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay ); + pFrame = pActualSection->GetSectionNode()->MakeFrame( + pLay, pActualSection->GetSectionNode()->GetSection().IsHiddenFlag()); pFrame->InsertBehind( pLay, pPrv ); static_cast<SwSectionFrame*>(pFrame)->Init(); @@ -2523,8 +2516,7 @@ void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame, // one as previous frame. const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev(); // OD 2004-02-13 #i25029# - skip hidden text frames. - while ( pPrevFrame && pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) + while (pPrevFrame && pPrevFrame->IsHiddenNow()) { pPrevFrame = pPrevFrame->GetPrev(); } @@ -2555,8 +2547,7 @@ void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame ) // corresponding attribute set is set at current text frame. // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames. const SwFrame* pNextFrame = _rFrame.GetNext(); - while ( pNextFrame && pNextFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pNextFrame)->IsHiddenNow() ) + while (pNextFrame && pNextFrame->IsHiddenNow()) { pNextFrame = pNextFrame->GetNext(); } diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx index 70453c572774..f4e6072498d1 100644 --- a/sw/source/core/layout/ftnfrm.cxx +++ b/sw/source/core/layout/ftnfrm.cxx @@ -2965,13 +2965,9 @@ SwContentFrame* SwFootnoteFrame::FindLastContent() while ( pTmpLastLower && pTmpLastLower->GetNext() ) { pTmpLastLower = pTmpLastLower->GetNext(); - if ( ( pTmpLastLower->IsTextFrame() && - !static_cast<SwTextFrame*>(pTmpLastLower)->IsHiddenNow() ) || - ( pTmpLastLower->IsSctFrame() && - static_cast<SwSectionFrame*>(pTmpLastLower)->GetSection() && - static_cast<SwSectionFrame*>(pTmpLastLower)->ContainsContent() ) || - ( pTmpLastLower->IsTabFrame() && - static_cast<SwTabFrame*>(pTmpLastLower)->ContainsContent() ) ) + if (!pTmpLastLower->IsHiddenNow() + && (!pTmpLastLower->IsLayoutFrame() + || static_cast<SwLayoutFrame*>(pTmpLastLower)->ContainsContent())) { pLastLowerOfFootnote = pTmpLastLower; } diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx index bf4bcc45ea3a..f8c029ea12cd 100644 --- a/sw/source/core/layout/pagechg.cxx +++ b/sw/source/core/layout/pagechg.cxx @@ -789,7 +789,10 @@ SwPageDesc *SwPageFrame::FindPageDesc() return pRet; } - SwFrame *pFlow = FindFirstBodyContent(); + SwContentFrame* pFirstContent = FindFirstBodyContent(); + while (pFirstContent && pFirstContent->IsHiddenNow()) + pFirstContent = pFirstContent->GetNextContentFrame(); + SwFrame* pFlow = pFirstContent; if ( pFlow && pFlow->IsInTab() ) pFlow = pFlow->FindTabFrame(); diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx index a1fd849ec495..3967a1f56442 100644 --- a/sw/source/core/layout/sectfrm.cxx +++ b/sw/source/core/layout/sectfrm.cxx @@ -189,6 +189,13 @@ SwSectionFrame::~SwSectionFrame() { } +//virtual +bool SwSectionFrame::IsHiddenNow() const +{ + const auto* pSection = GetSection(); + return !pSection || pSection->CalcHiddenFlag(); +} + void SwSectionFrame::DelEmpty( bool bRemove ) { if( IsColLocked() ) @@ -1371,6 +1378,20 @@ void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderA SwRectFnSet aRectFnSet(this); + if (GetSection()->CalcHiddenFlag()) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetHeight(aFrm, 0); + } + { + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetHeight(aPrt, 0); + } + setFrameAreaSizeValid(true); + setFramePrintAreaValid(true); + } + if ( !isFramePrintAreaValid() ) { PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr ) @@ -2182,6 +2203,11 @@ bool SwSectionFrame::Growable() const SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst ) { + if (GetSection()->CalcHiddenFlag()) + { + return 0; + } + if ( !IsColLocked() && !HasFixSize() ) { SwRectFnSet aRectFnSet(this); @@ -2646,6 +2672,17 @@ void SwSectionFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint) return; SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent()); } + else if (rHint.GetId() == SfxHintId::SwSectionHidden) + { + InvalidateAll(); + InvalidateObjs(false); + + for (SwFrame* pLowerFrame = Lower(); pLowerFrame; pLowerFrame = pLowerFrame->GetNext()) + { + pLowerFrame->InvalidateAll(); + pLowerFrame->InvalidateObjs(false); + } + } else SwFrame::SwClientNotify(rMod, rHint); } diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index f75782456181..a04f9a14a7b3 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1454,6 +1454,8 @@ namespace auto IsAllHiddenSection(SwSectionFrame const& rSection) -> bool { + if (rSection.IsHiddenNow()) + return true; for (SwFrame const* pFrame = rSection.Lower(); pFrame; pFrame = pFrame->GetNext()) { if (pFrame->IsColumnFrame()) @@ -1474,7 +1476,7 @@ namespace } else if (pFrame->IsTextFrame()) { - if (!static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()) + if (!pFrame->IsHiddenNow()) { return false; } @@ -1509,7 +1511,7 @@ namespace } else if (pFrame->IsTextFrame()) { - if (!static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()) + if (!pFrame->IsHiddenNow()) { return false; } diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx index a8445e90dd5a..89a5f03302ac 100644 --- a/sw/source/core/layout/trvlfrm.cxx +++ b/sw/source/core/layout/trvlfrm.cxx @@ -52,6 +52,7 @@ #include <ndtxt.hxx> #include <undobj.hxx> #include <flyfrms.hxx> +#include <sectfrm.hxx> #include <swselectionlist.hxx> #include <comphelper/lok.hxx> @@ -828,8 +829,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //If I'm in the DocumentBody, I want to stay there. if ( pStart->IsInDocBody() ) { - while ( pCnt && (!pCnt->IsInDocBody() || - (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()))) + while (pCnt && (!pCnt->IsInDocBody() || pCnt->IsHiddenNow())) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -840,8 +840,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //case of necessity. else if ( pStart->IsInFootnote() ) { - while ( pCnt && (!pCnt->IsInFootnote() || - (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()))) + while (pCnt && (!pCnt->IsInFootnote() || pCnt->IsHiddenNow())) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -851,7 +850,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //In Flys we can go ahead blindly as long as we find a Content. else if ( pStart->IsInFly() ) { - if ( pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow() ) + if (pCnt && pCnt->IsHiddenNow()) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -875,7 +874,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, } if ( !bSame ) pCnt = nullptr; - else if (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()) // i73332 + else if (pCnt->IsHiddenNow()) // i73332 { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -958,8 +957,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, } } - } while ( !bEnd || - (pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())); + } while (!bEnd || (pCnt && pCnt->IsHiddenNow())); if (pCnt == nullptr) { @@ -1246,7 +1244,7 @@ const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint, if ( pComp != pContent ) continue; - if ( !pContent->IsTextFrame() || !static_cast<const SwTextFrame*>(pContent)->IsHiddenNow() ) + if (!pContent->IsHiddenNow()) { SwRect aContentFrame( pContent->UnionFrame() ); if ( aContentFrame.Contains( rPoint ) ) @@ -1727,6 +1725,15 @@ bool SwFrame::IsProtected() const return false; } +// virtual +bool SwFrame::IsHiddenNow() const +{ + if (const auto* pSectFrame = FindSctFrame()) + return pSectFrame->IsHiddenNow(); + + return false; +} + /** @return the physical page number */ sal_uInt16 SwFrame::GetPhyPageNum() const { diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index 302302a9a256..52a51ff1a509 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -1485,6 +1485,9 @@ bool SwTextFrame::IsHiddenNow() const return true; } + if (SwContentFrame::IsHiddenNow()) + return true; + bool bHiddenCharsHidePara(false); bool bHiddenParaField(false); if (m_pMergedPara) @@ -1554,22 +1557,14 @@ bool SwTextFrame::IsHiddenNow() const // be visible - check this for the 1st body paragraph if (IsInDocBody() && FindPrevCnt() == nullptr) { - bool isAllHidden(true); for (SwContentFrame const* pNext = FindNextCnt(true); pNext != nullptr; pNext = pNext->FindNextCnt(true)) { - if (!pNext->IsTextFrame() - || !static_cast<SwTextFrame const*>(pNext)->IsHiddenNow()) - { - isAllHidden = false; - break; - } - } - if (isAllHidden) - { - SAL_INFO("sw.core", "unhiding one body paragraph"); - return false; + if (!pNext->IsHiddenNow()) + return true; } + SAL_INFO("sw.core", "unhiding one body paragraph"); + return false; } return true; } commit 844efae1255f17b10677cf93820eb62a439e75b3 Author: Noel Grandin <[email protected]> AuthorDate: Fri Feb 16 19:31:57 2024 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Feb 21 18:08:10 2024 +0100 tdf#101313 Copy-paste table from writer to calc With Merged Cells from Writer to Calc, Cells Placed in Wrong Position (Wrong cell offsets). regression from commit ed24564ce11683731b820c29d5a46e073ab7a2a7 Author: Noel Grandin <[email protected]> Date: Thu Jul 19 15:22:31 2012 +0200 SV_DECL_VARARR_SORT(ScRTFColTwips) o3tl::sorted_vector Change-Id: I8c90c19f6a27a368fd5807b3eaab84ce820e26e5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163518 Tested-by: Jenkins Reviewed-by: Noel Grandin <[email protected]> (cherry picked from commit 4df426e429e1aed92f074d8acd3ba3a5ec335ba9) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163532 diff --git a/sc/source/filter/rtf/rtfparse.cxx b/sc/source/filter/rtf/rtfparse.cxx index b2d2b8c2521f..0617a86c62b1 100644 --- a/sc/source/filter/rtf/rtfparse.cxx +++ b/sc/source/filter/rtf/rtfparse.cxx @@ -101,8 +101,8 @@ inline void ScRTFParser::NextRow() bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol ) { - ScRTFColTwips::const_iterator it = aColTwips.find( nTwips ); - bool bFound = it != aColTwips.end(); + ScRTFColTwips::const_iterator it = aColTwips.lower_bound( nTwips ); + bool bFound = it != aColTwips.end() && *it == nTwips; sal_uInt16 nPos = it - aColTwips.begin(); *pCol = static_cast<SCCOL>(nPos); if ( bFound )
