/src/libreoffice/sc/source/ui/view/cliputil.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 | | |
10 | | #include <cliputil.hxx> |
11 | | #include <attrib.hxx> |
12 | | #include <viewdata.hxx> |
13 | | #include <tabvwsh.hxx> |
14 | | #include <transobj.hxx> |
15 | | #include <document.hxx> |
16 | | #include <docsh.hxx> |
17 | | #include <docfunc.hxx> |
18 | | #include <dpobject.hxx> |
19 | | #include <globstr.hrc> |
20 | | #include <scresid.hxx> |
21 | | #include <clipparam.hxx> |
22 | | #include <clipoptions.hxx> |
23 | | #include <rangelst.hxx> |
24 | | #include <viewutil.hxx> |
25 | | #include <markdata.hxx> |
26 | | #include <gridwin.hxx> |
27 | | #include <scitems.hxx> |
28 | | #include <formulacell.hxx> |
29 | | |
30 | | #include <sfx2/classificationhelper.hxx> |
31 | | #include <vcl/weld/WaitObject.hxx> |
32 | | #include <comphelper/lok.hxx> |
33 | | #include <inputopt.hxx> |
34 | | #include <scmod.hxx> |
35 | | #include <warnbox.hxx> |
36 | | |
37 | | namespace |
38 | | { |
39 | | |
40 | | /// Paste only if SfxClassificationHelper recommends so. |
41 | | bool lcl_checkClassification(ScDocument* pSourceDoc, const ScDocument& rDestinationDoc) |
42 | 0 | { |
43 | 0 | if (!pSourceDoc) |
44 | 0 | return true; |
45 | | |
46 | 0 | ScClipOptions* pSourceOptions = pSourceDoc->GetClipOptions(); |
47 | 0 | ScDocShell* pDestinationShell = rDestinationDoc.GetDocumentShell(); |
48 | 0 | if (!pSourceOptions || !pDestinationShell) |
49 | 0 | return true; |
50 | | |
51 | 0 | SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSourceOptions->m_xDocumentProperties, pDestinationShell->getDocProperties()); |
52 | 0 | return SfxClassificationHelper::ShowPasteInfo(eResult); |
53 | 0 | } |
54 | | |
55 | | } |
56 | | |
57 | | void ScClipUtil::PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* pTabViewShell, bool bShowDialog ) |
58 | 0 | { |
59 | 0 | const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin())); |
60 | 0 | ScDocument& rThisDoc = rViewData.GetDocument(); |
61 | 0 | SCCOL nThisCol = rViewData.GetCurX(); |
62 | 0 | SCROW nThisRow = rViewData.GetCurY(); |
63 | 0 | SCTAB nThisTab = rViewData.CurrentTabForData(); |
64 | 0 | ScDPObject* pDPObj = rThisDoc.GetDPAtCursor( nThisCol, nThisRow, nThisTab ); |
65 | |
|
66 | 0 | if ( pOwnClip && pDPObj ) |
67 | 0 | { |
68 | | // paste from Calc into DataPilot table: sort (similar to drag & drop) |
69 | |
|
70 | 0 | ScDocument* pClipDoc = pOwnClip->GetDocument(); |
71 | 0 | SCTAB nSourceTab = pOwnClip->GetVisibleTab(); |
72 | |
|
73 | 0 | SCCOL nClipStartX; |
74 | 0 | SCROW nClipStartY; |
75 | 0 | SCCOL nClipEndX; |
76 | 0 | SCROW nClipEndY; |
77 | 0 | pClipDoc->GetClipStart( nClipStartX, nClipStartY ); |
78 | 0 | pClipDoc->GetClipArea( nClipEndX, nClipEndY, true ); |
79 | 0 | nClipEndX = nClipEndX + nClipStartX; |
80 | 0 | nClipEndY = nClipEndY + nClipStartY; // GetClipArea returns the difference |
81 | |
|
82 | 0 | ScRange aSource( nClipStartX, nClipStartY, nSourceTab, nClipEndX, nClipEndY, nSourceTab ); |
83 | 0 | bool bDone = pTabViewShell->DataPilotMove( aSource, rViewData.GetCurPos() ); |
84 | 0 | if ( !bDone ) |
85 | 0 | pTabViewShell->ErrorMessage( STR_ERR_DATAPILOT_INPUT ); |
86 | 0 | } |
87 | 0 | else |
88 | 0 | { |
89 | | // normal paste |
90 | 0 | weld::WaitObject aWait( rViewData.GetDialogParent() ); |
91 | 0 | if (!pOwnClip) |
92 | 0 | { |
93 | 0 | pTabViewShell->PasteFromSystem(true); |
94 | | // Anchor To Cell rather than To Page |
95 | 0 | ScDrawView* pDrawView = pTabViewShell->GetScDrawView(); |
96 | 0 | if(pDrawView) |
97 | 0 | { |
98 | 0 | const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList(); |
99 | 0 | if (1 == rMarkList.GetMarkCount()) |
100 | 0 | { |
101 | 0 | SdrObject* pPickObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); |
102 | 0 | if(pPickObj) |
103 | 0 | { |
104 | 0 | ScDrawLayer::SetCellAnchoredFromPosition( *pPickObj, rThisDoc, nThisTab, false ); |
105 | 0 | } |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
109 | 0 | else |
110 | 0 | { |
111 | 0 | ScDocument* pClipDoc = pOwnClip->GetDocument(); |
112 | 0 | const ScClipParam& rClipParam = pClipDoc->GetClipParam(); |
113 | |
|
114 | 0 | if (!lcl_checkClassification(pClipDoc, rThisDoc)) |
115 | 0 | return; |
116 | | |
117 | 0 | InsertDeleteFlags nFlags = InsertDeleteFlags::ALL; |
118 | | // For multi-range paste, we paste values by default. |
119 | 0 | if (rClipParam.isMultiRange()) |
120 | 0 | nFlags &= ~InsertDeleteFlags::FORMULA; |
121 | | |
122 | | // --- Origin-matrix handling --- |
123 | | // The clip doc contains the expanded bounding box (original + |
124 | | // origin matrices). Paste the original selection (non-matrix |
125 | | // cells only), then let EnterMatrix expand each matrix origin |
126 | | // to its full array range. |
127 | 0 | if (!rClipParam.maOriginMatrixRanges.empty()) |
128 | 0 | { |
129 | 0 | const ScRange& rOrigRange = rClipParam.maOriginalRange; |
130 | 0 | SCTAB nClipTab = rOrigRange.aStart.Tab(); |
131 | | |
132 | | // Pre-compute destination ranges and grab token arrays |
133 | | // from the clip doc (before any paste modifies anything). |
134 | 0 | struct OriginMatrixInfo |
135 | 0 | { |
136 | 0 | ScRange maDestRange; // destination range for the full matrix |
137 | 0 | const ScTokenArray* mpCode; // token array from the origin formula cell |
138 | 0 | bool mbDynamicArrayMaster; // source was a dynamic-array master |
139 | 0 | }; |
140 | 0 | std::vector<OriginMatrixInfo> aOriginInfos; |
141 | 0 | aOriginInfos.reserve(rClipParam.maOriginMatrixRanges.size()); |
142 | |
|
143 | 0 | formula::FormulaGrammar::Grammar eGram = rThisDoc.GetGrammar(); |
144 | 0 | for (size_t i = 0; i < rClipParam.maOriginMatrixRanges.size(); ++i) |
145 | 0 | { |
146 | 0 | const ScRange& rMat = rClipParam.maOriginMatrixRanges[i]; |
147 | 0 | SCCOL nDestCol = nThisCol + (rMat.aStart.Col() - rOrigRange.aStart.Col()); |
148 | 0 | SCROW nDestRow = nThisRow + (rMat.aStart.Row() - rOrigRange.aStart.Row()); |
149 | 0 | ScRange aDestMatRange(nDestCol, nDestRow, nThisTab, |
150 | 0 | nDestCol + rMat.aEnd.Col() - rMat.aStart.Col(), |
151 | 0 | nDestRow + rMat.aEnd.Row() - rMat.aStart.Row(), nThisTab); |
152 | | |
153 | | // Get the token array from the clip doc's origin cell. |
154 | | // Passing the token array (instead of a formula string) |
155 | | // to EnterMatrix ensures relative references are correctly |
156 | | // shifted to the destination position. |
157 | 0 | const ScTokenArray* pCode = nullptr; |
158 | 0 | bool bDynamic = false; |
159 | 0 | ScFormulaCell* pFC = pClipDoc->GetFormulaCell(rMat.aStart); |
160 | 0 | if (pFC) |
161 | 0 | { |
162 | 0 | pCode = pFC->GetCode(); |
163 | 0 | bDynamic = pFC->IsDynamicArrayMaster(); |
164 | 0 | } |
165 | |
|
166 | 0 | aOriginInfos.push_back({ aDestMatRange, pCode, bDynamic }); |
167 | 0 | } |
168 | | |
169 | | // Pre-check: detect existing matrices at the destination. |
170 | | // If fully covered by one of our expanded ranges, queue |
171 | | // for clearing. If only partially covered, abort. |
172 | 0 | ScRangeList aMatricesToClear; |
173 | 0 | for (const auto& rInfo : aOriginInfos) |
174 | 0 | { |
175 | 0 | const ScRange& rDest = rInfo.maDestRange; |
176 | 0 | for (SCROW r = rDest.aStart.Row(); r <= rDest.aEnd.Row(); ++r) |
177 | 0 | { |
178 | 0 | for (SCCOL c = rDest.aStart.Col(); c <= rDest.aEnd.Col(); ++c) |
179 | 0 | { |
180 | 0 | ScRange aExisting; |
181 | 0 | if (rThisDoc.GetMatrixFormulaRange(ScAddress(c, r, nThisTab), aExisting)) |
182 | 0 | { |
183 | 0 | if (rDest.Contains(aExisting)) |
184 | 0 | { |
185 | 0 | if (!aMatricesToClear.Contains(aExisting)) |
186 | 0 | aMatricesToClear.push_back(aExisting); |
187 | 0 | } |
188 | 0 | else |
189 | 0 | { |
190 | 0 | pTabViewShell->ErrorMessage(STR_MATRIXFRAGMENTERR); |
191 | 0 | return; |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | // Check if the paste area would overwrite non-empty cells. |
199 | 0 | if (bShowDialog |
200 | 0 | && (nFlags & InsertDeleteFlags::CONTENTS) |
201 | 0 | && ScModule::get()->GetInputOptions().GetReplaceCellsWarn()) |
202 | 0 | { |
203 | 0 | bool bIsEmpty = true; |
204 | 0 | SCCOL nOrigEndCol = nThisCol + (rOrigRange.aEnd.Col() - rOrigRange.aStart.Col()); |
205 | 0 | SCROW nOrigEndRow = nThisRow + (rOrigRange.aEnd.Row() - rOrigRange.aStart.Row()); |
206 | | |
207 | | // Check the original selection area. |
208 | 0 | bIsEmpty = rThisDoc.IsBlockEmpty( |
209 | 0 | nThisCol, nThisRow, nOrigEndCol, nOrigEndRow, nThisTab); |
210 | | |
211 | | // Check the expanded matrix areas (beyond original selection). |
212 | 0 | for (size_t i = 0; i < aOriginInfos.size() && bIsEmpty; ++i) |
213 | 0 | { |
214 | 0 | const ScRange& rDest = aOriginInfos[i].maDestRange; |
215 | 0 | if (rDest.aEnd.Col() > nOrigEndCol) |
216 | 0 | { |
217 | 0 | SCCOL nCheckStart = std::max(rDest.aStart.Col(), static_cast<SCCOL>(nOrigEndCol + 1)); |
218 | 0 | bIsEmpty = rThisDoc.IsBlockEmpty( |
219 | 0 | nCheckStart, rDest.aStart.Row(), |
220 | 0 | rDest.aEnd.Col(), rDest.aEnd.Row(), nThisTab); |
221 | 0 | } |
222 | 0 | if (bIsEmpty && rDest.aEnd.Row() > nOrigEndRow) |
223 | 0 | { |
224 | 0 | SCROW nCheckStart = std::max(rDest.aStart.Row(), static_cast<SCROW>(nOrigEndRow + 1)); |
225 | 0 | SCCOL nColEnd = std::min(rDest.aEnd.Col(), nOrigEndCol); |
226 | 0 | bIsEmpty = rThisDoc.IsBlockEmpty( |
227 | 0 | rDest.aStart.Col(), nCheckStart, |
228 | 0 | nColEnd, rDest.aEnd.Row(), nThisTab); |
229 | 0 | } |
230 | 0 | } |
231 | |
|
232 | 0 | if (!bIsEmpty) |
233 | 0 | { |
234 | 0 | ScReplaceWarnBox aBox(rViewData.GetDialogParent()); |
235 | 0 | if (aBox.run() != RET_YES) |
236 | 0 | return; |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | // --- All checks passed. Now modify. --- |
241 | 0 | ScDocShell* pDocSh = rViewData.GetDocShell(); |
242 | 0 | SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); |
243 | 0 | OUString aUndo = ScResId(pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY); |
244 | 0 | pUndoMgr->EnterListAction(aUndo, aUndo, 0, |
245 | 0 | rViewData.GetViewShell()->GetViewShellId()); |
246 | | |
247 | | // Clear existing complete matrices that will be replaced. |
248 | 0 | for (size_t i = 0; i < aMatricesToClear.size(); ++i) |
249 | 0 | { |
250 | 0 | const ScRange& rClear = aMatricesToClear[i]; |
251 | 0 | ScMarkData aClearMark(rThisDoc.GetSheetLimits()); |
252 | 0 | aClearMark.SelectTable(nThisTab, true); |
253 | 0 | aClearMark.SetMarkArea(rClear); |
254 | 0 | pDocSh->GetDocFunc().DeleteContents( |
255 | 0 | aClearMark, InsertDeleteFlags::ALL, true /*bRecord*/, true /*bApi*/); |
256 | 0 | } |
257 | | |
258 | | // Check whether the original selection contains any |
259 | | // non-matrix cells. If tracked matrices fully cover it |
260 | | // we can skip the expensive CopyToDocument + PasteFromClip |
261 | | // round-trip — EnterMatrix alone handles the paste. |
262 | 0 | sal_uLong nOrigCells |
263 | 0 | = static_cast<sal_uLong>(rOrigRange.aEnd.Col() - rOrigRange.aStart.Col() + 1) |
264 | 0 | * (rOrigRange.aEnd.Row() - rOrigRange.aStart.Row() + 1); |
265 | 0 | sal_uLong nMatrixCells = 0; |
266 | 0 | auto countMatrixCells = [&](const ScRangeList& rList) { |
267 | 0 | for (size_t i = 0; i < rList.size(); ++i) |
268 | 0 | { |
269 | 0 | ScRange aInt = rOrigRange.Intersection(rList[i]); |
270 | 0 | if (aInt.IsValid()) |
271 | 0 | nMatrixCells |
272 | 0 | += static_cast<sal_uLong>(aInt.aEnd.Col() - aInt.aStart.Col() + 1) |
273 | 0 | * (aInt.aEnd.Row() - aInt.aStart.Row() + 1); |
274 | 0 | } |
275 | 0 | }; |
276 | 0 | countMatrixCells(rClipParam.maOriginMatrixRanges); |
277 | 0 | countMatrixCells(rClipParam.maMatrixRanges); |
278 | |
|
279 | 0 | if (nMatrixCells < nOrigCells) |
280 | 0 | { |
281 | | // Build a temp clip doc with the original selection |
282 | | // and clear tracked matrix cells so that only |
283 | | // non-matrix cells remain for pasting. |
284 | 0 | auto pOrigDoc = std::make_shared<ScDocument>(SCDOCMODE_CLIP); |
285 | 0 | pOrigDoc->ResetClip(pClipDoc, nClipTab); |
286 | 0 | pClipDoc->CopyToDocument(rOrigRange, InsertDeleteFlags::ALL, false, *pOrigDoc); |
287 | |
|
288 | 0 | auto clearTrackedMatrixCells = [&](const ScRangeList& rList) { |
289 | 0 | for (size_t i = 0; i < rList.size(); ++i) |
290 | 0 | { |
291 | 0 | ScRange aInt = rOrigRange.Intersection(rList[i]); |
292 | 0 | if (aInt.IsValid()) |
293 | 0 | pOrigDoc->DeleteAreaTab(aInt.aStart.Col(), aInt.aStart.Row(), |
294 | 0 | aInt.aEnd.Col(), aInt.aEnd.Row(), |
295 | 0 | nClipTab, InsertDeleteFlags::ALL); |
296 | 0 | } |
297 | 0 | }; |
298 | 0 | clearTrackedMatrixCells(rClipParam.maOriginMatrixRanges); |
299 | 0 | clearTrackedMatrixCells(rClipParam.maMatrixRanges); |
300 | |
|
301 | 0 | ScClipParam aOrigParam(rOrigRange, false); |
302 | 0 | pOrigDoc->SetClipParam(aOrigParam); |
303 | |
|
304 | 0 | pTabViewShell->PasteFromClip(nFlags, pOrigDoc.get(), |
305 | 0 | ScPasteFunc::NONE, true /*bSkipEmpty*/, false, false, |
306 | 0 | INS_NONE, InsertDeleteFlags::NONE, false /*bShowDialog*/); |
307 | 0 | } |
308 | | |
309 | | // Expand each origin matrix using EnterMatrix. |
310 | 0 | for (const auto& rInfo : aOriginInfos) |
311 | 0 | { |
312 | 0 | if (!rInfo.mpCode) |
313 | 0 | continue; |
314 | | |
315 | 0 | const ScRange& rDest = rInfo.maDestRange; |
316 | 0 | ScMarkData aMark(rThisDoc.GetSheetLimits()); |
317 | 0 | aMark.SelectTable(nThisTab, true); |
318 | |
|
319 | 0 | pDocSh->GetDocFunc().EnterMatrix( |
320 | 0 | rDest, &aMark, rInfo.mpCode, OUString(), |
321 | 0 | true /*bApi*/, false /*bEnglish*/, |
322 | 0 | OUString(), eGram, |
323 | 0 | false /*bCheckForSpill*/, |
324 | 0 | rInfo.mbDynamicArrayMaster); |
325 | 0 | } |
326 | |
|
327 | 0 | pUndoMgr->LeaveListAction(); |
328 | | |
329 | | // Restore cursor and selection to the original paste range. |
330 | 0 | SCROW nOrigRows = rOrigRange.aEnd.Row() - rOrigRange.aStart.Row(); |
331 | 0 | SCCOL nOrigCols = rOrigRange.aEnd.Col() - rOrigRange.aStart.Col(); |
332 | 0 | ScRange aDestRange(nThisCol, nThisRow, nThisTab, |
333 | 0 | nThisCol + nOrigCols, nThisRow + nOrigRows, nThisTab); |
334 | 0 | rViewData.GetMarkData().ResetMark(); |
335 | 0 | pTabViewShell->SetCursor(nThisCol, nThisRow); |
336 | 0 | rViewData.GetMarkData().SetMarkArea(aDestRange); |
337 | 0 | pTabViewShell->MarkRange(aDestRange); |
338 | 0 | } |
339 | | // --- Non-origin matrix handling --- |
340 | 0 | else if (!rClipParam.maMatrixRanges.empty() |
341 | 0 | && rClipParam.maRanges.size() == 1 |
342 | 0 | && rClipParam.maRanges[0] == rClipParam.maOriginalRange) |
343 | 0 | { |
344 | 0 | ScRange aClipRange = rClipParam.getWholeRange(); |
345 | 0 | SCTAB nClipTab = aClipRange.aStart.Tab(); |
346 | | |
347 | | // If non-origin matrices fully cover the selection, |
348 | | // there are no non-matrix cells to paste — nothing to do. |
349 | 0 | sal_uLong nClipCells |
350 | 0 | = static_cast<sal_uLong>(aClipRange.aEnd.Col() - aClipRange.aStart.Col() + 1) |
351 | 0 | * (aClipRange.aEnd.Row() - aClipRange.aStart.Row() + 1); |
352 | 0 | sal_uLong nMatCells = 0; |
353 | 0 | for (size_t i = 0; i < rClipParam.maMatrixRanges.size(); ++i) |
354 | 0 | { |
355 | 0 | ScRange aInt = aClipRange.Intersection(rClipParam.maMatrixRanges[i]); |
356 | 0 | if (aInt.IsValid()) |
357 | 0 | nMatCells |
358 | 0 | += static_cast<sal_uLong>(aInt.aEnd.Col() - aInt.aStart.Col() + 1) |
359 | 0 | * (aInt.aEnd.Row() - aInt.aStart.Row() + 1); |
360 | 0 | } |
361 | 0 | if (nMatCells >= nClipCells) |
362 | 0 | return; |
363 | | |
364 | 0 | auto pTempDoc = std::make_shared<ScDocument>(SCDOCMODE_CLIP); |
365 | 0 | pTempDoc->ResetClip(pClipDoc, nClipTab); |
366 | 0 | pClipDoc->CopyToDocument(aClipRange, InsertDeleteFlags::ALL, false, *pTempDoc); |
367 | |
|
368 | 0 | for (size_t i = 0; i < rClipParam.maMatrixRanges.size(); ++i) |
369 | 0 | { |
370 | 0 | ScRange aClearRange = aClipRange.Intersection(rClipParam.maMatrixRanges[i]); |
371 | 0 | if (aClearRange.IsValid()) |
372 | 0 | pTempDoc->DeleteAreaTab(aClearRange.aStart.Col(), aClearRange.aStart.Row(), |
373 | 0 | aClearRange.aEnd.Col(), aClearRange.aEnd.Row(), |
374 | 0 | nClipTab, InsertDeleteFlags::ALL); |
375 | 0 | } |
376 | |
|
377 | 0 | ScClipParam aTempParam(rClipParam); |
378 | 0 | pTempDoc->SetClipParam(aTempParam); |
379 | |
|
380 | 0 | pTabViewShell->PasteFromClip( nFlags, pTempDoc.get(), |
381 | 0 | ScPasteFunc::NONE, true /*bSkipEmpty*/, false, false, |
382 | 0 | INS_NONE, InsertDeleteFlags::NONE, |
383 | 0 | bShowDialog ); |
384 | 0 | } |
385 | 0 | else |
386 | 0 | { |
387 | | // Normal paste: no matrix issues. |
388 | 0 | pTabViewShell->PasteFromClip( nFlags, pClipDoc, |
389 | 0 | ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, |
390 | 0 | bShowDialog ); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | } |
394 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
395 | 0 | { |
396 | 0 | bool entireColumnOrRowSelected = false; |
397 | 0 | if (pOwnClip) |
398 | 0 | { |
399 | 0 | ScClipParam clipParam = pOwnClip->GetDocument()->GetClipParam(); |
400 | 0 | if (clipParam.maRanges.size() > 0) |
401 | 0 | { |
402 | 0 | if (clipParam.maRanges[0].aEnd.Col() == pOwnClip->GetDocument()->MaxCol() |
403 | 0 | || clipParam.maRanges[0].aEnd.Row() == pOwnClip->GetDocument()->MaxRow()) |
404 | 0 | { |
405 | 0 | entireColumnOrRowSelected = true; |
406 | 0 | } |
407 | 0 | } |
408 | 0 | } |
409 | 0 | const SfxBoolItem& rItem = rThisDoc.GetAttr(nThisCol, nThisRow, nThisTab, ATTR_LINEBREAK); |
410 | 0 | if (rItem.GetValue() || entireColumnOrRowSelected) |
411 | 0 | { |
412 | 0 | pTabViewShell->OnLOKSetWidthOrHeight(nThisCol, true); |
413 | 0 | pTabViewShell->OnLOKSetWidthOrHeight(nThisRow, false); |
414 | |
|
415 | 0 | ScTabViewShell::notifyAllViewsSheetGeomInvalidation( |
416 | 0 | pTabViewShell, true /* bColumns */, true /* bRows */, true /* bSizes*/, |
417 | 0 | true /* bHidden */, true /* bFiltered */, true /* bGroups */, rViewData.GetTabNumber()); |
418 | 0 | } |
419 | 0 | } |
420 | 0 | pTabViewShell->CellContentChanged(); // => PasteFromSystem() ??? |
421 | 0 | } |
422 | | |
423 | | bool ScClipUtil::CheckDestRanges( |
424 | | const ScDocument& rDoc, SCCOL nSrcCols, SCROW nSrcRows, const ScMarkData& rMark, const ScRangeList& rDest) |
425 | 0 | { |
426 | 0 | for (size_t i = 0, n = rDest.size(); i < n; ++i) |
427 | 0 | { |
428 | 0 | ScRange aTest = rDest[i]; |
429 | | // Check for filtered rows in all selected sheets. |
430 | 0 | for (const auto& rTab : rMark) |
431 | 0 | { |
432 | 0 | aTest.aStart.SetTab(rTab); |
433 | 0 | aTest.aEnd.SetTab(rTab); |
434 | 0 | if (ScViewUtil::HasFiltered(aTest, rDoc)) |
435 | 0 | { |
436 | | // I don't know how to handle pasting into filtered rows yet. |
437 | 0 | return false; |
438 | 0 | } |
439 | 0 | } |
440 | | |
441 | | // Destination range must be an exact multiple of the source range. |
442 | 0 | SCROW nRows = aTest.aEnd.Row() - aTest.aStart.Row() + 1; |
443 | 0 | SCCOL nCols = aTest.aEnd.Col() - aTest.aStart.Col() + 1; |
444 | 0 | SCROW nRowTest = (nRows / nSrcRows) * nSrcRows; |
445 | 0 | SCCOL nColTest = (nCols / nSrcCols) * nSrcCols; |
446 | 0 | if ( rDest.size() > 1 && ( nRows != nRowTest || nCols != nColTest ) ) |
447 | 0 | { |
448 | | // Destination range is not a multiple of the source range. Bail out. |
449 | 0 | return false; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | return true; |
453 | 0 | } |
454 | | |
455 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |