/src/libreoffice/sc/source/ui/operation/MoveBlockOperation.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 <operation/MoveBlockOperation.hxx> |
11 | | |
12 | | #include <osl/diagnose.h> |
13 | | #include <sfx2/app.hxx> |
14 | | |
15 | | #include <clipparam.hxx> |
16 | | #include <docsh.hxx> |
17 | | #include <docfunc.hxx> |
18 | | #include <document.hxx> |
19 | | #include <drwlayer.hxx> |
20 | | #include <editable.hxx> |
21 | | #include <globstr.hrc> |
22 | | #include <markdata.hxx> |
23 | | #include <undoblk.hxx> |
24 | | |
25 | | namespace sc |
26 | | { |
27 | | MoveBlockOperation::MoveBlockOperation(ScDocShell& rDocShell, const ScRange& rSource, |
28 | | const ScAddress& rDestPos, bool bCut, bool bRecord, |
29 | | bool bPaint, bool bApi) |
30 | 0 | : Operation(OperationType::MoveBlock, bRecord, bApi) |
31 | 0 | , mrDocShell(rDocShell) |
32 | 0 | , maSource(rSource) |
33 | 0 | , maDestPos(rDestPos) |
34 | 0 | , mbCut(bCut) |
35 | 0 | , mbPaint(bPaint) |
36 | 0 | { |
37 | 0 | } |
38 | | |
39 | | bool MoveBlockOperation::canRunTheOperation() const |
40 | 0 | { |
41 | 0 | return !isInputOnSheetViewAutoFilter(maSource) |
42 | 0 | && !isInputOnSheetViewAutoFilter(ScRange(maDestPos)); |
43 | 0 | } |
44 | | |
45 | | bool MoveBlockOperation::runImplementation() |
46 | 0 | { |
47 | 0 | ScDocShellModificator aModificator(mrDocShell); |
48 | |
|
49 | 0 | ScRange aSource = convertRange(maSource); |
50 | 0 | ScAddress aDestPos = convertAddress(maDestPos); |
51 | |
|
52 | 0 | SCCOL nStartCol = aSource.aStart.Col(); |
53 | 0 | SCROW nStartRow = aSource.aStart.Row(); |
54 | 0 | SCTAB nStartTab = aSource.aStart.Tab(); |
55 | 0 | SCCOL nEndCol = aSource.aEnd.Col(); |
56 | 0 | SCROW nEndRow = aSource.aEnd.Row(); |
57 | 0 | SCTAB nEndTab = aSource.aEnd.Tab(); |
58 | 0 | SCCOL nDestCol = aDestPos.Col(); |
59 | 0 | SCROW nDestRow = aDestPos.Row(); |
60 | 0 | SCTAB nDestTab = aDestPos.Tab(); |
61 | |
|
62 | 0 | ScDocument& rDoc = mrDocShell.GetDocument(); |
63 | 0 | if (!rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) || !rDoc.ValidRow(nDestRow)) |
64 | 0 | { |
65 | 0 | OSL_FAIL("invalid row in MoveBlock"); |
66 | 0 | return false; |
67 | 0 | } |
68 | | |
69 | | // adjust related scenarios too - but only when moved within one sheet |
70 | 0 | bool bScenariosAdded = false; |
71 | 0 | if (mbRecord && !rDoc.IsUndoEnabled()) |
72 | 0 | mbRecord = false; |
73 | |
|
74 | 0 | SCTAB nTabCount = rDoc.GetTableCount(); |
75 | 0 | if (nDestTab == nStartTab && !rDoc.IsScenario(nEndTab)) |
76 | 0 | while (nEndTab + 1 < nTabCount && rDoc.IsScenario(nEndTab + 1)) |
77 | 0 | { |
78 | 0 | ++nEndTab; |
79 | 0 | bScenariosAdded = true; |
80 | 0 | } |
81 | |
|
82 | 0 | SCTAB nSrcTabCount = nEndTab - nStartTab + 1; |
83 | 0 | SCTAB nDestEndTab = nDestTab + nSrcTabCount - 1; |
84 | 0 | SCTAB nTab; |
85 | |
|
86 | 0 | ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP)); |
87 | |
|
88 | 0 | ScMarkData aSourceMark(rDoc.GetSheetLimits()); |
89 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
90 | 0 | aSourceMark.SelectTable(nTab, true); // select source |
91 | 0 | aSourceMark.SetMarkArea(aSource); |
92 | |
|
93 | 0 | ScDocShellRef aDragShellRef; |
94 | 0 | if (rDoc.HasOLEObjectsInArea(aSource)) |
95 | 0 | { |
96 | 0 | aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately |
97 | 0 | aDragShellRef->DoInitNew(); |
98 | 0 | } |
99 | 0 | ScDrawLayer::SetGlobalDrawPersist(aDragShellRef.get()); |
100 | |
|
101 | 0 | ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), |
102 | 0 | mbCut); |
103 | 0 | rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true); |
104 | |
|
105 | 0 | ScDrawLayer::SetGlobalDrawPersist(nullptr); |
106 | |
|
107 | 0 | SCCOL nOldEndCol = nEndCol; |
108 | 0 | SCROW nOldEndRow = nEndRow; |
109 | 0 | bool bClipOver = false; |
110 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
111 | 0 | { |
112 | 0 | SCCOL nTmpEndCol = nOldEndCol; |
113 | 0 | SCROW nTmpEndRow = nOldEndRow; |
114 | 0 | if (rDoc.ExtendMerge(nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab)) |
115 | 0 | bClipOver = true; |
116 | 0 | if (nTmpEndCol > nEndCol) |
117 | 0 | nEndCol = nTmpEndCol; |
118 | 0 | if (nTmpEndRow > nEndRow) |
119 | 0 | nEndRow = nTmpEndRow; |
120 | 0 | } |
121 | |
|
122 | 0 | SCCOL nDestEndCol = nDestCol + (nOldEndCol - nStartCol); |
123 | 0 | SCROW nDestEndRow = nDestRow + (nOldEndRow - nStartRow); |
124 | |
|
125 | 0 | SCCOL nUndoEndCol = nDestCol + (nEndCol - nStartCol); // extended in destination block |
126 | 0 | SCROW nUndoEndRow = nDestRow + (nEndRow - nStartRow); |
127 | |
|
128 | 0 | bool bIncludeFiltered = mbCut; |
129 | 0 | if (!bIncludeFiltered) |
130 | 0 | { |
131 | | // adjust sizes to include only non-filtered rows |
132 | |
|
133 | 0 | SCCOL nClipX; |
134 | 0 | SCROW nClipY; |
135 | 0 | pClipDoc->GetClipArea(nClipX, nClipY, false); |
136 | 0 | SCROW nUndoAdd = nUndoEndRow - nDestEndRow; |
137 | 0 | nDestEndRow = nDestRow + nClipY; |
138 | 0 | nUndoEndRow = nDestEndRow + nUndoAdd; |
139 | 0 | } |
140 | |
|
141 | 0 | if (!rDoc.ValidCol(nUndoEndCol) || !rDoc.ValidRow(nUndoEndRow)) |
142 | 0 | { |
143 | 0 | if (!mbApi) |
144 | 0 | mrDocShell.ErrorMessage(STR_PASTE_FULL); |
145 | 0 | return false; |
146 | 0 | } |
147 | | |
148 | | // Test for cell protection |
149 | | |
150 | 0 | ScEditableTester aTester; |
151 | 0 | for (nTab = nDestTab; nTab <= nDestEndTab; nTab++) |
152 | 0 | aTester.TestBlock(rDoc, nTab, nDestCol, nDestRow, nUndoEndCol, nUndoEndRow); |
153 | 0 | if (mbCut) |
154 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
155 | 0 | aTester.TestBlock(rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow); |
156 | |
|
157 | 0 | if (!aTester.IsEditable()) |
158 | 0 | { |
159 | 0 | if (!mbApi) |
160 | 0 | mrDocShell.ErrorMessage(aTester.GetMessageId()); |
161 | 0 | return false; |
162 | 0 | } |
163 | | |
164 | | // Test for merged cells- when moving after delete |
165 | | |
166 | 0 | if (bClipOver && !mbCut) |
167 | 0 | if (rDoc.HasAttrib(nDestCol, nDestRow, nDestTab, nUndoEndCol, nUndoEndRow, nDestEndTab, |
168 | 0 | HasAttrFlags::Merged | HasAttrFlags::Overlapped)) |
169 | 0 | { // "Merge of already merged cells not possible" |
170 | 0 | if (!mbApi) |
171 | 0 | mrDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0); |
172 | 0 | return false; |
173 | 0 | } |
174 | | |
175 | | // Are there borders in the cells? (for painting) |
176 | | |
177 | 0 | sal_uInt16 nSourceExt = 0; |
178 | 0 | mrDocShell.UpdatePaintExt(nSourceExt, nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, |
179 | 0 | nEndTab); |
180 | 0 | sal_uInt16 nDestExt = 0; |
181 | 0 | mrDocShell.UpdatePaintExt(nDestExt, nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, |
182 | 0 | nDestEndTab); |
183 | | |
184 | | // do it |
185 | |
|
186 | 0 | ScDocumentUniquePtr pUndoDoc; |
187 | |
|
188 | 0 | if (mbRecord) |
189 | 0 | { |
190 | 0 | bool bWholeCols = (nStartRow == 0 && nEndRow == rDoc.MaxRow()); |
191 | 0 | bool bWholeRows = (nStartCol == 0 && nEndCol == rDoc.MaxCol()); |
192 | 0 | InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) |
193 | 0 | | InsertDeleteFlags::NOCAPTIONS; |
194 | |
|
195 | 0 | pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); |
196 | 0 | pUndoDoc->InitUndo(rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows); |
197 | |
|
198 | 0 | if (mbCut) |
199 | 0 | { |
200 | 0 | rDoc.CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, |
201 | 0 | nUndoFlags, false, *pUndoDoc); |
202 | 0 | } |
203 | |
|
204 | 0 | if (nDestTab != nStartTab) |
205 | 0 | pUndoDoc->AddUndoTab(nDestTab, nDestEndTab, bWholeCols, bWholeRows); |
206 | 0 | rDoc.CopyToDocument(nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab, |
207 | 0 | nUndoFlags, false, *pUndoDoc); |
208 | 0 | rDoc.BeginDrawUndo(); |
209 | 0 | } |
210 | |
|
211 | 0 | bool bSourceHeight = false; // adjust heights? |
212 | 0 | if (mbCut) |
213 | 0 | { |
214 | 0 | ScMarkData aDelMark(rDoc.GetSheetLimits()); // only for tables |
215 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
216 | 0 | { |
217 | 0 | rDoc.DeleteAreaTab(nStartCol, nStartRow, nOldEndCol, nOldEndRow, nTab, |
218 | 0 | InsertDeleteFlags::ALL); |
219 | 0 | aDelMark.SelectTable(nTab, true); |
220 | 0 | } |
221 | 0 | rDoc.DeleteObjectsInArea(nStartCol, nStartRow, nOldEndCol, nOldEndRow, aDelMark); |
222 | | |
223 | | // Test for merged cells |
224 | |
|
225 | 0 | if (bClipOver) |
226 | 0 | if (rDoc.HasAttrib(nDestCol, nDestRow, nDestTab, nUndoEndCol, nUndoEndRow, nDestEndTab, |
227 | 0 | HasAttrFlags::Merged | HasAttrFlags::Overlapped)) |
228 | 0 | { |
229 | 0 | rDoc.CopyFromClip(aSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, |
230 | 0 | pClipDoc.get()); |
231 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
232 | 0 | { |
233 | 0 | SCCOL nTmpEndCol = nEndCol; |
234 | 0 | SCROW nTmpEndRow = nEndRow; |
235 | 0 | rDoc.ExtendMerge(nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true); |
236 | 0 | } |
237 | | |
238 | | // Report error only after restoring content |
239 | 0 | if (!mbApi) // "Merge of already merged cells not possible" |
240 | 0 | mrDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0); |
241 | |
|
242 | 0 | return false; |
243 | 0 | } |
244 | | |
245 | 0 | bSourceHeight = mrDocShell.GetDocFunc().AdjustRowHeight(aSource, false, mbApi); |
246 | 0 | } |
247 | | |
248 | 0 | ScRange aPasteDest(nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab); |
249 | |
|
250 | 0 | ScMarkData aDestMark(rDoc.GetSheetLimits()); |
251 | 0 | for (nTab = nDestTab; nTab <= nDestEndTab; nTab++) |
252 | 0 | aDestMark.SelectTable(nTab, true); // select destination |
253 | 0 | aDestMark.SetMarkArea(aPasteDest); |
254 | | |
255 | | /* Do not copy drawing objects here. While pasting, the |
256 | | function ScDocument::UpdateReference() is called which calls |
257 | | ScDrawLayer::MoveCells() which may move away inserted objects to wrong |
258 | | positions (e.g. if source and destination range overlaps).*/ |
259 | |
|
260 | 0 | rDoc.CopyFromClip(aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, |
261 | 0 | pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered); |
262 | | |
263 | | // skipped rows and merged cells don't mix |
264 | 0 | if (!bIncludeFiltered && pClipDoc->HasClipFilteredRows()) |
265 | 0 | mrDocShell.GetDocFunc().UnmergeCells(aPasteDest, false, nullptr); |
266 | |
|
267 | 0 | bool bDestHeight = mrDocShell.GetDocFunc().AdjustRowHeight( |
268 | 0 | ScRange(0, nDestRow, nDestTab, rDoc.MaxCol(), nDestEndRow, nDestEndTab), false, mbApi); |
269 | | |
270 | | /* Paste drawing objects after adjusting formula references |
271 | | and row heights. There are no cell notes or drawing objects, if the |
272 | | clipdoc does not contain a drawing layer.*/ |
273 | 0 | if (pClipDoc->GetDrawLayer()) |
274 | 0 | rDoc.CopyFromClip(aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS, nullptr, |
275 | 0 | pClipDoc.get(), true, false, bIncludeFiltered); |
276 | |
|
277 | 0 | if (mbRecord) |
278 | 0 | { |
279 | 0 | ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab); |
280 | 0 | ScAddress aDestPosUndo(nDestCol, nDestRow, nDestTab); |
281 | |
|
282 | 0 | mrDocShell.GetUndoManager()->AddUndoAction(std::make_unique<ScUndoDragDrop>( |
283 | 0 | mrDocShell, aUndoRange, aDestPosUndo, mbCut, std::move(pUndoDoc), bScenariosAdded)); |
284 | 0 | } |
285 | |
|
286 | 0 | SCCOL nDestPaintEndCol = nDestEndCol; |
287 | 0 | SCROW nDestPaintEndRow = nDestEndRow; |
288 | 0 | for (nTab = nDestTab; nTab <= nDestEndTab; nTab++) |
289 | 0 | { |
290 | 0 | SCCOL nTmpEndCol = nDestEndCol; |
291 | 0 | SCROW nTmpEndRow = nDestEndRow; |
292 | 0 | rDoc.ExtendMerge(nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true); |
293 | 0 | if (nTmpEndCol > nDestPaintEndCol) |
294 | 0 | nDestPaintEndCol = nTmpEndCol; |
295 | 0 | if (nTmpEndRow > nDestPaintEndRow) |
296 | 0 | nDestPaintEndRow = nTmpEndRow; |
297 | 0 | } |
298 | |
|
299 | 0 | if (mbCut) |
300 | 0 | for (nTab = nStartTab; nTab <= nEndTab; nTab++) |
301 | 0 | rDoc.RefreshAutoFilter(nStartCol, nStartRow, nEndCol, nEndRow, nTab); |
302 | |
|
303 | 0 | if (mbPaint) |
304 | 0 | { |
305 | | // destination range: |
306 | |
|
307 | 0 | SCCOL nPaintStartX = nDestCol; |
308 | 0 | SCROW nPaintStartY = nDestRow; |
309 | 0 | SCCOL nPaintEndX = nDestPaintEndCol; |
310 | 0 | SCROW nPaintEndY = nDestPaintEndRow; |
311 | 0 | PaintPartFlags nFlags = PaintPartFlags::Grid; |
312 | |
|
313 | 0 | if (nStartRow == 0 && nEndRow == rDoc.MaxRow()) // copy widths too? |
314 | 0 | { |
315 | 0 | nPaintEndX = rDoc.MaxCol(); |
316 | 0 | nPaintStartY = 0; |
317 | 0 | nPaintEndY = rDoc.MaxRow(); |
318 | 0 | nFlags |= PaintPartFlags::Top; |
319 | 0 | } |
320 | 0 | if (bDestHeight || (nStartCol == 0 && nEndCol == rDoc.MaxCol())) |
321 | 0 | { |
322 | 0 | nPaintEndY = rDoc.MaxRow(); |
323 | 0 | nPaintStartX = 0; |
324 | 0 | nPaintEndX = rDoc.MaxCol(); |
325 | 0 | nFlags |= PaintPartFlags::Left; |
326 | 0 | } |
327 | 0 | if (bScenariosAdded) |
328 | 0 | { |
329 | 0 | nPaintStartX = 0; |
330 | 0 | nPaintStartY = 0; |
331 | 0 | nPaintEndX = rDoc.MaxCol(); |
332 | 0 | nPaintEndY = rDoc.MaxRow(); |
333 | 0 | } |
334 | |
|
335 | 0 | mrDocShell.PostPaint(nPaintStartX, nPaintStartY, nDestTab, nPaintEndX, nPaintEndY, |
336 | 0 | nDestEndTab, nFlags, nSourceExt | nDestExt); |
337 | |
|
338 | 0 | if (mbCut) |
339 | 0 | { |
340 | | // source range: |
341 | |
|
342 | 0 | nPaintStartX = nStartCol; |
343 | 0 | nPaintStartY = nStartRow; |
344 | 0 | nPaintEndX = nEndCol; |
345 | 0 | nPaintEndY = nEndRow; |
346 | 0 | nFlags = PaintPartFlags::Grid; |
347 | |
|
348 | 0 | if (bSourceHeight) |
349 | 0 | { |
350 | 0 | nPaintEndY = rDoc.MaxRow(); |
351 | 0 | nPaintStartX = 0; |
352 | 0 | nPaintEndX = rDoc.MaxCol(); |
353 | 0 | nFlags |= PaintPartFlags::Left; |
354 | 0 | } |
355 | 0 | if (bScenariosAdded) |
356 | 0 | { |
357 | 0 | nPaintStartX = 0; |
358 | 0 | nPaintStartY = 0; |
359 | 0 | nPaintEndX = rDoc.MaxCol(); |
360 | 0 | nPaintEndY = rDoc.MaxRow(); |
361 | 0 | } |
362 | |
|
363 | 0 | mrDocShell.PostPaint(nPaintStartX, nPaintStartY, nStartTab, nPaintEndX, nPaintEndY, |
364 | 0 | nEndTab, nFlags, nSourceExt); |
365 | 0 | } |
366 | 0 | } |
367 | |
|
368 | 0 | syncSheetViews(); |
369 | |
|
370 | 0 | aModificator.SetDocumentModified(); |
371 | |
|
372 | 0 | SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreaLinksChanged)); |
373 | |
|
374 | 0 | mrDocShell.ResolveSpilledOutputs(); |
375 | |
|
376 | 0 | return true; |
377 | 0 | } |
378 | | } |
379 | | |
380 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |