/src/libreoffice/sc/source/ui/docshell/docsh5.cxx
Line | Count | Source |
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 <memory> |
21 | | #include <sal/config.h> |
22 | | |
23 | | #include <cassert> |
24 | | |
25 | | #include <osl/diagnose.h> |
26 | | #include <vcl/svapp.hxx> |
27 | | #include <vcl/vclenum.hxx> |
28 | | #include <vcl/weld/MessageDialog.hxx> |
29 | | #include <vcl/weld/WaitObject.hxx> |
30 | | #include <sfx2/app.hxx> |
31 | | #include <sfx2/bindings.hxx> |
32 | | #include <unotools/charclass.hxx> |
33 | | |
34 | | #include <com/sun/star/script/vba/XVBACompatibility.hpp> |
35 | | |
36 | | #include <dociter.hxx> |
37 | | #include <docsh.hxx> |
38 | | #include <global.hxx> |
39 | | #include <globstr.hrc> |
40 | | #include <scresid.hxx> |
41 | | #include <globalnames.hxx> |
42 | | #include <undodat.hxx> |
43 | | #include <undotab.hxx> |
44 | | #include <undoblk.hxx> |
45 | | #include <undo/UndoSpillPivotTable.hxx> |
46 | | #include <formulacell.hxx> |
47 | | #include <scmatrix.hxx> |
48 | | #include <dpobject.hxx> |
49 | | #include <dpshttab.hxx> |
50 | | #include <dbdocfun.hxx> |
51 | | #include <consoli.hxx> |
52 | | #include <dbdata.hxx> |
53 | | #include <progress.hxx> |
54 | | #include <olinetab.hxx> |
55 | | #include <patattr.hxx> |
56 | | #include <attrib.hxx> |
57 | | #include <docpool.hxx> |
58 | | #include <uiitems.hxx> |
59 | | #include <sc.hrc> |
60 | | #include <sizedev.hxx> |
61 | | #include <clipparam.hxx> |
62 | | #include <rowheightcontext.hxx> |
63 | | #include <refupdatecontext.hxx> |
64 | | |
65 | | using com::sun::star::script::XLibraryContainer; |
66 | | using com::sun::star::script::vba::XVBACompatibility; |
67 | | using com::sun::star::container::XNameContainer; |
68 | | using com::sun::star::uno::Reference; |
69 | | using com::sun::star::uno::UNO_QUERY; |
70 | | |
71 | | // former viewfunc/dbfunc methods |
72 | | |
73 | | void ScDocShell::ErrorMessage(TranslateId pGlobStrId) |
74 | 0 | { |
75 | | //! StopMarking at the (active) view? |
76 | |
|
77 | 0 | weld::Window* pParent = GetActiveDialogParent(); |
78 | 0 | weld::WaitObject aWaitOff( pParent ); |
79 | 0 | bool bFocus = pParent && pParent->has_focus(); |
80 | |
|
81 | 0 | if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR) |
82 | 0 | { |
83 | 0 | if (IsReadOnly()) |
84 | 0 | { |
85 | 0 | pGlobStrId = STR_READONLYERR; |
86 | 0 | } |
87 | 0 | } |
88 | |
|
89 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent, |
90 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
91 | 0 | ScResId(pGlobStrId))); |
92 | 0 | xInfoBox->run(); |
93 | |
|
94 | 0 | if (bFocus) |
95 | 0 | pParent->grab_focus(); |
96 | 0 | } |
97 | | |
98 | | bool ScDocShell::IsEditable() const |
99 | 12.3k | { |
100 | | // import into read-only document is possible - must be extended if other filters use api |
101 | | // #i108547# MSOOXML filter uses "IsChangeReadOnlyEnabled" property |
102 | | |
103 | 12.3k | return !IsReadOnly() || m_pDocument->IsImportingXML() || m_pDocument->IsChangeReadOnlyEnabled(); |
104 | 12.3k | } |
105 | | |
106 | | void ScDocShell::DBAreaDeleted( SCTAB nTab, SCCOL nX1, SCROW nY1, SCCOL nX2 ) |
107 | 0 | { |
108 | 0 | ScDocShellModificator aModificator( *this ); |
109 | | // the auto-filter is in the first row of the area |
110 | 0 | m_pDocument->RemoveFlagsTab( nX1, nY1, nX2, nY1, nTab, ScMF::Auto ); |
111 | 0 | PostPaint( nX1, nY1, nTab, nX2, nY1, nTab, PaintPartFlags::Grid ); |
112 | | // No SetDocumentModified, as the unnamed database range might have to be restored later. |
113 | | // The UNO hint is broadcast directly instead, to keep UNO objects in valid state. |
114 | 0 | m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) ); |
115 | 0 | } |
116 | | |
117 | | ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGetDBSelection eSel ) |
118 | 0 | { |
119 | 0 | SCCOL nCol = rMarked.aStart.Col(); |
120 | 0 | SCROW nRow = rMarked.aStart.Row(); |
121 | 0 | SCTAB nTab = rMarked.aStart.Tab(); |
122 | |
|
123 | 0 | SCCOL nStartCol = nCol; |
124 | 0 | SCROW nStartRow = nRow; |
125 | 0 | SCTAB nStartTab = nTab; |
126 | 0 | SCCOL nEndCol = rMarked.aEnd.Col(); |
127 | 0 | SCROW nEndRow = rMarked.aEnd.Row(); |
128 | | // Not simply GetDBAtCursor: The continuous data range for "unnamed" (GetDataArea) may be |
129 | | // located next to the cursor; so a named DB range needs to be searched for there as well. |
130 | 0 | ScDBCollection* pColl = m_pDocument->GetDBCollection(); |
131 | 0 | ScDBData* pData = m_pDocument->GetDBAtArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ); |
132 | 0 | if (!pData) |
133 | 0 | pData = pColl->GetDBNearCursor(nCol, nRow, nTab ); |
134 | |
|
135 | 0 | bool bSelected = ( eSel == ScGetDBSelection::ForceMark || |
136 | 0 | (rMarked.aStart != rMarked.aEnd && eSel != ScGetDBSelection::RowDown) ); |
137 | 0 | bool bOnlyDown = (!bSelected && eSel == ScGetDBSelection::RowDown && rMarked.aStart.Row() == rMarked.aEnd.Row()); |
138 | |
|
139 | 0 | bool bUseThis = false; |
140 | 0 | if (pData) |
141 | 0 | { |
142 | | // take range, if nothing else is marked |
143 | |
|
144 | 0 | SCTAB nDummy; |
145 | 0 | SCCOL nOldCol1; |
146 | 0 | SCROW nOldRow1; |
147 | 0 | SCCOL nOldCol2; |
148 | 0 | SCROW nOldRow2; |
149 | 0 | pData->GetArea( nDummy, nOldCol1,nOldRow1, nOldCol2,nOldRow2 ); |
150 | 0 | bool bIsNoName = ( pData->GetName() == STR_DB_LOCAL_NONAME ); |
151 | |
|
152 | 0 | if (!bSelected) |
153 | 0 | { |
154 | 0 | bUseThis = true; |
155 | 0 | if ( bIsNoName && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) ) |
156 | 0 | { |
157 | | // If nothing marked or only one row marked, adapt |
158 | | // "unnamed" to contiguous area. |
159 | 0 | nStartCol = nCol; |
160 | 0 | nStartRow = nRow; |
161 | 0 | if (bOnlyDown) |
162 | 0 | { |
163 | 0 | nEndCol = rMarked.aEnd.Col(); |
164 | 0 | nEndRow = rMarked.aEnd.Row(); |
165 | 0 | } |
166 | 0 | else |
167 | 0 | { |
168 | 0 | nEndCol = nStartCol; |
169 | 0 | nEndRow = nStartRow; |
170 | 0 | } |
171 | 0 | m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown ); |
172 | 0 | if ( nOldCol1 != nStartCol || nOldCol2 != nEndCol || nOldRow1 != nStartRow ) |
173 | 0 | bUseThis = false; // doesn't fit at all |
174 | 0 | else if ( nOldRow2 != nEndRow ) |
175 | 0 | { |
176 | | // extend range to new end row |
177 | 0 | pData->SetArea( nTab, nOldCol1,nOldRow1, nOldCol2,nEndRow ); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | } |
181 | 0 | else |
182 | 0 | { |
183 | 0 | if ( nOldCol1 == nStartCol && nOldRow1 == nStartRow && |
184 | 0 | nOldCol2 == nEndCol && nOldRow2 == nEndRow ) // marked precisely? |
185 | 0 | bUseThis = true; |
186 | 0 | else |
187 | 0 | bUseThis = false; // always take marking (Bug 11964) |
188 | 0 | } |
189 | | |
190 | | // never take "unnamed" for import |
191 | |
|
192 | 0 | if ( bUseThis && eMode == SC_DB_IMPORT && bIsNoName ) |
193 | 0 | bUseThis = false; |
194 | 0 | } |
195 | |
|
196 | 0 | if ( bUseThis ) |
197 | 0 | { |
198 | 0 | pData->GetArea( nStartTab, nStartCol,nStartRow, nEndCol,nEndRow ); |
199 | 0 | } |
200 | | // tdf#168478 - use the previously found range rather than returning an empty one |
201 | 0 | else if ( eMode != SC_DB_OLD ) |
202 | 0 | { |
203 | 0 | if ( !bSelected ) |
204 | 0 | { // continuous range |
205 | 0 | nStartCol = nCol; |
206 | 0 | nStartRow = nRow; |
207 | 0 | if (bOnlyDown) |
208 | 0 | { |
209 | 0 | nEndCol = rMarked.aEnd.Col(); |
210 | 0 | nEndRow = rMarked.aEnd.Row(); |
211 | 0 | } |
212 | 0 | else |
213 | 0 | { |
214 | 0 | nEndCol = nStartCol; |
215 | 0 | nEndRow = nStartRow; |
216 | 0 | } |
217 | 0 | m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown ); |
218 | 0 | } |
219 | |
|
220 | 0 | bool bHasHeader = m_pDocument->HasColHeader( nStartCol,nStartRow, nEndCol,nEndRow, nTab ); |
221 | |
|
222 | 0 | ScDBData* pNoNameData = m_pDocument->GetAnonymousDBData(nTab); |
223 | 0 | if ( eMode != SC_DB_IMPORT && pNoNameData) |
224 | 0 | { |
225 | | // Do not reset AutoFilter range during temporary operations on |
226 | | // other ranges, use the document global temporary anonymous range |
227 | | // instead. But, if AutoFilter is to be toggled then do use the |
228 | | // sheet-local DB range. |
229 | 0 | bool bSheetLocal = true; |
230 | 0 | if (eMode != SC_DB_AUTOFILTER && pNoNameData->HasAutoFilter()) |
231 | 0 | { |
232 | 0 | bSheetLocal = false; |
233 | 0 | pNoNameData = m_pDocument->GetAnonymousDBData(); |
234 | 0 | if (!pNoNameData) |
235 | 0 | { |
236 | 0 | m_pDocument->SetAnonymousDBData( std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME, |
237 | 0 | nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, bHasHeader) ) ); |
238 | 0 | pNoNameData = m_pDocument->GetAnonymousDBData(); |
239 | 0 | } |
240 | | // ScDocShell::CancelAutoDBRange() would restore the |
241 | | // sheet-local anonymous DBData from pOldAutoDBRange, unset so |
242 | | // that won't happen with data of a previous sheet-local |
243 | | // DBData. |
244 | 0 | m_pOldAutoDBRange.reset(); |
245 | 0 | } |
246 | 0 | else if (!m_pOldAutoDBRange) |
247 | 0 | { |
248 | | // store the old unnamed database range with its settings for undo |
249 | | // (store at the first change, get the state before all changes) |
250 | 0 | m_pOldAutoDBRange.reset( new ScDBData( *pNoNameData ) ); |
251 | 0 | } |
252 | 0 | else if (m_pOldAutoDBRange->GetTab() != pNoNameData->GetTab()) |
253 | 0 | { |
254 | | // Different sheet-local unnamed DB range than the previous one. |
255 | 0 | *m_pOldAutoDBRange = *pNoNameData; |
256 | 0 | } |
257 | |
|
258 | 0 | SCCOL nOldX1; // take old range away cleanly |
259 | 0 | SCROW nOldY1; //! (UNDO ???) |
260 | 0 | SCCOL nOldX2; |
261 | 0 | SCROW nOldY2; |
262 | 0 | SCTAB nOldTab; |
263 | 0 | pNoNameData->GetArea( nOldTab, nOldX1, nOldY1, nOldX2, nOldY2 ); |
264 | | |
265 | | // If previously bHasHeader was set and the new range starts on the |
266 | | // same row and intersects the old column range, then don't reset |
267 | | // bHasHeader but assume that the new range still has headers, just |
268 | | // some are empty or numeric. |
269 | 0 | if (!bHasHeader && pNoNameData->HasHeader() && nTab == nOldTab && nStartRow == nOldY1 && |
270 | 0 | nStartCol <= nOldY2 && nOldY1 <= nEndCol) |
271 | 0 | bHasHeader = true; |
272 | | |
273 | | // Remove AutoFilter button flags only for sheet-local DB range, |
274 | | // not if a temporary is used. |
275 | 0 | if (bSheetLocal) |
276 | 0 | DBAreaDeleted( nOldTab, nOldX1, nOldY1, nOldX2 ); |
277 | |
|
278 | 0 | pNoNameData->SetSortParam( ScSortParam() ); // reset parameter |
279 | 0 | pNoNameData->SetQueryParam( ScQueryParam() ); |
280 | 0 | pNoNameData->SetSubTotalParam( ScSubTotalParam() ); |
281 | |
|
282 | 0 | pNoNameData->SetArea( nTab, nStartCol,nStartRow, nEndCol,nEndRow ); // set anew |
283 | 0 | pNoNameData->SetByRow( true ); |
284 | 0 | pNoNameData->SetHeader( bHasHeader ); |
285 | 0 | pNoNameData->SetAutoFilter( false ); |
286 | 0 | } |
287 | 0 | else |
288 | 0 | { |
289 | 0 | std::unique_ptr<ScDBCollection> pUndoColl; |
290 | |
|
291 | 0 | if (eMode==SC_DB_IMPORT) |
292 | 0 | { |
293 | 0 | m_pDocument->PreprocessDBDataUpdate(); |
294 | 0 | pUndoColl.reset( new ScDBCollection( *pColl ) ); // Undo for import range |
295 | |
|
296 | 0 | OUString aImport = ScResId( STR_DBNAME_IMPORT ); |
297 | 0 | tools::Long nCount = 0; |
298 | 0 | const ScDBData* pDummy = nullptr; |
299 | 0 | ScDBCollection::NamedDBs& rDBs = pColl->getNamedDBs(); |
300 | 0 | OUString aNewName; |
301 | 0 | do |
302 | 0 | { |
303 | 0 | ++nCount; |
304 | 0 | aNewName = aImport + OUString::number( nCount ); |
305 | 0 | pDummy = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)); |
306 | 0 | } |
307 | 0 | while (pDummy); |
308 | 0 | pNoNameData = new ScDBData( aNewName, nTab, |
309 | 0 | nStartCol,nStartRow, nEndCol,nEndRow, |
310 | 0 | true, bHasHeader ); |
311 | 0 | bool ins = rDBs.insert(std::unique_ptr<ScDBData>(pNoNameData)); |
312 | 0 | assert(ins); (void)ins; |
313 | 0 | } |
314 | 0 | else |
315 | 0 | { |
316 | 0 | pNoNameData = new ScDBData(STR_DB_LOCAL_NONAME, nTab, |
317 | 0 | nStartCol,nStartRow, nEndCol,nEndRow, |
318 | 0 | true, bHasHeader ); |
319 | 0 | m_pDocument->SetAnonymousDBData(nTab, std::unique_ptr<ScDBData>(pNoNameData)); |
320 | 0 | } |
321 | |
|
322 | 0 | if ( pUndoColl ) |
323 | 0 | { |
324 | 0 | m_pDocument->CompileHybridFormula(); |
325 | |
|
326 | 0 | GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDBData>( *this, u""_ustr, |
327 | 0 | std::move(pUndoColl), pNoNameData ? pNoNameData->GetName() : u""_ustr, |
328 | 0 | std::make_unique<ScDBCollection>( *pColl ) ) ); |
329 | 0 | } |
330 | | |
331 | | // no longer needed to register new range at the Sba |
332 | | |
333 | | // announce "Import1", etc., at the Navigator |
334 | 0 | if (eMode==SC_DB_IMPORT) |
335 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); |
336 | 0 | } |
337 | 0 | pData = pNoNameData; |
338 | 0 | } |
339 | |
|
340 | 0 | return pData; |
341 | 0 | } |
342 | | |
343 | | ScDBData* ScDocShell::GetAnonymousDBData(const ScRange& rRange) |
344 | 0 | { |
345 | 0 | ScDBCollection* pColl = m_pDocument->GetDBCollection(); |
346 | 0 | if (!pColl) |
347 | 0 | return nullptr; |
348 | | |
349 | 0 | ScDBData* pData = pColl->getAnonDBs().getByRange(rRange); |
350 | 0 | if (!pData) |
351 | 0 | return nullptr; |
352 | | |
353 | 0 | if (!pData->HasHeader()) |
354 | 0 | { |
355 | 0 | bool bHasHeader = m_pDocument->HasColHeader( |
356 | 0 | rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab()); |
357 | 0 | pData->SetHeader(bHasHeader); |
358 | 0 | } |
359 | |
|
360 | 0 | return pData; |
361 | 0 | } |
362 | | |
363 | | std::unique_ptr<ScDBData> ScDocShell::GetOldAutoDBRange() |
364 | 0 | { |
365 | 0 | return std::move(m_pOldAutoDBRange); |
366 | 0 | } |
367 | | |
368 | | void ScDocShell::CancelAutoDBRange() |
369 | 0 | { |
370 | | // called when dialog is cancelled |
371 | | //moggi:TODO |
372 | 0 | if ( !m_pOldAutoDBRange ) |
373 | 0 | return; |
374 | | |
375 | 0 | SCTAB nTab = GetCurTab(); |
376 | 0 | ScDBData* pDBData = m_pDocument->GetAnonymousDBData(nTab); |
377 | 0 | if ( pDBData ) |
378 | 0 | { |
379 | 0 | SCCOL nRangeX1; |
380 | 0 | SCROW nRangeY1; |
381 | 0 | SCCOL nRangeX2; |
382 | 0 | SCROW nRangeY2; |
383 | 0 | SCTAB nRangeTab; |
384 | 0 | pDBData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 ); |
385 | 0 | DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 ); |
386 | |
|
387 | 0 | *pDBData = *m_pOldAutoDBRange; // restore old settings |
388 | |
|
389 | 0 | if ( m_pOldAutoDBRange->HasAutoFilter() ) |
390 | 0 | { |
391 | | // restore AutoFilter buttons |
392 | 0 | m_pOldAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 ); |
393 | 0 | m_pDocument->ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto ); |
394 | 0 | PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid ); |
395 | 0 | } |
396 | 0 | } |
397 | |
|
398 | 0 | m_pOldAutoDBRange.reset(); |
399 | 0 | } |
400 | | |
401 | | // adjust height |
402 | | //! merge with docfunc |
403 | | |
404 | | bool ScDocShell::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) |
405 | 0 | { |
406 | 0 | ScSizeDeviceProvider aProv(*this); |
407 | 0 | double fZoom(1.0); |
408 | 0 | sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), fZoom, fZoom, aProv.GetDevice()); |
409 | 0 | bool bChange = m_pDocument->SetOptimalHeight(aCxt, nStartRow,nEndRow, nTab, true); |
410 | |
|
411 | 0 | if (bChange) |
412 | 0 | { |
413 | | // tdf#76183: recalculate objects' positions |
414 | 0 | m_pDocument->SetDrawPageSize(nTab); |
415 | |
|
416 | 0 | PostPaint( 0,nStartRow,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid|PaintPartFlags::Left ); |
417 | 0 | } |
418 | |
|
419 | 0 | return bChange; |
420 | 0 | } |
421 | | |
422 | | void ScDocShell::UpdateAllRowHeights( const ScMarkData* pTabMark ) |
423 | 0 | { |
424 | | // update automatic row heights |
425 | |
|
426 | 0 | ScSizeDeviceProvider aProv(*this); |
427 | 0 | double fZoom(1.0); |
428 | 0 | sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), fZoom, fZoom, aProv.GetDevice()); |
429 | 0 | m_pDocument->UpdateAllRowHeights(aCxt, pTabMark); |
430 | 0 | } |
431 | | |
432 | | void ScDocShell::UpdateAllRowHeights(const bool bOnlyUsedRows) |
433 | 4.11k | { |
434 | | // update automatic row heights on all sheets using the newer ScDocRowHeightUpdater |
435 | 4.11k | ScSizeDeviceProvider aProv(*this); |
436 | 4.11k | ScDocRowHeightUpdater aUpdater(*m_pDocument, aProv.GetDevice(), aProv.GetPPTX(), |
437 | 4.11k | aProv.GetPPTY(), nullptr); |
438 | 4.11k | aUpdater.update(bOnlyUsedRows); |
439 | 4.11k | } |
440 | | |
441 | | void ScDocShell::UpdatePendingRowHeights( SCTAB nUpdateTab, bool bBefore ) |
442 | 30.0k | { |
443 | 30.0k | bool bIsUndoEnabled = m_pDocument->IsUndoEnabled(); |
444 | 30.0k | m_pDocument->EnableUndo( false ); |
445 | 30.0k | m_pDocument->LockStreamValid( true ); // ignore draw page size (but not formula results) |
446 | 30.0k | if ( bBefore ) // check all sheets up to nUpdateTab |
447 | 0 | { |
448 | 0 | SCTAB nTabCount = m_pDocument->GetTableCount(); |
449 | 0 | if ( nUpdateTab >= nTabCount ) |
450 | 0 | nUpdateTab = nTabCount-1; // nUpdateTab is inclusive |
451 | |
|
452 | 0 | ScMarkData aUpdateSheets(m_pDocument->GetSheetLimits()); |
453 | 0 | SCTAB nTab; |
454 | 0 | for (nTab=0; nTab<=nUpdateTab; ++nTab) |
455 | 0 | if ( m_pDocument->IsPendingRowHeights( nTab ) ) |
456 | 0 | aUpdateSheets.SelectTable( nTab, true ); |
457 | |
|
458 | 0 | if (aUpdateSheets.GetSelectCount()) |
459 | 0 | UpdateAllRowHeights(&aUpdateSheets); // update with a single progress bar |
460 | |
|
461 | 0 | for (nTab=0; nTab<=nUpdateTab; ++nTab) |
462 | 0 | if ( aUpdateSheets.GetTableSelect( nTab ) ) |
463 | 0 | { |
464 | 0 | m_pDocument->UpdatePageBreaks( nTab ); |
465 | 0 | m_pDocument->SetPendingRowHeights( nTab, false ); |
466 | 0 | } |
467 | 0 | } |
468 | 30.0k | else // only nUpdateTab |
469 | 30.0k | { |
470 | 30.0k | if ( m_pDocument->IsPendingRowHeights( nUpdateTab ) ) |
471 | 0 | { |
472 | 0 | AdjustRowHeight( 0, m_pDocument->MaxRow(), nUpdateTab ); |
473 | 0 | m_pDocument->UpdatePageBreaks( nUpdateTab ); |
474 | 0 | m_pDocument->SetPendingRowHeights( nUpdateTab, false ); |
475 | 0 | } |
476 | 30.0k | } |
477 | 30.0k | m_pDocument->LockStreamValid( false ); |
478 | 30.0k | m_pDocument->EnableUndo( bIsUndoEnabled ); |
479 | 30.0k | } |
480 | | |
481 | | void ScDocShell::RefreshPivotTables( const ScRange& rSource ) |
482 | 0 | { |
483 | 0 | ScDPCollection* pColl = m_pDocument->GetDPCollection(); |
484 | 0 | if (!pColl) |
485 | 0 | return; |
486 | | |
487 | 0 | ScDBDocFunc aFunc(*this); |
488 | 0 | for (size_t i = 0, n = pColl->GetCount(); i < n; ++i) |
489 | 0 | { |
490 | 0 | ScDPObject& rOld = (*pColl)[i]; |
491 | |
|
492 | 0 | const ScSheetSourceDesc* pSheetDesc = rOld.GetSheetDesc(); |
493 | 0 | if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rSource)) |
494 | 0 | aFunc.UpdatePivotTable(rOld, true, false); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | // Re-check outputs that were previously blocked by #SPILL! (array formula |
499 | | // cells and pivot tables). Called after cell-change operations that might |
500 | | // have freed blocking cells. |
501 | | void ScDocShell::ResolveSpilledOutputs() |
502 | 375 | { |
503 | 375 | bool bAnyResolved = false; |
504 | | |
505 | | // Array formula cells currently in #SPILL! state: re-interpretation will |
506 | | // clear the error if the blocker is gone and then expand to result dimensions. |
507 | 375 | const std::unordered_set<ScAddress> aSpilledCells = m_pDocument->GetSpilledFormulaCells(); |
508 | 375 | for (const ScAddress& rPosition : aSpilledCells) |
509 | 0 | { |
510 | 0 | ScFormulaCell* pFormulaCell = m_pDocument->GetFormulaCell(rPosition); |
511 | 0 | if (pFormulaCell && pFormulaCell->GetErrCode() == FormulaError::Spill) |
512 | 0 | { |
513 | 0 | pFormulaCell->SetDirty(); |
514 | 0 | pFormulaCell->Interpret(); |
515 | 0 | if (pFormulaCell->GetErrCode() != FormulaError::Spill) |
516 | 0 | { |
517 | 0 | bAnyResolved = true; |
518 | | // Expand the matrix to the result dimensions. |
519 | 0 | const ScMatrix* pMatrix = pFormulaCell->GetMatrix(); |
520 | 0 | if (pMatrix) |
521 | 0 | { |
522 | 0 | SCSIZE nResCols = 0; |
523 | 0 | SCSIZE nResRows = 0; |
524 | 0 | pMatrix->GetDimensions(nResCols, nResRows); |
525 | 0 | m_pDocument->ResizeMatrixFormula(rPosition, |
526 | 0 | SCCOL(nResCols), |
527 | 0 | SCROW(nResRows)); |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | | // Inverse direction: previously expanded dynamic array masters whose |
534 | | // declared range now contains a foreign cell. The matrix master doesn't |
535 | | // listen to its declared range, so we explicitly check. When a blocker |
536 | | // is found, collapse the matrix back to 1x1 (preserving the blocker) and |
537 | | // re-interpret. |
538 | 375 | const std::unordered_set<ScAddress> aExpandedMatrices = m_pDocument->GetExpandedDynamicArrays(); |
539 | 375 | for (const ScAddress& rPosition : aExpandedMatrices) |
540 | 0 | { |
541 | 0 | ScFormulaCell* pFormulaCell = m_pDocument->GetFormulaCell(rPosition); |
542 | 0 | if (!pFormulaCell || pFormulaCell->GetMatrixFlag() != ScMatrixMode::Formula) |
543 | 0 | { |
544 | 0 | m_pDocument->UnmarkExpandedDynamicArray(rPosition); |
545 | 0 | continue; |
546 | 0 | } |
547 | 0 | SCCOL nDeclCols = 0; |
548 | 0 | SCROW nDeclRows = 0; |
549 | 0 | pFormulaCell->GetMatColsRows(nDeclCols, nDeclRows); |
550 | 0 | if (nDeclCols <= 1 && nDeclRows <= 1) |
551 | 0 | { |
552 | 0 | m_pDocument->UnmarkExpandedDynamicArray(rPosition); |
553 | 0 | continue; |
554 | 0 | } |
555 | 0 | ScRange aRange(rPosition.Col(), rPosition.Row(), rPosition.Tab(), |
556 | 0 | rPosition.Col() + nDeclCols - 1, rPosition.Row() + nDeclRows - 1, |
557 | 0 | rPosition.Tab()); |
558 | 0 | if (!m_pDocument->HasMatrixBlocker(aRange)) |
559 | 0 | continue; |
560 | | |
561 | | // Collapse: drop our own reference cells from the declared range. |
562 | | // Then SetMatColsRows on the master and re-interpret - the runtime |
563 | | // spill check now sees res > declared and sets #SPILL!. |
564 | 0 | for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) |
565 | 0 | { |
566 | 0 | for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) |
567 | 0 | { |
568 | 0 | if (nCol == rPosition.Col() && nRow == rPosition.Row()) |
569 | 0 | continue; |
570 | 0 | ScRefCellValue aCell(*m_pDocument, ScAddress(nCol, nRow, rPosition.Tab())); |
571 | 0 | if (aCell.getType() == CELLTYPE_FORMULA |
572 | 0 | && aCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Reference) |
573 | 0 | { |
574 | 0 | m_pDocument->DeleteAreaTab(nCol, nRow, nCol, nRow, rPosition.Tab(), |
575 | 0 | InsertDeleteFlags::ALL); |
576 | 0 | } |
577 | 0 | } |
578 | 0 | } |
579 | 0 | pFormulaCell->SetMatColsRows(1, 1); |
580 | 0 | m_pDocument->UnmarkExpandedDynamicArray(rPosition); |
581 | | // Conventional matrix formulas don't normally opt into the runtime |
582 | | // spill check. Pre-marking the cell flips that bit so the next |
583 | | // interpret recognises this as a spill-blocked master. |
584 | 0 | m_pDocument->MarkFormulaSpilled(rPosition); |
585 | 0 | pFormulaCell->SetDirty(); |
586 | 0 | pFormulaCell->Interpret(); |
587 | 0 | bAnyResolved = true; |
588 | 0 | } |
589 | | |
590 | | // Re-check spilled pivot tables. |
591 | 375 | ScDPCollection* pCollection = m_pDocument->GetDPCollection(); |
592 | 375 | if (pCollection) |
593 | 375 | { |
594 | 375 | bool bUndoEnabled = m_pDocument->IsUndoEnabled(); |
595 | 375 | bool bEnteredList = false; |
596 | 375 | size_t nCount = pCollection->GetCount(); |
597 | 479 | for (size_t i = 0; i < nCount; ++i) |
598 | 104 | { |
599 | 104 | ScDPObject& rDPObject = (*pCollection)[i]; |
600 | 104 | if (!rDPObject.HasSpillError()) |
601 | 104 | continue; |
602 | | |
603 | | // Try to re-output with spill check. If the blocking cells have been |
604 | | // cleared, Output() will succeed and the pivot table will be rendered. |
605 | 0 | ScAddress aPosition = rDPObject.GetOutRange().aStart; |
606 | 0 | rDPObject.InvalidateData(); |
607 | 0 | if (rDPObject.Output(aPosition, true)) |
608 | 0 | { |
609 | 0 | bAnyResolved = true; |
610 | |
|
611 | 0 | if (bUndoEnabled) |
612 | 0 | { |
613 | 0 | if (!bEnteredList) |
614 | 0 | { |
615 | 0 | GetUndoManager()->EnterListAction(u""_ustr, u""_ustr, 0, ViewShellId(-1)); |
616 | 0 | bEnteredList = true; |
617 | 0 | } |
618 | 0 | GetUndoManager()->AddUndoAction( |
619 | 0 | std::make_unique<sc::UndoSpillPivotTable>( |
620 | 0 | *this, rDPObject.GetName(), rDPObject.GetOutRange())); |
621 | 0 | } |
622 | 0 | } |
623 | 0 | } |
624 | | |
625 | 375 | if (bEnteredList) |
626 | 0 | GetUndoManager()->LeaveAndMergeListAction(); |
627 | 375 | } |
628 | | |
629 | 375 | if (bAnyResolved) |
630 | 0 | PostPaintGridAll(); |
631 | 375 | } |
632 | | |
633 | | static OUString lcl_GetAreaName( ScDocument* pDoc, const ScArea* pArea ) |
634 | 0 | { |
635 | 0 | ScDBData* pData = pDoc->GetDBAtArea( pArea->nTab, pArea->nColStart, pArea->nRowStart, |
636 | 0 | pArea->nColEnd, pArea->nRowEnd ); |
637 | 0 | if (pData) |
638 | 0 | return pData->GetName(); |
639 | | |
640 | 0 | OUString aName; |
641 | 0 | pDoc->GetName( pArea->nTab, aName ); |
642 | 0 | return aName; |
643 | 0 | } |
644 | | |
645 | | static ScDBData* getUndoData(const ScDBData* pDestData) |
646 | 0 | { |
647 | 0 | if (!pDestData) |
648 | 0 | return nullptr; |
649 | 0 | return new ScDBData(*pDestData); |
650 | 0 | } |
651 | | |
652 | | void ScDocShell::DoConsolidate( const ScConsolidateParam& rParam, bool bRecord ) |
653 | 0 | { |
654 | 0 | ScConsData aData; |
655 | |
|
656 | 0 | sal_uInt16 nPos; |
657 | 0 | SCCOL nColSize = 0; |
658 | 0 | SCROW nRowSize = 0; |
659 | 0 | bool bErr = false; |
660 | 0 | for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) |
661 | 0 | { |
662 | 0 | ScArea const & rArea = rParam.pDataAreas[nPos]; |
663 | 0 | nColSize = std::max( nColSize, SCCOL( rArea.nColEnd - rArea.nColStart + 1 ) ); |
664 | 0 | nRowSize = std::max( nRowSize, SCROW( rArea.nRowEnd - rArea.nRowStart + 1 ) ); |
665 | | |
666 | | // test if source data were moved |
667 | 0 | if (rParam.bReferenceData) |
668 | 0 | if (rArea.nTab == rParam.nTab && rArea.nRowEnd >= rParam.nRow) |
669 | 0 | bErr = true; |
670 | 0 | } |
671 | |
|
672 | 0 | if (bErr) |
673 | 0 | { |
674 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), |
675 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
676 | 0 | ScResId(STR_CONSOLIDATE_ERR1))); |
677 | 0 | xInfoBox->run(); |
678 | 0 | return; |
679 | 0 | } |
680 | | |
681 | | // execute |
682 | | |
683 | 0 | weld::WaitObject aWait( GetActiveDialogParent() ); |
684 | 0 | ScDocShellModificator aModificator( *this ); |
685 | |
|
686 | 0 | ScRange aOldDest; |
687 | 0 | ScDBData* pDestData = m_pDocument->GetDBAtCursor( rParam.nCol, rParam.nRow, rParam.nTab, ScDBDataPortion::TOP_LEFT ); |
688 | 0 | if (pDestData) |
689 | 0 | pDestData->GetArea(aOldDest); |
690 | |
|
691 | 0 | aData.SetSize( nColSize, nRowSize ); |
692 | 0 | aData.SetFlags( rParam.eFunction, rParam.bByCol, rParam.bByRow, rParam.bReferenceData ); |
693 | 0 | if ( rParam.bByCol || rParam.bByRow ) |
694 | 0 | for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) |
695 | 0 | { |
696 | 0 | ScArea const & rArea = rParam.pDataAreas[nPos]; |
697 | 0 | aData.AddFields( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart, |
698 | 0 | rArea.nColEnd, rArea.nRowEnd ); |
699 | 0 | } |
700 | 0 | aData.DoneFields(); |
701 | 0 | for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) |
702 | 0 | { |
703 | 0 | ScArea const & rArea = rParam.pDataAreas[nPos]; |
704 | 0 | aData.AddData( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart, |
705 | 0 | rArea.nColEnd, rArea.nRowEnd ); |
706 | 0 | aData.AddName( lcl_GetAreaName(m_pDocument.get(), &rArea) ); |
707 | 0 | } |
708 | |
|
709 | 0 | aData.GetSize( nColSize, nRowSize ); |
710 | 0 | if (bRecord && nColSize > 0 && nRowSize > 0) |
711 | 0 | { |
712 | 0 | std::unique_ptr<ScDBData> pUndoData(getUndoData(pDestData)); |
713 | |
|
714 | 0 | SCTAB nDestTab = rParam.nTab; |
715 | 0 | ScArea aDestArea( rParam.nTab, rParam.nCol, rParam.nRow, |
716 | 0 | rParam.nCol+nColSize-1, rParam.nRow+nRowSize-1 ); |
717 | 0 | if (rParam.bByCol) ++aDestArea.nColEnd; |
718 | 0 | if (rParam.bByRow) ++aDestArea.nRowEnd; |
719 | |
|
720 | 0 | if (rParam.bReferenceData) |
721 | 0 | { |
722 | 0 | SCTAB nTabCount = m_pDocument->GetTableCount(); |
723 | 0 | SCROW nInsertCount = aData.GetInsertCount(); |
724 | | |
725 | | // old outlines |
726 | 0 | ScOutlineTable* pTable = m_pDocument->GetOutlineTable( nDestTab ); |
727 | 0 | std::unique_ptr<ScOutlineTable> pUndoTab(pTable ? new ScOutlineTable( *pTable ) : nullptr); |
728 | |
|
729 | 0 | ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); |
730 | 0 | pUndoDoc->InitUndo( *m_pDocument, 0, nTabCount-1, false, true ); |
731 | | |
732 | | // row state |
733 | 0 | m_pDocument->CopyToDocument(0, 0, nDestTab, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nDestTab, |
734 | 0 | InsertDeleteFlags::NONE, false, *pUndoDoc); |
735 | | |
736 | | // all formulas |
737 | 0 | m_pDocument->CopyToDocument(0, 0, 0, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nTabCount-1, |
738 | 0 | InsertDeleteFlags::FORMULA, false, *pUndoDoc); |
739 | | |
740 | | // complete output rows |
741 | 0 | m_pDocument->CopyToDocument(0, aDestArea.nRowStart, nDestTab, |
742 | 0 | m_pDocument->MaxCol(),aDestArea.nRowEnd, nDestTab, |
743 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc); |
744 | | |
745 | | // old output range |
746 | 0 | if (pDestData) |
747 | 0 | m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc); |
748 | |
|
749 | 0 | GetUndoManager()->AddUndoAction( |
750 | 0 | std::make_unique<ScUndoConsolidate>( *this, aDestArea, rParam, std::move(pUndoDoc), |
751 | 0 | true, nInsertCount, std::move(pUndoTab), std::move(pUndoData) ) ); |
752 | 0 | } |
753 | 0 | else |
754 | 0 | { |
755 | 0 | ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); |
756 | 0 | pUndoDoc->InitUndo( *m_pDocument, aDestArea.nTab, aDestArea.nTab ); |
757 | |
|
758 | 0 | m_pDocument->CopyToDocument(aDestArea.nColStart, aDestArea.nRowStart, aDestArea.nTab, |
759 | 0 | aDestArea.nColEnd, aDestArea.nRowEnd, aDestArea.nTab, |
760 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc); |
761 | | |
762 | | // old output range |
763 | 0 | if (pDestData) |
764 | 0 | m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc); |
765 | |
|
766 | 0 | GetUndoManager()->AddUndoAction( |
767 | 0 | std::make_unique<ScUndoConsolidate>( *this, aDestArea, rParam, std::move(pUndoDoc), |
768 | 0 | false, 0, nullptr, std::move(pUndoData) ) ); |
769 | 0 | } |
770 | 0 | } |
771 | |
|
772 | 0 | if (pDestData) // delete / adjust destination range |
773 | 0 | { |
774 | 0 | m_pDocument->DeleteAreaTab(aOldDest, InsertDeleteFlags::CONTENTS); |
775 | 0 | pDestData->SetArea( rParam.nTab, rParam.nCol, rParam.nRow, |
776 | 0 | rParam.nCol + nColSize - 1, rParam.nRow + nRowSize - 1 ); |
777 | 0 | pDestData->SetHeader( rParam.bByRow ); |
778 | 0 | } |
779 | |
|
780 | 0 | aData.OutputToDocument( *m_pDocument, rParam.nCol, rParam.nRow, rParam.nTab ); |
781 | |
|
782 | 0 | SCCOL nPaintStartCol = rParam.nCol; |
783 | 0 | SCROW nPaintStartRow = rParam.nRow; |
784 | 0 | SCCOL nPaintEndCol = nPaintStartCol + nColSize - 1; |
785 | 0 | SCROW nPaintEndRow = nPaintStartRow + nRowSize - 1; |
786 | 0 | PaintPartFlags nPaintFlags = PaintPartFlags::Grid; |
787 | 0 | if (rParam.bByCol) |
788 | 0 | ++nPaintEndRow; |
789 | 0 | if (rParam.bByRow) |
790 | 0 | ++nPaintEndCol; |
791 | 0 | if (rParam.bReferenceData) |
792 | 0 | { |
793 | 0 | nPaintStartCol = 0; |
794 | 0 | nPaintEndCol = m_pDocument->MaxCol(); |
795 | 0 | nPaintEndRow = m_pDocument->MaxRow(); |
796 | 0 | nPaintFlags |= PaintPartFlags::Left | PaintPartFlags::Size; |
797 | 0 | } |
798 | 0 | if (pDestData) |
799 | 0 | { |
800 | 0 | if ( aOldDest.aEnd.Col() > nPaintEndCol ) |
801 | 0 | nPaintEndCol = aOldDest.aEnd.Col(); |
802 | 0 | if ( aOldDest.aEnd.Row() > nPaintEndRow ) |
803 | 0 | nPaintEndRow = aOldDest.aEnd.Row(); |
804 | 0 | } |
805 | 0 | PostPaint( nPaintStartCol, nPaintStartRow, rParam.nTab, |
806 | 0 | nPaintEndCol, nPaintEndRow, rParam.nTab, nPaintFlags ); |
807 | 0 | aModificator.SetDocumentModified(); |
808 | 0 | } |
809 | | |
810 | | void ScDocShell::UseScenario( SCTAB nTab, const OUString& rName, bool bRecord ) |
811 | 0 | { |
812 | 0 | if (!m_pDocument->IsScenario(nTab)) |
813 | 0 | { |
814 | 0 | SCTAB nTabCount = m_pDocument->GetTableCount(); |
815 | 0 | SCTAB nSrcTab = SCTAB_MAX; |
816 | 0 | SCTAB nEndTab = nTab; |
817 | 0 | OUString aCompare; |
818 | 0 | while ( nEndTab+1 < nTabCount && m_pDocument->IsScenario(nEndTab+1) ) |
819 | 0 | { |
820 | 0 | ++nEndTab; |
821 | 0 | if (nSrcTab > MAXTAB) // still searching for the scenario? |
822 | 0 | { |
823 | 0 | m_pDocument->GetName( nEndTab, aCompare ); |
824 | 0 | if (aCompare == rName) |
825 | 0 | nSrcTab = nEndTab; // found |
826 | 0 | } |
827 | 0 | } |
828 | 0 | if (ValidTab(nSrcTab)) |
829 | 0 | { |
830 | 0 | if ( m_pDocument->TestCopyScenario( nSrcTab, nTab ) ) // test cell protection |
831 | 0 | { |
832 | 0 | ScDocShellModificator aModificator( *this ); |
833 | 0 | ScMarkData aScenMark(m_pDocument->GetSheetLimits()); |
834 | 0 | m_pDocument->MarkScenario( nSrcTab, nTab, aScenMark ); |
835 | 0 | const ScRange& aMultiRange = aScenMark.GetMultiMarkArea(); |
836 | 0 | SCCOL nStartCol = aMultiRange.aStart.Col(); |
837 | 0 | SCROW nStartRow = aMultiRange.aStart.Row(); |
838 | 0 | SCCOL nEndCol = aMultiRange.aEnd.Col(); |
839 | 0 | SCROW nEndRow = aMultiRange.aEnd.Row(); |
840 | |
|
841 | 0 | if (bRecord) |
842 | 0 | { |
843 | 0 | ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); |
844 | 0 | pUndoDoc->InitUndo( *m_pDocument, nTab,nEndTab ); // also all scenarios |
845 | | // shown table: |
846 | 0 | m_pDocument->CopyToDocument(nStartCol, nStartRow, nTab, |
847 | 0 | nEndCol, nEndRow, nTab, InsertDeleteFlags::ALL, |
848 | 0 | true, *pUndoDoc, &aScenMark); |
849 | | // scenarios |
850 | 0 | for (SCTAB i=nTab+1; i<=nEndTab; i++) |
851 | 0 | { |
852 | 0 | pUndoDoc->SetScenario( i, true ); |
853 | 0 | OUString aComment; |
854 | 0 | Color aColor; |
855 | 0 | ScScenarioFlags nScenFlags; |
856 | 0 | m_pDocument->GetScenarioData( i, aComment, aColor, nScenFlags ); |
857 | 0 | pUndoDoc->SetScenarioData( i, aComment, aColor, nScenFlags ); |
858 | 0 | bool bActive = m_pDocument->IsActiveScenario( i ); |
859 | 0 | pUndoDoc->SetActiveScenario( i, bActive ); |
860 | | // At copy-back scenarios also contents |
861 | 0 | if ( nScenFlags & ScScenarioFlags::TwoWay ) |
862 | 0 | m_pDocument->CopyToDocument(0, 0, i, m_pDocument->MaxCol(), m_pDocument->MaxRow(), i, |
863 | 0 | InsertDeleteFlags::ALL, false, *pUndoDoc ); |
864 | 0 | } |
865 | |
|
866 | 0 | GetUndoManager()->AddUndoAction( |
867 | 0 | std::make_unique<ScUndoUseScenario>( *this, aScenMark, |
868 | 0 | ScArea( nTab,nStartCol,nStartRow,nEndCol,nEndRow ), |
869 | 0 | std::move(pUndoDoc), rName ) ); |
870 | 0 | } |
871 | |
|
872 | 0 | m_pDocument->CopyScenario( nSrcTab, nTab ); |
873 | |
|
874 | 0 | sc::SetFormulaDirtyContext aCxt; |
875 | 0 | m_pDocument->SetAllFormulasDirty(aCxt); |
876 | | |
877 | | // paint all, because the active scenario may be modified in other ranges; |
878 | | //! only if there are visible frames? |
879 | 0 | PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid ); |
880 | 0 | aModificator.SetDocumentModified(); |
881 | 0 | } |
882 | 0 | else |
883 | 0 | { |
884 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), |
885 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
886 | 0 | ScResId(STR_PROTECTIONERR))); |
887 | 0 | xInfoBox->run(); |
888 | 0 | } |
889 | 0 | } |
890 | 0 | else |
891 | 0 | { |
892 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), |
893 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
894 | 0 | ScResId(STR_SCENARIO_NOTFOUND))); |
895 | 0 | xInfoBox->run(); |
896 | 0 | } |
897 | 0 | } |
898 | 0 | else |
899 | 0 | { |
900 | 0 | OSL_FAIL( "UseScenario on Scenario-Sheet" ); |
901 | 0 | } |
902 | 0 | } |
903 | | |
904 | | void ScDocShell::ModifyScenario( SCTAB nTab, const OUString& rName, const OUString& rComment, |
905 | | const Color& rColor, ScScenarioFlags nFlags ) |
906 | 88 | { |
907 | | // Undo |
908 | 88 | OUString aOldName; |
909 | 88 | m_pDocument->GetName( nTab, aOldName ); |
910 | 88 | OUString aOldComment; |
911 | 88 | Color aOldColor; |
912 | 88 | ScScenarioFlags nOldFlags; |
913 | 88 | m_pDocument->GetScenarioData( nTab, aOldComment, aOldColor, nOldFlags ); |
914 | 88 | GetUndoManager()->AddUndoAction( |
915 | 88 | std::make_unique<ScUndoScenarioFlags>(*this, nTab, |
916 | 88 | aOldName, rName, aOldComment, rComment, |
917 | 88 | aOldColor, rColor, nOldFlags, nFlags) ); |
918 | | |
919 | | // execute |
920 | 88 | ScDocShellModificator aModificator( *this ); |
921 | 88 | m_pDocument->RenameTab( nTab, rName ); |
922 | 88 | m_pDocument->SetScenarioData( nTab, rComment, rColor, nFlags ); |
923 | 88 | PostPaintGridAll(); |
924 | 88 | aModificator.SetDocumentModified(); |
925 | | |
926 | 88 | if (aOldName != rName) |
927 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); |
928 | | |
929 | 88 | SfxBindings* pBindings = GetViewBindings(); |
930 | 88 | if (pBindings) |
931 | 0 | pBindings->Invalidate( SID_SELECT_SCENARIO ); |
932 | 88 | } |
933 | | |
934 | | SCTAB ScDocShell::MakeScenario( SCTAB nTab, const OUString& rName, const OUString& rComment, |
935 | | const Color& rColor, ScScenarioFlags nFlags, |
936 | | ScMarkData& rMark, bool bRecord ) |
937 | 22 | { |
938 | 22 | rMark.MarkToMulti(); |
939 | 22 | if (rMark.IsMultiMarked()) |
940 | 22 | { |
941 | 22 | SCTAB nNewTab = nTab + 1; |
942 | 33 | while (m_pDocument->IsScenario(nNewTab)) |
943 | 11 | ++nNewTab; |
944 | | |
945 | 22 | bool bCopyAll = ( (nFlags & ScScenarioFlags::CopyAll) != ScScenarioFlags::NONE ); |
946 | 22 | const ScMarkData* pCopyMark = nullptr; |
947 | 22 | if (!bCopyAll) |
948 | 22 | pCopyMark = &rMark; |
949 | | |
950 | 22 | ScDocShellModificator aModificator( *this ); |
951 | | |
952 | 22 | if (bRecord) |
953 | 22 | m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions |
954 | | |
955 | 22 | if (m_pDocument->CopyTab( nTab, nNewTab, pCopyMark )) |
956 | 22 | { |
957 | 22 | if (bRecord) |
958 | 22 | { |
959 | 22 | GetUndoManager()->AddUndoAction( |
960 | 22 | std::make_unique<ScUndoMakeScenario>( *this, nTab, nNewTab, |
961 | 22 | rName, rComment, rColor, nFlags, rMark )); |
962 | 22 | } |
963 | | |
964 | 22 | m_pDocument->RenameTab( nNewTab, rName); |
965 | 22 | m_pDocument->SetScenario( nNewTab, true ); |
966 | 22 | m_pDocument->SetScenarioData( nNewTab, rComment, rColor, nFlags ); |
967 | | |
968 | 22 | ScMarkData aDestMark = rMark; |
969 | 22 | aDestMark.SelectOneTable( nNewTab ); |
970 | | |
971 | | //! test for filter / buttons / merging |
972 | | |
973 | 22 | ScPatternAttr aProtPattern(m_pDocument->getCellAttributeHelper()); |
974 | 22 | aProtPattern.ItemSetPut(ScProtectionAttr(true)); |
975 | 22 | m_pDocument->ApplyPatternAreaTab( 0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(), nNewTab, aProtPattern ); |
976 | | |
977 | 22 | ScPatternAttr aPattern(m_pDocument->getCellAttributeHelper()); |
978 | 22 | aPattern.ItemSetPut(ScMergeFlagAttr(ScMF::Scenario)); |
979 | 22 | aPattern.ItemSetPut(ScProtectionAttr(true)); |
980 | 22 | m_pDocument->ApplySelectionPattern( aPattern, aDestMark ); |
981 | | |
982 | 22 | if (!bCopyAll) |
983 | 22 | m_pDocument->SetVisible( nNewTab, false ); |
984 | | |
985 | | // this is the active scenario, then |
986 | 22 | m_pDocument->CopyScenario( nNewTab, nTab, true ); // sal_True - don't copy anything from scenario |
987 | | |
988 | 22 | if (nFlags & ScScenarioFlags::ShowFrame) |
989 | 22 | PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid ); // paint frames |
990 | 22 | PostPaintExtras(); // table tab |
991 | 22 | aModificator.SetDocumentModified(); |
992 | | |
993 | | // A scenario tab is like a hidden sheet, broadcasting also |
994 | | // notifies ScTabViewShell to add an ScViewData::maTabData entry. |
995 | 22 | Broadcast( ScTablesHint( SC_TAB_INSERTED, nNewTab )); |
996 | 22 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); |
997 | | |
998 | 22 | return nNewTab; |
999 | 22 | } |
1000 | 22 | } |
1001 | 0 | return nTab; |
1002 | 22 | } |
1003 | | |
1004 | | bool ScDocShell::TransferTab( ScDocShell& rSrcDocShell, SCTAB nSrcPos, |
1005 | | SCTAB nDestPos, bool bInsertNew, |
1006 | | bool bNotifyAndPaint ) |
1007 | 0 | { |
1008 | 0 | ScDocument& rSrcDoc = rSrcDocShell.GetDocument(); |
1009 | | |
1010 | | // set the transferred area to the copyparam to make adjusting formulas possible |
1011 | 0 | ScClipParam aParam; |
1012 | 0 | ScRange aRange(0, 0, nSrcPos, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nSrcPos); |
1013 | 0 | aParam.maRanges.push_back(aRange); |
1014 | 0 | rSrcDoc.SetClipParam(aParam); |
1015 | |
|
1016 | 0 | bool bValid = m_pDocument->TransferTab( rSrcDoc, nSrcPos, nDestPos, |
1017 | 0 | bInsertNew ); // no insert |
1018 | | |
1019 | | // TransferTab doesn't copy drawing objects with bInsertNew=FALSE |
1020 | 0 | if ( bValid && !bInsertNew) |
1021 | 0 | m_pDocument->TransferDrawPage( rSrcDoc, nSrcPos, nDestPos ); |
1022 | |
|
1023 | 0 | if(bValid && rSrcDoc.IsScenario( nSrcPos )) |
1024 | 0 | { |
1025 | 0 | OUString aComment; |
1026 | 0 | Color aColor; |
1027 | 0 | ScScenarioFlags nFlags; |
1028 | |
|
1029 | 0 | rSrcDoc.GetScenarioData( nSrcPos, aComment,aColor, nFlags); |
1030 | 0 | m_pDocument->SetScenario(nDestPos,true); |
1031 | 0 | m_pDocument->SetScenarioData(nDestPos,aComment,aColor,nFlags); |
1032 | 0 | bool bActive = rSrcDoc.IsActiveScenario(nSrcPos); |
1033 | 0 | m_pDocument->SetActiveScenario(nDestPos, bActive ); |
1034 | |
|
1035 | 0 | bool bVisible = rSrcDoc.IsVisible(nSrcPos); |
1036 | 0 | m_pDocument->SetVisible(nDestPos,bVisible ); |
1037 | |
|
1038 | 0 | } |
1039 | |
|
1040 | 0 | if ( bValid && rSrcDoc.IsTabProtected( nSrcPos ) ) |
1041 | 0 | m_pDocument->SetTabProtection(nDestPos, rSrcDoc.GetTabProtection(nSrcPos)); |
1042 | 0 | if ( bNotifyAndPaint ) |
1043 | 0 | { |
1044 | 0 | Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestPos ) ); |
1045 | 0 | PostPaintExtras(); |
1046 | 0 | PostPaintGridAll(); |
1047 | 0 | } |
1048 | 0 | return bValid; |
1049 | 0 | } |
1050 | | |
1051 | | bool ScDocShell::MoveTable( SCTAB nSrcTab, SCTAB nDestTab, bool bCopy, bool bRecord ) |
1052 | 0 | { |
1053 | 0 | ScDocShellModificator aModificator( *this ); |
1054 | | |
1055 | | // #i92477# be consistent with ScDocFunc::InsertTable: any index past the last sheet means "append" |
1056 | | // #i101139# nDestTab must be the target position, not APPEND (for CopyTabProtection etc.) |
1057 | 0 | if ( nDestTab >= m_pDocument->GetTableCount() ) |
1058 | 0 | nDestTab = m_pDocument->GetTableCount(); |
1059 | |
|
1060 | 0 | if (bCopy) |
1061 | 0 | { |
1062 | 0 | if (bRecord) |
1063 | 0 | m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions |
1064 | |
|
1065 | 0 | OUString sSrcCodeName; |
1066 | 0 | m_pDocument->GetCodeName( nSrcTab, sSrcCodeName ); |
1067 | 0 | if (!m_pDocument->CopyTab( nSrcTab, nDestTab )) |
1068 | 0 | { |
1069 | | //! EndDrawUndo? |
1070 | 0 | return false; |
1071 | 0 | } |
1072 | 0 | else |
1073 | 0 | { |
1074 | 0 | SCTAB nAdjSource = nSrcTab; |
1075 | 0 | if ( nDestTab <= nSrcTab ) |
1076 | 0 | ++nAdjSource; // new position of source table after CopyTab |
1077 | |
|
1078 | 0 | if ( m_pDocument->IsTabProtected( nAdjSource ) ) |
1079 | 0 | m_pDocument->CopyTabProtection(nAdjSource, nDestTab); |
1080 | |
|
1081 | 0 | if (bRecord) |
1082 | 0 | { |
1083 | 0 | std::unique_ptr<std::vector<SCTAB>> pSrcList(new std::vector<SCTAB>(1, nSrcTab)); |
1084 | 0 | std::unique_ptr<std::vector<SCTAB>> pDestList(new std::vector<SCTAB>(1, nDestTab)); |
1085 | 0 | GetUndoManager()->AddUndoAction( |
1086 | 0 | std::make_unique<ScUndoCopyTab>(*this, std::move(pSrcList), std::move(pDestList))); |
1087 | 0 | } |
1088 | |
|
1089 | 0 | bool bVbaEnabled = m_pDocument->IsInVBAMode(); |
1090 | 0 | if ( bVbaEnabled ) |
1091 | 0 | { |
1092 | 0 | OUString aLibName( u"Standard"_ustr ); |
1093 | 0 | Reference< XLibraryContainer > xLibContainer = GetBasicContainer(); |
1094 | 0 | Reference< XVBACompatibility > xVBACompat( xLibContainer, UNO_QUERY ); |
1095 | |
|
1096 | 0 | if ( xVBACompat.is() ) |
1097 | 0 | { |
1098 | 0 | aLibName = xVBACompat->getProjectName(); |
1099 | 0 | } |
1100 | |
|
1101 | 0 | SCTAB nTabToUse = nDestTab; |
1102 | 0 | if ( nDestTab == SC_TAB_APPEND ) |
1103 | 0 | nTabToUse = m_pDocument->GetMaxTableNumber() - 1; |
1104 | 0 | OUString sSource; |
1105 | 0 | try |
1106 | 0 | { |
1107 | 0 | Reference< XNameContainer > xLib; |
1108 | 0 | if( xLibContainer.is() ) |
1109 | 0 | { |
1110 | 0 | css::uno::Any aLibAny = xLibContainer->getByName( aLibName ); |
1111 | 0 | aLibAny >>= xLib; |
1112 | 0 | } |
1113 | 0 | if( xLib.is() ) |
1114 | 0 | { |
1115 | 0 | xLib->getByName( sSrcCodeName ) >>= sSource; |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | catch ( const css::uno::Exception& ) |
1119 | 0 | { |
1120 | 0 | } |
1121 | 0 | VBA_InsertModule( *m_pDocument, nTabToUse, sSource ); |
1122 | 0 | } |
1123 | 0 | } |
1124 | 0 | Broadcast( ScTablesHint( SC_TAB_COPIED, nSrcTab, nDestTab ) ); |
1125 | 0 | } |
1126 | 0 | else |
1127 | 0 | { |
1128 | 0 | if ( m_pDocument->GetChangeTrack() ) |
1129 | 0 | return false; |
1130 | | |
1131 | 0 | if ( nSrcTab == nDestTab ) |
1132 | 0 | { |
1133 | | //! allow only for api calls? |
1134 | 0 | return true; // nothing to do, but valid |
1135 | 0 | } |
1136 | | |
1137 | 0 | std::optional<ScProgress> pProgress(std::in_place, this, ScResId(STR_UNDO_MOVE_TAB), |
1138 | 0 | m_pDocument->GetCodeCount(), true); |
1139 | 0 | bool bDone = m_pDocument->MoveTab( nSrcTab, nDestTab, &*pProgress ); |
1140 | 0 | pProgress.reset(); |
1141 | 0 | if (!bDone) |
1142 | 0 | { |
1143 | 0 | return false; |
1144 | 0 | } |
1145 | 0 | else if (bRecord) |
1146 | 0 | { |
1147 | 0 | std::unique_ptr<std::vector<SCTAB>> pSrcList(new std::vector<SCTAB>(1, nSrcTab)); |
1148 | 0 | std::unique_ptr<std::vector<SCTAB>> pDestList(new std::vector<SCTAB>(1, nDestTab)); |
1149 | 0 | GetUndoManager()->AddUndoAction( |
1150 | 0 | std::make_unique<ScUndoMoveTab>(*this, std::move(pSrcList), std::move(pDestList))); |
1151 | 0 | } |
1152 | | |
1153 | 0 | Broadcast( ScTablesHint( SC_TAB_MOVED, nSrcTab, nDestTab ) ); |
1154 | 0 | } |
1155 | | |
1156 | 0 | PostPaintGridAll(); |
1157 | 0 | PostPaintExtras(); |
1158 | 0 | aModificator.SetDocumentModified(); |
1159 | 0 | SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); |
1160 | |
|
1161 | 0 | return true; |
1162 | 0 | } |
1163 | | |
1164 | | IMPL_LINK( ScDocShell, RefreshDBDataHdl, Timer*, pRefreshTimer, void ) |
1165 | 0 | { |
1166 | 0 | ScDBDocFunc aFunc(*this); |
1167 | |
|
1168 | 0 | ScDBData* pDBData = static_cast<ScDBData*>(pRefreshTimer); |
1169 | 0 | ScImportParam aImportParam; |
1170 | 0 | pDBData->GetImportParam( aImportParam ); |
1171 | 0 | if (aImportParam.bImport && !pDBData->HasImportSelection()) |
1172 | 0 | { |
1173 | 0 | ScRange aRange; |
1174 | 0 | pDBData->GetArea( aRange ); |
1175 | 0 | bool bContinue = aFunc.DoImport( aRange.aStart.Tab(), aImportParam, nullptr ); //! Api-Flag as parameter |
1176 | | // internal operations (sort, query, subtotal) only if no error |
1177 | 0 | if (bContinue) |
1178 | 0 | { |
1179 | 0 | aFunc.RepeatDB( pDBData->GetName(), true, true ); |
1180 | 0 | RefreshPivotTables(aRange); |
1181 | 0 | } |
1182 | 0 | } |
1183 | 0 | } |
1184 | | |
1185 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |