1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
21 #include <com/sun/star/document/XDocumentProperties.hpp>
22 
23 #include <scitems.hxx>
24 #include <rangelst.hxx>
25 #include <editeng/flstitem.hxx>
26 #include <editeng/paperinf.hxx>
27 #include <editeng/sizeitem.hxx>
28 #include <sal/log.hxx>
29 #include <sfx2/viewfrm.hxx>
30 #include <sfx2/app.hxx>
31 #include <sfx2/docfile.hxx>
32 #include <sfx2/printer.hxx>
33 #include <svx/pageitem.hxx>
34 #include <svx/postattr.hxx>
35 #include <svx/svxids.hrc>
36 #include <unotools/misccfg.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/virdev.hxx>
39 #include <vcl/weld.hxx>
40 
41 #include <docsh.hxx>
42 #include "docshimp.hxx"
43 #include <scmod.hxx>
44 #include <tabvwsh.hxx>
45 #include <viewdata.hxx>
46 #include <docpool.hxx>
47 #include <stlpool.hxx>
48 #include <patattr.hxx>
49 #include <uiitems.hxx>
50 #include <hints.hxx>
51 #include <docoptio.hxx>
52 #include <viewopti.hxx>
53 #include <pntlock.hxx>
54 #include <chgtrack.hxx>
55 #include <docfunc.hxx>
56 #include <formulacell.hxx>
57 #include <chgviset.hxx>
58 #include <progress.hxx>
59 #include <redcom.hxx>
60 #include <inputopt.hxx>
61 #include <drwlayer.hxx>
62 #include <inputhdl.hxx>
63 #include <conflictsdlg.hxx>
64 #include <globstr.hrc>
65 #include <scresid.hxx>
66 #include <markdata.hxx>
67 #include <memory>
68 #include <formulaopt.hxx>
69 
70 #include <comphelper/lok.hxx>
71 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
72 #include <sfx2/lokhelper.hxx>
73 
74 //          Redraw - Notifications
75 
PostEditView(ScEditEngineDefaulter * pEditEngine,const ScAddress & rCursorPos)76 void ScDocShell::PostEditView( ScEditEngineDefaulter* pEditEngine, const ScAddress& rCursorPos )
77 {
78 //  Broadcast( ScEditViewHint( pEditEngine, rCursorPos ) );
79 
80         //  Test: only active ViewShell
81 
82     ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
83     if (pViewSh && pViewSh->GetViewData().GetDocShell() == this)
84     {
85         ScEditViewHint aHint( pEditEngine, rCursorPos );
86         pViewSh->Notify( *this, aHint );
87     }
88 }
89 
PostDataChanged()90 void ScDocShell::PostDataChanged()
91 {
92     Broadcast( SfxHint( SfxHintId::ScDataChanged ) );
93     SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScAnyDataChanged ));      // Navigator
94     m_aDocument.PrepareFormulaCalc();
95     //! notify navigator directly!
96 }
97 
PostPaint(SCCOL nStartCol,SCROW nStartRow,SCTAB nStartTab,SCCOL nEndCol,SCROW nEndRow,SCTAB nEndTab,PaintPartFlags nPart,sal_uInt16 nExtFlags)98 void ScDocShell::PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
99                             SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, PaintPartFlags nPart,
100                             sal_uInt16 nExtFlags )
101 {
102     ScRange aRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
103     PostPaint(aRange, nPart, nExtFlags);
104 }
105 
PostPaint(const ScRangeList & rRanges,PaintPartFlags nPart,sal_uInt16 nExtFlags)106 void ScDocShell::PostPaint( const ScRangeList& rRanges, PaintPartFlags nPart, sal_uInt16 nExtFlags )
107 {
108     ScRangeList aPaintRanges;
109     for (size_t i = 0, n = rRanges.size(); i < n; ++i)
110     {
111         const ScRange& rRange = rRanges[i];
112         SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
113         SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
114         SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
115 
116         if (!ValidCol(nCol1)) nCol1 = m_aDocument.MaxCol();
117         if (!ValidRow(nRow1)) nRow1 = m_aDocument.MaxRow();
118         if (!ValidCol(nCol2)) nCol2 = m_aDocument.MaxCol();
119         if (!ValidRow(nRow2)) nRow2 = m_aDocument.MaxRow();
120 
121         if ( m_pPaintLockData )
122         {
123             // #i54081# PaintPartFlags::Extras still has to be broadcast because it changes the
124             // current sheet if it's invalid. All other flags added to pPaintLockData.
125             PaintPartFlags nLockPart = nPart & ~PaintPartFlags::Extras;
126             if ( nLockPart != PaintPartFlags::NONE )
127             {
128                 //! nExtFlags ???
129                 m_pPaintLockData->AddRange( ScRange( nCol1, nRow1, nTab1,
130                                                    nCol2, nRow2, nTab2 ), nLockPart );
131             }
132 
133             nPart &= PaintPartFlags::Extras;  // for broadcasting
134             if (nPart == PaintPartFlags::NONE)
135                 continue;
136         }
137 
138         if (nExtFlags & SC_PF_LINES)            // respect space for lines
139         {
140                                                 //! check for hidden columns/rows!
141             if (nCol1>0) --nCol1;
142             if (nCol2<m_aDocument.MaxCol()) ++nCol2;
143             if (nRow1>0) --nRow1;
144             if (nRow2<m_aDocument.MaxRow()) ++nRow2;
145         }
146 
147                                                 // expand for the merged ones
148         if (nExtFlags & SC_PF_TESTMERGE)
149             m_aDocument.ExtendMerge( nCol1, nRow1, nCol2, nRow2, nTab1 );
150 
151         if ( nCol1 != 0 || nCol2 != m_aDocument.MaxCol() )
152         {
153             //  Extend to whole rows if SC_PF_WHOLEROWS is set, or rotated or non-left
154             //  aligned cells are contained (see UpdatePaintExt).
155             //  Special handling for RTL text (#i9731#) is unnecessary now with full
156             //  support of right-aligned text.
157 
158             if ( ( nExtFlags & SC_PF_WHOLEROWS ) ||
159                  m_aDocument.HasAttrib( nCol1,nRow1,nTab1,
160                                       m_aDocument.MaxCol(),nRow2,nTab2, HasAttrFlags::Rotate | HasAttrFlags::RightOrCenter ) )
161             {
162                 nCol1 = 0;
163                 nCol2 = m_aDocument.MaxCol();
164             }
165         }
166         aPaintRanges.push_back(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
167     }
168 
169     Broadcast(ScPaintHint(aPaintRanges.Combine(), nPart));
170 
171     // LOK: we are supposed to update the row / columns headers (and actually
172     // the document size too - cell size affects that, obviously)
173     if ((nPart & (PaintPartFlags::Top | PaintPartFlags::Left)) && comphelper::LibreOfficeKit::isActive())
174     {
175         ScModelObj* pModel = comphelper::getUnoTunnelImplementation<ScModelObj>(this->GetModel());
176         SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
177     }
178 }
179 
PostPaintGridAll()180 void ScDocShell::PostPaintGridAll()
181 {
182     PostPaint( 0,0,0, m_aDocument.MaxCol(),m_aDocument.MaxRow(),MAXTAB, PaintPartFlags::Grid );
183 }
184 
PostPaintCell(SCCOL nCol,SCROW nRow,SCTAB nTab)185 void ScDocShell::PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
186 {
187     PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, SC_PF_TESTMERGE );
188 }
189 
PostPaintCell(const ScAddress & rPos)190 void ScDocShell::PostPaintCell( const ScAddress& rPos )
191 {
192     PostPaintCell( rPos.Col(), rPos.Row(), rPos.Tab() );
193 }
194 
PostPaintExtras()195 void ScDocShell::PostPaintExtras()
196 {
197     PostPaint( 0,0,0, m_aDocument.MaxCol(),m_aDocument.MaxRow(),MAXTAB, PaintPartFlags::Extras );
198 }
199 
UpdatePaintExt(sal_uInt16 & rExtFlags,const ScRange & rRange)200 void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, const ScRange& rRange )
201 {
202     if ( ( rExtFlags & SC_PF_LINES ) == 0 &&
203          m_aDocument.HasAttrib( rRange, HasAttrFlags::Lines | HasAttrFlags::Shadow | HasAttrFlags::Conditional ) )
204     {
205         //  If the range contains lines, shadow or conditional formats,
206         //  set SC_PF_LINES to include one extra cell in all directions.
207 
208         rExtFlags |= SC_PF_LINES;
209     }
210 
211     if ( ( rExtFlags & SC_PF_WHOLEROWS ) == 0 &&
212          ( rRange.aStart.Col() != 0 || rRange.aEnd.Col() != m_aDocument.MaxCol() ) &&
213          m_aDocument.HasAttrib( rRange, HasAttrFlags::Rotate | HasAttrFlags::RightOrCenter ) )
214     {
215         //  If the range contains (logically) right- or center-aligned cells,
216         //  or rotated cells, set SC_PF_WHOLEROWS to paint the whole rows.
217         //  This test isn't needed after the cell changes, because it's also
218         //  tested in PostPaint. UpdatePaintExt may later be changed to do this
219         //  only if called before the changes.
220 
221         rExtFlags |= SC_PF_WHOLEROWS;
222     }
223 }
224 
UpdatePaintExt(sal_uInt16 & rExtFlags,SCCOL nStartCol,SCROW nStartRow,SCTAB nStartTab,SCCOL nEndCol,SCROW nEndRow,SCTAB nEndTab)225 void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
226                                                    SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
227 {
228     UpdatePaintExt( rExtFlags, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ) );
229 }
230 
LockPaint_Impl(bool bDoc)231 void ScDocShell::LockPaint_Impl(bool bDoc)
232 {
233     if ( !m_pPaintLockData )
234         m_pPaintLockData.reset( new ScPaintLockData );
235     m_pPaintLockData->IncLevel(bDoc);
236 }
237 
UnlockPaint_Impl(bool bDoc)238 void ScDocShell::UnlockPaint_Impl(bool bDoc)
239 {
240     if ( m_pPaintLockData )
241     {
242         if ( m_pPaintLockData->GetLevel(bDoc) )
243             m_pPaintLockData->DecLevel(bDoc);
244         if (!m_pPaintLockData->GetLevel(!bDoc) && !m_pPaintLockData->GetLevel(bDoc))
245         {
246             //     Execute Paint now
247 
248             // don't continue collecting
249             std::unique_ptr<ScPaintLockData> pPaint = std::move(m_pPaintLockData);
250 
251             ScRangeListRef xRangeList = pPaint->GetRangeList();
252             if ( xRangeList.is() )
253             {
254                 PaintPartFlags nParts = pPaint->GetParts();
255                 for ( size_t i = 0, nCount = xRangeList->size(); i < nCount; i++ )
256                 {
257                     //! nExtFlags ???
258                     ScRange const & rRange = (*xRangeList)[i];
259                     PostPaint( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
260                                 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(),
261                                 nParts );
262                 }
263             }
264 
265             if ( pPaint->GetModified() )
266                 SetDocumentModified();
267         }
268     }
269     else
270     {
271         OSL_FAIL("UnlockPaint without LockPaint");
272     }
273 }
274 
LockDocument_Impl(sal_uInt16 nNew)275 void ScDocShell::LockDocument_Impl(sal_uInt16 nNew)
276 {
277     if (!m_nDocumentLock)
278     {
279         ScDrawLayer* pDrawLayer = m_aDocument.GetDrawLayer();
280         if (pDrawLayer)
281             pDrawLayer->setLock(true);
282     }
283     m_nDocumentLock = nNew;
284 }
285 
UnlockDocument_Impl(sal_uInt16 nNew)286 void ScDocShell::UnlockDocument_Impl(sal_uInt16 nNew)
287 {
288     m_nDocumentLock = nNew;
289     if (!m_nDocumentLock)
290     {
291         ScDrawLayer* pDrawLayer = m_aDocument.GetDrawLayer();
292         if (pDrawLayer)
293             pDrawLayer->setLock(false);
294     }
295 }
296 
SetLockCount(sal_uInt16 nNew)297 void ScDocShell::SetLockCount(sal_uInt16 nNew)
298 {
299     if (nNew)                   // set
300     {
301         if ( !m_pPaintLockData )
302             m_pPaintLockData.reset( new ScPaintLockData );
303         m_pPaintLockData->SetDocLevel(nNew-1);
304         LockDocument_Impl(nNew);
305     }
306     else if (m_pPaintLockData)    // delete
307     {
308         m_pPaintLockData->SetDocLevel(0);  // at unlock, execute immediately
309         UnlockPaint_Impl(true);                 // now
310         UnlockDocument_Impl(0);
311     }
312 }
313 
LockPaint()314 void ScDocShell::LockPaint()
315 {
316     LockPaint_Impl(false);
317 }
318 
UnlockPaint()319 void ScDocShell::UnlockPaint()
320 {
321     UnlockPaint_Impl(false);
322 }
323 
LockDocument()324 void ScDocShell::LockDocument()
325 {
326     LockPaint_Impl(true);
327     LockDocument_Impl(m_nDocumentLock + 1);
328 }
329 
UnlockDocument()330 void ScDocShell::UnlockDocument()
331 {
332     if (m_nDocumentLock)
333     {
334         UnlockPaint_Impl(true);
335         UnlockDocument_Impl(m_nDocumentLock - 1);
336     }
337     else
338     {
339         OSL_FAIL("UnlockDocument without LockDocument");
340     }
341 }
342 
SetInplace(bool bInplace)343 void ScDocShell::SetInplace( bool bInplace )
344 {
345     if (m_bIsInplace != bInplace)
346     {
347         m_bIsInplace = bInplace;
348         CalcOutputFactor();
349     }
350 }
351 
CalcOutputFactor()352 void ScDocShell::CalcOutputFactor()
353 {
354     if (m_bIsInplace)
355     {
356         m_nPrtToScreenFactor = 1.0;           // otherwise it does not match the inactive display
357         return;
358     }
359 
360     bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
361     if (bTextWysiwyg)
362     {
363         m_nPrtToScreenFactor = 1.0;
364         return;
365     }
366 
367     OUString aTestString(
368             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789");
369     long nPrinterWidth = 0;
370     long nWindowWidth = 0;
371     const ScPatternAttr* pPattern = &m_aDocument.GetPool()->GetDefaultItem(ATTR_PATTERN);
372 
373     vcl::Font aDefFont;
374     OutputDevice* pRefDev = GetRefDevice();
375     MapMode aOldMode = pRefDev->GetMapMode();
376     vcl::Font aOldFont = pRefDev->GetFont();
377 
378     pRefDev->SetMapMode(MapMode(MapUnit::MapPixel));
379     pPattern->GetFont(aDefFont, SC_AUTOCOL_BLACK, pRefDev); // font color doesn't matter here
380     pRefDev->SetFont(aDefFont);
381     nPrinterWidth = pRefDev->PixelToLogic(Size(pRefDev->GetTextWidth(aTestString), 0), MapMode(MapUnit::Map100thMM)).Width();
382     pRefDev->SetFont(aOldFont);
383     pRefDev->SetMapMode(aOldMode);
384 
385     ScopedVclPtrInstance< VirtualDevice > pVirtWindow( *Application::GetDefaultDevice() );
386     pVirtWindow->SetMapMode(MapMode(MapUnit::MapPixel));
387     pPattern->GetFont(aDefFont, SC_AUTOCOL_BLACK, pVirtWindow);    // font color doesn't matter here
388     pVirtWindow->SetFont(aDefFont);
389     nWindowWidth = pVirtWindow->GetTextWidth(aTestString);
390     nWindowWidth = static_cast<long>( nWindowWidth / ScGlobal::nScreenPPTX * HMM_PER_TWIPS );
391 
392     if (nPrinterWidth && nWindowWidth)
393         m_nPrtToScreenFactor = nPrinterWidth / static_cast<double>(nWindowWidth);
394     else
395     {
396         OSL_FAIL("GetTextSize returns 0 ??");
397         m_nPrtToScreenFactor = 1.0;
398     }
399 }
400 
InitOptions(bool bForLoading)401 void ScDocShell::InitOptions(bool bForLoading)      // called from InitNew and Load
402 {
403     //  Settings from the SpellCheckCfg get into Doc- and ViewOptions
404 
405     LanguageType nDefLang, nCjkLang, nCtlLang;
406     bool bAutoSpell;
407     ScModule::GetSpellSettings( nDefLang, nCjkLang, nCtlLang, bAutoSpell );
408     ScModule* pScMod = SC_MOD();
409 
410     ScDocOptions  aDocOpt  = pScMod->GetDocOptions();
411     ScFormulaOptions aFormulaOpt = pScMod->GetFormulaOptions();
412     ScViewOptions aViewOpt = pScMod->GetViewOptions();
413     aDocOpt.SetAutoSpell( bAutoSpell );
414 
415     // two-digit year entry from Tools->Options->General
416     aDocOpt.SetYear2000( sal::static_int_cast<sal_uInt16>( ::utl::MiscCfg().GetYear2000() ) );
417 
418     if (bForLoading)
419     {
420         // #i112123# No style:decimal-places attribute means automatic decimals, not the configured default,
421         // so it must not be taken from the global options.
422         // Calculation settings are handled separately in ScXMLBodyContext::EndElement.
423         aDocOpt.SetStdPrecision( SvNumberFormatter::UNLIMITED_PRECISION );
424 
425         // fdo#78294 The default null-date if
426         // <table:null-date table:date-value='...' />
427         // is absent is 1899-12-30 regardless what the configuration is set to.
428         // Import filters may override this value.
429         aDocOpt.SetDate( 30, 12, 1899);
430     }
431 
432     m_aDocument.SetDocOptions( aDocOpt );
433     m_aDocument.SetViewOptions( aViewOpt );
434     SetFormulaOptions( aFormulaOpt, bForLoading );
435 
436     //  print options are now set directly before the printing
437 
438     m_aDocument.SetLanguage( nDefLang, nCjkLang, nCtlLang );
439 }
440 
GetDocumentPrinter()441 Printer* ScDocShell::GetDocumentPrinter()       // for OLE
442 {
443     return m_aDocument.GetPrinter();
444 }
445 
GetPrinter(bool bCreateIfNotExist)446 SfxPrinter* ScDocShell::GetPrinter(bool bCreateIfNotExist)
447 {
448     return m_aDocument.GetPrinter(bCreateIfNotExist);
449 }
450 
UpdateFontList()451 void ScDocShell::UpdateFontList()
452 {
453     // pImpl->pFontList = new FontList( GetPrinter(), Application::GetDefaultDevice() );
454     m_pImpl->pFontList.reset(new FontList(GetRefDevice(), nullptr));
455     SvxFontListItem aFontListItem( m_pImpl->pFontList.get(), SID_ATTR_CHAR_FONTLIST );
456     PutItem( aFontListItem );
457 
458     CalcOutputFactor();
459 }
460 
GetRefDevice()461 OutputDevice* ScDocShell::GetRefDevice()
462 {
463     return m_aDocument.GetRefDevice();
464 }
465 
SetPrinter(VclPtr<SfxPrinter> const & pNewPrinter,SfxPrinterChangeFlags nDiffFlags)466 sal_uInt16 ScDocShell::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
467 {
468     SfxPrinter *pOld = m_aDocument.GetPrinter( false );
469     if ( pOld && pOld->IsPrinting() )
470         return SFX_PRINTERROR_BUSY;
471 
472     if (nDiffFlags & SfxPrinterChangeFlags::PRINTER)
473     {
474         if ( m_aDocument.GetPrinter() != pNewPrinter )
475         {
476             m_aDocument.SetPrinter( pNewPrinter );
477             m_aDocument.SetPrintOptions();
478 
479             // MT: Use UpdateFontList: Will use Printer fonts only if needed!
480             /*
481             delete pImpl->pFontList;
482             pImpl->pFontList = new FontList( pNewPrinter, Application::GetDefaultDevice() );
483             SvxFontListItem aFontListItem( pImpl->pFontList, SID_ATTR_CHAR_FONTLIST );
484             PutItem( aFontListItem );
485 
486             CalcOutputFactor();
487             */
488             if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
489                 UpdateFontList();
490 
491             ScModule* pScMod = SC_MOD();
492             SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this );
493             while (pFrame)
494             {
495                 SfxViewShell* pSh = pFrame->GetViewShell();
496                 if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pSh))
497                 {
498                     ScInputHandler* pInputHdl = pScMod->GetInputHdl(pViewSh);
499                     if (pInputHdl)
500                         pInputHdl->UpdateRefDevice();
501                 }
502                 pFrame = SfxViewFrame::GetNext( *pFrame, this );
503             }
504         }
505     }
506     else if (nDiffFlags & SfxPrinterChangeFlags::JOBSETUP)
507     {
508         SfxPrinter* pOldPrinter = m_aDocument.GetPrinter();
509         if (pOldPrinter)
510         {
511             pOldPrinter->SetJobSetup( pNewPrinter->GetJobSetup() );
512 
513             //  #i6706# Call SetPrinter with the old printer again, so the drawing layer
514             //  RefDevice is set (calling ReformatAllTextObjects and rebuilding charts),
515             //  because the JobSetup (printer device settings) may affect text layout.
516             m_aDocument.SetPrinter( pOldPrinter );
517             CalcOutputFactor();                         // also with the new settings
518         }
519     }
520 
521     if (nDiffFlags & SfxPrinterChangeFlags::OPTIONS)
522     {
523         m_aDocument.SetPrintOptions();        //! from new printer ???
524     }
525 
526     if (nDiffFlags & (SfxPrinterChangeFlags::CHG_ORIENTATION | SfxPrinterChangeFlags::CHG_SIZE))
527     {
528         OUString aStyle = m_aDocument.GetPageStyle( GetCurTab() );
529         ScStyleSheetPool* pStPl = m_aDocument.GetStyleSheetPool();
530         SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(pStPl->Find(aStyle, SfxStyleFamily::Page));
531         if (pStyleSheet)
532         {
533             SfxItemSet& rSet = pStyleSheet->GetItemSet();
534 
535             if (nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION)
536             {
537                 const SvxPageItem& rOldItem = rSet.Get(ATTR_PAGE);
538                 bool bWasLand = rOldItem.IsLandscape();
539                 bool bNewLand = ( pNewPrinter->GetOrientation() == Orientation::Landscape );
540                 if (bNewLand != bWasLand)
541                 {
542                     SvxPageItem aNewItem( rOldItem );
543                     aNewItem.SetLandscape( bNewLand );
544                     rSet.Put( aNewItem );
545 
546                     // flip size
547                     Size aOldSize = rSet.Get(ATTR_PAGE_SIZE).GetSize();
548                     Size aNewSize(aOldSize.Height(),aOldSize.Width());
549                     SvxSizeItem aNewSItem(ATTR_PAGE_SIZE,aNewSize);
550                     rSet.Put( aNewSItem );
551                 }
552             }
553             if (nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE)
554             {
555                 SvxSizeItem aPaperSizeItem( ATTR_PAGE_SIZE, SvxPaperInfo::GetPaperSize(pNewPrinter) );
556                 rSet.Put( aPaperSizeItem );
557             }
558         }
559     }
560 
561     PostPaint(0,0,0,m_aDocument.MaxCol(),m_aDocument.MaxRow(),MAXTAB,PaintPartFlags::All);
562 
563     return 0;
564 }
565 
GetChangeAction(const ScAddress & rPos)566 ScChangeAction* ScDocShell::GetChangeAction( const ScAddress& rPos )
567 {
568     ScChangeTrack* pTrack = GetDocument().GetChangeTrack();
569     if (!pTrack)
570         return nullptr;
571 
572     SCTAB nTab = rPos.Tab();
573 
574     const ScChangeAction* pFound = nullptr;
575     const ScChangeAction* pAction = pTrack->GetFirst();
576     while (pAction)
577     {
578         ScChangeActionType eType = pAction->GetType();
579         //! ScViewUtil::IsActionShown( *pAction, *pSettings, *pDoc )...
580         if ( pAction->IsVisible() && eType != SC_CAT_DELETE_TABS )
581         {
582             const ScBigRange& rBig = pAction->GetBigRange();
583             if ( rBig.aStart.Tab() == nTab )
584             {
585                 ScRange aRange = rBig.MakeRange();
586 
587                 if ( eType == SC_CAT_DELETE_ROWS )
588                     aRange.aEnd.SetRow( aRange.aStart.Row() );
589                 else if ( eType == SC_CAT_DELETE_COLS )
590                     aRange.aEnd.SetCol( aRange.aStart.Col() );
591 
592                 if ( aRange.In( rPos ) )
593                 {
594                     pFound = pAction;       // the last one wins
595                 }
596             }
597             if ( pAction->GetType() == SC_CAT_MOVE )
598             {
599                 ScRange aRange =
600                     static_cast<const ScChangeActionMove*>(pAction)->
601                     GetFromRange().MakeRange();
602                 if ( aRange.In( rPos ) )
603                 {
604                     pFound = pAction;
605                 }
606             }
607         }
608         pAction = pAction->GetNext();
609     }
610 
611     return const_cast<ScChangeAction*>(pFound);
612 }
613 
SetChangeComment(ScChangeAction * pAction,const OUString & rComment)614 void ScDocShell::SetChangeComment( ScChangeAction* pAction, const OUString& rComment )
615 {
616     if (pAction)
617     {
618         pAction->SetComment( rComment );
619         //! Undo ???
620         SetDocumentModified();
621 
622         //  Dialog-Notify
623         ScChangeTrack* pTrack = GetDocument().GetChangeTrack();
624         if (pTrack)
625         {
626             sal_uLong nNumber = pAction->GetActionNumber();
627             pTrack->NotifyModified( ScChangeTrackMsgType::Change, nNumber, nNumber );
628         }
629     }
630 }
631 
ExecuteChangeCommentDialog(ScChangeAction * pAction,weld::Window * pParent,bool bPrevNext)632 void ScDocShell::ExecuteChangeCommentDialog( ScChangeAction* pAction, weld::Window* pParent, bool bPrevNext)
633 {
634     if (!pAction) return;           // without action is nothing...
635 
636     OUString aComment = pAction->GetComment();
637     OUString aAuthor = pAction->GetUser();
638 
639     DateTime aDT = pAction->GetDateTime();
640     OUString aDate = ScGlobal::pLocaleData->getDate( aDT ) + " " +
641         ScGlobal::pLocaleData->getTime( aDT, false );
642 
643     SfxItemSet aSet(
644         GetPool(), svl::Items<SID_ATTR_POSTIT_AUTHOR, SID_ATTR_POSTIT_TEXT>{});
645 
646     aSet.Put( SvxPostItTextItem  ( aComment, SID_ATTR_POSTIT_TEXT ) );
647     aSet.Put( SvxPostItAuthorItem( aAuthor,  SID_ATTR_POSTIT_AUTHOR ) );
648     aSet.Put( SvxPostItDateItem  ( aDate,    SID_ATTR_POSTIT_DATE ) );
649 
650     std::unique_ptr<ScRedComDialog> pDlg(new ScRedComDialog( pParent, aSet,this,pAction,bPrevNext));
651 
652     pDlg->Execute();
653 }
654 
CompareDocument(ScDocument & rOtherDoc)655 void ScDocShell::CompareDocument( ScDocument& rOtherDoc )
656 {
657     ScChangeTrack* pTrack = m_aDocument.GetChangeTrack();
658     if ( pTrack && pTrack->GetFirst() )
659     {
660         //! there are changes -> inquiry if needs to be deleted
661     }
662 
663     m_aDocument.EndChangeTracking();
664     m_aDocument.StartChangeTracking();
665 
666     OUString aOldUser;
667     pTrack = m_aDocument.GetChangeTrack();
668     if ( pTrack )
669     {
670         aOldUser = pTrack->GetUser();
671 
672         //  check if comparing to same document
673 
674         OUString aThisFile;
675         const SfxMedium* pThisMed = GetMedium();
676         if (pThisMed)
677             aThisFile = pThisMed->GetName();
678         OUString aOtherFile;
679         SfxObjectShell* pOtherSh = rOtherDoc.GetDocumentShell();
680         if (pOtherSh)
681         {
682             const SfxMedium* pOtherMed = pOtherSh->GetMedium();
683             if (pOtherMed)
684                 aOtherFile = pOtherMed->GetName();
685         }
686         bool bSameDoc = ( aThisFile == aOtherFile && !aThisFile.isEmpty() );
687         if ( !bSameDoc )
688         {
689             //  create change actions from comparing with the name of the user
690             //  who last saved the document
691             //  (only if comparing different documents)
692 
693             using namespace ::com::sun::star;
694             uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
695                 GetModel(), uno::UNO_QUERY_THROW);
696             uno::Reference<document::XDocumentProperties> xDocProps(
697                 xDPS->getDocumentProperties());
698             OSL_ENSURE(xDocProps.is(), "no DocumentProperties");
699             OUString aDocUser = xDocProps->getModifiedBy();
700 
701             if ( !aDocUser.isEmpty() )
702                 pTrack->SetUser( aDocUser );
703         }
704     }
705 
706     m_aDocument.CompareDocument( rOtherDoc );
707 
708     pTrack = m_aDocument.GetChangeTrack();
709     if ( pTrack )
710         pTrack->SetUser( aOldUser );
711 
712     PostPaintGridAll();
713     SetDocumentModified();
714 }
715 
716 //              Merge (combine documents)
717 
lcl_Equal(const ScChangeAction * pA,const ScChangeAction * pB,bool bIgnore100Sec)718 static bool lcl_Equal( const ScChangeAction* pA, const ScChangeAction* pB, bool bIgnore100Sec )
719 {
720     return pA && pB &&
721         pA->GetActionNumber() == pB->GetActionNumber() &&
722         pA->GetType()         == pB->GetType() &&
723         pA->GetUser()         == pB->GetUser() &&
724         (bIgnore100Sec ?
725          pA->GetDateTimeUTC().IsEqualIgnoreNanoSec( pB->GetDateTimeUTC() ) :
726          pA->GetDateTimeUTC() == pB->GetDateTimeUTC());
727     //  don't compare state if an old change has been accepted
728 }
729 
lcl_FindAction(ScDocument * pDoc,const ScChangeAction * pAction,ScDocument * pSearchDoc,const ScChangeAction * pFirstSearchAction,const ScChangeAction * pLastSearchAction,bool bIgnore100Sec)730 static bool lcl_FindAction( ScDocument* pDoc, const ScChangeAction* pAction, ScDocument* pSearchDoc, const ScChangeAction* pFirstSearchAction, const ScChangeAction* pLastSearchAction, bool bIgnore100Sec )
731 {
732     if ( !pDoc || !pAction || !pSearchDoc || !pFirstSearchAction || !pLastSearchAction )
733     {
734         return false;
735     }
736 
737     sal_uLong nLastSearchAction = pLastSearchAction->GetActionNumber();
738     const ScChangeAction* pA = pFirstSearchAction;
739     while ( pA && pA->GetActionNumber() <= nLastSearchAction )
740     {
741         if ( pAction->GetType() == pA->GetType() &&
742              pAction->GetUser() == pA->GetUser() &&
743              (bIgnore100Sec ?
744                 pAction->GetDateTimeUTC().IsEqualIgnoreNanoSec( pA->GetDateTimeUTC() ) :
745                 pAction->GetDateTimeUTC() == pA->GetDateTimeUTC() ) &&
746              pAction->GetBigRange() == pA->GetBigRange() )
747         {
748             OUString aActionDesc;
749             pAction->GetDescription(aActionDesc, pDoc, true);
750             OUString aADesc;
751             pA->GetDescription(aADesc, pSearchDoc, true);
752             if (aActionDesc == aADesc)
753             {
754                 OSL_FAIL( "lcl_FindAction(): found equal action!" );
755                 return true;
756             }
757         }
758         pA = pA->GetNext();
759     }
760 
761     return false;
762 }
763 
MergeDocument(ScDocument & rOtherDoc,bool bShared,bool bCheckDuplicates,sal_uLong nOffset,ScChangeActionMergeMap * pMergeMap,bool bInverseMap)764 void ScDocShell::MergeDocument( ScDocument& rOtherDoc, bool bShared, bool bCheckDuplicates, sal_uLong nOffset, ScChangeActionMergeMap* pMergeMap, bool bInverseMap )
765 {
766     ScTabViewShell* pViewSh = GetBestViewShell( false );    //! functions to the DocShell
767     if (!pViewSh)
768         return;
769 
770     ScChangeTrack* pSourceTrack = rOtherDoc.GetChangeTrack();
771     if (!pSourceTrack)
772         return;             //! nothing to do - error notification?
773 
774     ScChangeTrack* pThisTrack = m_aDocument.GetChangeTrack();
775     if ( !pThisTrack )
776     {   // turn on
777         m_aDocument.StartChangeTracking();
778         pThisTrack = m_aDocument.GetChangeTrack();
779         OSL_ENSURE(pThisTrack,"ChangeTracking not enabled?");
780         if ( !bShared )
781         {
782             // turn on visual RedLining
783             ScChangeViewSettings aChangeViewSet;
784             aChangeViewSet.SetShowChanges(true);
785             m_aDocument.SetChangeViewSettings(aChangeViewSet);
786         }
787     }
788 
789     // include Nano seconds in compare?
790     bool bIgnore100Sec = !pSourceTrack->IsTimeNanoSeconds() ||
791             !pThisTrack->IsTimeNanoSeconds();
792 
793     //  find common initial position
794     sal_uLong nFirstNewNumber = 0;
795     const ScChangeAction* pSourceAction = pSourceTrack->GetFirst();
796     const ScChangeAction* pThisAction = pThisTrack->GetFirst();
797     // skip identical actions
798     while ( lcl_Equal( pSourceAction, pThisAction, bIgnore100Sec ) )
799     {
800         nFirstNewNumber = pSourceAction->GetActionNumber() + 1;
801         pSourceAction = pSourceAction->GetNext();
802         pThisAction = pThisAction->GetNext();
803     }
804     //  pSourceAction and pThisAction now point to the first "own" actions
805     //  The common actions before don't interest at all
806 
807     //! Inquiry if the documents where equal before the change tracking !!!
808 
809     const ScChangeAction* pFirstMergeAction = pSourceAction;
810     const ScChangeAction* pFirstSearchAction = pThisAction;
811 
812     // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
813     const ScChangeAction* pLastSearchAction = pThisTrack->GetLast();
814 
815     //  Create MergeChangeData from the following actions
816     sal_uLong nNewActionCount = 0;
817     const ScChangeAction* pCount = pSourceAction;
818     while ( pCount )
819     {
820         if ( bShared || !ScChangeTrack::MergeIgnore( *pCount, nFirstNewNumber ) )
821             ++nNewActionCount;
822         pCount = pCount->GetNext();
823     }
824     if (!nNewActionCount)
825         return;             //! nothing to do - error notification?
826                             //  from here on no return
827 
828     ScProgress aProgress( this, "...", nNewActionCount, true );
829 
830     sal_uLong nLastMergeAction = pSourceTrack->GetLast()->GetActionNumber();
831     // UpdateReference-Undo, valid references for the last common state
832     pSourceTrack->MergePrepare( pFirstMergeAction, bShared );
833 
834     //  adjust MergeChangeData to all yet following actions in this document
835     //  -> references valid for this document
836     while ( pThisAction )
837     {
838         // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
839         if ( !bShared || !ScChangeTrack::MergeIgnore( *pThisAction, nFirstNewNumber ) )
840         {
841             ScChangeActionType eType = pThisAction->GetType();
842             switch ( eType )
843             {
844                 case SC_CAT_INSERT_COLS :
845                 case SC_CAT_INSERT_ROWS :
846                 case SC_CAT_INSERT_TABS :
847                     pSourceTrack->AppendInsert( pThisAction->GetBigRange().MakeRange() );
848                 break;
849                 case SC_CAT_DELETE_COLS :
850                 case SC_CAT_DELETE_ROWS :
851                 case SC_CAT_DELETE_TABS :
852                 {
853                     const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pThisAction);
854                     if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
855                     {   // deleted table contains deleted cols, which are not
856                         sal_uLong nStart, nEnd;
857                         pSourceTrack->AppendDeleteRange(
858                             pDel->GetOverAllRange().MakeRange(), nullptr, nStart, nEnd );
859                     }
860                 }
861                 break;
862                 case SC_CAT_MOVE :
863                 {
864                     const ScChangeActionMove* pMove = static_cast<const ScChangeActionMove*>(pThisAction);
865                     pSourceTrack->AppendMove( pMove->GetFromRange().MakeRange(),
866                         pMove->GetBigRange().MakeRange(), nullptr );
867                 }
868                 break;
869                 default:
870                 {
871                     // added to avoid warnings
872                 }
873             }
874         }
875         pThisAction = pThisAction->GetNext();
876     }
877 
878     LockPaint();    // #i73877# no repainting after each action
879 
880     //  take over MergeChangeData into the current document
881     bool bHasRejected = false;
882     OUString aOldUser = pThisTrack->GetUser();
883     pThisTrack->SetUseFixDateTime( true );
884     ScMarkData& rMarkData = pViewSh->GetViewData().GetMarkData();
885     ScMarkData aOldMarkData( rMarkData );
886     pSourceAction = pFirstMergeAction;
887     while ( pSourceAction && pSourceAction->GetActionNumber() <= nLastMergeAction )
888     {
889         bool bMergeAction = false;
890         if ( bShared )
891         {
892             if ( !bCheckDuplicates || !lcl_FindAction( &rOtherDoc, pSourceAction, &m_aDocument, pFirstSearchAction, pLastSearchAction, bIgnore100Sec ) )
893             {
894                 bMergeAction = true;
895             }
896         }
897         else
898         {
899             if ( !ScChangeTrack::MergeIgnore( *pSourceAction, nFirstNewNumber ) )
900             {
901                 bMergeAction = true;
902             }
903         }
904 
905         if ( bMergeAction )
906         {
907             ScChangeActionType eSourceType = pSourceAction->GetType();
908             if ( !bShared && pSourceAction->IsDeletedIn() )
909             {
910                 //! does it need to be determined yet if really deleted in
911                 //! _this_ document?
912 
913                 //  lies in a range, which was deleted in this document
914                 //  -> is omitted
915                 //! ??? revert deletion action ???
916                 //! ??? save action somewhere else  ???
917 #if OSL_DEBUG_LEVEL > 0
918                 OUString aValue;
919                 if ( eSourceType == SC_CAT_CONTENT )
920                     static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewString( aValue, &m_aDocument );
921                 SAL_WARN( "sc", aValue << " omitted");
922 #endif
923             }
924             else
925             {
926                 //! Take over date/author/comment of the source action!
927 
928                 pThisTrack->SetUser( pSourceAction->GetUser() );
929                 pThisTrack->SetFixDateTimeUTC( pSourceAction->GetDateTimeUTC() );
930                 sal_uLong nOldActionMax = pThisTrack->GetActionMax();
931 
932                 bool bExecute = true;
933                 sal_uLong nReject = pSourceAction->GetRejectAction();
934                 if ( nReject )
935                 {
936                     if ( bShared )
937                     {
938                         if ( nReject >= nFirstNewNumber )
939                         {
940                             nReject += nOffset;
941                         }
942                         ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
943                         if ( pOldAction && pOldAction->IsVirgin() )
944                         {
945                             pThisTrack->Reject( pOldAction );
946                             bHasRejected = true;
947                             bExecute = false;
948                         }
949                     }
950                     else
951                     {
952                         //  decline old action (of the common ones)
953                         ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
954                         if (pOldAction && pOldAction->GetState() == SC_CAS_VIRGIN)
955                         {
956                             //! what happens at actions, which were accepted in this document???
957                             //! error notification or what???
958                             //! or execute reject change normally
959 
960                             pThisTrack->Reject(pOldAction);
961                             bHasRejected = true;                // for Paint
962                         }
963                         bExecute = false;
964                     }
965                 }
966 
967                 if ( bExecute )
968                 {
969                     //  execute normally
970                     ScRange aSourceRange = pSourceAction->GetBigRange().MakeRange();
971                     rMarkData.SelectOneTable( aSourceRange.aStart.Tab() );
972                     switch ( eSourceType )
973                     {
974                         case SC_CAT_CONTENT:
975                         {
976                             //! Test if it was at the very bottom in the document, then automatic
977                             //! row insert ???
978 
979                             OSL_ENSURE( aSourceRange.aStart == aSourceRange.aEnd, "huch?" );
980                             ScAddress aPos = aSourceRange.aStart;
981                             OUString aValue;
982                             static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewString( aValue, &m_aDocument );
983                             ScMatrixMode eMatrix = ScMatrixMode::NONE;
984                             const ScCellValue& rCell = static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewCell();
985                             if (rCell.meType == CELLTYPE_FORMULA)
986                                 eMatrix = rCell.mpFormula->GetMatrixFlag();
987                             switch ( eMatrix )
988                             {
989                                 case ScMatrixMode::NONE :
990                                     pViewSh->EnterData( aPos.Col(), aPos.Row(), aPos.Tab(), aValue );
991                                 break;
992                                 case ScMatrixMode::Formula :
993                                 {
994                                     SCCOL nCols;
995                                     SCROW nRows;
996                                     rCell.mpFormula->GetMatColsRows(nCols, nRows);
997                                     aSourceRange.aEnd.SetCol( aPos.Col() + nCols - 1 );
998                                     aSourceRange.aEnd.SetRow( aPos.Row() + nRows - 1 );
999                                     aValue = aValue.copy(1, aValue.getLength()-2); // remove the 1st and last characters.
1000                                     GetDocFunc().EnterMatrix( aSourceRange,
1001                                         nullptr, nullptr, aValue, false, false,
1002                                         EMPTY_OUSTRING, formula::FormulaGrammar::GRAM_DEFAULT );
1003                                 }
1004                                 break;
1005                                 case ScMatrixMode::Reference :     // do nothing
1006                                 break;
1007                             }
1008                         }
1009                         break;
1010                         case SC_CAT_INSERT_TABS :
1011                         {
1012                             OUString aName;
1013                             m_aDocument.CreateValidTabName( aName );
1014                             (void)GetDocFunc().InsertTable( aSourceRange.aStart.Tab(), aName, true, false );
1015                         }
1016                         break;
1017                         case SC_CAT_INSERT_ROWS:
1018                             (void)GetDocFunc().InsertCells( aSourceRange, nullptr, INS_INSROWS_BEFORE, true, false );
1019                         break;
1020                         case SC_CAT_INSERT_COLS:
1021                             (void)GetDocFunc().InsertCells( aSourceRange, nullptr, INS_INSCOLS_BEFORE, true, false );
1022                         break;
1023                         case SC_CAT_DELETE_TABS :
1024                             (void)GetDocFunc().DeleteTable( aSourceRange.aStart.Tab(), true );
1025                         break;
1026                         case SC_CAT_DELETE_ROWS:
1027                         {
1028                             const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pSourceAction);
1029                             if ( pDel->IsTopDelete() )
1030                             {
1031                                 aSourceRange = pDel->GetOverAllRange().MakeRange();
1032                                 (void)GetDocFunc().DeleteCells( aSourceRange, nullptr, DelCellCmd::Rows, false );
1033 
1034                                 // #i101099# [Collaboration] Changes are not correctly shown
1035                                 if ( bShared )
1036                                 {
1037                                     ScChangeAction* pAct = pThisTrack->GetLast();
1038                                     if ( pAct && pAct->GetType() == eSourceType && pAct->IsDeletedIn() && !pSourceAction->IsDeletedIn() )
1039                                     {
1040                                         pAct->RemoveAllDeletedIn();
1041                                     }
1042                                 }
1043                             }
1044                         }
1045                         break;
1046                         case SC_CAT_DELETE_COLS:
1047                         {
1048                             const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pSourceAction);
1049                             if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
1050                             {   // deleted table contains deleted cols, which are not
1051                                 aSourceRange = pDel->GetOverAllRange().MakeRange();
1052                                 (void)GetDocFunc().DeleteCells( aSourceRange, nullptr, DelCellCmd::Cols, false );
1053                             }
1054                         }
1055                         break;
1056                         case SC_CAT_MOVE :
1057                         {
1058                             const ScChangeActionMove* pMove = static_cast<const ScChangeActionMove*>(pSourceAction);
1059                             ScRange aFromRange( pMove->GetFromRange().MakeRange() );
1060                             (void)GetDocFunc().MoveBlock( aFromRange,
1061                                 aSourceRange.aStart, true, true, false, false );
1062                         }
1063                         break;
1064                         default:
1065                         {
1066                             // added to avoid warnings
1067                         }
1068                     }
1069                 }
1070                 const OUString& rComment = pSourceAction->GetComment();
1071                 if ( !rComment.isEmpty() )
1072                 {
1073                     ScChangeAction* pAct = pThisTrack->GetLast();
1074                     if ( pAct && pAct->GetActionNumber() > nOldActionMax )
1075                         pAct->SetComment( rComment );
1076                     else
1077                         OSL_FAIL( "MergeDocument: what to do with the comment?!?" );
1078                 }
1079 
1080                 // adjust references
1081                 pSourceTrack->MergeOwn( const_cast<ScChangeAction*>(pSourceAction), nFirstNewNumber, bShared );
1082 
1083                 // merge action state
1084                 if ( bShared && !pSourceAction->IsRejected() )
1085                 {
1086                     ScChangeAction* pAct = pThisTrack->GetLast();
1087                     if ( pAct && pAct->GetActionNumber() > nOldActionMax )
1088                     {
1089                         ScChangeTrack::MergeActionState( pAct, pSourceAction );
1090                     }
1091                 }
1092 
1093                 // fill merge map
1094                 if ( bShared && pMergeMap )
1095                 {
1096                     ScChangeAction* pAct = pThisTrack->GetLast();
1097                     if ( pAct && pAct->GetActionNumber() > nOldActionMax )
1098                     {
1099                         sal_uLong nActionMax = pAct->GetActionNumber();
1100                         sal_uLong nActionCount = nActionMax - nOldActionMax;
1101                         sal_uLong nAction = nActionMax - nActionCount + 1;
1102                         sal_uLong nSourceAction = pSourceAction->GetActionNumber() - nActionCount + 1;
1103                         while ( nAction <= nActionMax )
1104                         {
1105                             if ( bInverseMap )
1106                             {
1107                                 (*pMergeMap)[ nAction++ ] = nSourceAction++;
1108                             }
1109                             else
1110                             {
1111                                 (*pMergeMap)[ nSourceAction++ ] = nAction++;
1112                             }
1113                         }
1114                     }
1115                 }
1116             }
1117             aProgress.SetStateCountDown( --nNewActionCount );
1118         }
1119         pSourceAction = pSourceAction->GetNext();
1120     }
1121 
1122     rMarkData = aOldMarkData;
1123     pThisTrack->SetUser(aOldUser);
1124     pThisTrack->SetUseFixDateTime( false );
1125 
1126     pSourceTrack->Clear();      //! this one is bungled now
1127 
1128     if (bHasRejected)
1129         PostPaintGridAll();         // Reject() doesn't paint itself
1130 
1131     UnlockPaint();
1132 }
1133 
MergeSharedDocument(ScDocShell * pSharedDocShell)1134 bool ScDocShell::MergeSharedDocument( ScDocShell* pSharedDocShell )
1135 {
1136     if ( !pSharedDocShell )
1137     {
1138         return false;
1139     }
1140 
1141     ScChangeTrack* pThisTrack = m_aDocument.GetChangeTrack();
1142     if ( !pThisTrack )
1143     {
1144         return false;
1145     }
1146 
1147     ScDocument& rSharedDoc = pSharedDocShell->GetDocument();
1148     ScChangeTrack* pSharedTrack = rSharedDoc.GetChangeTrack();
1149     if ( !pSharedTrack )
1150     {
1151         return false;
1152     }
1153 
1154     // reset show changes
1155     ScChangeViewSettings aChangeViewSet;
1156     aChangeViewSet.SetShowChanges( false );
1157     m_aDocument.SetChangeViewSettings( aChangeViewSet );
1158 
1159     // find first merge action in this document
1160     bool bIgnore100Sec = !pThisTrack->IsTimeNanoSeconds() || !pSharedTrack->IsTimeNanoSeconds();
1161     ScChangeAction* pThisAction = pThisTrack->GetFirst();
1162     ScChangeAction* pSharedAction = pSharedTrack->GetFirst();
1163     while ( lcl_Equal( pThisAction, pSharedAction, bIgnore100Sec ) )
1164     {
1165         pThisAction = pThisAction->GetNext();
1166         pSharedAction = pSharedAction->GetNext();
1167     }
1168 
1169     if ( pSharedAction )
1170     {
1171         if ( pThisAction )
1172         {
1173             // merge own changes into shared document
1174             sal_uLong nActStartShared = pSharedAction->GetActionNumber();
1175             sal_uLong nActEndShared = pSharedTrack->GetActionMax();
1176             std::unique_ptr<ScDocument> pTmpDoc(new ScDocument);
1177             for ( sal_Int32 nIndex = 0; nIndex < m_aDocument.GetTableCount(); ++nIndex )
1178             {
1179                 OUString sTabName;
1180                 pTmpDoc->CreateValidTabName( sTabName );
1181                 pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
1182             }
1183             m_aDocument.GetChangeTrack()->Clone( pTmpDoc.get() );
1184             ScChangeActionMergeMap aOwnInverseMergeMap;
1185             pSharedDocShell->MergeDocument( *pTmpDoc, true, true, 0, &aOwnInverseMergeMap, true );
1186             pTmpDoc.reset();
1187             sal_uLong nActStartOwn = nActEndShared + 1;
1188             sal_uLong nActEndOwn = pSharedTrack->GetActionMax();
1189 
1190             // find conflicts
1191             ScConflictsList aConflictsList;
1192             ScConflictsFinder aFinder( pSharedTrack, nActStartShared, nActEndShared, nActStartOwn, nActEndOwn, aConflictsList );
1193             if ( aFinder.Find() )
1194             {
1195                 ScConflictsListHelper::TransformConflictsList( aConflictsList, nullptr, &aOwnInverseMergeMap );
1196                 bool bLoop = true;
1197                 while ( bLoop )
1198                 {
1199                     bLoop = false;
1200                     weld::Window* pWin = GetActiveDialogParent();
1201                     ScConflictsDlg aDlg(pWin, GetViewData(), &rSharedDoc, aConflictsList);
1202                     if (aDlg.run() == RET_CANCEL)
1203                     {
1204                         std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pWin,
1205                                                                        VclMessageType::Question, VclButtonsType::YesNo,
1206                                                                        ScResId(STR_DOC_WILLNOTBESAVED)));
1207                         xQueryBox->set_default_response(RET_YES);
1208                         if (xQueryBox->run() == RET_YES)
1209                         {
1210                             return false;
1211                         }
1212                         else
1213                         {
1214                             bLoop = true;
1215                         }
1216                     }
1217                 }
1218             }
1219 
1220             // undo own changes in shared document
1221             pSharedTrack->Undo( nActStartOwn, nActEndOwn );
1222 
1223             // clone change track for merging into own document
1224             pTmpDoc.reset(new ScDocument);
1225             for ( sal_Int32 nIndex = 0; nIndex < m_aDocument.GetTableCount(); ++nIndex )
1226             {
1227                 OUString sTabName;
1228                 pTmpDoc->CreateValidTabName( sTabName );
1229                 pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
1230             }
1231             pThisTrack->Clone( pTmpDoc.get() );
1232 
1233             // undo own changes since last save in own document
1234             sal_uLong nStartShared = pThisAction->GetActionNumber();
1235             ScChangeAction* pAction = pThisTrack->GetLast();
1236             while ( pAction && pAction->GetActionNumber() >= nStartShared )
1237             {
1238                 pThisTrack->Reject( pAction, true );
1239                 pAction = pAction->GetPrev();
1240             }
1241 
1242             // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
1243             pThisTrack->Undo( nStartShared, pThisTrack->GetActionMax(), true );
1244 
1245             // merge shared changes into own document
1246             ScChangeActionMergeMap aSharedMergeMap;
1247             MergeDocument( rSharedDoc, true, true, 0, &aSharedMergeMap );
1248             sal_uLong nEndShared = pThisTrack->GetActionMax();
1249 
1250             // resolve conflicts for shared non-content actions
1251             if ( !aConflictsList.empty() )
1252             {
1253                 ScConflictsListHelper::TransformConflictsList( aConflictsList, &aSharedMergeMap, nullptr );
1254                 ScConflictsResolver aResolver( pThisTrack, aConflictsList );
1255                 pAction = pThisTrack->GetAction( nEndShared );
1256                 while ( pAction && pAction->GetActionNumber() >= nStartShared )
1257                 {
1258                     aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
1259                         false /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
1260                     pAction = pAction->GetPrev();
1261                 }
1262             }
1263             nEndShared = pThisTrack->GetActionMax();
1264 
1265             // only show changes from shared document
1266             aChangeViewSet.SetShowChanges( true );
1267             aChangeViewSet.SetShowAccepted( true );
1268             aChangeViewSet.SetHasActionRange();
1269             aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
1270             m_aDocument.SetChangeViewSettings( aChangeViewSet );
1271 
1272             // merge own changes back into own document
1273             sal_uLong nStartOwn = nEndShared + 1;
1274             ScChangeActionMergeMap aOwnMergeMap;
1275             MergeDocument( *pTmpDoc, true, true, nEndShared - nStartShared + 1, &aOwnMergeMap );
1276             pTmpDoc.reset();
1277             sal_uLong nEndOwn = pThisTrack->GetActionMax();
1278 
1279             // resolve conflicts for shared content actions and own actions
1280             if ( !aConflictsList.empty() )
1281             {
1282                 ScConflictsListHelper::TransformConflictsList( aConflictsList, nullptr, &aOwnMergeMap );
1283                 ScConflictsResolver aResolver( pThisTrack, aConflictsList );
1284                 pAction = pThisTrack->GetAction( nEndShared );
1285                 while ( pAction && pAction->GetActionNumber() >= nStartShared )
1286                 {
1287                     aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
1288                         true /*bHandleContentAction*/, false /*bHandleNonContentAction*/ );
1289                     pAction = pAction->GetPrev();
1290                 }
1291 
1292                 pAction = pThisTrack->GetAction( nEndOwn );
1293                 while ( pAction && pAction->GetActionNumber() >= nStartOwn )
1294                 {
1295                     aResolver.HandleAction( pAction, false /*bIsSharedAction*/,
1296                         true /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
1297                     pAction = pAction->GetPrev();
1298                 }
1299             }
1300         }
1301         else
1302         {
1303             // merge shared changes into own document
1304             sal_uLong nStartShared = pThisTrack->GetActionMax() + 1;
1305             MergeDocument( rSharedDoc, true, true );
1306             sal_uLong nEndShared = pThisTrack->GetActionMax();
1307 
1308             // only show changes from shared document
1309             aChangeViewSet.SetShowChanges( true );
1310             aChangeViewSet.SetShowAccepted( true );
1311             aChangeViewSet.SetHasActionRange();
1312             aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
1313             m_aDocument.SetChangeViewSettings( aChangeViewSet );
1314         }
1315 
1316         // update view
1317         PostPaintExtras();
1318         PostPaintGridAll();
1319 
1320         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
1321                                                       VclMessageType::Info, VclButtonsType::Ok,
1322                                                       ScResId(STR_DOC_UPDATED)));
1323         xInfoBox->run();
1324     }
1325 
1326     return ( pThisAction != nullptr );
1327 }
1328 
1329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1330