Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <tools/resid.hxx>
31 : : #include <doc.hxx>
32 : : #include <IDocumentUndoRedo.hxx>
33 : : #include <swundo.hxx>
34 : : #include <pagedesc.hxx>
35 : : #include <SwUndoPageDesc.hxx>
36 : : #include <SwRewriter.hxx>
37 : : #include <undobj.hxx>
38 : : #include <comcore.hrc>
39 : : #include <fmtcntnt.hxx>
40 : : #include <fmthdft.hxx>
41 : :
42 : : #if OSL_DEBUG_LEVEL > 1
43 : : #include <ndindex.hxx>
44 : : #endif
45 : :
46 : :
47 : : #if OSL_DEBUG_LEVEL > 1
48 : : // Pure debug help function to have a quick look at the header/footer attributes.
49 : : void DebugHeaderFooterContent( const SwPageDesc& rPageDesc )
50 : : {
51 : : sal_uLong nHeaderMaster = ULONG_MAX;
52 : : sal_uLong nHeaderLeft = ULONG_MAX;
53 : : sal_uLong nFooterMaster = ULONG_MAX;
54 : : sal_uLong nFooterLeft = ULONG_MAX;
55 : :
56 : : SwFmtHeader& rHead = (SwFmtHeader&)rPageDesc.GetMaster().GetHeader();
57 : : SwFmtFooter& rFoot = (SwFmtFooter&)rPageDesc.GetMaster().GetFooter();
58 : : SwFmtHeader& rLeftHead = (SwFmtHeader&)rPageDesc.GetLeft().GetHeader();
59 : : SwFmtFooter& rLeftFoot = (SwFmtFooter&)rPageDesc.GetLeft().GetFooter();
60 : : if( rHead.IsActive() )
61 : : {
62 : : SwFrmFmt* pHeaderFmt = rHead.GetHeaderFmt();
63 : : if( pHeaderFmt )
64 : : {
65 : : const SwFmtCntnt* pCntnt = &pHeaderFmt->GetCntnt();
66 : : if( pCntnt->GetCntntIdx() )
67 : : nHeaderMaster = pCntnt->GetCntntIdx()->GetIndex();
68 : : else
69 : : nHeaderMaster = 0;
70 : : }
71 : : SwFrmFmt* pLeftHeaderFmt = rLeftHead.GetHeaderFmt();
72 : : if( pLeftHeaderFmt )
73 : : {
74 : : const SwFmtCntnt* pLeftCntnt = &pLeftHeaderFmt->GetCntnt();
75 : : if( pLeftCntnt->GetCntntIdx() )
76 : : nHeaderLeft = pLeftCntnt->GetCntntIdx()->GetIndex();
77 : : else
78 : : nHeaderLeft = 0;
79 : : }
80 : : }
81 : : if( rFoot.IsActive() )
82 : : {
83 : : SwFrmFmt* pFooterFmt = rFoot.GetFooterFmt();
84 : : if( pFooterFmt )
85 : : {
86 : : const SwFmtCntnt* pCntnt = &pFooterFmt->GetCntnt();
87 : : if( pCntnt->GetCntntIdx() )
88 : : nFooterMaster = pCntnt->GetCntntIdx()->GetIndex();
89 : : else
90 : : nFooterMaster = 0;
91 : : }
92 : : SwFrmFmt* pLeftFooterFmt = rLeftFoot.GetFooterFmt();
93 : : if( pLeftFooterFmt )
94 : : {
95 : : const SwFmtCntnt* pLeftCntnt = &pLeftFooterFmt->GetCntnt();
96 : : if( pLeftCntnt->GetCntntIdx() )
97 : : nFooterLeft = pLeftCntnt->GetCntntIdx()->GetIndex();
98 : : else
99 : : nFooterLeft = 0;
100 : : }
101 : : }
102 : :
103 : : (void)nHeaderMaster;
104 : : (void)nHeaderLeft;
105 : : (void)nFooterMaster;
106 : : (void)nFooterLeft;
107 : : }
108 : : #endif
109 : :
110 : 8 : SwUndoPageDesc::SwUndoPageDesc(const SwPageDesc & _aOld,
111 : : const SwPageDesc & _aNew,
112 : : SwDoc * _pDoc)
113 : 8 : : SwUndo( _aOld.GetName() != _aNew.GetName() ?
114 : : UNDO_RENAME_PAGEDESC :
115 : : UNDO_CHANGE_PAGEDESC ),
116 [ + - ][ + - ]: 8 : aOld(_aOld, _pDoc), aNew(_aNew, _pDoc), pDoc(_pDoc), bExchange( false )
[ + + ]
117 : : {
118 : : OSL_ENSURE(0 != pDoc, "no document?");
119 : :
120 : : #if OSL_DEBUG_LEVEL > 1
121 : : DebugHeaderFooterContent( (SwPageDesc&)aOld );
122 : : DebugHeaderFooterContent( (SwPageDesc&)aNew );
123 : : #endif
124 : :
125 : : /*
126 : : The page description changes.
127 : : If there are no header/footer content changes like header on/off or change from shared content
128 : : to unshared etc., there is no reason to duplicate the content nodes (Crash i55547)
129 : : But this happens, this Undo Ctor will destroy the unnecessary duplicate and manipulate the
130 : : content pointer of the both page descriptions.
131 : : */
132 : 8 : SwPageDesc &rOldDesc = (SwPageDesc&)aOld;
133 : 8 : SwPageDesc &rNewDesc = (SwPageDesc&)aNew;
134 [ + - ]: 8 : const SwFmtHeader& rOldHead = rOldDesc.GetMaster().GetHeader();
135 [ + - ]: 8 : const SwFmtHeader& rNewHead = rNewDesc.GetMaster().GetHeader();
136 [ + - ]: 8 : const SwFmtFooter& rOldFoot = rOldDesc.GetMaster().GetFooter();
137 [ + - ]: 8 : const SwFmtFooter& rNewFoot = rNewDesc.GetMaster().GetFooter();
138 : : /* bExchange must not be set, if the old page descriptor will stay active.
139 : : Two known situations:
140 : : #i67735#: renaming a page descriptor
141 : : #i67334#: changing the follow style
142 : : If header/footer will be activated or deactivated, this undo will not work.
143 : : */
144 [ + - ][ + - ]: 8 : bExchange = ( aOld.GetName() == aNew.GetName() ) &&
[ + - ]
145 : 4 : ( _aOld.GetFollow() == _aNew.GetFollow() ) &&
146 : 2 : ( rOldHead.IsActive() == rNewHead.IsActive() ) &&
147 [ + + + + : 14 : ( rOldFoot.IsActive() == rNewFoot.IsActive() );
+ - ][ + - ]
148 [ - + ][ # # ]: 8 : if( rOldHead.IsActive() && ( rOldDesc.IsHeaderShared() != rNewDesc.IsHeaderShared() ) )
[ - + ]
149 : 0 : bExchange = false;
150 [ - + ][ # # ]: 8 : if( rOldFoot.IsActive() && ( rOldDesc.IsFooterShared() != rNewDesc.IsFooterShared() ) )
[ - + ]
151 : 0 : bExchange = false;
152 [ + + ]: 8 : if( bExchange )
153 : : {
154 [ - + ]: 2 : if( rNewHead.IsActive() )
155 : : {
156 [ # # ][ # # ]: 0 : SwFrmFmt* pFormat = new SwFrmFmt( *rNewHead.GetHeaderFmt() );
157 : : // The Ctor of this object will remove the duplicate!
158 [ # # ]: 0 : SwFmtHeader aFmtHeader( pFormat );
159 [ # # ]: 0 : if( !rNewDesc.IsHeaderShared() )
160 : : {
161 [ # # ][ # # ]: 0 : pFormat = new SwFrmFmt( *rNewDesc.GetLeft().GetHeader().GetHeaderFmt() );
[ # # ]
162 : : // The Ctor of this object will remove the duplicate!
163 [ # # ][ # # ]: 0 : SwFmtHeader aFormatHeader( pFormat );
164 [ # # ]: 0 : }
165 : : }
166 : : // Same procedure for footers...
167 [ - + ]: 2 : if( rNewFoot.IsActive() )
168 : : {
169 [ # # ][ # # ]: 0 : SwFrmFmt* pFormat = new SwFrmFmt( *rNewFoot.GetFooterFmt() );
170 : : // The Ctor of this object will remove the duplicate!
171 [ # # ]: 0 : SwFmtFooter aFmtFooter( pFormat );
172 [ # # ]: 0 : if( !rNewDesc.IsFooterShared() )
173 : : {
174 [ # # ][ # # ]: 0 : pFormat = new SwFrmFmt( *rNewDesc.GetLeft().GetFooter().GetFooterFmt() );
[ # # ]
175 : : // The Ctor of this object will remove the duplicate!
176 [ # # ][ # # ]: 0 : SwFmtFooter aFormatFooter( pFormat );
177 [ # # ]: 0 : }
178 : : }
179 : :
180 : : // After this exchange method the old page description will point to zero,
181 : : // the new one will point to the node position of the original content nodes.
182 [ + - ]: 2 : ExchangeContentNodes( (SwPageDesc&)aOld, (SwPageDesc&)aNew );
183 : : #if OSL_DEBUG_LEVEL > 1
184 : : DebugHeaderFooterContent( (SwPageDesc&)aOld );
185 : : DebugHeaderFooterContent( (SwPageDesc&)aNew );
186 : : #endif
187 : : }
188 : 8 : }
189 : :
190 [ + - ][ + - ]: 8 : SwUndoPageDesc::~SwUndoPageDesc()
191 : : {
192 [ - + ]: 16 : }
193 : :
194 : :
195 : 2 : void SwUndoPageDesc::ExchangeContentNodes( SwPageDesc& rSource, SwPageDesc &rDest )
196 : : {
197 : : OSL_ENSURE( bExchange, "You shouldn't do that." );
198 : 2 : const SwFmtHeader& rDestHead = rDest.GetMaster().GetHeader();
199 : 2 : const SwFmtHeader& rSourceHead = rSource.GetMaster().GetHeader();
200 [ - + ]: 2 : if( rDestHead.IsActive() )
201 : : {
202 : : // Let the destination page descrition point to the source node position,
203 : : // from now on this descriptor is responsible for the content nodes!
204 : : const SfxPoolItem* pItem;
205 [ # # ]: 0 : rDest.GetMaster().GetAttrSet().GetItemState( RES_HEADER, sal_False, &pItem );
206 [ # # ]: 0 : SfxPoolItem *pNewItem = pItem->Clone();
207 : 0 : SwFrmFmt* pNewFmt = ((SwFmtHeader*)pNewItem)->GetHeaderFmt();
208 : : #if OSL_DEBUG_LEVEL > 1
209 : : const SwFmtCntnt& rSourceCntnt = rSourceHead.GetHeaderFmt()->GetCntnt();
210 : : (void)rSourceCntnt;
211 : : const SwFmtCntnt& rDestCntnt = rDestHead.GetHeaderFmt()->GetCntnt();
212 : : (void)rDestCntnt;
213 : : #endif
214 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( rSourceHead.GetHeaderFmt()->GetCntnt() );
215 [ # # ][ # # ]: 0 : delete pNewItem;
216 : :
217 : : // Let the source page description point to zero node position,
218 : : // it loses the responsible and can be destroyed without removing the content nodes.
219 [ # # ]: 0 : rSource.GetMaster().GetAttrSet().GetItemState( RES_HEADER, sal_False, &pItem );
220 [ # # ]: 0 : pNewItem = pItem->Clone();
221 : 0 : pNewFmt = ((SwFmtHeader*)pNewItem)->GetHeaderFmt();
222 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( SwFmtCntnt() );
[ # # ]
223 [ # # ][ # # ]: 0 : delete pNewItem;
224 : :
225 [ # # ]: 0 : if( !rDest.IsHeaderShared() )
226 : : {
227 : : // Same procedure for unshared header..
228 [ # # ]: 0 : const SwFmtHeader& rSourceLeftHead = rSource.GetLeft().GetHeader();
229 [ # # ]: 0 : rDest.GetLeft().GetAttrSet().GetItemState( RES_HEADER, sal_False, &pItem );
230 [ # # ]: 0 : pNewItem = pItem->Clone();
231 : 0 : pNewFmt = ((SwFmtHeader*)pNewItem)->GetHeaderFmt();
232 : : #if OSL_DEBUG_LEVEL > 1
233 : : const SwFmtCntnt& rSourceCntnt1 = rSourceLeftHead.GetHeaderFmt()->GetCntnt();
234 : : (void)rSourceCntnt1;
235 : : const SwFmtCntnt& rDestCntnt1 = rDest.GetLeft().GetHeader().GetHeaderFmt()->GetCntnt();
236 : : (void)rDestCntnt1;
237 : : #endif
238 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( rSourceLeftHead.GetHeaderFmt()->GetCntnt() );
239 [ # # ][ # # ]: 0 : delete pNewItem;
240 [ # # ]: 0 : rSource.GetLeft().GetAttrSet().GetItemState( RES_HEADER, sal_False, &pItem );
241 [ # # ]: 0 : pNewItem = pItem->Clone();
242 : 0 : pNewFmt = ((SwFmtHeader*)pNewItem)->GetHeaderFmt();
243 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( SwFmtCntnt() );
[ # # ]
244 [ # # ][ # # ]: 0 : delete pNewItem;
245 : : }
246 : : }
247 : : // Same procedure for footers...
248 : 2 : const SwFmtFooter& rDestFoot = rDest.GetMaster().GetFooter();
249 : 2 : const SwFmtFooter& rSourceFoot = rSource.GetMaster().GetFooter();
250 [ - + ]: 2 : if( rDestFoot.IsActive() )
251 : : {
252 : : const SfxPoolItem* pItem;
253 [ # # ]: 0 : rDest.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, sal_False, &pItem );
254 [ # # ]: 0 : SfxPoolItem *pNewItem = pItem->Clone();
255 : 0 : SwFrmFmt *pNewFmt = ((SwFmtFooter*)pNewItem)->GetFooterFmt();
256 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( rSourceFoot.GetFooterFmt()->GetCntnt() );
257 [ # # ][ # # ]: 0 : delete pNewItem;
258 : :
259 : : #if OSL_DEBUG_LEVEL > 1
260 : : const SwFmtCntnt& rFooterSourceCntnt = rSourceFoot.GetFooterFmt()->GetCntnt();
261 : : (void)rFooterSourceCntnt;
262 : : const SwFmtCntnt& rFooterDestCntnt = rDestFoot.GetFooterFmt()->GetCntnt();
263 : : (void)rFooterDestCntnt;
264 : : #endif
265 [ # # ]: 0 : rSource.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, sal_False, &pItem );
266 [ # # ]: 0 : pNewItem = pItem->Clone();
267 : 0 : pNewFmt = ((SwFmtFooter*)pNewItem)->GetFooterFmt();
268 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( SwFmtCntnt() );
[ # # ]
269 [ # # ][ # # ]: 0 : delete pNewItem;
270 : :
271 [ # # ]: 0 : if( !rDest.IsFooterShared() )
272 : : {
273 [ # # ]: 0 : const SwFmtFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter();
274 : : #if OSL_DEBUG_LEVEL > 1
275 : : const SwFmtCntnt& rFooterSourceCntnt2 = rSourceLeftFoot.GetFooterFmt()->GetCntnt();
276 : : const SwFmtCntnt& rFooterDestCntnt2 =
277 : : rDest.GetLeft().GetFooter().GetFooterFmt()->GetCntnt();
278 : : (void)rFooterSourceCntnt2;
279 : : (void)rFooterDestCntnt2;
280 : : #endif
281 [ # # ]: 0 : rDest.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, sal_False, &pItem );
282 [ # # ]: 0 : pNewItem = pItem->Clone();
283 : 0 : pNewFmt = ((SwFmtFooter*)pNewItem)->GetFooterFmt();
284 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( rSourceLeftFoot.GetFooterFmt()->GetCntnt() );
285 [ # # ][ # # ]: 0 : delete pNewItem;
286 [ # # ]: 0 : rSource.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, sal_False, &pItem );
287 [ # # ]: 0 : pNewItem = pItem->Clone();
288 : 0 : pNewFmt = ((SwFmtFooter*)pNewItem)->GetFooterFmt();
289 [ # # ][ # # ]: 0 : pNewFmt->SetFmtAttr( SwFmtCntnt() );
[ # # ]
290 [ # # ][ # # ]: 0 : delete pNewItem;
291 : : }
292 : : }
293 : 2 : }
294 : :
295 : 0 : void SwUndoPageDesc::UndoImpl(::sw::UndoRedoContext &)
296 : : {
297 : : // Move (header/footer)content node responsibility from new page descriptor to old one again.
298 [ # # ]: 0 : if( bExchange )
299 : 0 : ExchangeContentNodes( (SwPageDesc&)aNew, (SwPageDesc&)aOld );
300 [ # # ][ # # ]: 0 : pDoc->ChgPageDesc(aOld.GetName(), aOld);
301 : 0 : }
302 : :
303 : 0 : void SwUndoPageDesc::RedoImpl(::sw::UndoRedoContext &)
304 : : {
305 : : // Move (header/footer)content node responsibility from old page descriptor to new one again.
306 [ # # ]: 0 : if( bExchange )
307 : 0 : ExchangeContentNodes( (SwPageDesc&)aOld, (SwPageDesc&)aNew );
308 [ # # ][ # # ]: 0 : pDoc->ChgPageDesc(aNew.GetName(), aNew);
309 : 0 : }
310 : :
311 : 8 : SwRewriter SwUndoPageDesc::GetRewriter() const
312 : : {
313 : 8 : SwRewriter aResult;
314 : :
315 [ + - ][ + - ]: 8 : aResult.AddRule(UndoArg1, aOld.GetName());
316 [ + - ][ + - ]: 8 : aResult.AddRule(UndoArg2, SW_RESSTR(STR_YIELDS));
[ + - ][ + - ]
317 [ + - ][ + - ]: 8 : aResult.AddRule(UndoArg3, aNew.GetName());
318 : :
319 : 8 : return aResult;
320 : : }
321 : :
322 : : // #116530#
323 : 4 : SwUndoPageDescCreate::SwUndoPageDescCreate(const SwPageDesc * pNew,
324 : : SwDoc * _pDoc)
325 : : : SwUndo(UNDO_CREATE_PAGEDESC), pDesc(pNew), aNew(*pNew, _pDoc),
326 [ + - ]: 4 : pDoc(_pDoc)
327 : : {
328 : : OSL_ENSURE(0 != pDoc, "no document?");
329 : 4 : }
330 : :
331 [ + - ]: 4 : SwUndoPageDescCreate::~SwUndoPageDescCreate()
332 : : {
333 [ - + ]: 8 : }
334 : :
335 : 0 : void SwUndoPageDescCreate::UndoImpl(::sw::UndoRedoContext &)
336 : : {
337 : : // -> #116530#
338 [ # # ]: 0 : if (pDesc)
339 : : {
340 : 0 : aNew = *pDesc;
341 : 0 : pDesc = NULL;
342 : : }
343 : : // <- #116530#
344 : :
345 : 0 : pDoc->DelPageDesc(aNew.GetName(), sal_True);
346 : 0 : }
347 : :
348 : 0 : void SwUndoPageDescCreate::DoImpl()
349 : : {
350 [ # # ]: 0 : SwPageDesc aPageDesc = aNew;
351 [ # # ][ # # ]: 0 : pDoc->MakePageDesc(aNew.GetName(), &aPageDesc, sal_False, sal_True); // #116530#
[ # # ]
352 : 0 : }
353 : :
354 : 0 : void SwUndoPageDescCreate::RedoImpl(::sw::UndoRedoContext &)
355 : : {
356 : 0 : DoImpl();
357 : 0 : }
358 : :
359 : 0 : void SwUndoPageDescCreate::RepeatImpl(::sw::RepeatContext &)
360 : : {
361 [ # # ][ # # ]: 0 : ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
362 [ # # ][ # # ]: 0 : DoImpl();
363 : 0 : }
364 : :
365 : 4 : SwRewriter SwUndoPageDescCreate::GetRewriter() const
366 : : {
367 : 4 : SwRewriter aResult;
368 : :
369 [ + - ]: 4 : if (pDesc)
370 [ + - ]: 4 : aResult.AddRule(UndoArg1, pDesc->GetName());
371 : : else
372 [ # # ][ # # ]: 0 : aResult.AddRule(UndoArg1, aNew.GetName());
373 : :
374 : :
375 : 4 : return aResult;
376 : : }
377 : :
378 : 0 : SwUndoPageDescDelete::SwUndoPageDescDelete(const SwPageDesc & _aOld,
379 : : SwDoc * _pDoc)
380 [ # # ]: 0 : : SwUndo(UNDO_DELETE_PAGEDESC), aOld(_aOld, _pDoc), pDoc(_pDoc)
381 : : {
382 : : OSL_ENSURE(0 != pDoc, "no document?");
383 : 0 : }
384 : :
385 [ # # ]: 0 : SwUndoPageDescDelete::~SwUndoPageDescDelete()
386 : : {
387 [ # # ]: 0 : }
388 : :
389 : 0 : void SwUndoPageDescDelete::UndoImpl(::sw::UndoRedoContext &)
390 : : {
391 [ # # ]: 0 : SwPageDesc aPageDesc = aOld;
392 [ # # ][ # # ]: 0 : pDoc->MakePageDesc(aOld.GetName(), &aPageDesc, sal_False, sal_True); // #116530#
[ # # ]
393 : 0 : }
394 : :
395 : 0 : void SwUndoPageDescDelete::DoImpl()
396 : : {
397 : 0 : pDoc->DelPageDesc(aOld.GetName(), sal_True); // #116530#
398 : 0 : }
399 : :
400 : 0 : void SwUndoPageDescDelete::RedoImpl(::sw::UndoRedoContext &)
401 : : {
402 : 0 : DoImpl();
403 : 0 : }
404 : :
405 : 0 : void SwUndoPageDescDelete::RepeatImpl(::sw::RepeatContext &)
406 : : {
407 [ # # ][ # # ]: 0 : ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
408 [ # # ][ # # ]: 0 : DoImpl();
409 : 0 : }
410 : :
411 : 0 : SwRewriter SwUndoPageDescDelete::GetRewriter() const
412 : : {
413 : 0 : SwRewriter aResult;
414 : :
415 [ # # ][ # # ]: 0 : aResult.AddRule(UndoArg1, aOld.GetName());
416 : :
417 : 0 : return aResult;
418 : : }
419 : :
420 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|