Line data Source code
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 <tools/gen.hxx>
21 : #include <hintids.hxx>
22 : #include <editeng/protitem.hxx>
23 : #include <cntfrm.hxx>
24 : #include <pagefrm.hxx>
25 : #include <doc.hxx>
26 : #include <IDocumentLayoutAccess.hxx>
27 : #include <docary.hxx>
28 : #include <pam.hxx>
29 : #include <pamtyp.hxx>
30 : #include <txtfrm.hxx>
31 : #include <fmtcntnt.hxx>
32 : #include <frmatr.hxx>
33 : #include <swtable.hxx>
34 : #include <crsskip.hxx>
35 : #include <flyfrm.hxx>
36 : #include <fmteiro.hxx>
37 : #include <section.hxx>
38 : #include <sectfrm.hxx>
39 : #include <ndtxt.hxx>
40 :
41 : #include <IMark.hxx>
42 : #include <DocumentSettingManager.hxx>
43 : #include <hints.hxx>
44 : #include <xmloff/odffields.hxx>
45 :
46 : // for the dump "MSC-" compiler
47 332753 : inline sal_Int32 GetSttOrEnd( bool bCondition, const SwCntntNode& rNd )
48 : {
49 332753 : return bCondition ? 0 : rNd.Len();
50 : }
51 :
52 57622 : SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwIndex & rCntnt )
53 57622 : : nNode( rNodeIndex ), nContent( rCntnt )
54 : {
55 57622 : }
56 :
57 1673679 : SwPosition::SwPosition( const SwNodeIndex & rNodeIndex )
58 1673679 : : nNode( rNodeIndex ), nContent( nNode.GetNode().GetCntntNode() )
59 : {
60 1673679 : }
61 :
62 1074960 : SwPosition::SwPosition( const SwNode& rNode )
63 1074960 : : nNode( rNode ), nContent( nNode.GetNode().GetCntntNode() )
64 : {
65 1074960 : }
66 :
67 69620 : SwPosition::SwPosition( SwCntntNode & rNode, const sal_Int32 nOffset )
68 69620 : : nNode( rNode ), nContent( &rNode, nOffset )
69 : {
70 69620 : }
71 :
72 3595275 : SwPosition::SwPosition( const SwPosition & rPos )
73 3595275 : : nNode( rPos.nNode ), nContent( rPos.nContent )
74 : {
75 3595275 : }
76 :
77 1037580 : SwPosition &SwPosition::operator=(const SwPosition &rPos)
78 : {
79 1037580 : nNode = rPos.nNode;
80 1037580 : nContent = rPos.nContent;
81 1037580 : return *this;
82 : }
83 :
84 10519198 : bool SwPosition::operator<(const SwPosition &rPos) const
85 : {
86 10519198 : if( nNode < rPos.nNode )
87 900440 : return true;
88 9618758 : if( nNode == rPos.nNode )
89 : {
90 : // note that positions with text node but no SwIndex registered are
91 : // created for text frames anchored at para (see SwXFrame::getAnchor())
92 9276025 : SwIndexReg const*const pThisReg(nContent.GetIdxReg());
93 9276025 : SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
94 9276025 : if (pThisReg && pOtherReg)
95 : {
96 9270931 : return (nContent < rPos.nContent);
97 : }
98 : else // by convention position with no index is smaller
99 : {
100 5094 : return (pOtherReg) ? true : false;
101 : }
102 : }
103 342733 : return false;
104 : }
105 :
106 1697101 : bool SwPosition::operator>(const SwPosition &rPos) const
107 : {
108 1697101 : if(nNode > rPos.nNode )
109 329554 : return true;
110 1367547 : if( nNode == rPos.nNode )
111 : {
112 : // note that positions with text node but no SwIndex registered are
113 : // created for text frames anchored at para (see SwXFrame::getAnchor())
114 1180443 : SwIndexReg const*const pThisReg(nContent.GetIdxReg());
115 1180443 : SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
116 1180443 : if (pThisReg && pOtherReg)
117 : {
118 1176414 : return (nContent > rPos.nContent);
119 : }
120 : else // by convention position with no index is smaller
121 : {
122 4029 : return (pThisReg) ? true : false;
123 : }
124 : }
125 187104 : return false;
126 : }
127 :
128 9255977 : bool SwPosition::operator<=(const SwPosition &rPos) const
129 : {
130 9255977 : if(nNode < rPos.nNode )
131 1584429 : return true;
132 7671548 : if( nNode == rPos.nNode )
133 : {
134 : // note that positions with text node but no SwIndex registered are
135 : // created for text frames anchored at para (see SwXFrame::getAnchor())
136 2746287 : SwIndexReg const*const pThisReg(nContent.GetIdxReg());
137 2746287 : SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
138 2746287 : if (pThisReg && pOtherReg)
139 : {
140 2742542 : return (nContent <= rPos.nContent);
141 : }
142 : else // by convention position with no index is smaller
143 : {
144 3745 : return (pThisReg) ? false : true;
145 : }
146 : }
147 4925261 : return false;
148 : }
149 :
150 8702862 : bool SwPosition::operator>=(const SwPosition &rPos) const
151 : {
152 8702862 : if(nNode > rPos.nNode )
153 23480 : return true;
154 8679382 : if( nNode == rPos.nNode )
155 : {
156 : // note that positions with text node but no SwIndex registered are
157 : // created for text frames anchored at para (see SwXFrame::getAnchor())
158 8492276 : SwIndexReg const*const pThisReg(nContent.GetIdxReg());
159 8492276 : SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
160 8492276 : if (pThisReg && pOtherReg)
161 : {
162 8490165 : return (nContent >= rPos.nContent);
163 : }
164 : else // by convention position with no index is smaller
165 : {
166 2111 : return (pOtherReg) ? false : true;
167 : }
168 : }
169 187106 : return false;
170 : }
171 :
172 414741 : bool SwPosition::operator==(const SwPosition &rPos) const
173 : {
174 414741 : return (nNode == rPos.nNode)
175 : // GetIndexReg may be null for FLY_AT_PARA frame anchor position
176 240863 : && (nContent.GetIdxReg() == rPos.nContent.GetIdxReg())
177 652256 : && (nContent == rPos.nContent);
178 : }
179 :
180 681051 : bool SwPosition::operator!=(const SwPosition &rPos) const
181 : {
182 681051 : return (nNode != rPos.nNode)
183 : // GetIndexReg may be null for FLY_AT_PARA frame anchor position
184 637994 : || (nContent.GetIdxReg() != rPos.nContent.GetIdxReg())
185 1319045 : || (nContent != rPos.nContent);
186 : }
187 :
188 2858 : SwDoc * SwPosition::GetDoc() const
189 : {
190 2858 : return nNode.GetNode().GetDoc();
191 : }
192 :
193 : enum CHKSECTION { Chk_Both, Chk_One, Chk_None };
194 :
195 124652 : static CHKSECTION lcl_TstIdx( sal_uLong nSttIdx, sal_uLong nEndIdx, const SwNode& rEndNd )
196 : {
197 124652 : sal_uLong nStt = rEndNd.StartOfSectionIndex(), nEnd = rEndNd.GetIndex();
198 124652 : CHKSECTION eSec = nStt < nSttIdx && nEnd >= nSttIdx ? Chk_One : Chk_None;
199 124652 : if( nStt < nEndIdx && nEnd >= nEndIdx )
200 112892 : return( eSec == Chk_One ? Chk_Both : Chk_One );
201 11760 : return eSec;
202 : }
203 :
204 11492 : static bool lcl_ChkOneRange( CHKSECTION eSec, bool bChkSections,
205 : const SwNode& rBaseEnd, sal_uLong nStt, sal_uLong nEnd )
206 : {
207 11492 : if( eSec != Chk_Both )
208 86 : return false;
209 :
210 11406 : if( !bChkSections )
211 0 : return true;
212 :
213 : // search the surrounding section
214 11406 : const SwNodes& rNds = rBaseEnd.GetNodes();
215 11406 : const SwNode *pTmp, *pNd = rNds[ nStt ];
216 11406 : if( !pNd->IsStartNode() )
217 11120 : pNd = pNd->StartOfSectionNode();
218 :
219 11406 : if( pNd == rNds[ nEnd ]->StartOfSectionNode() )
220 8128 : return true; // same StartNode, same section
221 :
222 : // already on a base node => error
223 3278 : if( !pNd->StartOfSectionIndex() )
224 0 : return false;
225 :
226 8068 : while( ( pTmp = pNd->StartOfSectionNode())->EndOfSectionNode() !=
227 : &rBaseEnd )
228 1512 : pNd = pTmp;
229 :
230 3278 : sal_uLong nSttIdx = pNd->GetIndex(), nEndIdx = pNd->EndOfSectionIndex();
231 3278 : return nSttIdx <= nStt && nStt <= nEndIdx &&
232 6208 : nSttIdx <= nEnd && nEnd <= nEndIdx;
233 : }
234 :
235 112936 : bool CheckNodesRange( const SwNodeIndex& rStt,
236 : const SwNodeIndex& rEnd, bool bChkSection )
237 : {
238 112936 : const SwNodes& rNds = rStt.GetNodes();
239 112936 : sal_uLong nStt = rStt.GetIndex(), nEnd = rEnd.GetIndex();
240 112936 : CHKSECTION eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfContent() );
241 112936 : if( Chk_None != eSec )
242 101444 : return eSec == Chk_Both;
243 :
244 11492 : eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfAutotext() );
245 11492 : if( Chk_None != eSec )
246 : return lcl_ChkOneRange( eSec, bChkSection,
247 11380 : rNds.GetEndOfAutotext(), nStt, nEnd );
248 :
249 112 : eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfPostIts() );
250 112 : if( Chk_None != eSec )
251 : return lcl_ChkOneRange( eSec, bChkSection,
252 0 : rNds.GetEndOfPostIts(), nStt, nEnd );
253 :
254 112 : eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfInserts() );
255 112 : if( Chk_None != eSec )
256 : return lcl_ChkOneRange( eSec, bChkSection,
257 112 : rNds.GetEndOfInserts(), nStt, nEnd );
258 :
259 0 : eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfRedlines() );
260 0 : if( Chk_None != eSec )
261 : return lcl_ChkOneRange( eSec, bChkSection,
262 0 : rNds.GetEndOfRedlines(), nStt, nEnd );
263 :
264 0 : return false; // somewhere in between => error
265 : }
266 :
267 101208 : bool GoNext(SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
268 : {
269 101208 : if( pNd->IsCntntNode() )
270 101148 : return ((SwCntntNode*)pNd)->GoNext( pIdx, nMode );
271 60 : return false;
272 : }
273 :
274 127594 : bool GoPrevious( SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
275 : {
276 127594 : if( pNd->IsCntntNode() )
277 127550 : return ((SwCntntNode*)pNd)->GoPrevious( pIdx, nMode );
278 44 : return false;
279 : }
280 :
281 117674 : SwCntntNode* GoNextNds( SwNodeIndex* pIdx, bool bChk )
282 : {
283 117674 : SwNodeIndex aIdx( *pIdx );
284 117674 : SwCntntNode* pNd = aIdx.GetNodes().GoNext( &aIdx );
285 117674 : if( pNd )
286 : {
287 129810 : if( bChk && 1 != aIdx.GetIndex() - pIdx->GetIndex() &&
288 20992 : !CheckNodesRange( *pIdx, aIdx, true ) )
289 9652 : pNd = 0;
290 : else
291 99166 : *pIdx = aIdx;
292 : }
293 117674 : return pNd;
294 : }
295 :
296 351200 : SwCntntNode* GoPreviousNds( SwNodeIndex * pIdx, bool bChk )
297 : {
298 351200 : SwNodeIndex aIdx( *pIdx );
299 351200 : SwCntntNode* pNd = aIdx.GetNodes().GoPrevious( &aIdx );
300 351200 : if( pNd )
301 : {
302 341371 : if( bChk && 1 != pIdx->GetIndex() - aIdx.GetIndex() &&
303 3688 : !CheckNodesRange( *pIdx, aIdx, true ) )
304 376 : pNd = 0;
305 : else
306 337307 : *pIdx = aIdx;
307 : }
308 351200 : return pNd;
309 : }
310 :
311 821316 : SwPaM::SwPaM( const SwPosition& rPos, SwPaM* pRing )
312 : : Ring( pRing )
313 : , m_Bound1( rPos )
314 821316 : , m_Bound2( rPos.nNode.GetNode().GetNodes() ) // default initialize
315 : , m_pPoint( &m_Bound1 )
316 : , m_pMark( m_pPoint )
317 1642632 : , m_bIsInFrontOfLabel( false )
318 : {
319 821316 : }
320 :
321 282458 : SwPaM::SwPaM( const SwPosition& rMark, const SwPosition& rPoint, SwPaM* pRing )
322 : : Ring( pRing )
323 : , m_Bound1( rMark )
324 : , m_Bound2( rPoint )
325 : , m_pPoint( &m_Bound2 )
326 : , m_pMark( &m_Bound1 )
327 282458 : , m_bIsInFrontOfLabel( false )
328 : {
329 282458 : }
330 :
331 2580 : SwPaM::SwPaM( const SwNodeIndex& rMark, const SwNodeIndex& rPoint,
332 : long nMarkOffset, long nPointOffset, SwPaM* pRing )
333 : : Ring( pRing )
334 : , m_Bound1( rMark )
335 : , m_Bound2( rPoint )
336 : , m_pPoint( &m_Bound2 )
337 : , m_pMark( &m_Bound1 )
338 2580 : , m_bIsInFrontOfLabel( false )
339 : {
340 2580 : if ( nMarkOffset )
341 : {
342 2 : m_pMark->nNode += nMarkOffset;
343 : }
344 2580 : if ( nPointOffset )
345 : {
346 2 : m_pPoint->nNode += nPointOffset;
347 : }
348 2580 : m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetCntntNode(), 0 );
349 2580 : m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetCntntNode(), 0 );
350 2580 : }
351 :
352 220270 : SwPaM::SwPaM( const SwNode& rMark, const SwNode& rPoint,
353 : long nMarkOffset, long nPointOffset, SwPaM* pRing )
354 : : Ring( pRing )
355 : , m_Bound1( rMark )
356 : , m_Bound2( rPoint )
357 : , m_pPoint( &m_Bound2 )
358 : , m_pMark( &m_Bound1 )
359 220270 : , m_bIsInFrontOfLabel( false )
360 : {
361 220270 : if ( nMarkOffset )
362 : {
363 22 : m_pMark->nNode += nMarkOffset;
364 : }
365 220270 : if ( nPointOffset )
366 : {
367 12 : m_pPoint->nNode += nPointOffset;
368 : }
369 220270 : m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetCntntNode(), 0 );
370 220270 : m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetCntntNode(), 0 );
371 220270 : }
372 :
373 16030 : SwPaM::SwPaM( const SwNodeIndex& rMark, sal_Int32 nMarkCntnt,
374 : const SwNodeIndex& rPoint, sal_Int32 nPointCntnt, SwPaM* pRing )
375 : : Ring( pRing )
376 : , m_Bound1( rMark )
377 : , m_Bound2( rPoint )
378 : , m_pPoint( &m_Bound2 )
379 : , m_pMark( &m_Bound1 )
380 16030 : , m_bIsInFrontOfLabel( false )
381 : {
382 16030 : m_pPoint->nContent.Assign( rPoint.GetNode().GetCntntNode(), nPointCntnt);
383 16030 : m_pMark ->nContent.Assign( rMark .GetNode().GetCntntNode(), nMarkCntnt );
384 16030 : }
385 :
386 7085 : SwPaM::SwPaM( const SwNode& rMark, sal_Int32 nMarkCntnt,
387 : const SwNode& rPoint, sal_Int32 nPointCntnt, SwPaM* pRing )
388 : : Ring( pRing )
389 : , m_Bound1( rMark )
390 : , m_Bound2( rPoint )
391 : , m_pPoint( &m_Bound2 )
392 : , m_pMark( &m_Bound1 )
393 7085 : , m_bIsInFrontOfLabel( false )
394 : {
395 7085 : m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetCntntNode(),
396 7085 : nPointCntnt);
397 7085 : m_pMark ->nContent.Assign( m_pMark ->nNode.GetNode().GetCntntNode(),
398 7085 : nMarkCntnt );
399 7085 : }
400 :
401 108042 : SwPaM::SwPaM( const SwNode& rNode, sal_Int32 nCntnt, SwPaM* pRing )
402 : : Ring( pRing )
403 : , m_Bound1( rNode )
404 108042 : , m_Bound2( m_Bound1.nNode.GetNode().GetNodes() ) // default initialize
405 : , m_pPoint( &m_Bound1 )
406 : , m_pMark( &m_Bound1 )
407 216084 : , m_bIsInFrontOfLabel( false )
408 : {
409 108042 : m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetCntntNode(),
410 108042 : nCntnt );
411 108042 : }
412 :
413 294860 : SwPaM::SwPaM( const SwNodeIndex& rNodeIdx, sal_Int32 nCntnt, SwPaM* pRing )
414 : : Ring( pRing )
415 : , m_Bound1( rNodeIdx )
416 294860 : , m_Bound2( rNodeIdx.GetNode().GetNodes() ) // default initialize
417 : , m_pPoint( &m_Bound1 )
418 : , m_pMark( &m_Bound1 )
419 589720 : , m_bIsInFrontOfLabel( false )
420 : {
421 294860 : m_pPoint->nContent.Assign( rNodeIdx.GetNode().GetCntntNode(), nCntnt );
422 294860 : }
423 :
424 2038967 : SwPaM::~SwPaM() {}
425 :
426 : // @@@ semantic: no copy ctor.
427 288 : SwPaM::SwPaM( SwPaM &rPam )
428 : : Ring( &rPam )
429 : , m_Bound1( *(rPam.m_pPoint) )
430 : , m_Bound2( *(rPam.m_pMark) )
431 288 : , m_pPoint( &m_Bound1 ), m_pMark( rPam.HasMark() ? &m_Bound2 : m_pPoint )
432 576 : , m_bIsInFrontOfLabel( false )
433 : {
434 288 : }
435 :
436 : // @@@ semantic: no copy assignment for super class Ring.
437 1392 : SwPaM &SwPaM::operator=( const SwPaM &rPam )
438 : {
439 1392 : *m_pPoint = *( rPam.m_pPoint );
440 1392 : if ( rPam.HasMark() )
441 : {
442 1358 : SetMark();
443 1358 : *m_pMark = *( rPam.m_pMark );
444 : }
445 : else
446 : {
447 34 : DeleteMark();
448 : }
449 1392 : return *this;
450 : }
451 :
452 477447 : void SwPaM::SetMark()
453 : {
454 477447 : if (m_pPoint == &m_Bound1)
455 : {
456 477261 : m_pMark = &m_Bound2;
457 : }
458 : else
459 : {
460 186 : m_pMark = &m_Bound1;
461 : }
462 477447 : (*m_pMark) = (*m_pPoint);
463 477447 : }
464 :
465 : #ifdef DBG_UTIL
466 : void SwPaM::Exchange()
467 : {
468 : if (m_pPoint != m_pMark)
469 : {
470 : SwPosition *pTmp = m_pPoint;
471 : m_pPoint = m_pMark;
472 : m_pMark = pTmp;
473 : }
474 : }
475 : #endif
476 :
477 : /// movement of cursor
478 840792 : bool SwPaM::Move( SwMoveFn fnMove, SwGoInDoc fnGo )
479 : {
480 840792 : const bool bRet = (*fnGo)( *this, fnMove );
481 :
482 840792 : m_bIsInFrontOfLabel = false;
483 840792 : return bRet;
484 : }
485 :
486 : /** make a new region
487 :
488 : Sets the first SwPaM onto the given SwPaM, or to the beginning or end of a
489 : document. SPoint stays at its position, GetMark will be changed respectively.
490 :
491 : @param fnMove Contains information if beginning or end of document.
492 : @param pOrigRg The given region.
493 :
494 : @return Newly created area.
495 : */
496 180 : SwPaM* SwPaM::MakeRegion( SwMoveFn fnMove, const SwPaM * pOrigRg )
497 : {
498 : SwPaM* pPam;
499 180 : if( pOrigRg == 0 )
500 : {
501 0 : pPam = new SwPaM( *m_pPoint );
502 0 : pPam->SetMark(); // set beginning
503 0 : pPam->Move( fnMove, fnGoSection); // to beginning or end of a node
504 :
505 : // set SPoint onto its old position; set GetMark to the "end"
506 0 : pPam->Exchange();
507 : }
508 : else
509 : {
510 180 : pPam = new SwPaM( *(SwPaM*)pOrigRg ); // given search area
511 : // make sure that SPoint is on the "real" start position
512 : // FORWARD: SPoint always smaller than GetMark
513 : // BACKWARD: SPoint always bigger than GetMark
514 180 : if( (pPam->GetMark()->*fnMove->fnCmpOp)( *pPam->GetPoint() ) )
515 180 : pPam->Exchange();
516 : }
517 180 : return pPam;
518 : }
519 :
520 12528 : SwPaM & SwPaM::Normalize(bool bPointFirst)
521 : {
522 12528 : if (HasMark())
523 17388 : if ( ( bPointFirst && *m_pPoint > *m_pMark) ||
524 16190 : (!bPointFirst && *m_pPoint < *m_pMark) )
525 : {
526 782 : Exchange();
527 : }
528 12528 : return *this;
529 : }
530 :
531 : /// return page number at cursor (for reader and page bound frames)
532 60 : sal_uInt16 SwPaM::GetPageNum( bool bAtPoint, const Point* pLayPos )
533 : {
534 : const SwCntntFrm* pCFrm;
535 : const SwPageFrm *pPg;
536 : const SwCntntNode *pNd ;
537 60 : const SwPosition* pPos = bAtPoint ? m_pPoint : m_pMark;
538 :
539 180 : if( 0 != ( pNd = pPos->nNode.GetNode().GetCntntNode() ) &&
540 120 : 0 != ( pCFrm = pNd->getLayoutFrm( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), pLayPos, pPos, false )) &&
541 60 : 0 != ( pPg = pCFrm->FindPageFrm() ))
542 60 : return pPg->GetPhyPageNum();
543 0 : return 0;
544 : }
545 :
546 : // Formular view - See also SwCrsrShell::IsCrsrReadonly()
547 0 : static const SwFrm* lcl_FindEditInReadonlyFrm( const SwFrm& rFrm )
548 : {
549 0 : const SwFrm* pRet = 0;
550 :
551 : const SwFlyFrm* pFly;
552 : const SwSectionFrm* pSectionFrm;
553 :
554 0 : if( rFrm.IsInFly() &&
555 0 : (pFly = rFrm.FindFlyFrm())->GetFmt()->GetEditInReadonly().GetValue() &&
556 0 : pFly->Lower() &&
557 0 : !pFly->Lower()->IsNoTxtFrm() )
558 : {
559 0 : pRet = pFly;
560 : }
561 0 : else if ( rFrm.IsInSct() &&
562 0 : 0 != ( pSectionFrm = rFrm.FindSctFrm() )->GetSection() &&
563 0 : pSectionFrm->GetSection()->IsEditInReadonlyFlag() )
564 : {
565 0 : pRet = pSectionFrm;
566 : }
567 :
568 0 : return pRet;
569 : }
570 :
571 : /// is in protected section or selection surrounds something protected
572 32224 : bool SwPaM::HasReadonlySel( bool bFormView, bool bAnnotationMode ) const
573 : {
574 32224 : bool bRet = false;
575 :
576 32224 : const SwCntntNode* pNd = GetPoint()->nNode.GetNode().GetCntntNode();
577 32224 : const SwCntntFrm *pFrm = NULL;
578 32224 : if ( pNd != NULL )
579 : {
580 32224 : Point aTmpPt;
581 32224 : pFrm = pNd->getLayoutFrm( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), &aTmpPt, GetPoint(), false );
582 : }
583 :
584 : // Will be set if point are inside edit-in-readonly environment
585 32224 : const SwFrm* pPointEditInReadonlyFrm = NULL;
586 32224 : if ( pFrm != NULL
587 32236 : && ( pFrm->IsProtected()
588 32212 : || ( bFormView
589 0 : && 0 == ( pPointEditInReadonlyFrm = lcl_FindEditInReadonlyFrm( *pFrm ) ) ) ) )
590 : {
591 12 : bRet = true;
592 : }
593 32212 : else if( pNd != NULL )
594 : {
595 32212 : const SwSectionNode* pSNd = pNd->GetSectionNode();
596 32212 : if ( pSNd != NULL
597 32212 : && ( pSNd->GetSection().IsProtectFlag()
598 0 : || ( bFormView
599 0 : && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
600 : {
601 0 : bRet = true;
602 : }
603 : }
604 :
605 64448 : if ( !bRet
606 32212 : && HasMark()
607 32564 : && GetPoint()->nNode != GetMark()->nNode )
608 : {
609 66 : pNd = GetMark()->nNode.GetNode().GetCntntNode();
610 66 : pFrm = NULL;
611 66 : if ( pNd != NULL )
612 : {
613 66 : Point aTmpPt;
614 66 : pFrm = pNd->getLayoutFrm( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), &aTmpPt, GetMark(), false );
615 : }
616 :
617 66 : const SwFrm* pMarkEditInReadonlyFrm = NULL;
618 66 : if ( pFrm != NULL
619 66 : && ( pFrm->IsProtected()
620 66 : || ( bFormView
621 0 : && 0 == ( pMarkEditInReadonlyFrm = lcl_FindEditInReadonlyFrm( *pFrm ) ) ) ) )
622 : {
623 0 : bRet = true;
624 : }
625 66 : else if( pNd != NULL )
626 : {
627 66 : const SwSectionNode* pSNd = pNd->GetSectionNode();
628 66 : if ( pSNd != NULL
629 66 : && ( pSNd->GetSection().IsProtectFlag()
630 0 : || ( bFormView
631 0 : && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
632 : {
633 0 : bRet = true;
634 : }
635 : }
636 :
637 66 : if ( !bRet && bFormView )
638 : {
639 : // Check if start and end frame are inside the _same_
640 : // edit-in-readonly-environment. Otherwise we better return 'true'
641 0 : if ( pPointEditInReadonlyFrm != pMarkEditInReadonlyFrm )
642 0 : bRet = true;
643 : }
644 :
645 : // check for protected section inside the selection
646 66 : if( !bRet )
647 : {
648 66 : sal_uLong nSttIdx = GetMark()->nNode.GetIndex(),
649 66 : nEndIdx = GetPoint()->nNode.GetIndex();
650 66 : if( nEndIdx <= nSttIdx )
651 : {
652 32 : sal_uLong nTmp = nSttIdx;
653 32 : nSttIdx = nEndIdx;
654 32 : nEndIdx = nTmp;
655 : }
656 :
657 : // If a protected section should be between nodes, then the
658 : // selection needs to contain already x nodes.
659 : // (TxtNd, SectNd, TxtNd, EndNd, TxtNd )
660 66 : if( nSttIdx + 3 < nEndIdx )
661 : {
662 48 : const SwSectionFmts& rFmts = GetDoc()->GetSections();
663 96 : for( sal_uInt16 n = rFmts.size(); n; )
664 : {
665 0 : const SwSectionFmt* pFmt = rFmts[ --n ];
666 0 : if( pFmt->GetProtect().IsCntntProtected() )
667 : {
668 0 : const SwFmtCntnt& rCntnt = pFmt->GetCntnt(false);
669 : OSL_ENSURE( rCntnt.GetCntntIdx(), "where is the SectionNode?" );
670 0 : sal_uLong nIdx = rCntnt.GetCntntIdx()->GetIndex();
671 0 : if( nSttIdx <= nIdx && nEndIdx >= nIdx &&
672 0 : rCntnt.GetCntntIdx()->GetNode().GetNodes().IsDocNodes() )
673 : {
674 0 : bRet = true;
675 0 : break;
676 : }
677 : }
678 : }
679 : }
680 : }
681 : }
682 :
683 : //FIXME FieldBk
684 : // TODO: Form Protection when Enhanced Fields are enabled
685 32224 : const SwDoc *pDoc = GetDoc();
686 32224 : const IDocumentMarkAccess* pMarksAccess = pDoc->getIDocumentMarkAccess();
687 32224 : sw::mark::IMark* pA = GetPoint() ? pMarksAccess->getFieldmarkFor( *GetPoint( ) ) : NULL;
688 32224 : sw::mark::IMark* pB = GetMark( ) ? pMarksAccess->getFieldmarkFor( *GetMark( ) ) : pA;
689 :
690 32224 : bool bUnhandledMark = false;
691 32224 : sw::mark::IFieldmark* pFieldmark = pMarksAccess->getFieldmarkFor( *GetPoint() );
692 32224 : if ( pFieldmark )
693 26 : bUnhandledMark = pFieldmark->GetFieldname( ) == ODF_UNHANDLED;
694 :
695 32224 : if (!bRet)
696 : {
697 : // Unhandled fieldmarks case shouldn't be edited manually to avoid breaking anything
698 32212 : if ( ( pA == pB ) && bUnhandledMark )
699 0 : bRet = true;
700 : else
701 : {
702 : // Form protection case
703 32212 : bool bAtStartA = pA != NULL && pA->GetMarkStart() == *GetPoint();
704 32212 : bool bAtStartB = pB != NULL && pB->GetMarkStart() == *GetMark();
705 32212 : bRet = ( pA != pB ) || bAtStartA || bAtStartB;
706 32212 : bool bProtectForm = pDoc->GetDocumentSettingManager().get( IDocumentSettingAccess::PROTECT_FORM );
707 32212 : if ( bProtectForm )
708 4 : bRet |= ( pA == NULL || pB == NULL );
709 : }
710 : }
711 : else
712 : {
713 12 : bRet = !( pA == pB && pA != NULL );
714 : }
715 :
716 : // Don't allow inserting characters between the 'field mark end' and
717 : // the 'comment anchor', unless the cursor is inside the annotation.
718 32224 : if (!bRet && !bAnnotationMode)
719 : {
720 32190 : if (!pA && GetPoint() && GetPoint()->nNode.GetNode().IsTxtNode() && GetPoint()->nContent.GetIndex() > 0)
721 : {
722 : // getFieldmarkFor() searches for >= start and < end, so check for
723 : // the previous character, to also get the fieldmark, if we're
724 : // exactly at the end.
725 16974 : SwPosition aPrevChar(*GetPoint());
726 16974 : aPrevChar.nContent--;
727 16974 : pFieldmark = pMarksAccess->getFieldmarkFor(aPrevChar);
728 16974 : if (pFieldmark && pFieldmark->GetMarkEnd() == *GetPoint())
729 0 : bRet = true;
730 : }
731 : }
732 :
733 32224 : return bRet;
734 : }
735 :
736 : /// This function returns the next node in direction of search. If there is no
737 : /// left or the next is out of the area, then a null-pointer is returned.
738 : /// @param rbFirst If <true> than first time request. If so than the position of
739 : /// the PaM must not be changed!
740 612 : SwCntntNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFn fnMove,
741 : bool bInReadOnly )
742 : {
743 612 : SwCntntNode * pNd = 0;
744 : SwCntntFrm* pFrm;
745 1224 : if( ((*rPam.GetPoint()).*fnMove->fnCmpOp)( *rPam.GetMark() ) ||
746 0 : ( *rPam.GetPoint() == *rPam.GetMark() && rbFirst ) )
747 : {
748 612 : if( rbFirst )
749 : {
750 180 : rbFirst = false;
751 180 : pNd = rPam.GetCntntNode();
752 180 : if( pNd )
753 : {
754 180 : if(
755 : (
756 360 : 0 == ( pFrm = pNd->getLayoutFrm( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ) ) ||
757 452 : ( !bInReadOnly && pFrm->IsProtected() ) ||
758 360 : (pFrm->IsTxtFrm() && ((SwTxtFrm*)pFrm)->IsHiddenNow())
759 360 : ) ||
760 316 : ( !bInReadOnly && pNd->FindSectionNode() &&
761 44 : pNd->FindSectionNode()->GetSection().IsProtect()
762 : )
763 : )
764 : {
765 0 : pNd = 0;
766 : }
767 : }
768 : }
769 :
770 612 : if( !pNd ) // is the cursor not on a CntntNode?
771 : {
772 432 : SwPosition aPos( *rPam.GetPoint() );
773 432 : bool bSrchForward = fnMove == fnMoveForward;
774 432 : SwNodes& rNodes = aPos.nNode.GetNodes();
775 :
776 : // go to next/previous CntntNode
777 : while( true )
778 : {
779 : pNd = bSrchForward
780 432 : ? rNodes.GoNextSection( &aPos.nNode, true, !bInReadOnly )
781 864 : : rNodes.GoPrevSection( &aPos.nNode, true, !bInReadOnly );
782 432 : if( pNd )
783 : {
784 432 : aPos.nContent.Assign( pNd, ::GetSttOrEnd( bSrchForward,*pNd ));
785 : // is the position still in the area
786 432 : if( (aPos.*fnMove->fnCmpOp)( *rPam.GetMark() ) )
787 : {
788 : // only in AutoTextSection can be nodes that are hidden
789 1260 : if( 0 == ( pFrm = pNd->getLayoutFrm( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ) ) ||
790 1000 : ( !bInReadOnly && pFrm->IsProtected() ) ||
791 832 : ( pFrm->IsTxtFrm() &&
792 412 : ((SwTxtFrm*)pFrm)->IsHiddenNow() ) )
793 : {
794 0 : pNd = 0;
795 0 : continue;
796 : }
797 420 : *(SwPosition*)rPam.GetPoint() = aPos;
798 : }
799 : else
800 12 : pNd = 0; // no valid node
801 432 : break;
802 : }
803 0 : break;
804 432 : }
805 : }
806 : }
807 612 : return pNd;
808 : }
809 :
810 256826 : void GoStartDoc( SwPosition * pPos )
811 : {
812 256826 : SwNodes& rNodes = pPos->nNode.GetNodes();
813 256826 : pPos->nNode = *rNodes.GetEndOfContent().StartOfSectionNode();
814 : // we always need to find a ContentNode!
815 256826 : SwCntntNode* pCNd = rNodes.GoNext( &pPos->nNode );
816 256826 : if( pCNd )
817 256826 : pCNd->MakeStartIndex( &pPos->nContent );
818 256826 : }
819 :
820 151908 : void GoEndDoc( SwPosition * pPos )
821 : {
822 151908 : SwNodes& rNodes = pPos->nNode.GetNodes();
823 151908 : pPos->nNode = rNodes.GetEndOfContent();
824 151908 : SwCntntNode* pCNd = GoPreviousNds( &pPos->nNode, true );
825 151908 : if( pCNd )
826 151908 : pCNd->MakeEndIndex( &pPos->nContent );
827 151908 : }
828 :
829 0 : void GoStartSection( SwPosition * pPos )
830 : {
831 : // jump to section's beginning
832 0 : SwNodes& rNodes = pPos->nNode.GetNodes();
833 0 : sal_uInt16 nLevel = rNodes.GetSectionLevel( pPos->nNode );
834 0 : if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
835 0 : nLevel--;
836 0 : do { rNodes.GoStartOfSection( &pPos->nNode ); } while( nLevel-- );
837 :
838 : // already on a CntntNode
839 0 : pPos->nNode.GetNode().GetCntntNode()->MakeStartIndex( &pPos->nContent );
840 0 : }
841 :
842 : /// go to the end of the current base section
843 0 : void GoEndSection( SwPosition * pPos )
844 : {
845 : // jump to section's beginning/end
846 0 : SwNodes& rNodes = pPos->nNode.GetNodes();
847 0 : sal_uInt16 nLevel = rNodes.GetSectionLevel( pPos->nNode );
848 0 : if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
849 0 : nLevel--;
850 0 : do { rNodes.GoEndOfSection( &pPos->nNode ); } while( nLevel-- );
851 :
852 : // now on a EndNode, thus to the previous CntntNode
853 0 : if( GoPreviousNds( &pPos->nNode, true ) )
854 0 : pPos->nNode.GetNode().GetCntntNode()->MakeEndIndex( &pPos->nContent );
855 0 : }
856 :
857 408734 : bool GoInDoc( SwPaM & rPam, SwMoveFn fnMove )
858 : {
859 408734 : (*fnMove->fnDoc)( rPam.GetPoint() );
860 408734 : return true;
861 : }
862 :
863 0 : bool GoInSection( SwPaM & rPam, SwMoveFn fnMove )
864 : {
865 0 : (*fnMove->fnSections)( (SwPosition*)rPam.GetPoint() );
866 0 : return true;
867 : }
868 :
869 266800 : bool GoInNode( SwPaM & rPam, SwMoveFn fnMove )
870 : {
871 266800 : SwCntntNode *pNd = (*fnMove->fnNds)( &rPam.GetPoint()->nNode, true );
872 266800 : if( pNd )
873 234413 : rPam.GetPoint()->nContent.Assign( pNd,
874 468826 : ::GetSttOrEnd( fnMove == fnMoveForward, *pNd ) );
875 266800 : return pNd;
876 : }
877 :
878 228730 : bool GoInCntnt( SwPaM & rPam, SwMoveFn fnMove )
879 : {
880 228730 : if( (*fnMove->fnNd)( &rPam.GetPoint()->nNode.GetNode(),
881 228730 : &rPam.GetPoint()->nContent, CRSR_SKIP_CHARS ))
882 165192 : return true;
883 63538 : return GoInNode( rPam, fnMove );
884 : }
885 :
886 0 : bool GoInCntntCells( SwPaM & rPam, SwMoveFn fnMove )
887 : {
888 0 : if( (*fnMove->fnNd)( &rPam.GetPoint()->nNode.GetNode(),
889 0 : &rPam.GetPoint()->nContent, CRSR_SKIP_CELLS ))
890 0 : return true;
891 0 : return GoInNode( rPam, fnMove );
892 : }
893 :
894 72 : bool GoInCntntSkipHidden( SwPaM & rPam, SwMoveFn fnMove )
895 : {
896 72 : if( (*fnMove->fnNd)( &rPam.GetPoint()->nNode.GetNode(),
897 72 : &rPam.GetPoint()->nContent, CRSR_SKIP_CHARS | CRSR_SKIP_HIDDEN ) )
898 66 : return true;
899 6 : return GoInNode( rPam, fnMove );
900 : }
901 :
902 0 : bool GoInCntntCellsSkipHidden( SwPaM & rPam, SwMoveFn fnMove )
903 : {
904 0 : if( (*fnMove->fnNd)( &rPam.GetPoint()->nNode.GetNode(),
905 0 : &rPam.GetPoint()->nContent, CRSR_SKIP_CELLS | CRSR_SKIP_HIDDEN ) )
906 0 : return true;
907 0 : return GoInNode( rPam, fnMove );
908 : }
909 :
910 16 : bool GoPrevPara( SwPaM & rPam, SwPosPara aPosPara )
911 : {
912 16 : if( rPam.Move( fnMoveBackward, fnGoNode ) )
913 : {
914 : // always on a CntntNode
915 16 : SwPosition& rPos = *rPam.GetPoint();
916 16 : SwCntntNode * pNd = rPos.nNode.GetNode().GetCntntNode();
917 : rPos.nContent.Assign( pNd,
918 16 : ::GetSttOrEnd( aPosPara == fnMoveForward, *pNd ) );
919 16 : return true;
920 : }
921 0 : return false;
922 : }
923 :
924 44992 : bool GoCurrPara( SwPaM & rPam, SwPosPara aPosPara )
925 : {
926 44992 : SwPosition& rPos = *rPam.GetPoint();
927 44992 : SwCntntNode * pNd = rPos.nNode.GetNode().GetCntntNode();
928 44992 : if( pNd )
929 : {
930 44992 : const sal_Int32 nOld = rPos.nContent.GetIndex();
931 44992 : const sal_Int32 nNew = aPosPara == fnMoveForward ? 0 : pNd->Len();
932 : // if already at beginning/end then to the next/previous
933 44992 : if( nOld != nNew )
934 : {
935 44936 : rPos.nContent.Assign( pNd, nNew );
936 44936 : return true;
937 : }
938 : }
939 : // move node to next/previous CntntNode
940 140 : if( ( aPosPara==fnParaStart && 0 != ( pNd =
941 140 : GoPreviousNds( &rPos.nNode, true ))) ||
942 70 : ( aPosPara==fnParaEnd && 0 != ( pNd =
943 28 : GoNextNds( &rPos.nNode, true ))) )
944 : {
945 : rPos.nContent.Assign( pNd,
946 42 : ::GetSttOrEnd( aPosPara == fnMoveForward, *pNd ));
947 42 : return true;
948 : }
949 14 : return false;
950 : }
951 :
952 23490 : bool GoNextPara( SwPaM & rPam, SwPosPara aPosPara )
953 : {
954 23490 : if( rPam.Move( fnMoveForward, fnGoNode ) )
955 : {
956 : // always on a CntntNode
957 16008 : SwPosition& rPos = *rPam.GetPoint();
958 16008 : SwCntntNode * pNd = rPos.nNode.GetNode().GetCntntNode();
959 : rPos.nContent.Assign( pNd,
960 16008 : ::GetSttOrEnd( aPosPara == fnMoveForward, *pNd ) );
961 16008 : return true;
962 : }
963 7482 : return false;
964 : }
965 :
966 81842 : bool GoCurrSection( SwPaM & rPam, SwMoveFn fnMove )
967 : {
968 81842 : SwPosition& rPos = *rPam.GetPoint();
969 81842 : SwPosition aSavePos( rPos ); // position for comparison
970 81842 : SwNodes& rNds = aSavePos.nNode.GetNodes();
971 81842 : (rNds.*fnMove->fnSection)( &rPos.nNode );
972 : SwCntntNode *pNd;
973 131952 : if( 0 == ( pNd = rPos.nNode.GetNode().GetCntntNode()) &&
974 50110 : 0 == ( pNd = (*fnMove->fnNds)( &rPos.nNode, true )) )
975 : {
976 0 : rPos = aSavePos; // do not change cursor
977 0 : return false;
978 : }
979 :
980 : rPos.nContent.Assign( pNd,
981 81842 : ::GetSttOrEnd( fnMove == fnMoveForward, *pNd ) );
982 81842 : return aSavePos != rPos;
983 : }
984 :
985 0 : bool GoNextSection( SwPaM & rPam, SwMoveFn fnMove )
986 : {
987 0 : SwPosition& rPos = *rPam.GetPoint();
988 0 : SwPosition aSavePos( rPos ); // position for comparison
989 0 : SwNodes& rNds = aSavePos.nNode.GetNodes();
990 0 : rNds.GoEndOfSection( &rPos.nNode );
991 :
992 : // no other CntntNode existent?
993 0 : if( !GoInCntnt( rPam, fnMoveForward ) )
994 : {
995 0 : rPos = aSavePos; // do not change cursor
996 0 : return false;
997 : }
998 0 : (rNds.*fnMove->fnSection)( &rPos.nNode );
999 0 : SwCntntNode *pNd = rPos.nNode.GetNode().GetCntntNode();
1000 : rPos.nContent.Assign( pNd,
1001 0 : ::GetSttOrEnd( fnMove == fnMoveForward, *pNd ) );
1002 0 : return true;
1003 : }
1004 :
1005 0 : bool GoPrevSection( SwPaM & rPam, SwMoveFn fnMove )
1006 : {
1007 0 : SwPosition& rPos = *rPam.GetPoint();
1008 0 : SwPosition aSavePos( rPos ); // position for comparison
1009 0 : SwNodes& rNds = aSavePos.nNode.GetNodes();
1010 0 : rNds.GoStartOfSection( &rPos.nNode );
1011 :
1012 : // no further CntntNode existent?
1013 0 : if( !GoInCntnt( rPam, fnMoveBackward ))
1014 : {
1015 0 : rPos = aSavePos; // do not change cursor
1016 0 : return false;
1017 : }
1018 0 : (rNds.*fnMove->fnSection)( &rPos.nNode );
1019 0 : SwCntntNode *pNd = rPos.nNode.GetNode().GetCntntNode();
1020 : rPos.nContent.Assign( pNd,
1021 0 : ::GetSttOrEnd( fnMove == fnMoveForward, *pNd ));
1022 0 : return true;
1023 : }
1024 :
1025 14 : OUString SwPaM::GetTxt() const
1026 : {
1027 14 : OUString aResult;
1028 :
1029 28 : SwNodeIndex aNodeIndex = Start()->nNode;
1030 :
1031 : // The first node can be already the end node.
1032 : // Use a "forever" loop with an exit condition in the middle
1033 : // of its body, in order to correctly handle all cases.
1034 14 : bool bIsStartNode = true;
1035 : for (;;)
1036 : {
1037 14 : const bool bIsEndNode = aNodeIndex == End()->nNode;
1038 14 : SwTxtNode * pTxtNode = aNodeIndex.GetNode().GetTxtNode();
1039 :
1040 14 : if (pTxtNode != NULL)
1041 : {
1042 14 : const OUString aTmpStr = pTxtNode->GetTxt();
1043 :
1044 14 : if (bIsStartNode || bIsEndNode)
1045 : {
1046 : // Handle corner cases of start/end node(s)
1047 : const sal_Int32 nStart = bIsStartNode
1048 14 : ? Start()->nContent.GetIndex()
1049 28 : : 0;
1050 : const sal_Int32 nEnd = bIsEndNode
1051 14 : ? End()->nContent.GetIndex()
1052 28 : : aTmpStr.getLength();
1053 :
1054 14 : aResult += aTmpStr.copy(nStart, nEnd-nStart);
1055 : }
1056 : else
1057 : {
1058 0 : aResult += aTmpStr;
1059 14 : }
1060 : }
1061 :
1062 14 : if (bIsEndNode)
1063 : {
1064 14 : break;
1065 : }
1066 :
1067 0 : ++aNodeIndex;
1068 0 : bIsStartNode = false;
1069 0 : }
1070 :
1071 28 : return aResult;
1072 : }
1073 :
1074 0 : void SwPaM::InvalidatePaM()
1075 : {
1076 0 : const SwNode &_pNd = this->GetNode();
1077 0 : const SwTxtNode *_pTxtNd = _pNd.GetTxtNode();
1078 0 : if (_pTxtNd != NULL)
1079 : {
1080 : // pretend that the PaM marks inserted text to recalc the portion...
1081 0 : SwInsTxt aHint( Start()->nContent.GetIndex(),
1082 0 : End()->nContent.GetIndex() - Start()->nContent.GetIndex() + 1 );
1083 0 : SwModify *_pModify=(SwModify*)_pTxtNd;
1084 0 : _pModify->ModifyNotification( 0, &aHint);
1085 : }
1086 270 : }
1087 :
1088 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|