summaryrefslogtreecommitdiff
path: root/sw/source/core/txtnode/atrflyin.cxx
blob: cec358730c63d80e60eb2d0ce8a9cecc2b900ff9 (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
/* -*- 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 "cntfrm.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 "swfont.hxx"
#include "txtfrm.hxx"
#include "flyfrms.hxx"
#include <objectformatter.hxx>
#include <calbck.hxx>

SwFormatFlyCnt::SwFormatFlyCnt( SwFrameFormat *pFrameFormat )
    : SfxPoolItem( RES_TXTATR_FLYCNT ),
    pTextAttr( nullptr ),
    pFormat( pFrameFormat )
{
}

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

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

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

/*
 * An dieser Stelle soll einmal der Gesamtzusammenhang bei der Erzeugung
 * eines neuen SwTextFlyCnt erlaeutert werden.
 * Das MakeTextHint() wird z.B. im SwTextNode::Copy() gerufen.
 * Fuer die komplette Verdopplung sind folgende Schritte notwendig:
 * 1) Duplizieren des pFormat incl. Inhalt, Attributen etc.
 * 2) Setzen des Ankers
 * 3) Benachrichtigung
 * Da fuer die Bewaeltigung der Aufgaben nicht immer alle Informationen
 * bereitstehen und darueber hinaus bestimmte Methoden erst zu einem
 * spaeteren Zeitpunkt gerufen werden duerfen (weil nocht nicht alle
 * Nodeinformationen vorliegen), verteilt sich der Ablauf.
 * ad 1) MakeTextHint() wird durch den Aufruf von SwDoc::CopyLayout()
 * der das neue FlyFrameFormat erzeugt und mit dem duplizierten Inhalt des
 * FlyFrame verbunden.
 * ad 2) SetAnchor() wird von SwTextNode::Insert() gerufen und sorgt fuer das
 * setzen des Ankers (die SwPosition des Dummy-Zeichens wird dem FlyFrameFormat
 * per SetAttr bekannt gegeben). Dies kann nicht im MakeTextHint erledigt
 * werden, da der Zielnode unbestimmt ist.
 * ad 3) GetFlyFrame_() wird im Formatierungsprozess vom LineIter gerufen
 * und sucht den FlyFrame zum Dummyzeichen des aktuellen ContentFrame. Wird keiner
 * gefunden, so wird ein neuer FlyFrame angelegt.
 * Kritisch an diesem Vorgehen ist, dass das pContent->AppendFly() eine
 * sofortige Neuformatierung von pContent anstoesst. Die Rekursion kommt
 * allerdings durch den Lockmechanismus in SwTextFrame::Format() nicht
 * zu stande.
 * Attraktiv ist der Umstand, dass niemand ueber die vom Node abhaengigen
 * ContentFrames iterieren braucht, um die FlyInContentFrame anzulegen. Dies geschieht
 * bei der Arbeit.
 */

void SwTextFlyCnt::CopyFlyFormat( SwDoc* pDoc )
{
    SwFrameFormat* pFormat = GetFlyCnt().GetFrameFormat();
    OSL_ENSURE( pFormat, "von welchem Format soll ich eine Kopie erzeugen?" );
    // Das FlyFrameFormat muss dupliziert werden.
    // In CopyLayoutFormat (siehe doclay.cxx) wird das FlyFrameFormat erzeugt
    // und der Inhalt dupliziert.

    // disable undo while copying attribute
    ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
    SwFormatAnchor aAnchor( pFormat->GetAnchor() );
    if ((FLY_AT_PAGE != aAnchor.GetAnchorId()) &&
        (pDoc != pFormat->GetDoc()))   // different documents?
    {
        // JP 03.06.96: dann sorge dafuer, das der koperierte Anker auf
        //              gueltigen Content zeigt! Die Umsetzung auf die
        //              richtige Position erfolgt spaeter.
        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 (FLY_AS_CHAR == aAnchor.GetAnchorId())
        {
            pos.nContent.Assign( pCNd, 0 );
        }
        else
        {
            pos.nContent.Assign( nullptr, 0 );
            OSL_ENSURE( false, "CopyFlyFormat: Was fuer ein Anker?" );
        }
        aAnchor.SetAnchor( &pos );
    }

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

// SetAnchor() wird von SwTextNode::Insert() gerufen und sorgt fuer das
// setzen des Ankers (die SwPosition des Dummy-Zeichens wird dem FlyFrameFormat
// per SetAttr bekannt gegeben). Dies kann nicht im MakeTextHint erledigt
// werden, da der Zielnode unbestimmt ist.
// (siehe Kommentar in SwTextFlyCnt::MakeTextHint)
void SwTextFlyCnt::SetAnchor( const SwTextNode *pNode )
{
    // fuers Undo muss der neue Anker schon bekannt sein !

    // Wir ermitteln den Index im Nodesarray zum Node

    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( FLY_AS_CHAR );        // default!
    aAnchor.SetAnchor( &aPos );

    // beim Ankerwechsel werden immer alle FlyFrames vom Attribut geloescht
    // JP 25.04.95: wird innerhalb des SplitNodes die Frames verschoben
    //              koennen die Frames erhalten bleiben.
    if( ( !pNode->GetpSwpHints() || !pNode->GetpSwpHints()->IsInSplitNode() )
        && RES_DRAWFRMFMT != pFormat->Which() )
        pFormat->DelFrames();

    // stehen wir noch im falschen Dokument ?
    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 );
        ((SwFormatFlyCnt&)GetFlyCnt()).SetFlyFormat( pNew );
    }
    else if( pNode->GetpSwpHints() &&
            pNode->GetpSwpHints()->IsInSplitNode() &&
            RES_DRAWFRMFMT != pFormat->Which() )
    {
        pFormat->LockModify();
        pFormat->SetFormatAttr( aAnchor );        // nur den Anker neu setzen
        // 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
        pFormat->SetFormatAttr( aAnchor );        // nur den Anker neu setzen
    }

    // Am Node haengen u.a. abhaengige ContentFrames.
    // Fuer jeden ContentFrame wird ein SwFlyInContentFrame angelegt.
}

// GetFlyFrame_() wird im Formatierungsprozess vom LineIter gerufen
// und sucht den FlyFrame zum Dummyzeichen des aktuellen ContentFrame. Wird keiner
// gefunden, so wird ein neuer FlyFrame angelegt.
// (siehe Kommentar ind SwTextFlyCnt::MakeTextHint)
SwFlyInContentFrame *SwTextFlyCnt::GetFlyFrame_( const SwFrame *pCurrFrame )
{
    SwFrameFormat* pFrameFormat = GetFlyCnt().GetFrameFormat();
    if( RES_DRAWFRMFMT == pFrameFormat->Which() )
    {
        OSL_ENSURE(  false, "SwTextFlyCnt::GetFlyFrame_: DrawInCnt-Baustelle!" );
        return nullptr;
    }

    SwIterator<SwFlyFrame,SwFormat> aIter( *GetFlyCnt().pFormat );
    OSL_ENSURE( pCurrFrame->IsTextFrame(), "SwTextFlyCnt::GetFlyFrame_ for TextFrames only." );
    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 );
    }

    // Wir haben keinen passenden FlyFrame gefunden, deswegen wird ein
    // neuer angelegt.
    // Dabei wird eine sofortige Neuformatierung von pCurrFrame angestossen.
    // Die Rekursion wird durch den Lockmechanismus in SwTextFrame::Format()
    // abgewuergt.
    SwFrame* pCurrentFrame = const_cast<SwFrame*>(pCurrFrame);
    SwFlyInContentFrame *pFly = new SwFlyInContentFrame(static_cast<SwFlyFrameFormat*>(pFrameFormat), pCurrentFrame, pCurrentFrame);
    pCurrentFrame->AppendFly(pFly);
    pFly->RegistFlys();

    // 7922: Wir muessen dafuer sorgen, dass der Inhalt des FlyInCnt
    // nach seiner Konstruktion stramm durchformatiert wird.
    // #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: */