summaryrefslogtreecommitdiffstats
path: root/sw/source/core/txtnode/atrflyin.cxx
blob: 4e61e568e702eb969d9c007ae8ed20cf34310082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <hintids.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pam.hxx>
#include <flyfrm.hxx>
#include <ndtxt.hxx>
#include <frmfmt.hxx>
#include <fmtflcnt.hxx>
#include <txtflcnt.hxx>
#include <fmtanchr.hxx>
#include <txtfrm.hxx>
#include <flyfrms.hxx>
#include <objectformatter.hxx>
#include <calbck.hxx>
#include <dcontact.hxx>
#include <textboxhelper.hxx>

SwFormatFlyCnt::SwFormatFlyCnt( SwFrameFormat *pFrameFormat )
    : SfxPoolItem( RES_TXTATR_FLYCNT ),
    m_pTextAttr( nullptr ),
    m_pFormat( pFrameFormat )
{
}

bool SwFormatFlyCnt::operator==( const SfxPoolItem& rAttr ) const
{
    assert(SfxPoolItem::operator==(rAttr));
    return( m_pTextAttr && static_cast<const SwFormatFlyCnt&>(rAttr).m_pTextAttr &&
            m_pTextAttr->GetStart() == static_cast<const SwFormatFlyCnt&>(rAttr).m_pTextAttr->GetStart() &&
            m_pFormat == static_cast<const SwFormatFlyCnt&>(rAttr).GetFrameFormat() );
}

SwFormatFlyCnt* SwFormatFlyCnt::Clone( SfxItemPool* ) const
{
    return new SwFormatFlyCnt( m_pFormat );
}

SwTextFlyCnt::SwTextFlyCnt( SwFormatFlyCnt& rAttr, sal_Int32 nStartPos )
    : SwTextAttr( rAttr, nStartPos )
{
    rAttr.m_pTextAttr = this;
    SetHasDummyChar(true);
}

/** An overview of how a new SwTextFlyCnt is created:
 * MakeTextAttr() is called e.g. by SwTextNode::CopyText().
 * The following steps are required to clone:
 * 1) copying the pFormat with content, attributes etc.
 * 2) setting the anchor
 * 3) notification
 * Because not all required information is available at all times,
 * the steps are distributed variously:
 * ad 1) MakeTextAttr() calls DocumentLayoutManager::CopyLayoutFormat()
 *  which creates the new SwFlyFrameFormat and copies the content of the
 *  fly frame.
 * ad 2) SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
 *  position in the SwFlyFrameFormat to the SwPosition of the dummy
 *  CH_TXTATR_BREAKWORD.  This cannot be done in MakeTextAttr() because it
 *  doesn't know the target text node.
 * ad 3) GetFlyFrame_() is called during text formatting by SwTextFormatter
 *  and searches for the SwFlyFrame for the dummy char of the current
 *  SwTextFrame.  If none is found, a new SwFlyInContentFrame is created.
 *  Important: pTextFrame->AppendFly() immediately triggers a reformat
 *  of pTextFrame.  However, the recursion is blocked by the lock mechanism
 *  in SwTextFrame::Format().
 * The advantage of all this is that it's not necessary to explicitly iterate
 * over all SwTextFrames that depend on the SwTextNode to create the
 * SwFlyInContentFrame - this is done automatically already.
 */

void SwTextFlyCnt::CopyFlyFormat( SwDoc* pDoc )
{
    SwFrameFormat* pFormat = GetFlyCnt().GetFrameFormat();
    assert(pFormat);
    // The FlyFrameFormat must be copied - CopyLayoutFormat
    // (DocumentLayoutManager.cxx) creates the FlyFrameFormat and copies the
    // content.

    // disable undo while copying attribute
    ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
    SwFormatAnchor aAnchor( pFormat->GetAnchor() );
    if ((RndStdIds::FLY_AT_PAGE != aAnchor.GetAnchorId()) &&
        (pDoc != pFormat->GetDoc()))   // different documents?
    {
        // JP 03.06.96: ensure that the copied anchor points to valid content!
        //              setting it to the correct position is done later.
        SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), +2 );
        SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
        if( !pCNd )
            pCNd = pDoc->GetNodes().GoNext( &aIdx );

        SwPosition pos = *aAnchor.GetContentAnchor();
        pos.nNode = aIdx;
        if (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())
        {
            pos.nContent.Assign( pCNd, 0 );
        }
        else
        {
            pos.nContent.Assign( nullptr, 0 );
            assert(false);
        }
        aAnchor.SetAnchor( &pos );
    }

    SwFrameFormat* pNew = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, false, false );
    const_cast<SwFormatFlyCnt&>(GetFlyCnt()).SetFlyFormat( pNew );
}

/** SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
 *  position in the SwFlyFrameFormat to the SwPosition of the dummy
 *  CH_TXTATR_BREAKWORD.  This cannot be done in MakeTextAttr() because it
 *  doesn't know the target text node.
 */
void SwTextFlyCnt::SetAnchor( const SwTextNode *pNode )
{
    // for Undo, the new anchor must be known already!

    SwDoc* pDoc = const_cast<SwDoc*>(pNode->GetDoc());

    SwIndex aIdx( const_cast<SwTextNode*>(pNode), GetStart() );
    SwPosition aPos( *pNode->StartOfSectionNode(), aIdx );
    SwFrameFormat* pFormat = GetFlyCnt().GetFrameFormat();
    SwFormatAnchor aAnchor( pFormat->GetAnchor() );
    SwNode *const pOldNode(aAnchor.GetContentAnchor()
            ? &aAnchor.GetContentAnchor()->nNode.GetNode()
            : nullptr);

    if (!pOldNode || !pOldNode->GetNodes().IsDocNodes() ||
        pOldNode != static_cast<SwNode const *>(pNode))
    {
        aPos.nNode = *pNode;
    }
    else
    {
        aPos.nNode = *pOldNode;
    }

    aAnchor.SetType( RndStdIds::FLY_AS_CHAR );        // default!
    aAnchor.SetAnchor( &aPos );

    // in case of anchor change, delete all FlyFrames
    // JP 25.04.95: if the Frames can be moved within SplitNode, they don't
    //              need to be deleted
    if( ( !pNode->GetpSwpHints() || !pNode->GetpSwpHints()->IsInSplitNode() )
        && RES_DRAWFRMFMT != pFormat->Which() )
        pFormat->DelFrames();

    // copy into a different document?
    if( pDoc != pFormat->GetDoc() )
    {
        // disable undo while copying attribute
        ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
        SwFrameFormat* pNew = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, false, false );

        ::sw::UndoGuard const undoGuardFormat(
            pFormat->GetDoc()->GetIDocumentUndoRedo());
        pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
        const_cast<SwFormatFlyCnt&>(GetFlyCnt()).SetFlyFormat( pNew );
    }
    else if( pNode->GetpSwpHints() &&
            pNode->GetpSwpHints()->IsInSplitNode() &&
            RES_DRAWFRMFMT != pFormat->Which() )
    {
        pFormat->LockModify();
        pFormat->SetFormatAttr( aAnchor );  // only set the anchor
        // tdf#91228 must notify the anchor nodes despite LockModify
        assert(pOldNode);
        pOldNode->RemoveAnchoredFly(pFormat);
        aPos.nNode.GetNode().AddAnchoredFly(pFormat);
        pFormat->UnlockModify();
    }
    else
    {
        assert(!pFormat->IsModifyLocked()); // need to notify anchor node
        if (RES_DRAWFRMFMT == pFormat->Which())
        {
            if (SdrObject const*const pObj = pFormat->FindSdrObject())
            {   // tdf#123259 disconnect with *old* anchor position
                static_cast<SwDrawContact*>(::GetUserCall(pObj))->DisconnectFromLayout(false);
            }
        }
        pFormat->SetFormatAttr( aAnchor );  // only set the anchor

        // If the draw format has a TextBox, then set its anchor as well.
        if (SwFrameFormat* pTextBox
            = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
        {
            SwFormatAnchor aTextBoxAnchor(pTextBox->GetAnchor());
            aTextBoxAnchor.SetAnchor(aAnchor.GetContentAnchor());

            // SwFlyAtContentFrame::Modify() assumes the anchor has a matching layout frame, which
            // may not be the case when we're in the process of a node split, so block
            // notifications.
            bool bIsInSplitNode = pNode->GetpSwpHints() && pNode->GetpSwpHints()->IsInSplitNode();
            if (bIsInSplitNode)
            {
                pTextBox->LockModify();
            }

            pTextBox->SetFormatAttr(aTextBoxAnchor);

            if (bIsInSplitNode)
            {
                pOldNode->RemoveAnchoredFly(pTextBox);
                aPos.nNode.GetNode().AddAnchoredFly(pTextBox);
                pTextBox->UnlockModify();
            }
        }
    }

    // The node may have several SwTextFrames - for every SwTextFrame a
    // SwFlyInContentFrame is created.
}


/** GetFlyFrame_() is called during text formatting by SwTextFormatter
 *  and searches for the SwFlyFrame for the dummy char of the current
 *  SwTextFrame.  If none is found, a new SwFlyInContentFrame is created.
 */
SwFlyInContentFrame *SwTextFlyCnt::GetFlyFrame_( const SwFrame *pCurrFrame )
{
    SwFrameFormat* pFrameFormat = GetFlyCnt().GetFrameFormat();
    if( RES_DRAWFRMFMT == pFrameFormat->Which() )
    {
        OSL_ENSURE(  false, "SwTextFlyCnt::GetFlyFrame_: DrawInCnt-under construction!" );
        return nullptr;
    }

    SwIterator<SwFlyFrame,SwFormat> aIter( *GetFlyCnt().m_pFormat );
    assert(pCurrFrame->IsTextFrame());
    SwFrame* pFrame = aIter.First();
    if ( pFrame )
    {
        SwTextFrame *pFirst = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCurrFrame));
        while ( pFirst->IsFollow() )
            pFirst = pFirst->FindMaster();
        do
            {
                SwTextFrame *pTmp = pFirst;
                do
                {   if( static_cast<SwFlyFrame*>(pFrame)->GetAnchorFrame() == static_cast<SwFrame*>(pTmp) )
                    {
                        if ( pTmp != pCurrFrame )
                        {
                            pTmp->RemoveFly( static_cast<SwFlyFrame*>(pFrame) );
                            const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCurrFrame))->AppendFly( static_cast<SwFlyFrame*>(pFrame) );
                        }
                        return static_cast<SwFlyInContentFrame*>(pFrame);
                    }
                    pTmp = pTmp->GetFollow();
                } while ( pTmp );

                pFrame = aIter.Next();

        } while( pFrame );
    }

    // We did not find a matching FlyFrame, so create a new one.
    // AppendFly() triggers a reformat of pCurrentFrame.  However, the
    // recursion is blocked by the lock mechanism in SwTextFrame::Format().
    SwFrame* pCurrentFrame = const_cast<SwFrame*>(pCurrFrame);
    SwFlyInContentFrame *pFly = new SwFlyInContentFrame(static_cast<SwFlyFrameFormat*>(pFrameFormat), pCurrentFrame, pCurrentFrame);
    pCurrentFrame->AppendFly(pFly);
    pFly->RegistFlys();

    // We must ensure that the content of the FlyInCnt is fully formatted
    // right after construction.
    // #i26945# - Use new object formatter to format Writer
    // fly frame and its content.
    SwObjectFormatter::FormatObj( *pFly, const_cast<SwFrame*>(pCurrFrame),
                                  pCurrFrame->FindPageFrame() );

    return pFly;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */