tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / tool / dbdata.cxx
blobb719cb056221824e7470089dd65d959ac8a3be4d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <unotools/transliterationwrapper.hxx>
25 #include <unotools/charclass.hxx>
27 #include <dbdata.hxx>
28 #include <globalnames.hxx>
29 #include <refupdat.hxx>
30 #include <document.hxx>
31 #include <queryparam.hxx>
32 #include <queryentry.hxx>
33 #include <globstr.hrc>
34 #include <scresid.hxx>
35 #include <subtotalparam.hxx>
36 #include <sortparam.hxx>
37 #include <dociter.hxx>
38 #include <brdcst.hxx>
40 #include <comphelper/stl_types.hxx>
42 #include <memory>
43 #include <utility>
45 bool ScDBData::less::operator() (const std::unique_ptr<ScDBData>& left, const std::unique_ptr<ScDBData>& right) const
47 return ScGlobal::GetTransliteration().compareString(left->GetUpperName(), right->GetUpperName()) < 0;
50 ScDBData::ScDBData( const OUString& rName,
51 SCTAB nTab,
52 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
53 bool bByR, bool bHasH, bool bTotals) :
54 // Listeners are to be setup by the "parent" container.
55 mpSortParam(new ScSortParam),
56 mpQueryParam(new ScQueryParam),
57 mpSubTotal(new ScSubTotalParam),
58 mpImportParam(new ScImportParam),
59 mpContainer (nullptr),
60 aName (rName),
61 aUpper (rName),
62 nTable (nTab),
63 nStartCol (nCol1),
64 nStartRow (nRow1),
65 nEndCol (nCol2),
66 nEndRow (nRow2),
67 bByRow (bByR),
68 bHasHeader (bHasH),
69 bHasTotals (bTotals),
70 bDoSize (false),
71 bKeepFmt (false),
72 bStripData (false),
73 bIsAdvanced (false),
74 bDBSelection(false),
75 nIndex (0),
76 bAutoFilter (false),
77 bModified (false),
78 mbTableColumnNamesDirty(true),
79 nFilteredRowCount(SCSIZE_MAX)
81 aUpper = ScGlobal::getCharClass().uppercase(aUpper);
84 ScDBData::ScDBData( const ScDBData& rData ) :
85 // Listeners are to be setup by the "parent" container.
86 SvtListener (),
87 ScRefreshTimer ( rData ),
88 mpSortParam(new ScSortParam(*rData.mpSortParam)),
89 mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
90 mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
91 mpImportParam(new ScImportParam(*rData.mpImportParam)),
92 mpContainer (nullptr),
93 aName (rData.aName),
94 aUpper (rData.aUpper),
95 nTable (rData.nTable),
96 nStartCol (rData.nStartCol),
97 nStartRow (rData.nStartRow),
98 nEndCol (rData.nEndCol),
99 nEndRow (rData.nEndRow),
100 bByRow (rData.bByRow),
101 bHasHeader (rData.bHasHeader),
102 bHasTotals (rData.bHasTotals),
103 bDoSize (rData.bDoSize),
104 bKeepFmt (rData.bKeepFmt),
105 bStripData (rData.bStripData),
106 bIsAdvanced (rData.bIsAdvanced),
107 aAdvSource (rData.aAdvSource),
108 bDBSelection (rData.bDBSelection),
109 nIndex (rData.nIndex),
110 bAutoFilter (rData.bAutoFilter),
111 bModified (rData.bModified),
112 maTableColumnNames (rData.maTableColumnNames),
113 maTableColumnAttributes(rData.maTableColumnAttributes),
114 mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty),
115 nFilteredRowCount (rData.nFilteredRowCount)
119 ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) :
120 // Listeners are to be setup by the "parent" container.
121 SvtListener (),
122 ScRefreshTimer ( rData ),
123 mpSortParam(new ScSortParam(*rData.mpSortParam)),
124 mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
125 mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
126 mpImportParam(new ScImportParam(*rData.mpImportParam)),
127 mpContainer (nullptr),
128 aName (rName),
129 aUpper (rName),
130 nTable (rData.nTable),
131 nStartCol (rData.nStartCol),
132 nStartRow (rData.nStartRow),
133 nEndCol (rData.nEndCol),
134 nEndRow (rData.nEndRow),
135 bByRow (rData.bByRow),
136 bHasHeader (rData.bHasHeader),
137 bHasTotals (rData.bHasTotals),
138 bDoSize (rData.bDoSize),
139 bKeepFmt (rData.bKeepFmt),
140 bStripData (rData.bStripData),
141 bIsAdvanced (rData.bIsAdvanced),
142 aAdvSource (rData.aAdvSource),
143 bDBSelection (rData.bDBSelection),
144 nIndex (rData.nIndex),
145 bAutoFilter (rData.bAutoFilter),
146 bModified (rData.bModified),
147 maTableColumnNames (rData.maTableColumnNames),
148 maTableColumnAttributes(rData.maTableColumnAttributes),
149 mbTableColumnNamesDirty (rData.mbTableColumnNamesDirty),
150 nFilteredRowCount (rData.nFilteredRowCount)
152 aUpper = ScGlobal::getCharClass().uppercase(aUpper);
155 ScDBData& ScDBData::operator= (const ScDBData& rData)
157 if (this != &rData)
159 // Don't modify the name. The name is not mutable as it is used as a key
160 // in the container to keep the db ranges sorted by the name.
162 bool bHeaderRangeDiffers = (nTable != rData.nTable || nStartCol != rData.nStartCol ||
163 nEndCol != rData.nEndCol || nStartRow != rData.nStartRow);
164 bool bNeedsListening = ((bHasHeader && bHeaderRangeDiffers) || (!bHasHeader && rData.bHasHeader));
165 if (bHasHeader && (!rData.bHasHeader || bHeaderRangeDiffers))
167 EndTableColumnNamesListener();
169 ScRefreshTimer::operator=( rData );
170 mpSortParam.reset(new ScSortParam(*rData.mpSortParam));
171 mpQueryParam.reset(new ScQueryParam(*rData.mpQueryParam));
172 mpSubTotal.reset(new ScSubTotalParam(*rData.mpSubTotal));
173 mpImportParam.reset(new ScImportParam(*rData.mpImportParam));
174 // Keep mpContainer.
175 nTable = rData.nTable;
176 nStartCol = rData.nStartCol;
177 nStartRow = rData.nStartRow;
178 nEndCol = rData.nEndCol;
179 nEndRow = rData.nEndRow;
180 bByRow = rData.bByRow;
181 bHasHeader = rData.bHasHeader;
182 bHasTotals = rData.bHasTotals;
183 bDoSize = rData.bDoSize;
184 bKeepFmt = rData.bKeepFmt;
185 bStripData = rData.bStripData;
186 bIsAdvanced = rData.bIsAdvanced;
187 aAdvSource = rData.aAdvSource;
188 bDBSelection = rData.bDBSelection;
189 nIndex = rData.nIndex;
190 bAutoFilter = rData.bAutoFilter;
191 nFilteredRowCount = rData.nFilteredRowCount;
193 if (bHeaderRangeDiffers)
194 InvalidateTableColumnNames( true);
195 else
197 maTableColumnNames = rData.maTableColumnNames;
198 maTableColumnAttributes = rData.maTableColumnAttributes;
199 mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty;
202 if (bNeedsListening)
203 StartTableColumnNamesListener();
205 return *this;
208 bool ScDBData::operator== (const ScDBData& rData) const
210 // Data that is not in sort or query params.
212 if ( nTable != rData.nTable ||
213 bDoSize != rData.bDoSize ||
214 bKeepFmt != rData.bKeepFmt ||
215 bIsAdvanced!= rData.bIsAdvanced||
216 bStripData != rData.bStripData ||
217 // SAB: I think this should be here, but I don't want to break something
218 // bAutoFilter!= rData.bAutoFilter||
219 ScRefreshTimer::operator!=( rData )
221 return false;
223 if ( bIsAdvanced && aAdvSource != rData.aAdvSource )
224 return false;
226 ScSortParam aSort1, aSort2;
227 GetSortParam(aSort1);
228 rData.GetSortParam(aSort2);
229 if (!(aSort1 == aSort2))
230 return false;
232 ScQueryParam aQuery1, aQuery2;
233 GetQueryParam(aQuery1);
234 rData.GetQueryParam(aQuery2);
235 if (!(aQuery1 == aQuery2))
236 return false;
238 ScSubTotalParam aSubTotal1, aSubTotal2;
239 GetSubTotalParam(aSubTotal1);
240 rData.GetSubTotalParam(aSubTotal2);
241 if (!(aSubTotal1 == aSubTotal2))
242 return false;
244 ScImportParam aImport1, aImport2;
245 GetImportParam(aImport1);
246 rData.GetImportParam(aImport2);
247 return aImport1 == aImport2;
250 ScDBData::~ScDBData()
252 StopRefreshTimer();
255 OUString ScDBData::GetSourceString() const
257 if (mpImportParam->bImport)
258 return mpImportParam->aDBName + "/" + mpImportParam->aStatement;
259 return OUString();
262 OUString ScDBData::GetOperations() const
264 OUStringBuffer aBuf;
265 if (mpQueryParam->GetEntryCount())
267 const ScQueryEntry& rEntry = mpQueryParam->GetEntry(0);
268 if (rEntry.bDoQuery)
269 aBuf.append(ScResId(STR_OPERATION_FILTER));
272 if (mpSortParam->maKeyState[0].bDoSort)
274 if (!aBuf.isEmpty())
275 aBuf.append(", ");
276 aBuf.append(ScResId(STR_OPERATION_SORT));
279 if (mpSubTotal->aGroups[0].bActive && !mpSubTotal->bRemoveOnly)
281 if (!aBuf.isEmpty())
282 aBuf.append(", ");
283 aBuf.append(ScResId(STR_OPERATION_SUBTOTAL));
286 if (aBuf.isEmpty())
287 aBuf.append(ScResId(STR_OPERATION_NONE));
289 return aBuf.makeStringAndClear();
292 void ScDBData::GetArea(SCTAB& rTab, SCCOL& rCol1, SCROW& rRow1, SCCOL& rCol2, SCROW& rRow2) const
294 rTab = nTable;
295 rCol1 = nStartCol;
296 rRow1 = nStartRow;
297 rCol2 = nEndCol;
298 rRow2 = nEndRow;
301 void ScDBData::GetArea(ScRange& rRange) const
303 SCROW nNewEndRow = nEndRow;
304 rRange = ScRange( nStartCol, nStartRow, nTable, nEndCol, nNewEndRow, nTable );
307 ScRange ScDBData::GetHeaderArea() const
309 if (HasHeader())
310 return ScRange( nStartCol, nStartRow, nTable, nEndCol, nStartRow, nTable);
311 return ScRange( ScAddress::INITIALIZE_INVALID);
314 void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
316 bool bHeaderRangeChange = (nTab != nTable || nCol1 != nStartCol || nCol2 != nEndCol || nRow1 != nStartRow);
317 if (bHeaderRangeChange)
318 EndTableColumnNamesListener();
320 nTable = nTab;
321 nStartCol = nCol1;
322 nStartRow = nRow1;
323 nEndCol = nCol2;
324 nEndRow = nRow2;
326 if (bHeaderRangeChange)
328 SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::SetArea - invalidating column names/offsets");
329 // Invalidate *after* new area has been set above to add the proper
330 // header range to dirty list.
331 InvalidateTableColumnNames( true);
332 StartTableColumnNamesListener();
336 void ScDBData::MoveTo(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
337 SCCOL nUpdateCol)
339 tools::Long nDifX = static_cast<tools::Long>(nCol1) - static_cast<tools::Long>(nStartCol);
340 tools::Long nDifY = static_cast<tools::Long>(nRow1) - static_cast<tools::Long>(nStartRow);
342 tools::Long nSortDif = bByRow ? nDifX : nDifY;
343 tools::Long nSortEnd = bByRow ? static_cast<tools::Long>(nCol2) : static_cast<tools::Long>(nRow2);
345 for (sal_uInt16 i=0; i<mpSortParam->GetSortKeyCount(); i++)
347 mpSortParam->maKeyState[i].nField += nSortDif;
348 if (mpSortParam->maKeyState[i].nField > nSortEnd)
350 mpSortParam->maKeyState[i].nField = 0;
351 mpSortParam->maKeyState[i].bDoSort = false;
355 SCSIZE nCount = mpQueryParam->GetEntryCount();
356 for (SCSIZE i = 0; i < nCount; ++i)
358 ScQueryEntry& rEntry = mpQueryParam->GetEntry(i);
359 rEntry.nField += nDifX;
361 // tdf#48025, tdf#141946: update the column index of the filter criteria,
362 // when the deleted/inserted columns are inside the data range
363 if (nUpdateCol != -1)
365 nUpdateCol += nDifX;
366 tools::Long nDifX2
367 = static_cast<tools::Long>(nCol2) - static_cast<tools::Long>(nEndCol);
368 if (rEntry.nField >= nUpdateCol)
369 rEntry.nField += nDifX2;
370 else if (rEntry.nField >= nUpdateCol + nDifX2)
371 rEntry.Clear();
374 if (rEntry.nField > nCol2)
376 rEntry.nField = 0;
377 rEntry.bDoQuery = false;
380 for (auto& group : mpSubTotal->aGroups)
382 group.nField += nDifX;
383 if (group.nField > nCol2)
385 group.nField = 0;
386 group.bActive = false;
390 SetArea( nTab, nCol1, nRow1, nCol2, nRow2 );
393 void ScDBData::GetSortParam( ScSortParam& rSortParam ) const
395 rSortParam = *mpSortParam;
396 rSortParam.nCol1 = nStartCol;
397 rSortParam.nRow1 = nStartRow;
398 rSortParam.nCol2 = nEndCol;
399 rSortParam.nRow2 = nEndRow;
400 rSortParam.bByRow = bByRow;
401 rSortParam.bHasHeader = bHasHeader;
402 /* TODO: add Totals to ScSortParam? */
405 void ScDBData::SetSortParam( const ScSortParam& rSortParam )
407 mpSortParam.reset(new ScSortParam(rSortParam));
408 bByRow = rSortParam.bByRow;
411 void ScDBData::UpdateFromSortParam( const ScSortParam& rSortParam )
413 bHasHeader = rSortParam.bHasHeader;
416 void ScDBData::GetQueryParam( ScQueryParam& rQueryParam ) const
418 rQueryParam = *mpQueryParam;
419 rQueryParam.nCol1 = nStartCol;
420 rQueryParam.nRow1 = nStartRow;
421 rQueryParam.nCol2 = nEndCol;
422 rQueryParam.nRow2 = nEndRow;
423 rQueryParam.nTab = nTable;
424 rQueryParam.bByRow = bByRow;
425 rQueryParam.bHasHeader = bHasHeader;
426 rQueryParam.bHasTotals = bHasTotals;
429 void ScDBData::SetQueryParam(const ScQueryParam& rQueryParam)
431 mpQueryParam.reset(new ScQueryParam(rQueryParam));
433 // set bIsAdvanced to false for everything that is not from the
434 // advanced filter dialog
435 bIsAdvanced = false;
438 void ScDBData::SetAdvancedQuerySource(const ScRange* pSource)
440 if (pSource)
442 aAdvSource = *pSource;
443 bIsAdvanced = true;
445 else
446 bIsAdvanced = false;
449 bool ScDBData::GetAdvancedQuerySource(ScRange& rSource) const
451 rSource = aAdvSource;
452 return bIsAdvanced;
455 void ScDBData::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const
457 rSubTotalParam = *mpSubTotal;
459 // Share the data range with the parent db data. The range in the subtotal
460 // param struct is not used.
461 rSubTotalParam.nCol1 = nStartCol;
462 rSubTotalParam.nRow1 = nStartRow;
463 rSubTotalParam.nCol2 = nEndCol;
464 rSubTotalParam.nRow2 = nEndRow;
467 void ScDBData::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam)
469 mpSubTotal.reset(new ScSubTotalParam(rSubTotalParam));
472 void ScDBData::GetImportParam(ScImportParam& rImportParam) const
474 rImportParam = *mpImportParam;
475 // set the range.
476 rImportParam.nCol1 = nStartCol;
477 rImportParam.nRow1 = nStartRow;
478 rImportParam.nCol2 = nEndCol;
479 rImportParam.nRow2 = nEndRow;
482 void ScDBData::SetImportParam(const ScImportParam& rImportParam)
484 // the range is ignored.
485 mpImportParam.reset(new ScImportParam(rImportParam));
488 bool ScDBData::IsDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
490 if (nTab == nTable)
492 switch (ePortion)
494 case ScDBDataPortion::TOP_LEFT:
495 return nCol == nStartCol && nRow == nStartRow;
496 case ScDBDataPortion::AREA:
497 return nCol >= nStartCol && nCol <= nEndCol && nRow >= nStartRow && nRow <= nEndRow;
501 return false;
504 bool ScDBData::IsDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
506 return (nTab == nTable)
507 && (nCol1 == nStartCol) && (nRow1 == nStartRow)
508 && (nCol2 == nEndCol) && (nRow2 == nEndRow);
511 bool ScDBData::HasImportParam() const
513 return mpImportParam && mpImportParam->bImport;
516 bool ScDBData::HasQueryParam() const
518 if (!mpQueryParam)
519 return false;
521 if (!mpQueryParam->GetEntryCount())
522 return false;
524 return mpQueryParam->GetEntry(0).bDoQuery;
527 bool ScDBData::HasSortParam() const
529 return mpSortParam &&
530 !mpSortParam->maKeyState.empty() &&
531 mpSortParam->maKeyState[0].bDoSort;
534 bool ScDBData::HasSubTotalParam() const
536 return mpSubTotal && mpSubTotal->aGroups[0].bActive;
539 void ScDBData::UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos)
541 ScRange aRange;
542 GetArea(aRange);
543 SCTAB nTab = aRange.aStart.Tab(); // a database range is only on one sheet
545 // customize as the current table as ScTablesHint (tabvwsh5.cxx)
547 if (nTab == nOldPos) // moved sheet
548 nTab = nNewPos;
549 else if (nOldPos < nNewPos) // moved to the back
551 if (nTab > nOldPos && nTab <= nNewPos) // move this sheet
552 --nTab;
554 else // moved to the front
556 if (nTab >= nNewPos && nTab < nOldPos) // move this sheet
557 ++nTab;
560 bool bChanged = (nTab != aRange.aStart.Tab());
561 if (bChanged)
563 // SetArea() invalidates column names, but it is the same column range
564 // just on a different sheet; remember and set new.
565 ::std::vector<OUString> aNames(maTableColumnNames);
566 bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
567 // Same column range.
568 SetArea(nTab, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
569 aRange.aEnd.Row());
570 // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
571 maTableColumnNames = aNames;
572 maTableColumnAttributes.resize(aNames.size());
573 mbTableColumnNamesDirty = bTableColumnNamesDirty;
576 // MoveTo() is not necessary if only the sheet changed.
578 SetModified(bChanged);
581 bool ScDBData::UpdateReference(const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
582 SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
583 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
584 SCCOL nDx, SCROW nDy, SCTAB nDz)
586 SCCOL theCol1;
587 SCROW theRow1;
588 SCTAB theTab1;
589 SCCOL theCol2;
590 SCROW theRow2;
591 SCTAB theTab2;
592 GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 );
593 theTab2 = theTab1;
594 SCCOL nOldCol1 = theCol1, nOldCol2 = theCol2;
596 ScRefUpdateRes eRet
597 = ScRefUpdate::Update(pDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx,
598 nDy, nDz, theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
600 bool bDoUpdate = eRet != UR_NOTHING;
602 if (bDoUpdate && eRet != UR_INVALID)
604 // MoveTo() invalidates column names via SetArea(); adjust, remember and set new.
605 AdjustTableColumnAttributes( eUpdateRefMode, nDx, nCol1, nOldCol1, nOldCol2, theCol1, theCol2);
606 ::std::vector<OUString> aNames( maTableColumnNames);
607 bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
608 // tdf#48025, tdf#141946: update the column index of the filter criteria,
609 // when the deleted/inserted columns are inside the data range
610 if (HasAutoFilter() && theCol1 - nOldCol1 != theCol2 - nOldCol2)
611 MoveTo(theTab1, theCol1, theRow1, theCol2, theRow2, nCol1);
612 else
613 MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
614 // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
615 maTableColumnNames = aNames;
616 maTableColumnAttributes.resize(aNames.size());
617 mbTableColumnNamesDirty = bTableColumnNamesDirty;
620 ScRange aRangeAdvSource;
621 if ( GetAdvancedQuerySource(aRangeAdvSource) )
623 aRangeAdvSource.GetVars( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
624 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
625 nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
626 theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
628 aRangeAdvSource.aStart.Set( theCol1,theRow1,theTab1 );
629 aRangeAdvSource.aEnd.Set( theCol2,theRow2,theTab2 );
630 SetAdvancedQuerySource( &aRangeAdvSource );
632 bDoUpdate = true; // DBData is modified
636 SetModified(bDoUpdate);
638 return eRet == UR_INVALID;
640 //TODO: check if something was deleted/inserted with-in the range !!!
643 void ScDBData::ExtendDataArea(const ScDocument& rDoc)
645 // Extend the DB area to include data rows immediately below.
646 SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol;
647 SCROW nOldEndRow = nEndRow;
648 rDoc.GetDataArea(nTable, nStartCol, nStartRow, nEndCol, nEndRow, false, true);
649 // nOldEndRow==rDoc.MaxRow() may easily happen when selecting whole columns and
650 // setting an AutoFilter (i.e. creating an anonymous database-range). We
651 // certainly don't want to iterate over nearly a million empty cells, but
652 // keep only an intentionally user selected range.
653 if (nOldEndRow < rDoc.MaxRow() && nEndRow < nOldEndRow)
654 nEndRow = nOldEndRow;
655 if (nStartCol != nOldCol1 || nEndCol != nOldCol2)
657 SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::ExtendDataArea - invalidating column names/offsets");
658 InvalidateTableColumnNames( true);
662 void ScDBData::ExtendBackColorArea(const ScDocument& rDoc)
664 // Extend the DB area to include data rows immediately below.
665 SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol;
666 SCROW nOldEndRow = nEndRow;
667 rDoc.GetBackColorArea(nTable, nStartCol, nStartRow, nEndCol, nEndRow);
669 if (nOldEndRow < rDoc.MaxRow() && nEndRow < nOldEndRow)
670 nEndRow = nOldEndRow;
672 if (nStartCol != nOldCol1 || nEndCol != nOldCol2)
674 SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::ExtendBackColorArea - invalidating column names/offsets");
675 InvalidateTableColumnNames( true);
679 void ScDBData::StartTableColumnNamesListener()
681 if (mpContainer && bHasHeader)
683 ScDocument& rDoc = mpContainer->GetDocument();
684 if (!rDoc.IsClipOrUndo())
685 rDoc.StartListeningArea( GetHeaderArea(), false, this);
689 void ScDBData::EndTableColumnNamesListener()
691 EndListeningAll();
694 void ScDBData::SetTableColumnNames( ::std::vector< OUString >&& rNames )
696 maTableColumnNames = std::move(rNames);
697 mbTableColumnNamesDirty = false;
700 void ScDBData::SetTableColumnAttributes( ::std::vector< TableColumnAttributes >&& rAttributes )
702 maTableColumnAttributes = std::move(rAttributes);
705 void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1,
706 SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 )
708 if (maTableColumnNames.empty())
709 return;
711 SCCOL nDiff1 = nNewCol1 - nOldCol1;
712 SCCOL nDiff2 = nNewCol2 - nOldCol2;
713 if (nDiff1 == nDiff2)
714 return; // not moved or entirely moved, nothing to do
716 ::std::vector<OUString> aNewNames;
717 ::std::vector<TableColumnAttributes> aNewAttributes;
718 if (eUpdateRefMode == URM_INSDEL)
720 if (nDx > 0)
721 mbTableColumnNamesDirty = true; // inserted columns will have empty names
723 // nCol1 is the first column of the block that gets shifted, determine
724 // the head and tail elements that are to be copied for deletion or
725 // insertion.
726 size_t nHead = static_cast<size_t>(::std::max( nCol1 + std::min<SCCOL>(nDx, 0) - nOldCol1, 0));
727 size_t nTail = static_cast<size_t>(::std::max( nOldCol2 - nCol1 + 1, 0));
728 size_t n = nHead + nTail;
729 if (0 < n && n <= maTableColumnNames.size())
731 if (nDx > 0)
732 n += nDx;
733 aNewNames.resize(n);
734 aNewAttributes.resize(n);
735 // Copy head.
736 for (size_t i = 0; i < nHead; ++i)
738 aNewNames[i] = maTableColumnNames[i];
739 aNewAttributes[i] = maTableColumnAttributes[i];
741 // Copy tail, inserted middle range, if any, stays empty.
742 for (size_t i = n - nTail, j = maTableColumnNames.size() - nTail; i < n; ++i, ++j)
744 aNewNames[i] = maTableColumnNames[j];
745 aNewAttributes[i] = maTableColumnAttributes[j];
748 } // else empty aNewNames invalidates names/offsets
750 SAL_WARN_IF( !maTableColumnNames.empty() && aNewNames.empty(),
751 "sc.core", "ScDBData::AdjustTableColumnAttributes - invalidating column attributes/offsets");
752 aNewNames.swap( maTableColumnNames);
753 aNewAttributes.swap(maTableColumnAttributes);
754 if (maTableColumnNames.empty())
755 mbTableColumnNamesDirty = true;
756 if (mbTableColumnNamesDirty)
757 InvalidateTableColumnNames( false); // preserve new column names array
760 void ScDBData::InvalidateTableColumnNames( bool bSwapToEmptyNames )
762 mbTableColumnNamesDirty = true;
763 if (bSwapToEmptyNames && !maTableColumnNames.empty())
764 ::std::vector<OUString>().swap( maTableColumnNames);
765 if (mpContainer)
767 // Add header range to dirty list.
768 if (HasHeader())
769 mpContainer->GetDirtyTableColumnNames().Join( GetHeaderArea());
770 else
772 // We need *some* range in the dirty list even without header area,
773 // otherwise the container would not attempt to call a refresh.
774 mpContainer->GetDirtyTableColumnNames().Join( ScRange( nStartCol, nStartRow, nTable));
779 namespace {
780 class TableColumnNameSearch
782 public:
783 explicit TableColumnNameSearch( OUString aSearchName ) :
784 maSearchName(std::move( aSearchName ))
788 bool operator()( const OUString& rName ) const
790 return ScGlobal::GetTransliteration().isEqual( maSearchName, rName);
793 private:
794 OUString maSearchName;
797 /** Set a numbered table column name at given nIndex, preventing duplicates,
798 numbering starting at nCount. If nCount==0 then the first attempt is made
799 with an unnumbered name and if already present the next attempt with
800 nCount=2, so "Original" and "Original2". No check whether nIndex is valid. */
801 void SetTableColumnName( ::std::vector<OUString>& rVec, size_t nIndex, const OUString& rName, size_t nCount )
803 OUString aStr;
806 if (nCount)
807 aStr = rName + OUString::number( nCount);
808 else
810 aStr = rName;
811 ++nCount;
814 if (std::none_of( rVec.begin(), rVec.end(), TableColumnNameSearch( aStr)))
816 rVec[nIndex] = aStr;
817 break; // do while
819 ++nCount;
820 } while(true);
824 void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
826 ::std::vector<OUString> aNewNames;
827 aNewNames.resize( nEndCol - nStartCol + 1);
828 bool bHaveEmpty = false;
829 if (!HasHeader() || !pDoc)
830 bHaveEmpty = true; // Assume we have empty ones and fill below.
831 else
833 ScHorizontalCellIterator aIter(*pDoc, nTable, nStartCol, nStartRow, nEndCol, nStartRow); // header row only
834 ScRefCellValue* pCell;
835 SCCOL nCol, nLastColFilled = nStartCol - 1;
836 SCROW nRow;
837 while ((pCell = aIter.GetNext( nCol, nRow)) != nullptr)
839 if (pCell->hasString())
841 const OUString aStr = pCell->getString( pDoc);
842 if (aStr.isEmpty())
843 bHaveEmpty = true;
844 else
846 SetTableColumnName( aNewNames, nCol-nStartCol, aStr, 0);
847 if (nLastColFilled < nCol-1)
848 bHaveEmpty = true;
850 nLastColFilled = nCol;
852 else
853 bHaveEmpty = true;
857 // Never leave us with empty names, try to remember previous name that
858 // might had been used to compile formulas, but only if same number of
859 // columns and no duplicates.
860 if (bHaveEmpty && aNewNames.size() == maTableColumnNames.size())
862 bHaveEmpty = false;
863 for (size_t i=0, n=aNewNames.size(); i < n; ++i)
865 if (aNewNames[i].isEmpty())
867 const OUString& rStr = maTableColumnNames[i];
868 if (rStr.isEmpty())
869 bHaveEmpty = true;
870 else
871 SetTableColumnName( aNewNames, i, rStr, 0);
876 // If we still have empty ones then fill those with "Column#" with #
877 // starting at the column offset number. Still no duplicates of course.
878 if (bHaveEmpty)
880 OUString aColumn( ScResId(STR_COLUMN));
881 for (size_t i=0, n=aNewNames.size(); i < n; ++i)
883 if (aNewNames[i].isEmpty())
884 SetTableColumnName( aNewNames, i, aColumn, i+1);
888 aNewNames.swap( maTableColumnNames);
889 maTableColumnAttributes.resize(maTableColumnNames.size());
890 mbTableColumnNamesDirty = false;
893 void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange )
895 // Header-less tables get names generated, completely empty a full refresh.
896 if (mbTableColumnNamesDirty && (!HasHeader() || maTableColumnNames.empty()))
898 RefreshTableColumnNames( pDoc);
899 return;
902 // Check if this is affected for the range requested.
903 ScRange aIntersection( GetHeaderArea().Intersection( rRange));
904 if (!aIntersection.IsValid())
905 return;
907 // Always fully refresh, only one cell of a range was broadcasted per area
908 // listener if multiple cells were affected. We don't know if there were
909 // more. Also, we need the full check anyway in case a duplicated name was
910 // entered.
911 RefreshTableColumnNames( pDoc);
914 sal_Int32 ScDBData::GetColumnNameOffset( const OUString& rName ) const
916 if (maTableColumnNames.empty())
917 return -1;
919 ::std::vector<OUString>::const_iterator it(
920 ::std::find_if( maTableColumnNames.begin(), maTableColumnNames.end(), TableColumnNameSearch( rName)));
921 if (it != maTableColumnNames.end())
922 return it - maTableColumnNames.begin();
924 return -1;
927 const OUString & ScDBData::GetTableColumnName( SCCOL nCol ) const
929 if (maTableColumnNames.empty())
930 return EMPTY_OUSTRING;
932 SCCOL nOffset = nCol - nStartCol;
933 if (nOffset < 0 || maTableColumnNames.size() <= o3tl::make_unsigned(nOffset))
934 return EMPTY_OUSTRING;
936 return maTableColumnNames[nOffset];
939 void ScDBData::Notify( const SfxHint& rHint )
941 if (rHint.GetId() != SfxHintId::ScDataChanged)
942 return;
943 const ScHint* pScHint = static_cast<const ScHint*>(&rHint);
945 mbTableColumnNamesDirty = true;
946 if (!mpContainer)
947 assert(!"ScDBData::Notify - how did we end up here without container?");
948 else
950 // Only one cell of a range is broadcasted per area listener if
951 // multiple cells are affected. Expand the range to what this is
952 // listening to. Broadcasted address outside should not happen,
953 // but... let it trigger a refresh if.
954 const ScRange aHeaderRange( GetHeaderArea());
955 ScAddress aHintAddress( pScHint->GetStartAddress());
956 if (aHeaderRange.IsValid())
958 mpContainer->GetDirtyTableColumnNames().Join( aHeaderRange);
959 // Header range is one row.
960 // The ScHint's "range" is an address with row count.
961 // Though broadcasted is usually only one cell, check for the
962 // possible case of row block and for one cell in the same row.
963 if (aHintAddress.Row() <= aHeaderRange.aStart.Row()
964 && aHeaderRange.aStart.Row() < aHintAddress.Row() + pScHint->GetRowCount())
966 aHintAddress.SetRow( aHeaderRange.aStart.Row());
967 if (!aHeaderRange.Contains( aHintAddress))
968 mpContainer->GetDirtyTableColumnNames().Join( ScRange(aHintAddress) );
971 else
973 // We need *some* range in the dirty list even without header area,
974 // otherwise the container would not attempt to call a refresh.
975 aHintAddress.SetRow( nStartRow);
976 mpContainer->GetDirtyTableColumnNames().Join( ScRange(aHintAddress) );
980 // Do not refresh column names here, which might trigger unwanted
981 // recalculation.
984 void ScDBData::CalcSaveFilteredCount( SCSIZE nNonFilteredRowCount )
986 SCSIZE nTotal = nEndRow - nStartRow + 1;
987 if ( bHasHeader )
988 nTotal -= 1;
989 nFilteredRowCount = nTotal - nNonFilteredRowCount;
992 void ScDBData::GetFilterSelCount( SCSIZE& nSelected, SCSIZE& nTotal )
994 nTotal = nEndRow - nStartRow + 1;
995 if ( bHasHeader )
996 nTotal -= 1;
997 if( nFilteredRowCount != SCSIZE_MAX )
998 nSelected = nTotal - nFilteredRowCount;
999 else
1000 nSelected = nFilteredRowCount;
1003 namespace {
1005 class FindByTable
1007 SCTAB mnTab;
1008 public:
1009 explicit FindByTable(SCTAB nTab) : mnTab(nTab) {}
1011 bool operator() (std::unique_ptr<ScDBData> const& p) const
1013 ScRange aRange;
1014 p->GetArea(aRange);
1015 return aRange.aStart.Tab() == mnTab;
1019 class UpdateMoveTabFunc
1021 SCTAB mnOldTab;
1022 SCTAB mnNewTab;
1023 public:
1024 UpdateMoveTabFunc(SCTAB nOld, SCTAB nNew) : mnOldTab(nOld), mnNewTab(nNew) {}
1025 void operator() (std::unique_ptr<ScDBData> const& p)
1027 p->UpdateMoveTab(mnOldTab, mnNewTab);
1031 OUString lcl_IncrementNumberInNamedRange(ScDBCollection::NamedDBs& namedDBs,
1032 std::u16string_view rOldName)
1034 // Append or increment a numeric suffix and do not generate names that
1035 // could result in a cell reference by ensuring at least one underscore is
1036 // present.
1037 // "aa" => "aa_2"
1038 // "aaaa1" => "aaaa1_2"
1039 // "aa_a" => "aa_a_2"
1040 // "aa_a_" => "aa_a__2"
1041 // "aa_a1" => "aa_a1_2"
1042 // "aa_1a" => "aa_1a_2"
1043 // "aa_1" => "aa_2"
1044 // "aa_2" => "aa_3"
1046 size_t nLastIndex = rOldName.rfind('_');
1047 sal_Int32 nOldNumber = 1;
1048 OUString aPrefix;
1049 if (nLastIndex != std::u16string_view::npos)
1051 ++nLastIndex;
1052 std::u16string_view sLastPart(rOldName.substr(nLastIndex));
1053 nOldNumber = o3tl::toInt32(sLastPart);
1055 // If that number is exactly at the end then increment the number; else
1056 // append "_" and number.
1057 // toInt32() returns 0 on failure and also stops at trailing non-digit
1058 // characters (toInt32("1a")==1).
1059 if (OUString::number(nOldNumber) == sLastPart)
1060 aPrefix = rOldName.substr(0, nLastIndex);
1061 else
1063 aPrefix = OUString::Concat(rOldName) + "_";
1064 nOldNumber = 1;
1067 else // No "_" found, append "_" and number.
1068 aPrefix = OUString::Concat(rOldName) + "_";
1069 OUString sNewName;
1072 sNewName = aPrefix + OUString::number(++nOldNumber);
1073 } while (namedDBs.findByName(sNewName) != nullptr);
1074 return sNewName;
1077 class FindByCursor
1079 SCCOL mnCol;
1080 SCROW mnRow;
1081 SCTAB mnTab;
1082 ScDBDataPortion mePortion;
1083 public:
1084 FindByCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) :
1085 mnCol(nCol), mnRow(nRow), mnTab(nTab), mePortion(ePortion) {}
1087 bool operator() (std::unique_ptr<ScDBData> const& p)
1089 return p->IsDBAtCursor(mnCol, mnRow, mnTab, mePortion);
1093 class FindByRange
1095 const ScRange& mrRange;
1096 public:
1097 explicit FindByRange(const ScRange& rRange) : mrRange(rRange) {}
1099 bool operator() (std::unique_ptr<ScDBData> const& p)
1101 return p->IsDBAtArea(
1102 mrRange.aStart.Tab(), mrRange.aStart.Col(), mrRange.aStart.Row(), mrRange.aEnd.Col(), mrRange.aEnd.Row());
1106 class FindByIndex
1108 sal_uInt16 mnIndex;
1109 public:
1110 explicit FindByIndex(sal_uInt16 nIndex) : mnIndex(nIndex) {}
1111 bool operator() (std::unique_ptr<ScDBData> const& p) const
1113 return p->GetIndex() == mnIndex;
1117 class FindByUpperName
1119 const OUString& mrName;
1120 public:
1121 explicit FindByUpperName(const OUString& rName) : mrName(rName) {}
1122 bool operator() (std::unique_ptr<ScDBData> const& p) const
1124 return p->GetUpperName() == mrName;
1128 class FindByName
1130 const OUString& mrName;
1131 public:
1132 explicit FindByName(const OUString& rName) : mrName(rName) {}
1133 bool operator() (std::unique_ptr<ScDBData> const& p) const
1135 return p->GetName() == mrName;
1139 class FindByPointer
1141 const ScDBData* mpDBData;
1142 public:
1143 explicit FindByPointer(const ScDBData* pDBData) : mpDBData(pDBData) {}
1144 bool operator() (std::unique_ptr<ScDBData> const& p) const
1146 return p.get() == mpDBData;
1152 ScDocument& ScDBDataContainerBase::GetDocument() const
1154 return mrDoc;
1157 ScRangeList& ScDBDataContainerBase::GetDirtyTableColumnNames()
1159 return maDirtyTableColumnNames;
1162 ScDBCollection::NamedDBs::NamedDBs(ScDBCollection& rParent, ScDocument& rDoc) :
1163 ScDBDataContainerBase(rDoc), mrParent(rParent) {}
1165 ScDBCollection::NamedDBs::NamedDBs(const NamedDBs& r, ScDBCollection& rParent)
1166 : ScDBDataContainerBase(r.mrDoc)
1167 , mrParent(rParent)
1169 for (auto const& it : r.m_DBs)
1171 ScDBData* p = new ScDBData(*it);
1172 std::unique_ptr<ScDBData> pData(p);
1173 if (m_DBs.insert( std::move(pData)).second)
1174 initInserted(p);
1178 ScDBCollection::NamedDBs::~NamedDBs()
1182 void ScDBCollection::NamedDBs::initInserted( ScDBData* p )
1184 p->SetContainer( this);
1185 if (mrDoc.IsClipOrUndo())
1186 return;
1188 p->StartTableColumnNamesListener(); // needs the container be set already
1189 if (!p->AreTableColumnNamesDirty())
1190 return;
1192 if (p->HasHeader())
1194 // Refresh table column names in next round.
1195 maDirtyTableColumnNames.Join( p->GetHeaderArea());
1197 else
1199 // Header-less table can generate its column names
1200 // already without accessing the document.
1201 p->RefreshTableColumnNames( nullptr);
1205 ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::begin()
1207 return m_DBs.begin();
1210 ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::end()
1212 return m_DBs.end();
1215 ScDBCollection::NamedDBs::const_iterator ScDBCollection::NamedDBs::begin() const
1217 return m_DBs.begin();
1220 ScDBCollection::NamedDBs::const_iterator ScDBCollection::NamedDBs::end() const
1222 return m_DBs.end();
1225 ScDBData* ScDBCollection::NamedDBs::findByIndex(sal_uInt16 nIndex)
1227 DBsType::iterator itr = find_if(
1228 m_DBs.begin(), m_DBs.end(), FindByIndex(nIndex));
1229 return itr == m_DBs.end() ? nullptr : itr->get();
1232 ScDBData* ScDBCollection::NamedDBs::findByUpperName(const OUString& rName)
1234 DBsType::iterator itr = find_if(
1235 m_DBs.begin(), m_DBs.end(), FindByUpperName(rName));
1236 return itr == m_DBs.end() ? nullptr : itr->get();
1239 auto ScDBCollection::NamedDBs::findByUpperName2(const OUString& rName) -> iterator
1241 return find_if(
1242 m_DBs.begin(), m_DBs.end(), FindByUpperName(rName));
1245 ScDBData* ScDBCollection::NamedDBs::findByName(const OUString& rName)
1247 DBsType::iterator itr = find_if(m_DBs.begin(), m_DBs.end(), FindByName(rName));
1248 return itr == m_DBs.end() ? nullptr : itr->get();
1251 bool ScDBCollection::NamedDBs::insert(std::unique_ptr<ScDBData> pData)
1253 auto p = pData.get();
1254 if (!pData->GetIndex())
1255 pData->SetIndex(mrParent.nEntryIndex++);
1257 std::pair<DBsType::iterator, bool> r = m_DBs.insert(std::move(pData));
1259 if (r.second)
1261 initInserted(p);
1263 /* TODO: shouldn't the import refresh not be setup for
1264 * clipboard/undo documents? It was already like this before... */
1265 if (p->HasImportParam() && !p->HasImportSelection())
1267 p->SetRefreshHandler(mrParent.GetRefreshHandler());
1268 p->SetRefreshControl(&mrDoc.GetRefreshTimerControlAddress());
1271 return r.second;
1274 ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::erase(const iterator& itr)
1276 return m_DBs.erase(itr);
1279 bool ScDBCollection::NamedDBs::empty() const
1281 return m_DBs.empty();
1284 size_t ScDBCollection::NamedDBs::size() const
1286 return m_DBs.size();
1289 bool ScDBCollection::NamedDBs::operator== (const NamedDBs& r) const
1291 return ::comphelper::ContainerUniquePtrEquals(m_DBs, r.m_DBs);
1294 ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::begin()
1296 return m_DBs.begin();
1299 ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::end()
1301 return m_DBs.end();
1304 ScDBCollection::AnonDBs::const_iterator ScDBCollection::AnonDBs::begin() const
1306 return m_DBs.begin();
1309 ScDBCollection::AnonDBs::const_iterator ScDBCollection::AnonDBs::end() const
1311 return m_DBs.end();
1314 const ScDBData* ScDBCollection::AnonDBs::findAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab,
1315 ScDBDataPortion ePortion) const
1317 DBsType::const_iterator itr = find_if(
1318 m_DBs.begin(), m_DBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
1319 return itr == m_DBs.end() ? nullptr : itr->get();
1322 const ScDBData* ScDBCollection::AnonDBs::findByRange(const ScRange& rRange) const
1324 DBsType::const_iterator itr = find_if(
1325 m_DBs.begin(), m_DBs.end(), FindByRange(rRange));
1326 return itr == m_DBs.end() ? nullptr : itr->get();
1329 void ScDBCollection::AnonDBs::deleteOnTab(SCTAB nTab)
1331 FindByTable func(nTab);
1332 std::erase_if(m_DBs, func);
1335 ScDBData* ScDBCollection::AnonDBs::getByRange(const ScRange& rRange)
1337 const ScDBData* pData = findByRange(rRange);
1338 if (!pData)
1340 // Insert a new db data. They all have identical names.
1341 ::std::unique_ptr<ScDBData> pNew(new ScDBData(
1342 STR_DB_GLOBAL_NONAME, rRange.aStart.Tab(), rRange.aStart.Col(), rRange.aStart.Row(),
1343 rRange.aEnd.Col(), rRange.aEnd.Row(), true, false, false));
1344 pData = pNew.get();
1345 m_DBs.push_back(std::move(pNew));
1347 return const_cast<ScDBData*>(pData);
1350 void ScDBCollection::AnonDBs::insert(ScDBData* p)
1352 m_DBs.push_back(std::unique_ptr<ScDBData>(p));
1355 ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::erase(const iterator& itr)
1357 return m_DBs.erase(itr);
1360 bool ScDBCollection::AnonDBs::empty() const
1362 return m_DBs.empty();
1365 bool ScDBCollection::AnonDBs::has( const ScDBData* p ) const
1367 return any_of(m_DBs.begin(), m_DBs.end(), FindByPointer(p));
1370 bool ScDBCollection::AnonDBs::operator== (const AnonDBs& r) const
1372 return ::comphelper::ContainerUniquePtrEquals(m_DBs, r.m_DBs);
1375 ScDBCollection::AnonDBs::AnonDBs()
1379 ScDBCollection::AnonDBs::AnonDBs(AnonDBs const& r)
1381 m_DBs.reserve(r.m_DBs.size());
1382 for (auto const& it : r.m_DBs)
1384 m_DBs.push_back(std::make_unique<ScDBData>(*it));
1388 ScDBCollection::ScDBCollection(ScDocument& rDocument) :
1389 rDoc(rDocument), nEntryIndex(1), maNamedDBs(*this, rDocument) {}
1391 ScDBCollection::ScDBCollection(const ScDBCollection& r) :
1392 rDoc(r.rDoc), nEntryIndex(r.nEntryIndex), maNamedDBs(r.maNamedDBs, *this), maAnonDBs(r.maAnonDBs) {}
1394 const ScDBData* ScDBCollection::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
1396 // First, search the global named db ranges.
1397 NamedDBs::DBsType::const_iterator itr = find_if(
1398 maNamedDBs.begin(), maNamedDBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
1399 if (itr != maNamedDBs.end())
1400 return itr->get();
1402 // Check for the sheet-local anonymous db range.
1403 const ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
1404 if (pNoNameData)
1405 if (pNoNameData->IsDBAtCursor(nCol,nRow,nTab,ePortion))
1406 return pNoNameData;
1408 // Check the global anonymous db ranges.
1409 const ScDBData* pData = getAnonDBs().findAtCursor(nCol, nRow, nTab, ePortion);
1410 if (pData)
1411 return pData;
1413 // Do NOT check for the document global temporary anonymous db range here.
1415 return nullptr;
1418 ScDBData* ScDBCollection::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion)
1420 // First, search the global named db ranges.
1421 NamedDBs::DBsType::iterator itr = find_if(
1422 maNamedDBs.begin(), maNamedDBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
1423 if (itr != maNamedDBs.end())
1424 return itr->get();
1426 // Check for the sheet-local anonymous db range.
1427 ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
1428 if (pNoNameData)
1429 if (pNoNameData->IsDBAtCursor(nCol,nRow,nTab,ePortion))
1430 return pNoNameData;
1432 // Check the global anonymous db ranges.
1433 const ScDBData* pData = getAnonDBs().findAtCursor(nCol, nRow, nTab, ePortion);
1434 if (pData)
1435 return const_cast<ScDBData*>(pData);
1437 // Do NOT check for the document global temporary anonymous db range here.
1439 return nullptr;
1442 const ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
1444 // First, search the global named db ranges.
1445 ScRange aRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1446 NamedDBs::DBsType::const_iterator itr = find_if(
1447 maNamedDBs.begin(), maNamedDBs.end(), FindByRange(aRange));
1448 if (itr != maNamedDBs.end())
1449 return itr->get();
1451 // Check for the sheet-local anonymous db range.
1452 ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
1453 if (pNoNameData)
1454 if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
1455 return pNoNameData;
1457 // Lastly, check the global anonymous db ranges.
1458 const ScDBData* pData = maAnonDBs.findByRange(aRange);
1459 if (pData)
1460 return pData;
1462 // As a last resort, check for the document global temporary anonymous db range.
1463 pNoNameData = rDoc.GetAnonymousDBData();
1464 if (pNoNameData)
1465 if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
1466 return pNoNameData;
1468 return nullptr;
1471 ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
1473 // First, search the global named db ranges.
1474 ScRange aRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1475 NamedDBs::DBsType::iterator itr = find_if(
1476 maNamedDBs.begin(), maNamedDBs.end(), FindByRange(aRange));
1477 if (itr != maNamedDBs.end())
1478 return itr->get();
1480 // Check for the sheet-local anonymous db range.
1481 ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
1482 if (pNoNameData)
1483 if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
1484 return pNoNameData;
1486 // Lastly, check the global anonymous db ranges.
1487 const ScDBData* pData = getAnonDBs().findByRange(aRange);
1488 if (pData)
1489 return const_cast<ScDBData*>(pData);
1491 // As a last resort, check for the document global temporary anonymous db range.
1492 pNoNameData = rDoc.GetAnonymousDBData();
1493 if (pNoNameData)
1494 if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
1495 return pNoNameData;
1497 return nullptr;
1500 void ScDBCollection::RefreshDirtyTableColumnNames()
1502 for (size_t i=0; i < maNamedDBs.maDirtyTableColumnNames.size(); ++i)
1504 const ScRange & rRange = maNamedDBs.maDirtyTableColumnNames[i];
1505 for (auto const& it : maNamedDBs)
1507 if (it->AreTableColumnNamesDirty())
1508 it->RefreshTableColumnNames( &maNamedDBs.mrDoc, rRange);
1511 maNamedDBs.maDirtyTableColumnNames.RemoveAll();
1514 void ScDBCollection::DeleteOnTab( SCTAB nTab )
1516 FindByTable func(nTab);
1517 // First, collect the positions of all items that need to be deleted.
1518 ::std::vector<NamedDBs::DBsType::iterator> v;
1520 NamedDBs::DBsType::iterator itr = maNamedDBs.begin(), itrEnd = maNamedDBs.end();
1521 for (; itr != itrEnd; ++itr)
1523 if (func(*itr))
1524 v.push_back(itr);
1528 // Delete them all.
1529 for (const auto& rIter : v)
1530 maNamedDBs.erase(rIter);
1532 maAnonDBs.deleteOnTab(nTab);
1535 void ScDBCollection::UpdateReference(UpdateRefMode eUpdateRefMode,
1536 SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
1537 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
1538 SCCOL nDx, SCROW nDy, SCTAB nDz )
1540 ScDBData* pData = rDoc.GetAnonymousDBData(nTab1);
1541 if (pData)
1543 if (nTab1 == nTab2 && nDz == 0)
1545 // Delete the database range, if some part of the reference became invalid.
1546 if (pData->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
1547 nTab2, nDx, nDy, nDz))
1548 rDoc.SetAnonymousDBData(nTab1, nullptr);
1550 else
1552 //this will perhaps break undo
1556 for (auto it = maNamedDBs.begin(); it != maNamedDBs.end(); )
1558 // Delete the database range, if some part of the reference became invalid.
1559 if (it->get()->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
1560 nTab2, nDx, nDy, nDz))
1561 it = maNamedDBs.erase(it);
1562 else
1563 ++it;
1565 for (auto it = maAnonDBs.begin(); it != maAnonDBs.end(); )
1567 // Delete the database range, if some part of the reference became invalid.
1568 if (it->get()->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
1569 nTab2, nDx, nDy, nDz))
1570 it = maAnonDBs.erase(it);
1571 else
1572 ++it;
1576 void ScDBCollection::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
1578 UpdateMoveTabFunc func(nOldPos, nNewPos);
1579 for_each(maNamedDBs.begin(), maNamedDBs.end(), func);
1580 for_each(maAnonDBs.begin(), maAnonDBs.end(), func);
1583 void ScDBCollection::CopyToTable(SCTAB nOldPos, SCTAB nNewPos)
1585 // Create temporary copy of pointers to not insert in a set we are
1586 // iterating over.
1587 std::vector<const ScDBData*> aTemp;
1588 aTemp.reserve( maNamedDBs.size());
1589 for (const auto& rxNamedDB : maNamedDBs)
1591 if (rxNamedDB->GetTab() != nOldPos)
1592 continue;
1593 aTemp.emplace_back( rxNamedDB.get());
1595 for (const auto& rxNamedDB : aTemp)
1597 const OUString newName( lcl_IncrementNumberInNamedRange( maNamedDBs, rxNamedDB->GetName()));
1598 std::unique_ptr<ScDBData> pDataCopy = std::make_unique<ScDBData>(newName, *rxNamedDB);
1599 pDataCopy->UpdateMoveTab(nOldPos, nNewPos);
1600 pDataCopy->SetIndex(0);
1601 (void)maNamedDBs.insert(std::move(pDataCopy));
1605 ScDBData* ScDBCollection::GetDBNearCursor(SCCOL nCol, SCROW nRow, SCTAB nTab )
1607 ScDBData* pNearData = nullptr;
1608 for (const auto& rxNamedDB : maNamedDBs)
1610 SCTAB nAreaTab;
1611 SCCOL nStartCol, nEndCol;
1612 SCROW nStartRow, nEndRow;
1613 rxNamedDB->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
1614 if ( nTab == nAreaTab && nCol+1 >= nStartCol && nCol <= nEndCol+1 &&
1615 nRow+1 >= nStartRow && nRow <= nEndRow+1 )
1617 if ( nCol < nStartCol || nCol > nEndCol || nRow < nStartRow || nRow > nEndRow )
1619 if (!pNearData)
1620 pNearData = rxNamedDB.get(); // remember first adjacent area
1622 else
1623 return rxNamedDB.get(); // not "unbenannt"/"unnamed" and cursor within
1626 if (pNearData)
1627 return pNearData; // adjacent, if no direct hit
1628 return rDoc.GetAnonymousDBData(nTab); // "unbenannt"/"unnamed" only if nothing else
1631 std::vector<ScDBData*> ScDBCollection::GetAllDBsFromTab(SCTAB nTab)
1633 std::vector<ScDBData*> pTabData;
1634 for (const auto& rxNamedDB : maNamedDBs)
1636 if (rxNamedDB->GetTab() == nTab)
1637 pTabData.emplace_back(rxNamedDB.get());
1639 auto pAnonDBData = rDoc.GetAnonymousDBData(nTab);
1640 if (pAnonDBData)
1641 pTabData.emplace_back(pAnonDBData);
1642 return pTabData;
1645 bool ScDBCollection::empty() const
1647 return maNamedDBs.empty() && maAnonDBs.empty();
1650 bool ScDBCollection::operator== (const ScDBCollection& r) const
1652 return maNamedDBs == r.maNamedDBs && maAnonDBs == r.maAnonDBs &&
1653 nEntryIndex == r.nEntryIndex && &rDoc == &r.rDoc && aRefreshHandler == r.aRefreshHandler;
1656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */