Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */