/src/libreoffice/vcl/source/edit/textundo.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 "textundo.hxx" |
21 | | #include "textund2.hxx" |
22 | | #include <strings.hrc> |
23 | | |
24 | | #include <sal/log.hxx> |
25 | | #include <utility> |
26 | | #include <vcl/texteng.hxx> |
27 | | #include <vcl/textview.hxx> |
28 | | #include <vcl/textdata.hxx> |
29 | | #include "textdoc.hxx" |
30 | | #include "textdat2.hxx" |
31 | | #include <svdata.hxx> |
32 | | |
33 | | namespace |
34 | | { |
35 | | |
36 | | // Shorten() -- inserts ellipsis (...) in the middle of a long text |
37 | | void Shorten (OUString& rString) |
38 | 0 | { |
39 | 0 | auto const nLen = rString.getLength(); |
40 | 0 | if (nLen <= 48) |
41 | 0 | return; |
42 | | |
43 | | // If possible, we don't break a word, hence first we look for a space. |
44 | | // Space before the ellipsis: |
45 | 0 | auto iFirst = rString.lastIndexOf(' ', 32); |
46 | 0 | if (iFirst == -1 || iFirst < 16) |
47 | 0 | iFirst = 24; // not possible |
48 | | // Space after the ellipsis: |
49 | 0 | auto iLast = rString.indexOf(' ', nLen - 16); |
50 | 0 | if (iLast == -1 || iLast > nLen - 4) |
51 | 0 | iLast = nLen - 8; // not possible |
52 | | // finally: |
53 | 0 | rString = |
54 | 0 | OUString::Concat(rString.subView(0, iFirst + 1)) + |
55 | 0 | "..." + |
56 | 0 | rString.subView(iLast); |
57 | 0 | } |
58 | | |
59 | | } // namespace |
60 | | |
61 | | TextUndoManager::TextUndoManager( TextEngine* p ) |
62 | 0 | { |
63 | 0 | mpTextEngine = p; |
64 | 0 | } |
65 | | |
66 | | TextUndoManager::~TextUndoManager() |
67 | 0 | { |
68 | 0 | } |
69 | | |
70 | | bool TextUndoManager::Undo() |
71 | 0 | { |
72 | 0 | if ( GetUndoActionCount() == 0 ) |
73 | 0 | return false; |
74 | | |
75 | 0 | UndoRedoStart(); |
76 | |
|
77 | 0 | mpTextEngine->SetIsInUndo( true ); |
78 | 0 | bool bDone = SfxUndoManager::Undo(); |
79 | 0 | mpTextEngine->SetIsInUndo( false ); |
80 | |
|
81 | 0 | UndoRedoEnd(); |
82 | |
|
83 | 0 | return bDone; |
84 | 0 | } |
85 | | |
86 | | bool TextUndoManager::Redo() |
87 | 0 | { |
88 | 0 | if ( GetRedoActionCount() == 0 ) |
89 | 0 | return false; |
90 | | |
91 | 0 | UndoRedoStart(); |
92 | |
|
93 | 0 | mpTextEngine->SetIsInUndo( true ); |
94 | 0 | bool bDone = SfxUndoManager::Redo(); |
95 | 0 | mpTextEngine->SetIsInUndo( false ); |
96 | |
|
97 | 0 | UndoRedoEnd(); |
98 | |
|
99 | 0 | return bDone; |
100 | 0 | } |
101 | | |
102 | | void TextUndoManager::UndoRedoStart() |
103 | 0 | { |
104 | 0 | SAL_WARN_IF( !GetView(), "vcl", "Undo/Redo: Active View?" ); |
105 | 0 | } |
106 | | |
107 | | void TextUndoManager::UndoRedoEnd() |
108 | 0 | { |
109 | 0 | if ( GetView() ) |
110 | 0 | { |
111 | 0 | TextSelection aNewSel( GetView()->GetSelection() ); |
112 | 0 | aNewSel.GetStart() = aNewSel.GetEnd(); |
113 | 0 | GetView()->ImpSetSelection( aNewSel ); |
114 | 0 | } |
115 | |
|
116 | 0 | mpTextEngine->FormatAndUpdate( GetView() ); |
117 | 0 | } |
118 | | |
119 | | TextUndo::TextUndo( TextEngine* p ) |
120 | 0 | { |
121 | 0 | mpTextEngine = p; |
122 | 0 | } |
123 | | |
124 | | TextUndo::~TextUndo() |
125 | 0 | { |
126 | 0 | } |
127 | | |
128 | | OUString TextUndo::GetComment() const |
129 | 0 | { |
130 | 0 | return OUString(); |
131 | 0 | } |
132 | | |
133 | | void TextUndo::SetSelection( const TextSelection& rSel ) |
134 | 0 | { |
135 | 0 | if ( GetView() ) |
136 | 0 | GetView()->ImpSetSelection( rSel ); |
137 | 0 | } |
138 | | |
139 | | TextUndoDelPara::TextUndoDelPara( TextEngine* pTextEngine, TextNode* pNode, sal_uInt32 nPara ) |
140 | 0 | : TextUndo( pTextEngine ) |
141 | 0 | , mbDelObject( true) |
142 | 0 | , mnPara( nPara ) |
143 | 0 | , mpNode( pNode ) |
144 | 0 | { |
145 | 0 | } |
146 | | |
147 | | TextUndoDelPara::~TextUndoDelPara() |
148 | 0 | { |
149 | 0 | if ( mbDelObject ) |
150 | 0 | delete mpNode; |
151 | 0 | } |
152 | | |
153 | | void TextUndoDelPara::Undo() |
154 | 0 | { |
155 | 0 | GetTextEngine()->InsertContent( std::unique_ptr<TextNode>(mpNode), mnPara ); |
156 | 0 | mbDelObject = false; // belongs again to the engine |
157 | |
|
158 | 0 | if ( GetView() ) |
159 | 0 | { |
160 | 0 | TextSelection aSel( TextPaM( mnPara, 0 ), TextPaM( mnPara, mpNode->GetText().getLength() ) ); |
161 | 0 | SetSelection( aSel ); |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | void TextUndoDelPara::Redo() |
166 | 0 | { |
167 | 0 | auto & rDocNodes = GetDoc()->GetNodes(); |
168 | | // pNode is not valid anymore in case an Undo joined paragraphs |
169 | 0 | mpNode = rDocNodes[ mnPara ].get(); |
170 | |
|
171 | 0 | GetTEParaPortions()->Remove( mnPara ); |
172 | | |
173 | | // do not delete Node because of Undo! |
174 | 0 | auto it = ::std::find_if( rDocNodes.begin(), rDocNodes.end(), |
175 | 0 | [&] (std::unique_ptr<TextNode> const & p) { return p.get() == mpNode; } ); |
176 | 0 | assert(it != rDocNodes.end()); |
177 | | // coverity[leaked_storage : FALSE] - ownership transferred to this with mbDelObject |
178 | 0 | it->release(); |
179 | 0 | GetDoc()->GetNodes().erase( it ); |
180 | |
|
181 | 0 | GetTextEngine()->ImpParagraphRemoved( mnPara ); |
182 | |
|
183 | 0 | mbDelObject = true; // belongs again to the Undo |
184 | |
|
185 | 0 | const sal_uInt32 nParas = static_cast<sal_uInt32>(GetDoc()->GetNodes().size()); |
186 | 0 | const sal_uInt32 n = mnPara < nParas ? mnPara : nParas-1; |
187 | 0 | TextNode* pN = GetDoc()->GetNodes()[ n ].get(); |
188 | 0 | TextPaM aPaM( n, pN->GetText().getLength() ); |
189 | 0 | SetSelection( aPaM ); |
190 | 0 | } |
191 | | |
192 | | OUString TextUndoDelPara::GetComment () const |
193 | 0 | { |
194 | 0 | return VclResId(STR_TEXTUNDO_DELPARA); |
195 | 0 | } |
196 | | |
197 | | TextUndoConnectParas::TextUndoConnectParas( TextEngine* pTextEngine, sal_uInt32 nPara, sal_Int32 nPos ) |
198 | 0 | : TextUndo( pTextEngine ) |
199 | 0 | , mnPara( nPara ) |
200 | 0 | , mnSepPos( nPos ) |
201 | 0 | { |
202 | 0 | } |
203 | | |
204 | | TextUndoConnectParas::~TextUndoConnectParas() |
205 | | { |
206 | | } |
207 | | |
208 | | void TextUndoConnectParas::Undo() |
209 | 0 | { |
210 | 0 | TextPaM aPaM = GetTextEngine()->SplitContent( mnPara, mnSepPos ); |
211 | 0 | SetSelection( aPaM ); |
212 | 0 | } |
213 | | |
214 | | void TextUndoConnectParas::Redo() |
215 | 0 | { |
216 | 0 | TextPaM aPaM = GetTextEngine()->ConnectContents( mnPara ); |
217 | 0 | SetSelection( aPaM ); |
218 | 0 | } |
219 | | |
220 | | OUString TextUndoConnectParas::GetComment () const |
221 | 0 | { |
222 | 0 | return VclResId(STR_TEXTUNDO_CONNECTPARAS); |
223 | 0 | } |
224 | | |
225 | | TextUndoSplitPara::TextUndoSplitPara( TextEngine* pTextEngine, sal_uInt32 nPara, sal_Int32 nPos ) |
226 | 0 | : TextUndo( pTextEngine ) |
227 | 0 | , mnPara( nPara ) |
228 | 0 | , mnSepPos ( nPos ) |
229 | 0 | { |
230 | 0 | } |
231 | | |
232 | | TextUndoSplitPara::~TextUndoSplitPara() |
233 | | { |
234 | | } |
235 | | |
236 | | void TextUndoSplitPara::Undo() |
237 | 0 | { |
238 | 0 | TextPaM aPaM = GetTextEngine()->ConnectContents( mnPara ); |
239 | 0 | SetSelection( aPaM ); |
240 | 0 | } |
241 | | |
242 | | void TextUndoSplitPara::Redo() |
243 | 0 | { |
244 | 0 | TextPaM aPaM = GetTextEngine()->SplitContent( mnPara, mnSepPos ); |
245 | 0 | SetSelection( aPaM ); |
246 | 0 | } |
247 | | |
248 | | OUString TextUndoSplitPara::GetComment () const |
249 | 0 | { |
250 | 0 | return VclResId(STR_TEXTUNDO_SPLITPARA); |
251 | 0 | } |
252 | | |
253 | | TextUndoInsertChars::TextUndoInsertChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, OUString aStr ) |
254 | 0 | : TextUndo( pTextEngine ), |
255 | 0 | maTextPaM( rTextPaM ), maText(std::move( aStr )) |
256 | 0 | { |
257 | 0 | } |
258 | | |
259 | | void TextUndoInsertChars::Undo() |
260 | 0 | { |
261 | 0 | TextSelection aSel( maTextPaM, maTextPaM ); |
262 | 0 | aSel.GetEnd().GetIndex() += maText.getLength(); |
263 | 0 | TextPaM aPaM = GetTextEngine()->ImpDeleteText( aSel ); |
264 | 0 | SetSelection( aPaM ); |
265 | 0 | } |
266 | | |
267 | | void TextUndoInsertChars::Redo() |
268 | 0 | { |
269 | 0 | TextSelection aSel( maTextPaM, maTextPaM ); |
270 | 0 | GetTextEngine()->ImpInsertText( aSel, maText ); |
271 | 0 | TextPaM aNewPaM( maTextPaM ); |
272 | 0 | aNewPaM.GetIndex() += maText.getLength(); |
273 | 0 | SetSelection( TextSelection( aSel.GetStart(), aNewPaM ) ); |
274 | 0 | } |
275 | | |
276 | | bool TextUndoInsertChars::Merge( SfxUndoAction* pNextAction ) |
277 | 0 | { |
278 | 0 | TextUndoInsertChars* pNext = dynamic_cast<TextUndoInsertChars*>(pNextAction); |
279 | 0 | if ( !pNext ) |
280 | 0 | return false; |
281 | | |
282 | 0 | if ( maTextPaM.GetPara() != pNext->maTextPaM.GetPara() ) |
283 | 0 | return false; |
284 | | |
285 | 0 | if ( ( maTextPaM.GetIndex() + maText.getLength() ) == pNext->maTextPaM.GetIndex() ) |
286 | 0 | { |
287 | 0 | maText += pNext->maText; |
288 | 0 | return true; |
289 | 0 | } |
290 | 0 | return false; |
291 | 0 | } |
292 | | |
293 | | OUString TextUndoInsertChars::GetComment () const |
294 | 0 | { |
295 | | // multiple lines? |
296 | 0 | OUString sText(maText); |
297 | 0 | Shorten(sText); |
298 | 0 | return VclResId(STR_TEXTUNDO_INSERTCHARS).replaceAll("$1", sText); |
299 | 0 | } |
300 | | |
301 | | TextUndoRemoveChars::TextUndoRemoveChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, OUString aStr ) |
302 | 0 | : TextUndo( pTextEngine ), |
303 | 0 | maTextPaM( rTextPaM ), maText(std::move( aStr )) |
304 | 0 | { |
305 | 0 | } |
306 | | |
307 | | void TextUndoRemoveChars::Undo() |
308 | 0 | { |
309 | 0 | TextSelection aSel( maTextPaM, maTextPaM ); |
310 | 0 | GetTextEngine()->ImpInsertText( aSel, maText ); |
311 | 0 | aSel.GetEnd().GetIndex() += maText.getLength(); |
312 | 0 | SetSelection( aSel ); |
313 | 0 | } |
314 | | |
315 | | void TextUndoRemoveChars::Redo() |
316 | 0 | { |
317 | 0 | TextSelection aSel( maTextPaM, maTextPaM ); |
318 | 0 | aSel.GetEnd().GetIndex() += maText.getLength(); |
319 | 0 | TextPaM aPaM = GetTextEngine()->ImpDeleteText( aSel ); |
320 | 0 | SetSelection( aPaM ); |
321 | 0 | } |
322 | | |
323 | | OUString TextUndoRemoveChars::GetComment () const |
324 | 0 | { |
325 | | // multiple lines? |
326 | 0 | OUString sText(maText); |
327 | 0 | Shorten(sText); |
328 | 0 | return VclResId(STR_TEXTUNDO_REMOVECHARS).replaceAll("$1", sText); |
329 | 0 | } |
330 | | |
331 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |