/src/libreoffice/svx/source/svdraw/textchainflow.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 | | |
21 | | #include <svx/svdotext.hxx> |
22 | | #include <svx/svdoutl.hxx> |
23 | | #include <editeng/outlobj.hxx> |
24 | | #include <editeng/editobj.hxx> |
25 | | #include <editeng/overflowingtxt.hxx> |
26 | | #include <textchainflow.hxx> |
27 | | #include <sal/log.hxx> |
28 | | |
29 | | TextChainFlow::TextChainFlow(SdrTextObj *pChainTarget) |
30 | 0 | : mpTargetLink(pChainTarget) |
31 | 0 | { |
32 | 0 | SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new TextChainFlow"); |
33 | | |
34 | 0 | mpTextChain = mpTargetLink->GetTextChain(); |
35 | 0 | mpNextLink = mpTargetLink->GetNextLinkInChain(); |
36 | |
|
37 | 0 | bUnderflow = bOverflow = false; |
38 | |
|
39 | 0 | mbOFisUFinduced = false; |
40 | |
|
41 | 0 | mpOverflChText = nullptr; |
42 | 0 | mpUnderflChText = nullptr; |
43 | |
|
44 | 0 | mbPossiblyCursorOut = false; |
45 | 0 | } |
46 | | |
47 | | |
48 | | TextChainFlow::~TextChainFlow() |
49 | 0 | { |
50 | 0 | mpOverflChText.reset(); |
51 | 0 | mpUnderflChText.reset(); |
52 | 0 | } |
53 | | |
54 | | void TextChainFlow::impSetFlowOutlinerParams(SdrOutliner *, SdrOutliner *) |
55 | 0 | { |
56 | | // Nothing to do if not in editing mode |
57 | 0 | } |
58 | | |
59 | | /* |
60 | | * Check for overflow in the state of pFlowOutl. |
61 | | * If pParamOutl is not NULL sets some parameters from there. |
62 | | * This is useful in case the outliner is not set for overflow |
63 | | * (e.g. in editing mode we check for overflow in drawing outl but |
64 | | * parameters come from editing outliner) |
65 | | */ |
66 | | void TextChainFlow::impCheckForFlowEvents(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl) |
67 | 0 | { |
68 | 0 | bool bOldUpdateMode = pFlowOutl->IsUpdateLayout(); |
69 | | |
70 | | // XXX: This could be reorganized moving most of this stuff inside EditingTextChainFlow |
71 | 0 | if (pParamOutl != nullptr) |
72 | 0 | { |
73 | | // We need this since it's required to check overflow |
74 | 0 | pFlowOutl->SetUpdateLayout(true); |
75 | | |
76 | | // XXX: does this work if you do it before setting the text? Seems so. |
77 | 0 | impSetFlowOutlinerParams(pFlowOutl, pParamOutl); |
78 | 0 | } |
79 | |
|
80 | 0 | bool bIsPageOverflow = pFlowOutl->IsPageOverflow(); |
81 | | |
82 | | // NOTE: overflow and underflow cannot be both true |
83 | 0 | bOverflow = bIsPageOverflow && mpNextLink; |
84 | 0 | bUnderflow = !bIsPageOverflow && mpNextLink && mpNextLink->HasText(); |
85 | | |
86 | | // Get old state on whether to merge para-s or not |
87 | | // NOTE: We handle UF/OF using the _old_ state. The new one is simply saved |
88 | 0 | bool bMustMergeParaAmongLinks = GetTextChain()->GetIsPartOfLastParaInNextLink(mpTargetLink); |
89 | | |
90 | | // Set (Non)OverflowingTxt here (if any) |
91 | | |
92 | | // If we had an underflow before we have to deep merge paras anyway |
93 | 0 | bool bMustMergeParaOF = bMustMergeParaAmongLinks || mbOFisUFinduced; |
94 | |
|
95 | 0 | mpOverflChText.reset( bOverflow ? |
96 | 0 | new OFlowChainedText(pFlowOutl, bMustMergeParaOF) : |
97 | 0 | nullptr ); |
98 | | |
99 | | // Set current underflowing text (if any) |
100 | 0 | mpUnderflChText.reset( bUnderflow ? |
101 | 0 | new UFlowChainedText(pFlowOutl, bMustMergeParaAmongLinks) : |
102 | 0 | nullptr ); |
103 | | |
104 | | // Reset update mode // Reset it here because we use WriteRTF (needing updatemode = true) in the two constructors above |
105 | 0 | if (!bOldUpdateMode) // Reset only if the old value was false |
106 | 0 | pFlowOutl->SetUpdateLayout(bOldUpdateMode); |
107 | | |
108 | | // NOTE: Must be called after mp*ChText and b*flow have been set but before mbOFisUFinduced is reset |
109 | 0 | impUpdateCursorInfo(); |
110 | | |
111 | | // To check whether an overflow is underflow induced or not (useful in cursor checking) |
112 | 0 | mbOFisUFinduced = bUnderflow; |
113 | 0 | } |
114 | | |
115 | | void TextChainFlow::impUpdateCursorInfo() |
116 | 0 | { |
117 | | // XXX: Maybe we can get rid of mbOFisUFinduced by not allowing handling of more than one event by the same TextChainFlow |
118 | | // if this is not an OF triggered during an UF |
119 | |
|
120 | 0 | mbPossiblyCursorOut = bOverflow; |
121 | |
|
122 | 0 | if(mbPossiblyCursorOut ) { |
123 | 0 | maOverflowPosSel = mpOverflChText->GetOverflowPointSel(); |
124 | 0 | ESelection aSelAtUFTime = GetTextChain()->GetPreChainingSel(GetLinkTarget()); |
125 | | // Might be an invalid selection if the cursor at UF time was before |
126 | | // the (possibly UF-induced) Overflowing point but we don't use it in that case |
127 | 0 | maPostChainingSel = ESelection(aSelAtUFTime.start.nPara-maOverflowPosSel.start.nPara, |
128 | 0 | aSelAtUFTime.start.nIndex-maOverflowPosSel.start.nIndex ); |
129 | 0 | } |
130 | | |
131 | | // XXX: It may not be necessary anymore to keep this method separated from EditingTextChainFlow::impBroadcastCursorInfo |
132 | 0 | } |
133 | | |
134 | | void TextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl) |
135 | 0 | { |
136 | 0 | impCheckForFlowEvents(pFlowOutl, nullptr); |
137 | 0 | } |
138 | | |
139 | | |
140 | | bool TextChainFlow::IsOverflow() const |
141 | 0 | { |
142 | 0 | return bOverflow; |
143 | 0 | } |
144 | | |
145 | | bool TextChainFlow::IsUnderflow() const |
146 | 0 | { |
147 | 0 | return bUnderflow; |
148 | 0 | } |
149 | | |
150 | | |
151 | | // XXX: In editing mode you need to get "underflowing" text from editing outliner, so it's kinda separated from the drawing one! |
152 | | |
153 | | // XXX:Would it be possible to unify underflow and its possibly following overflow? |
154 | | void TextChainFlow::ExecuteUnderflow(SdrOutliner *pOutl) |
155 | 0 | { |
156 | | //GetTextChain()->SetNilChainingEvent(mpTargetLink, true); |
157 | | // making whole text |
158 | | // merges underflowing text with the one in the next box |
159 | 0 | std::optional<OutlinerParaObject> pNewText = mpUnderflChText->CreateMergedUnderflowParaObject(pOutl, mpNextLink->GetOutlinerParaObject()); |
160 | | |
161 | | // Set the other box empty; it will be replaced by the rest of the text if overflow occurs |
162 | 0 | if (!mpTargetLink->GetPreventChainable()) |
163 | 0 | mpNextLink->NbcSetOutlinerParaObject(pOutl->GetEmptyParaObject()); |
164 | | |
165 | | // We store the size since NbcSetOutlinerParaObject can change it |
166 | | //Size aOldSize = pOutl->GetMaxAutoPaperSize(); |
167 | | |
168 | | // This should not be done in editing mode!! //XXX |
169 | 0 | if (!mpTargetLink->IsInEditMode()) |
170 | 0 | { |
171 | 0 | mpTargetLink->NbcSetOutlinerParaObject(pNewText); |
172 | 0 | } |
173 | | |
174 | | // Restore size and set new text |
175 | | //pOutl->SetMaxAutoPaperSize(aOldSize); // XXX (it seems to be working anyway without this) |
176 | 0 | pOutl->SetText(*pNewText); |
177 | | |
178 | | //GetTextChain()->SetNilChainingEvent(mpTargetLink, false); |
179 | | |
180 | | // Check for new overflow |
181 | 0 | CheckForFlowEvents(pOutl); |
182 | 0 | } |
183 | | |
184 | | void TextChainFlow::ExecuteOverflow(SdrOutliner *pNonOverflOutl, SdrOutliner *pOverflOutl) |
185 | 0 | { |
186 | | //GetTextChain()->SetNilChainingEvent(mpTargetLink, true); |
187 | | // Leave only non overflowing text |
188 | 0 | impLeaveOnlyNonOverflowingText(pNonOverflOutl); |
189 | | |
190 | | // Transfer of text to next link |
191 | 0 | if (!mpTargetLink->GetPreventChainable() ) // we don't transfer text while dragging because of resizing |
192 | 0 | { |
193 | 0 | impMoveChainedTextToNextLink(pOverflOutl); |
194 | 0 | } |
195 | | |
196 | | //GetTextChain()->SetNilChainingEvent(mpTargetLink, false); |
197 | 0 | } |
198 | | |
199 | | void TextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl) |
200 | 0 | { |
201 | 0 | std::optional<OutlinerParaObject> pNewText = mpOverflChText->RemoveOverflowingText(pNonOverflOutl); |
202 | |
|
203 | 0 | SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] SOURCE box set to " |
204 | 0 | << pNewText->GetTextObject().GetParagraphCount() << " paras"); |
205 | | |
206 | | // adds it to current outliner anyway (useful in static decomposition) |
207 | 0 | pNonOverflOutl->SetText(*pNewText); |
208 | |
|
209 | 0 | mpTargetLink->NbcSetOutlinerParaObject(std::move(pNewText)); |
210 | | // For some reason the paper size is lost after last instruction, so we set it. |
211 | 0 | pNonOverflOutl->SetPaperSize(Size(pNonOverflOutl->GetPaperSize().Width(), |
212 | 0 | pNonOverflOutl->GetTextHeight())); |
213 | |
|
214 | 0 | } |
215 | | |
216 | | void TextChainFlow::impMoveChainedTextToNextLink(SdrOutliner *pOverflOutl) |
217 | 0 | { |
218 | | // prevent copying text in same box |
219 | 0 | if ( mpNextLink == mpTargetLink ) { |
220 | 0 | SAL_INFO("svx.chaining", "[CHAINING] Trying to copy text for next link in same object"); |
221 | 0 | return; |
222 | 0 | } |
223 | | |
224 | 0 | std::optional<OutlinerParaObject> pNewText = |
225 | 0 | mpOverflChText->InsertOverflowingText(pOverflOutl, |
226 | 0 | mpNextLink->GetOutlinerParaObject()); |
227 | 0 | SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] DEST box set to " |
228 | 0 | << pNewText->GetTextObject().GetParagraphCount() << " paras"); |
229 | | |
230 | 0 | if (pNewText) |
231 | 0 | mpNextLink->NbcSetOutlinerParaObject(std::move(pNewText)); |
232 | | |
233 | | // Set Deep Merge status |
234 | 0 | SAL_INFO("svx.chaining", "[DEEPMERGE] Setting deepMerge to " |
235 | 0 | << mpOverflChText->IsLastParaInterrupted()); |
236 | 0 | GetTextChain()->SetIsPartOfLastParaInNextLink( |
237 | 0 | mpTargetLink, |
238 | 0 | mpOverflChText->IsLastParaInterrupted()); |
239 | 0 | } |
240 | | |
241 | | SdrTextObj *TextChainFlow::GetLinkTarget() const |
242 | 0 | { |
243 | 0 | return mpTargetLink; |
244 | 0 | } |
245 | | |
246 | | TextChain *TextChainFlow::GetTextChain() const |
247 | 0 | { |
248 | 0 | return mpTextChain; |
249 | 0 | } |
250 | | |
251 | | // EditingTextChainFlow |
252 | | |
253 | | EditingTextChainFlow::EditingTextChainFlow(SdrTextObj *pLinkTarget) : |
254 | 0 | TextChainFlow(pLinkTarget) |
255 | 0 | { |
256 | 0 | SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new EditingTextChainFlow"); |
257 | 0 | } |
258 | | |
259 | | void EditingTextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl) |
260 | 0 | { |
261 | | // if this is editing outliner no need to set parameters |
262 | 0 | if (pFlowOutl == GetLinkTarget()->mpEditingOutliner) |
263 | 0 | impCheckForFlowEvents(pFlowOutl, nullptr); |
264 | 0 | else |
265 | 0 | impCheckForFlowEvents(pFlowOutl, GetLinkTarget()->mpEditingOutliner); |
266 | | |
267 | | // Broadcast events for cursor handling |
268 | 0 | impBroadcastCursorInfo(); |
269 | 0 | } |
270 | | |
271 | | void EditingTextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl) |
272 | 0 | { |
273 | 0 | mpOverflChText->RemoveOverflowingText(pNonOverflOutl); |
274 | | //impSetTextForEditingOutliner(pNewText); //XXX: Don't call it since we do everything with NonOverflowingText::ToParaObject // XXX: You may need this for Underflow |
275 | | |
276 | | // XXX: I'm not sure whether we need this (after all operations such as Paste don't change this - as far as I understand) |
277 | | //GetLinkTarget()->NbcSetOutlinerParaObject(pNewText); |
278 | 0 | } |
279 | | |
280 | | void EditingTextChainFlow::impSetFlowOutlinerParams(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl) |
281 | 0 | { |
282 | | // Set right size for overflow |
283 | 0 | pFlowOutl->SetMaxAutoPaperSize(pParamOutl->GetMaxAutoPaperSize()); |
284 | 0 | pFlowOutl->SetMinAutoPaperSize(pParamOutl->GetMinAutoPaperSize()); |
285 | 0 | pFlowOutl->SetPaperSize(pParamOutl->GetPaperSize()); |
286 | 0 | } |
287 | | |
288 | | void EditingTextChainFlow::impBroadcastCursorInfo() const |
289 | 0 | { |
290 | 0 | ESelection aPreChainingSel = GetTextChain()->GetPreChainingSel(GetLinkTarget()) ; |
291 | | |
292 | | // Test whether the cursor is out of the box. |
293 | 0 | bool bCursorOut = mbPossiblyCursorOut && maOverflowPosSel < aPreChainingSel; |
294 | | |
295 | | // NOTE: I handled already the stuff for the comments below. They will be kept temporarily till stuff settles down. |
296 | | // Possibility: 1) why don't we stop passing the actual event to the TextChain and instead we pass |
297 | | // the overflow pos and mbPossiblyCursorOut |
298 | | // 2) We pass the current selection before anything happens and we make impBroadcastCursorInfo compute it. |
299 | | |
300 | |
|
301 | 0 | if (bCursorOut) { |
302 | | //maCursorEvent = CursorChainingEvent::TO_NEXT_LINK; |
303 | 0 | GetTextChain()->SetPostChainingSel(GetLinkTarget(), maPostChainingSel); |
304 | 0 | GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::TO_NEXT_LINK); |
305 | 0 | } else { |
306 | | //maCursorEvent = CursorChainingEvent::UNCHANGED; |
307 | 0 | GetTextChain()->SetPostChainingSel(GetLinkTarget(), aPreChainingSel); |
308 | 0 | GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::UNCHANGED); |
309 | 0 | } |
310 | | |
311 | |
|
312 | 0 | } |
313 | | |
314 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |