summaryrefslogtreecommitdiff
path: root/sw/source/core/doc/docftn.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/doc/docftn.cxx')
-rw-r--r--sw/source/core/doc/docftn.cxx529
1 files changed, 529 insertions, 0 deletions
diff --git a/sw/source/core/doc/docftn.cxx b/sw/source/core/doc/docftn.cxx
new file mode 100644
index 000000000000..8d7c8523aa46
--- /dev/null
+++ b/sw/source/core/doc/docftn.cxx
@@ -0,0 +1,529 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sw.hxx"
+
+
+#include <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <pam.hxx>
+#include <pagedesc.hxx>
+#include <charfmt.hxx>
+#include <UndoAttribute.hxx>
+#include <hints.hxx>
+#include <rolbck.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <ftninfo.hxx>
+
+/*********************** SwFtnInfo ***************************/
+
+SwEndNoteInfo& SwEndNoteInfo::operator=(const SwEndNoteInfo& rInfo)
+{
+ if( rInfo.GetFtnTxtColl() )
+ rInfo.GetFtnTxtColl()->Add(this);
+ else if ( GetRegisteredIn())
+ GetRegisteredInNonConst()->Remove(this);
+
+ if ( rInfo.aPageDescDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aPageDescDep.GetRegisteredIn())->Add( &aPageDescDep );
+ else if ( aPageDescDep.GetRegisteredIn() )
+ ((SwModify*)aPageDescDep.GetRegisteredIn())->Remove( &aPageDescDep );
+
+ if ( rInfo.aCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aCharFmtDep.GetRegisteredIn())->Add( &aCharFmtDep );
+ else if ( aCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)aCharFmtDep.GetRegisteredIn())->Remove( &aCharFmtDep );
+
+ if ( rInfo.aAnchorCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aAnchorCharFmtDep.GetRegisteredIn())->Add(
+ &aAnchorCharFmtDep );
+ else if( aAnchorCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)aAnchorCharFmtDep.GetRegisteredIn())->Remove(
+ &aAnchorCharFmtDep );
+
+ aFmt = rInfo.aFmt;
+ nFtnOffset = rInfo.nFtnOffset;
+ m_bEndNote = rInfo.m_bEndNote;
+ sPrefix = rInfo.sPrefix;
+ sSuffix = rInfo.sSuffix;
+ return *this;
+}
+
+
+sal_Bool SwEndNoteInfo::operator==( const SwEndNoteInfo& rInfo ) const
+{
+ return aPageDescDep.GetRegisteredIn() ==
+ rInfo.aPageDescDep.GetRegisteredIn() &&
+ aCharFmtDep.GetRegisteredIn() ==
+ rInfo.aCharFmtDep.GetRegisteredIn() &&
+ aAnchorCharFmtDep.GetRegisteredIn() ==
+ rInfo.aAnchorCharFmtDep.GetRegisteredIn() &&
+ GetFtnTxtColl() == rInfo.GetFtnTxtColl() &&
+ aFmt.GetNumberingType() == rInfo.aFmt.GetNumberingType() &&
+ nFtnOffset == rInfo.nFtnOffset &&
+ m_bEndNote == rInfo.m_bEndNote &&
+ sPrefix == rInfo.sPrefix &&
+ sSuffix == rInfo.sSuffix;
+}
+
+
+SwEndNoteInfo::SwEndNoteInfo(const SwEndNoteInfo& rInfo) :
+ SwClient( rInfo.GetFtnTxtColl() ),
+ aPageDescDep( this, 0 ),
+ aCharFmtDep( this, 0 ),
+ aAnchorCharFmtDep( this, 0 ),
+ sPrefix( rInfo.sPrefix ),
+ sSuffix( rInfo.sSuffix ),
+ m_bEndNote( true ),
+ aFmt( rInfo.aFmt ),
+ nFtnOffset( rInfo.nFtnOffset )
+{
+ if( rInfo.aPageDescDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aPageDescDep.GetRegisteredIn())->Add( &aPageDescDep );
+
+ if( rInfo.aCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aCharFmtDep.GetRegisteredIn())->Add( &aCharFmtDep );
+
+ if( rInfo.aAnchorCharFmtDep.GetRegisteredIn() )
+ ((SwModify*)rInfo.aAnchorCharFmtDep.GetRegisteredIn())->Add(
+ &aAnchorCharFmtDep );
+}
+
+SwEndNoteInfo::SwEndNoteInfo(SwTxtFmtColl *pFmt) :
+ SwClient(pFmt),
+ aPageDescDep( this, 0 ),
+ aCharFmtDep( this, 0 ),
+ aAnchorCharFmtDep( this, 0 ),
+ m_bEndNote( true ),
+ nFtnOffset( 0 )
+{
+ aFmt.SetNumberingType(SVX_NUM_ROMAN_LOWER);
+}
+
+SwPageDesc *SwEndNoteInfo::GetPageDesc( SwDoc &rDoc ) const
+{
+ if ( !aPageDescDep.GetRegisteredIn() )
+ {
+ SwPageDesc *pDesc = rDoc.GetPageDescFromPool( static_cast<sal_uInt16>(
+ m_bEndNote ? RES_POOLPAGE_ENDNOTE : RES_POOLPAGE_FOOTNOTE ) );
+ pDesc->Add( &((SwClient&)aPageDescDep) );
+ }
+
+ return (SwPageDesc*)( aPageDescDep.GetRegisteredIn() );
+}
+
+bool SwEndNoteInfo::KnowsPageDesc() const
+{
+ return (aPageDescDep.GetRegisteredIn() != 0);
+}
+
+bool SwEndNoteInfo::DependsOn( const SwPageDesc* pDesc ) const
+{
+ return ( aPageDescDep.GetRegisteredIn() == pDesc );
+}
+
+void SwEndNoteInfo::ChgPageDesc( SwPageDesc *pDesc )
+{
+ pDesc->Add( &((SwClient&)aPageDescDep) );
+}
+
+void SwEndNoteInfo::SetFtnTxtColl(SwTxtFmtColl& rFmt)
+{
+ rFmt.Add(this);
+}
+
+SwCharFmt* SwEndNoteInfo::GetCharFmt(SwDoc &rDoc) const
+{
+ if ( !aCharFmtDep.GetRegisteredIn() )
+ {
+ SwCharFmt* pFmt = rDoc.GetCharFmtFromPool( static_cast<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE ) );
+ pFmt->Add( &((SwClient&)aCharFmtDep) );
+ }
+ return (SwCharFmt*)aCharFmtDep.GetRegisteredIn();
+}
+
+void SwEndNoteInfo::SetCharFmt( SwCharFmt* pChFmt )
+{
+ DBG_ASSERT(pChFmt, "kein CharFmt?");
+ pChFmt->Add( &((SwClient&)aCharFmtDep) );
+}
+
+SwCharFmt* SwEndNoteInfo::GetAnchorCharFmt(SwDoc &rDoc) const
+{
+ if( !aAnchorCharFmtDep.GetRegisteredIn() )
+ {
+ SwCharFmt* pFmt = rDoc.GetCharFmtFromPool( static_cast<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE_ANCHOR : RES_POOLCHR_FOOTNOTE_ANCHOR ) );
+ pFmt->Add( &((SwClient&)aAnchorCharFmtDep) );
+ }
+ return (SwCharFmt*)aAnchorCharFmtDep.GetRegisteredIn();
+}
+
+void SwEndNoteInfo::SetAnchorCharFmt( SwCharFmt* pChFmt )
+{
+ DBG_ASSERT(pChFmt, "kein CharFmt?");
+ pChFmt->Add( &((SwClient&)aAnchorCharFmtDep) );
+}
+
+void SwEndNoteInfo::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
+{
+ sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ;
+
+ if( RES_ATTRSET_CHG == nWhich ||
+ RES_FMT_CHG == nWhich )
+ {
+ SwDoc* pDoc;
+ if( aCharFmtDep.GetRegisteredIn() )
+ pDoc = ((SwCharFmt*)aCharFmtDep.GetRegisteredIn())->GetDoc();
+ else
+ pDoc = ((SwCharFmt*)aAnchorCharFmtDep.GetRegisteredIn())->GetDoc();
+ SwFtnIdxs& rFtnIdxs = pDoc->GetFtnIdxs();
+ for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
+ {
+ SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
+ const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
+ if ( rFtn.IsEndNote() == m_bEndNote )
+ {
+ pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
+ }
+ }
+ }
+ else
+ CheckRegistration( pOld, pNew );
+}
+
+SwFtnInfo& SwFtnInfo::operator=(const SwFtnInfo& rInfo)
+{
+ SwEndNoteInfo::operator=(rInfo);
+ aQuoVadis = rInfo.aQuoVadis;
+ aErgoSum = rInfo.aErgoSum;
+ ePos = rInfo.ePos;
+ eNum = rInfo.eNum;
+ return *this;
+}
+
+
+sal_Bool SwFtnInfo::operator==( const SwFtnInfo& rInfo ) const
+{
+ return ePos == rInfo.ePos &&
+ eNum == rInfo.eNum &&
+ SwEndNoteInfo::operator==(rInfo) &&
+ aQuoVadis == rInfo.aQuoVadis &&
+ aErgoSum == rInfo.aErgoSum;
+}
+
+
+SwFtnInfo::SwFtnInfo(const SwFtnInfo& rInfo) :
+ SwEndNoteInfo( rInfo ),
+ aQuoVadis( rInfo.aQuoVadis ),
+ aErgoSum( rInfo.aErgoSum ),
+ ePos( rInfo.ePos ),
+ eNum( rInfo.eNum )
+{
+ m_bEndNote = false;
+}
+
+SwFtnInfo::SwFtnInfo(SwTxtFmtColl *pFmt) :
+ SwEndNoteInfo( pFmt ),
+ ePos( FTNPOS_PAGE ),
+ eNum( FTNNUM_DOC )
+{
+ aFmt.SetNumberingType(SVX_NUM_ARABIC);
+ m_bEndNote = false;
+}
+
+/*********************** SwDoc ***************************/
+
+
+void SwDoc::SetFtnInfo(const SwFtnInfo& rInfo)
+{
+ SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
+ if( !(GetFtnInfo() == rInfo) )
+ {
+ const SwFtnInfo &rOld = GetFtnInfo();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( new SwUndoFootNoteInfo(rOld) );
+ }
+
+ sal_Bool bFtnPos = rInfo.ePos != rOld.ePos;
+ sal_Bool bFtnDesc = rOld.ePos == FTNPOS_CHAPTER &&
+ rInfo.GetPageDesc( *this ) != rOld.GetPageDesc( *this );
+ sal_Bool bExtra = rInfo.aQuoVadis != rOld.aQuoVadis ||
+ rInfo.aErgoSum != rOld.aErgoSum ||
+ rInfo.aFmt.GetNumberingType() != rOld.aFmt.GetNumberingType() ||
+ rInfo.GetPrefix() != rOld.GetPrefix() ||
+ rInfo.GetSuffix() != rOld.GetSuffix();
+ SwCharFmt *pOldChrFmt = rOld.GetCharFmt( *this ),
+ *pNewChrFmt = rInfo.GetCharFmt( *this );
+ sal_Bool bFtnChrFmts = pOldChrFmt != pNewChrFmt;
+
+ *pFtnInfo = rInfo;
+
+ if (pTmpRoot)
+ {
+ std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();//swmod 080304
+ if ( bFtnPos )
+ //pTmpRoot->RemoveFtns();
+ std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::AllRemoveFtns));//swmod 080305
+ else
+ {
+ //pTmpRoot->UpdateFtnNums();
+ std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::UpdateFtnNums));//swmod 080304
+ if ( bFtnDesc )
+ //pTmpRoot->CheckFtnPageDescs( FALSE );
+ std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), sal_False));//swmod 080304
+ if ( bExtra )
+ {
+ //Fuer die Benachrichtung bezueglich ErgoSum usw. sparen wir uns
+ //extra-Code und nutzen die vorhandenen Wege.
+ SwFtnIdxs& rFtnIdxs = GetFtnIdxs();
+ for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
+ {
+ SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
+ const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
+ if ( !rFtn.IsEndNote() )
+ pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
+ }
+ }
+ }
+ } //swmod 080219
+ if( FTNNUM_PAGE != rInfo.eNum )
+ GetFtnIdxs().UpdateAllFtn();
+ else if( bFtnChrFmts )
+ {
+ SwFmtChg aOld( pOldChrFmt );
+ SwFmtChg aNew( pNewChrFmt );
+ pFtnInfo->ModifyNotification( &aOld, &aNew );
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ UpdateRefFlds(NULL);
+ }
+ SetModified();
+ }
+}
+
+void SwDoc::SetEndNoteInfo(const SwEndNoteInfo& rInfo)
+{
+ SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
+ if( !(GetEndNoteInfo() == rInfo) )
+ {
+ if(GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndo *const pUndo( new SwUndoEndNoteInfo( GetEndNoteInfo() ) );
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+
+ sal_Bool bNumChg = rInfo.nFtnOffset != GetEndNoteInfo().nFtnOffset;
+ // this seems to be an optimization: UpdateAllFtn() is only called
+ // if the offset changes; if the offset is the same,
+ // but type/prefix/suffix changes, just set new numbers.
+ bool const bExtra = !bNumChg &&
+ ( (rInfo.aFmt.GetNumberingType() !=
+ GetEndNoteInfo().aFmt.GetNumberingType())
+ || (rInfo.GetPrefix() != GetEndNoteInfo().GetPrefix())
+ || (rInfo.GetSuffix() != GetEndNoteInfo().GetSuffix())
+ );
+ sal_Bool bFtnDesc = rInfo.GetPageDesc( *this ) !=
+ GetEndNoteInfo().GetPageDesc( *this );
+ SwCharFmt *pOldChrFmt = GetEndNoteInfo().GetCharFmt( *this ),
+ *pNewChrFmt = rInfo.GetCharFmt( *this );
+ sal_Bool bFtnChrFmts = pOldChrFmt != pNewChrFmt;
+
+ *pEndNoteInfo = rInfo;
+
+ if ( pTmpRoot )
+ {
+ if ( bFtnDesc )
+ //pTmpRoot->CheckFtnPageDescs( TRUE );
+ {
+ std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
+ std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), sal_True));//swmod 080304
+ }
+ if ( bExtra )
+ {
+ //Fuer die Benachrichtung bezueglich ErgoSum usw. sparen wir uns
+ //extra-Code und nutzen die vorhandenen Wege.
+ SwFtnIdxs& rFtnIdxs = GetFtnIdxs();
+ for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
+ {
+ SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
+ const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
+ if ( rFtn.IsEndNote() )
+ pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
+ }
+ }
+ } //swmod 080219
+ if( bNumChg )
+ GetFtnIdxs().UpdateAllFtn();
+ else if( bFtnChrFmts )
+ {
+ SwFmtChg aOld( pOldChrFmt );
+ SwFmtChg aNew( pNewChrFmt );
+ pEndNoteInfo->ModifyNotification( &aOld, &aNew );
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ UpdateRefFlds(NULL);
+ }
+ SetModified();
+ }
+}
+
+
+bool SwDoc::SetCurFtn( const SwPaM& rPam, const String& rNumStr,
+ sal_uInt16 nNumber, bool bIsEndNote )
+{
+ SwFtnIdxs& rFtnArr = GetFtnIdxs();
+ SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
+
+ const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
+ const sal_uLong nSttNd = pStt->nNode.GetIndex();
+ const xub_StrLen nSttCnt = pStt->nContent.GetIndex();
+ const sal_uLong nEndNd = pEnd->nNode.GetIndex();
+ const xub_StrLen nEndCnt = pEnd->nContent.GetIndex();
+
+ sal_uInt16 nPos;
+ rFtnArr.SeekEntry( pStt->nNode, &nPos );
+
+ SwUndoChangeFootNote* pUndo = 0;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
+ pUndo = new SwUndoChangeFootNote( rPam, rNumStr, nNumber, bIsEndNote );
+ }
+
+ SwTxtFtn* pTxtFtn;
+ sal_uLong nIdx;
+ sal_Bool bChg = sal_False;
+ sal_Bool bTypeChgd = sal_False;
+ sal_uInt16 n = nPos; // sichern
+ while( nPos < rFtnArr.Count() &&
+ (( nIdx = _SwTxtFtn_GetIndex((pTxtFtn = rFtnArr[ nPos++ ] )))
+ < nEndNd || ( nIdx == nEndNd &&
+ nEndCnt >= *pTxtFtn->GetStart() )) )
+ if( nIdx > nSttNd || ( nIdx == nSttNd &&
+ nSttCnt <= *pTxtFtn->GetStart() ) )
+ {
+ const SwFmtFtn& rFtn = pTxtFtn->GetFtn();
+ if( rFtn.GetNumStr() != rNumStr ||
+ rFtn.IsEndNote() != bIsEndNote )
+ {
+ bChg = sal_True;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().Add( *pTxtFtn );
+ }
+
+ pTxtFtn->SetNumber( nNumber, &rNumStr );
+ if( rFtn.IsEndNote() != bIsEndNote )
+ {
+ ((SwFmtFtn&)rFtn).SetEndNote( bIsEndNote );
+ bTypeChgd = sal_True;
+ pTxtFtn->CheckCondColl();
+ //#i11339# dispose UNO wrapper when a footnote is changed to an endnote or vice versa
+ SwPtrMsgPoolItem aMsgHint( RES_FOOTNOTE_DELETED, (void*)&pTxtFtn->GetAttr() );
+ GetUnoCallBack()->ModifyNotification( &aMsgHint, &aMsgHint );
+ }
+ }
+ }
+
+ nPos = n; // nach vorne gibt es auch noch welche !
+ while( nPos &&
+ (( nIdx = _SwTxtFtn_GetIndex((pTxtFtn = rFtnArr[ --nPos ] )))
+ > nSttNd || ( nIdx == nSttNd &&
+ nSttCnt <= *pTxtFtn->GetStart() )) )
+ if( nIdx < nEndNd || ( nIdx == nEndNd &&
+ nEndCnt >= *pTxtFtn->GetStart() ) )
+ {
+ const SwFmtFtn& rFtn = pTxtFtn->GetFtn();
+ if( rFtn.GetNumStr() != rNumStr ||
+ rFtn.IsEndNote() != bIsEndNote )
+ {
+ bChg = sal_True;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().Add( *pTxtFtn );
+ }
+
+ pTxtFtn->SetNumber( nNumber, &rNumStr );
+ if( rFtn.IsEndNote() != bIsEndNote )
+ {
+ ((SwFmtFtn&)rFtn).SetEndNote( bIsEndNote );
+ bTypeChgd = sal_True;
+ pTxtFtn->CheckCondColl();
+ }
+ }
+ }
+
+ // wer muss angestossen werden ??
+ if( bChg )
+ {
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+
+ if ( bTypeChgd )
+ rFtnArr.UpdateAllFtn();
+ if( FTNNUM_PAGE != GetFtnInfo().eNum )
+ {
+ if ( !bTypeChgd )
+ rFtnArr.UpdateAllFtn();
+ }
+ else if( pTmpRoot )
+ //
+ {
+ std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
+ std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::UpdateFtnNums));
+ } //swmod 080304pTmpRoot->UpdateFtnNums(); //swmod 080219
+ SetModified();
+ }
+ else
+ delete pUndo;
+ return bChg;
+}
+
+
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */