diff options
Diffstat (limited to 'sw/source/core/attr/format.cxx')
-rw-r--r-- | sw/source/core/attr/format.cxx | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/sw/source/core/attr/format.cxx b/sw/source/core/attr/format.cxx new file mode 100644 index 000000000000..d15daee10f9a --- /dev/null +++ b/sw/source/core/attr/format.cxx @@ -0,0 +1,677 @@ +/* -*- 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 <hintids.hxx> // fuer RES_.. +#include <frame.hxx> // fuer AttrCache +#include <format.hxx> +#include <hints.hxx> // fuer SwFmtChg +#include <doc.hxx> +#include <paratr.hxx> // fuer SwParaFmt - SwHyphenBug +#include <swcache.hxx> +#include <fmtcolfunc.hxx> + +TYPEINIT1( SwFmt, SwClient ); //rtti fuer SwFmt + +/************************************************************************* +|* SwFmt::SwFmt +*************************************************************************/ + + +SwFmt::SwFmt( SwAttrPool& rPool, const sal_Char* pFmtNm, + const sal_uInt16* pWhichRanges, SwFmt *pDrvdFrm, sal_uInt16 nFmtWhich ) + : SwModify( pDrvdFrm ), + aSet( rPool, pWhichRanges ), + nWhichId( nFmtWhich ), + nFmtId( 0 ), + nPoolFmtId( USHRT_MAX ), + nPoolHelpId( USHRT_MAX ), + nPoolHlpFileId( UCHAR_MAX ) +{ + aFmtName.AssignAscii( pFmtNm ); + bWritten = bFmtInDTOR = bAutoUpdateFmt = sal_False; // LAYER_IMPL + bAutoFmt = sal_True; + + if( pDrvdFrm ) + aSet.SetParent( &pDrvdFrm->aSet ); +} + + +SwFmt::SwFmt( SwAttrPool& rPool, const String &rFmtNm, + const sal_uInt16* pWhichRanges, SwFmt *pDrvdFrm, sal_uInt16 nFmtWhich ) + : SwModify( pDrvdFrm ), + aFmtName( rFmtNm ), + aSet( rPool, pWhichRanges ), + nWhichId( nFmtWhich ), + nFmtId( 0 ), + nPoolFmtId( USHRT_MAX ), + nPoolHelpId( USHRT_MAX ), + nPoolHlpFileId( UCHAR_MAX ) +{ + bWritten = bFmtInDTOR = bAutoUpdateFmt = sal_False; // LAYER_IMPL + bAutoFmt = sal_True; + + if( pDrvdFrm ) + aSet.SetParent( &pDrvdFrm->aSet ); +} + + +SwFmt::SwFmt( const SwFmt& rFmt ) + : SwModify( rFmt.DerivedFrom() ), + aFmtName( rFmt.aFmtName ), + aSet( rFmt.aSet ), + nWhichId( rFmt.nWhichId ), + nFmtId( 0 ), + nPoolFmtId( rFmt.GetPoolFmtId() ), + nPoolHelpId( rFmt.GetPoolHelpId() ), + nPoolHlpFileId( rFmt.GetPoolHlpFileId() ) +{ + bWritten = bFmtInDTOR = sal_False; // LAYER_IMPL + bAutoFmt = rFmt.bAutoFmt; + bAutoUpdateFmt = rFmt.bAutoUpdateFmt; + + if( rFmt.DerivedFrom() ) + aSet.SetParent( &rFmt.DerivedFrom()->aSet ); + // einige Sonderbehandlungen fuer Attribute + aSet.SetModifyAtAttr( this ); +} + +/************************************************************************* +|* SwFmt &SwFmt::operator=(const SwFmt& aFmt) +|* +|* Beschreibung Dokument 1.14 +*************************************************************************/ + + +SwFmt &SwFmt::operator=(const SwFmt& rFmt) +{ + nWhichId = rFmt.nWhichId; + nPoolFmtId = rFmt.GetPoolFmtId(); + nPoolHelpId = rFmt.GetPoolHelpId(); + nPoolHlpFileId = rFmt.GetPoolHlpFileId(); + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + // kopiere nur das Attribut-Delta Array + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + aSet.Intersect_BC( rFmt.aSet, &aOld, &aNew ); + aSet.Put_BC( rFmt.aSet, &aOld, &aNew ); + + // einige Sonderbehandlungen fuer Attribute + aSet.SetModifyAtAttr( this ); + + // PoolItem-Attr erzeugen fuers Modify !!! + if( aOld.Count() ) + { + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + + if( GetRegisteredIn() != rFmt.GetRegisteredIn() ) + { + if( GetRegisteredIn() ) + GetRegisteredInNonConst()->Remove(this); + if(rFmt.GetRegisteredIn()) + { + const_cast<SwFmt&>(rFmt).GetRegisteredInNonConst()->Add(this); + aSet.SetParent( &rFmt.aSet ); + } + else + aSet.SetParent( 0 ); + } + bAutoFmt = rFmt.bAutoFmt; + bAutoUpdateFmt = rFmt.bAutoUpdateFmt; + return *this; +} + +void SwFmt::SetName( const String& rNewName, sal_Bool bBroadcast ) +{ + OSL_ENSURE(!IsDefault(), "SetName: Defaultformat" ); + if( bBroadcast ) + { + SwStringMsgPoolItem aOld( RES_NAME_CHANGED, aFmtName ); + SwStringMsgPoolItem aNew( RES_NAME_CHANGED, rNewName ); + aFmtName = rNewName; + ModifyNotification( &aOld, &aNew ); + } + else + { + aFmtName = rNewName; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * diese Funktion wird in jedem Copy-Ctor gerufen, damit die + * Attribute kopiert werden. Diese koennen erst kopiert werden, + * wenn die abgeleitet Klasse existiert, denn beim Setzen der + * Attribute wird die Which()-Methode gerufen, die hier in der + * Basis-Klasse auf 0 defaultet ist. + * + * Zusatz: JP 8.4.1994 + * Wird ueber Dokumentgrenzen kopiert, so muss das neue Dokument + * mit angeben werden, in dem this steht. Z.Z. ist das fuers + * DropCaps wichtig, dieses haelt Daten, die tief kopiert werden + * muessen !! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +void SwFmt::CopyAttrs( const SwFmt& rFmt, sal_Bool bReplace ) +{ + // kopiere nur das Attribut-Delta Array + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + // Sonderbehandlung fuer einige Attribute + SwAttrSet* pChgSet = (SwAttrSet*)&rFmt.aSet; + + if( !bReplace ) // nur die neu, die nicht gesetzt sind ?? + { + if( pChgSet == (SwAttrSet*)&rFmt.aSet ) // Set hier kopieren + pChgSet = new SwAttrSet( rFmt.aSet ); + pChgSet->Differentiate( aSet ); + } + + // kopiere nur das Attribut-Delta Array + if( pChgSet->GetPool() != aSet.GetPool() ) + pChgSet->CopyToModify( *this ); + else + { + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + + if ( 0 != aSet.Put_BC( *pChgSet, &aOld, &aNew ) ) + { + // einige Sonderbehandlungen fuer Attribute + aSet.SetModifyAtAttr( this ); + + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + } + + if( pChgSet != (SwAttrSet*)&rFmt.aSet ) // Set hier angelegt ? + delete pChgSet; +} + +/************************************************************************* +|* SwFmt::~SwFmt() +|* +|* Beschreibung Dokument 1.14 +*************************************************************************/ + + +SwFmt::~SwFmt() +{ + /* das passiert bei der ObjectDying Message */ + /* alle Abhaengigen auf DerivedFrom umhaengen */ + if( GetDepends() ) + { + OSL_ENSURE(DerivedFrom(), "SwFmt::~SwFmt: Def Abhaengige!" ); + + bFmtInDTOR = sal_True; + + SwFmt *pParentFmt = DerivedFrom(); + if (!pParentFmt) + { + OSL_FAIL( "~SwFmt: parent format missing" ); + } + else + { + while( GetDepends() ) + { + SwFmtChg aOldFmt(this); + SwFmtChg aNewFmt(pParentFmt); + SwClient * pDepend = (SwClient*)GetDepends(); + pParentFmt->Add(pDepend); + pDepend->ModifyNotification(&aOldFmt, &aNewFmt); + } + } + } +} + + +/************************************************************************* +|* void SwFmt::Modify( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue ) +|* +|* Beschreibung Dokument 1.14 +*************************************************************************/ + + +void SwFmt::Modify( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue ) +{ + sal_Bool bWeiter = sal_True; // sal_True = Propagierung an die Abhaengigen + + sal_uInt16 nWhich = pOldValue ? pOldValue->Which() : + pNewValue ? pNewValue->Which() : 0 ; + switch( nWhich ) + { + case 0: break; // Which-Id von 0 ??? + + case RES_OBJECTDYING : + { + // ist das sterbende Object das "Parent"-Format von diesen Format, + // dann haengt sich dieses Format an den Parent vom Parent + SwFmt * pFmt = (SwFmt *) ((SwPtrMsgPoolItem *)pNewValue)->pObject; + + // nicht umhaengen wenn dieses das oberste Format ist !! + if( GetRegisteredIn() && GetRegisteredIn() == pFmt ) + { + if( pFmt->GetRegisteredIn() ) + { + // wenn Parent, dann im neuen Parent wieder anmelden + pFmt->DerivedFrom()->Add( this ); + aSet.SetParent( &DerivedFrom()->aSet ); + } + else + { + // sonst auf jeden Fall beim sterbenden abmelden + DerivedFrom()->Remove( this ); + aSet.SetParent( 0 ); + } + } + } // OBJECTDYING + break; + + case RES_ATTRSET_CHG: + if( ((SwAttrSetChg*)pOldValue)->GetTheChgdSet() != &aSet ) + { + //nur die weiter geben, die hier nicht gesetzt sind !! + SwAttrSetChg aOld( *(SwAttrSetChg*)pOldValue ); + SwAttrSetChg aNew( *(SwAttrSetChg*)pNewValue ); + + aOld.GetChgSet()->Differentiate( aSet ); + aNew.GetChgSet()->Differentiate( aSet ); + + if( aNew.Count() ) + // keine mehr gesetzt, dann Ende !! + NotifyClients( &aOld, &aNew ); + bWeiter = sal_False; + } + break; + case RES_FMT_CHG: + // falls mein Format Parent umgesetzt wird, dann melde ich + // meinen Attrset beim Neuen an. + + // sein eigenes Modify ueberspringen !! + if( ((SwFmtChg*)pOldValue)->pChangedFmt != this && + ((SwFmtChg*)pNewValue)->pChangedFmt == DerivedFrom() ) + { + // den Set an den neuen Parent haengen + aSet.SetParent( DerivedFrom() ? &DerivedFrom()->aSet : 0 ); + } + break; + + case RES_RESET_FMTWRITTEN: + { + // IsWritten-Flag zuruecksetzen. Hint nur an abhanegige + // Formate (und keine Frames) propagieren. + // mba: the code does the opposite from what is written in the comment! + ResetWritten(); + // mba: here we don't use the additional stuff from NotifyClients(). + // should we?! + // mba: move the code that ignores this event to the clients + ModifyBroadcast( pOldValue, pNewValue, TYPE(SwFmt) ); + bWeiter = sal_False; + } + break; + + default: + { + // Ist das Attribut in diesem Format definiert, dann auf + // NICHT weiter propagieren !! + if( SFX_ITEM_SET == aSet.GetItemState( nWhich, sal_False )) + { +// wie finde ich heraus, ob nicht ich die Message versende ?? +// aber wer ruft das hier ???? +// OSL_ENSURE( FALSE, "Modify ohne Absender verschickt" ); +//JP 11.06.96: DropCaps koennen hierher kommen + OSL_ENSURE( RES_PARATR_DROP == nWhich, "Modify ohne Absender verschickt" ); + bWeiter = sal_False; + } + + } // default + } // switch + + if( bWeiter ) + { + // laufe durch alle abhaengigen Formate + NotifyClients( pOldValue, pNewValue ); + } + +} + + +sal_Bool SwFmt::SetDerivedFrom(SwFmt *pDerFrom) +{ + if ( pDerFrom ) + { + // Zyklus? + const SwFmt* pFmt = pDerFrom; + while ( pFmt != 0 ) + { + if ( pFmt == this ) + return sal_False; + + pFmt=pFmt->DerivedFrom(); + } + } + else + { + // Nichts angegeben, Dflt-Format suchen + pDerFrom = this; + while ( pDerFrom->DerivedFrom() ) + pDerFrom = pDerFrom->DerivedFrom(); + } + if ( (pDerFrom == DerivedFrom()) || (pDerFrom == this) ) + return sal_False; + + OSL_ENSURE( Which()==pDerFrom->Which() + || ( Which()==RES_CONDTXTFMTCOLL && pDerFrom->Which()==RES_TXTFMTCOLL) + || ( Which()==RES_TXTFMTCOLL && pDerFrom->Which()==RES_CONDTXTFMTCOLL) + || ( Which()==RES_FLYFRMFMT && pDerFrom->Which()==RES_FRMFMT ), + "SetDerivedFrom: Aepfel von Birnen ableiten?"); + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + pDerFrom->Add(this); + aSet.SetParent( &pDerFrom->aSet ); + + SwFmtChg aOldFmt(this); + SwFmtChg aNewFmt(this); + ModifyNotification( &aOldFmt, &aNewFmt ); + + return sal_True; +} + + +sal_Bool SwFmt::SetFmtAttr(const SfxPoolItem& rAttr ) +{ + if ( IsInCache() || IsInSwFntCache() ) + { + const sal_uInt16 nWhich = rAttr.Which(); + CheckCaching( nWhich ); + } + + // wenn Modify gelockt ist, werden keine Modifies verschickt; + // fuer FrmFmt's immer das Modify verschicken! + sal_Bool bRet = sal_False; + const sal_uInt16 nFmtWhich = Which(); + if( IsModifyLocked() || (!GetDepends() && + (RES_GRFFMTCOLL == nFmtWhich || + RES_TXTFMTCOLL == nFmtWhich ) ) ) + { + if( 0 != ( bRet = (0 != aSet.Put( rAttr ))) ) + aSet.SetModifyAtAttr( this ); + // #i71574# + if ( nFmtWhich == RES_TXTFMTCOLL && rAttr.Which() == RES_PARATR_NUMRULE ) + { + TxtFmtCollFunc::CheckTxtFmtCollForDeletionOfAssignmentToOutlineStyle( this ); + } + } + else + { + // kopiere nur das Attribut-Delta Array + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + + bRet = 0 != aSet.Put_BC( rAttr, &aOld, &aNew ); + if( bRet ) + { + // einige Sonderbehandlungen fuer Attribute + aSet.SetModifyAtAttr( this ); + + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + } + return bRet; +} + + +sal_Bool SwFmt::SetFmtAttr( const SfxItemSet& rSet ) +{ + if( !rSet.Count() ) + return sal_False; + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + // wenn Modify gelockt ist, werden keine Modifies verschickt; + // fuer FrmFmt's immer das Modify verschicken! + sal_Bool bRet = sal_False; + const sal_uInt16 nFmtWhich = Which(); + if ( IsModifyLocked() || + ( !GetDepends() && + ( RES_GRFFMTCOLL == nFmtWhich || + RES_TXTFMTCOLL == nFmtWhich ) ) ) + { + if( 0 != ( bRet = (0 != aSet.Put( rSet ))) ) + aSet.SetModifyAtAttr( this ); + // #i71574# + if ( nFmtWhich == RES_TXTFMTCOLL ) + { + TxtFmtCollFunc::CheckTxtFmtCollForDeletionOfAssignmentToOutlineStyle( this ); + } + } + else + { + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + bRet = 0 != aSet.Put_BC( rSet, &aOld, &aNew ); + if( bRet ) + { + // einige Sonderbehandlungen fuer Attribute + aSet.SetModifyAtAttr( this ); + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + } + return bRet; +} + +// Nimmt den Hint mit nWhich aus dem Delta-Array + + +sal_Bool SwFmt::ResetFmtAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 ) +{ + if( !aSet.Count() ) + return sal_False; + + if( !nWhich2 || nWhich2 < nWhich1 ) + nWhich2 = nWhich1; // dann setze auf 1. Id, nur dieses Item + + if ( IsInCache() || IsInSwFntCache() ) + { + for( sal_uInt16 n = nWhich1; n < nWhich2; ++n ) + CheckCaching( n ); + } + + // wenn Modify gelockt ist, werden keine Modifies verschickt + if( IsModifyLocked() ) + return 0 != (( nWhich2 == nWhich1 ) + ? aSet.ClearItem( nWhich1 ) + : aSet.ClearItem_BC( nWhich1, nWhich2 )); + + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + sal_Bool bRet = 0 != aSet.ClearItem_BC( nWhich1, nWhich2, &aOld, &aNew ); + + if( bRet ) + { + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + return bRet; +} + + + +// #i73790# +// method renamed +sal_uInt16 SwFmt::ResetAllFmtAttr() +{ + if( !aSet.Count() ) + return 0; + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + // wenn Modify gelockt ist, werden keine Modifies verschickt + if( IsModifyLocked() ) + return aSet.ClearItem( 0 ); + + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + sal_Bool bRet = 0 != aSet.ClearItem_BC( 0, &aOld, &aNew ); + + if( bRet ) + { + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } + return aNew.Count(); +} + + +/************************************************************************* +|* void SwFmt::GetInfo( const SfxPoolItem& ) const +*************************************************************************/ + + +sal_Bool SwFmt::GetInfo( SfxPoolItem& rInfo ) const +{ + sal_Bool bRet = SwModify::GetInfo( rInfo ); + return bRet; +} + + +void SwFmt::DelDiffs( const SfxItemSet& rSet ) +{ + if( !aSet.Count() ) + return; + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( sal_False ); + } + SetInSwFntCache( sal_False ); + + // wenn Modify gelockt ist, werden keine Modifies verschickt + if( IsModifyLocked() ) + { + aSet.Intersect( rSet ); + return; + } + + SwAttrSet aOld( *aSet.GetPool(), aSet.GetRanges() ), + aNew( *aSet.GetPool(), aSet.GetRanges() ); + sal_Bool bRet = 0 != aSet.Intersect_BC( rSet, &aOld, &aNew ); + + if( bRet ) + { + SwAttrSetChg aChgOld( aSet, aOld ); + SwAttrSetChg aChgNew( aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // alle veraenderten werden verschickt + } +} + +/** SwFmt::IsBackgroundTransparent + + Virtual method to determine, if background of format is transparent. + Default implementation returns false. Thus, subclasses have to overload + method, if the specific subclass can have a transparent background. + + @author OD + + @return false, default implementation +*/ +sal_Bool SwFmt::IsBackgroundTransparent() const +{ + return sal_False; +} + +/** SwFmt::IsShadowTransparent + + Virtual method to determine, if shadow of format is transparent. + Default implementation returns false. Thus, subclasses have to overload + method, if the specific subclass can have a transparent shadow. + + @return false, default implementation +*/ +sal_Bool SwFmt::IsShadowTransparent() const +{ + return sal_False; +} + +/* + * Document Interface Access + */ +const IDocumentSettingAccess* SwFmt::getIDocumentSettingAccess() const { return GetDoc(); } +const IDocumentDrawModelAccess* SwFmt::getIDocumentDrawModelAccess() const { return GetDoc(); } +IDocumentDrawModelAccess* SwFmt::getIDocumentDrawModelAccess() { return GetDoc(); } +const IDocumentLayoutAccess* SwFmt::getIDocumentLayoutAccess() const { return GetDoc(); } +IDocumentLayoutAccess* SwFmt::getIDocumentLayoutAccess() { return GetDoc(); } +IDocumentTimerAccess* SwFmt::getIDocumentTimerAccess() { return GetDoc(); } +IDocumentFieldsAccess* SwFmt::getIDocumentFieldsAccess() { return GetDoc(); } +IDocumentChartDataProviderAccess* SwFmt::getIDocumentChartDataProviderAccess() { return GetDoc(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |