1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/poly.hxx>
21 #include <vcl/metric.hxx>
22 #include <vcl/svapp.hxx>
23 #include <svtools/colorcfg.hxx>
24 #include <svx/swframeexample.hxx>
25 #include <com/sun/star/text/TextContentAnchorType.hpp>
26 #include <com/sun/star/text/HoriOrientation.hpp>
27 #include <com/sun/star/text/VertOrientation.hpp>
28 #include <com/sun/star/text/RelOrientation.hpp>
29 #include <com/sun/star/text/WrapTextMode.hpp>
31 using namespace ::com::sun::star::text
;
33 #define FLYINFLY_BORDER 3
36 SvxSwFrameExample::SvxSwFrameExample( Window
*pParent
, const ResId
& rResID
) :
38 Window(pParent
, rResID
),
40 nHAlign (HoriOrientation::CENTER
),
41 nHRel (RelOrientation::FRAME
),
42 nVAlign (VertOrientation::TOP
),
43 nVRel (RelOrientation::PRINT_AREA
),
44 nWrap (WrapTextMode_NONE
),
45 nAnchor (TextContentAnchorType_AT_PAGE
),
50 SetMapMode(MAP_PIXEL
);
53 SvxSwFrameExample::~SvxSwFrameExample()
57 void SvxSwFrameExample::InitColors_Impl( void )
59 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
60 m_aBgCol
= Color( rSettings
.GetWindowColor() );
62 sal_Bool bHC
= rSettings
.GetHighContrastMode();
64 m_aFrameColor
= Color( COL_LIGHTGREEN
);
65 m_aAlignColor
= Color( COL_LIGHTRED
);
66 m_aTransColor
= Color( COL_TRANSPARENT
);
69 svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR
).nColor
:
71 m_aPrintAreaCol
= bHC
? m_aTxtCol
: Color( COL_GRAY
);
72 m_aBorderCol
= m_aTxtCol
;
73 m_aBlankCol
= bHC
? m_aTxtCol
: Color( COL_LIGHTGRAY
);
74 m_aBlankFrameCol
= bHC
? m_aTxtCol
: Color( COL_GRAY
);
77 void SvxSwFrameExample::DataChanged( const DataChangedEvent
& rDCEvt
)
79 Window::DataChanged( rDCEvt
);
81 if( rDCEvt
.GetType() == DATACHANGED_SETTINGS
&& ( rDCEvt
.GetFlags() & SETTINGS_STYLE
) )
85 void SvxSwFrameExample::InitAllRects_Impl()
87 aPage
.SetSize( GetOutputSizePixel() );
89 sal_uIntPtr nOutWPix
= aPage
.GetWidth();
90 sal_uIntPtr nOutHPix
= aPage
.GetHeight();
98 sal_uIntPtr nLTxtBorder
;
99 sal_uIntPtr nRTxtBorder
;
100 sal_uIntPtr nTTxtBorder
;
101 sal_uIntPtr nBTxtBorder
;
103 if (nAnchor
!= TextContentAnchorType_AS_CHARACTER
)
127 aPagePrtArea
= Rectangle(Point(nLBorder
, nTBorder
), Point((nOutWPix
- 1) - nRBorder
, (nOutHPix
- 1) - nBBorder
));
129 // Example text: Preparing for the text output
131 aTextLine
= aPagePrtArea
;
132 aTextLine
.SetSize(Size(aTextLine
.GetWidth(), 2));
133 aTextLine
.Left() += nLTxtBorder
;
134 aTextLine
.Right() -= nRTxtBorder
;
135 aTextLine
.Move(0, nTTxtBorder
);
137 // Rectangle to edges including paragraph
138 sal_uInt16 nLines
= (sal_uInt16
)((aPagePrtArea
.GetHeight() / 2 - nTTxtBorder
- nBTxtBorder
)
139 / (aTextLine
.GetHeight() + 2));
140 aPara
= aPagePrtArea
;
141 aPara
.SetSize(Size(aPara
.GetWidth(),
142 (aTextLine
.GetHeight() + 2) * nLines
+ nTTxtBorder
+ nBTxtBorder
));
144 // Rectangle around paragraph without borders
145 aParaPrtArea
= aPara
;
146 aParaPrtArea
.Left() += nLTxtBorder
;
147 aParaPrtArea
.Right() -= nRTxtBorder
;
148 aParaPrtArea
.Top() += nTTxtBorder
;
149 aParaPrtArea
.Bottom() -= nBTxtBorder
;
151 if (nAnchor
== TextContentAnchorType_AS_CHARACTER
|| nAnchor
== TextContentAnchorType_AT_CHARACTER
)
153 Font aFont
= OutputDevice::GetDefaultFont(
154 DEFAULTFONT_LATIN_TEXT
, Application::GetSettings().GetLanguageTag().getLanguageType(),
155 DEFAULTFONT_FLAGS_ONLYONE
, this );
156 aFont
.SetColor( m_aTxtCol
);
157 aFont
.SetFillColor( m_aBgCol
);
158 aFont
.SetWeight(WEIGHT_NORMAL
);
160 if (nAnchor
== TextContentAnchorType_AS_CHARACTER
)
162 aFont
.SetSize(Size(0, aParaPrtArea
.GetHeight() - 2));
164 aParaPrtArea
.SetSize(Size(GetTextWidth(OUString(DEMOTEXT
)), GetTextHeight()));
168 aFont
.SetSize(Size(0, aParaPrtArea
.GetHeight() / 2));
170 aAutoCharFrame
.SetSize(Size(GetTextWidth(OUString('A')), GetTextHeight()));
171 aAutoCharFrame
.SetPos(Point(aParaPrtArea
.Left() + (aParaPrtArea
.GetWidth() - aAutoCharFrame
.GetWidth()) / 2,
172 aParaPrtArea
.Top() + (aParaPrtArea
.GetHeight() - aAutoCharFrame
.GetHeight()) / 2));
176 // Inner Frame anchored at the Frame
177 aFrameAtFrame
= aPara
;
178 aFrameAtFrame
.Left() += 9;
179 aFrameAtFrame
.Right() -= 5;
180 aFrameAtFrame
.Bottom() += 5;
181 aFrameAtFrame
.SetPos(Point(aFrameAtFrame
.Left() + 2, (aPagePrtArea
.Bottom() - aFrameAtFrame
.GetHeight()) / 2 + 5));
183 // Size of the frame to be positioned
184 if (nAnchor
!= TextContentAnchorType_AS_CHARACTER
)
186 sal_uIntPtr nLFBorder
= nAnchor
== TextContentAnchorType_AT_PAGE
? nLBorder
: nLTxtBorder
;
187 sal_uIntPtr nRFBorder
= nAnchor
== TextContentAnchorType_AT_PAGE
? nRBorder
: nRTxtBorder
;
191 case RelOrientation::PAGE_LEFT
:
192 case RelOrientation::FRAME_LEFT
:
193 aFrmSize
= Size(nLFBorder
- 4, (aTextLine
.GetHeight() + 2) * 3);
196 case RelOrientation::PAGE_RIGHT
:
197 case RelOrientation::FRAME_RIGHT
:
198 aFrmSize
= Size(nRFBorder
- 4, (aTextLine
.GetHeight() + 2) * 3);
202 aFrmSize
= Size(nLBorder
- 3, (aTextLine
.GetHeight() + 2) * 3);
205 aFrmSize
.Width() = std::max(5L, aFrmSize
.Width());
206 aFrmSize
.Height() = std::max(5L, aFrmSize
.Height());
210 sal_uIntPtr nFreeWidth
= aPagePrtArea
.GetWidth() - GetTextWidth(OUString(DEMOTEXT
));
212 aFrmSize
= Size(nFreeWidth
/ 2, (aTextLine
.GetHeight() + 2) * 3);
213 aDrawObj
.SetSize(Size(std::max(5L, (long)nFreeWidth
/ 3L), std::max(5L, aFrmSize
.Height() * 3L)));
214 aDrawObj
.SetPos(Point(aParaPrtArea
.Right() + 1, aParaPrtArea
.Bottom() / 2));
215 aParaPrtArea
.Right() = aDrawObj
.Right();
219 void SvxSwFrameExample::CalcBoundRect_Impl(Rectangle
&rRect
)
223 case TextContentAnchorType_AT_PAGE
:
227 case RelOrientation::FRAME
:
228 case RelOrientation::PAGE_FRAME
:
229 rRect
.Left() = aPage
.Left();
230 rRect
.Right() = aPage
.Right();
233 case RelOrientation::PRINT_AREA
:
234 case RelOrientation::PAGE_PRINT_AREA
:
235 rRect
.Left() = aPagePrtArea
.Left();
236 rRect
.Right() = aPagePrtArea
.Right();
239 case RelOrientation::PAGE_LEFT
:
240 rRect
.Left() = aPage
.Left();
241 rRect
.Right() = aPagePrtArea
.Left();
244 case RelOrientation::PAGE_RIGHT
:
245 rRect
.Left() = aPagePrtArea
.Right();
246 rRect
.Right() = aPage
.Right();
252 case RelOrientation::PRINT_AREA
:
253 case RelOrientation::PAGE_PRINT_AREA
:
254 rRect
.Top() = aPagePrtArea
.Top();
255 rRect
.Bottom() = aPagePrtArea
.Bottom();
258 case RelOrientation::FRAME
:
259 case RelOrientation::PAGE_FRAME
:
260 rRect
.Top() = aPage
.Top();
261 rRect
.Bottom() = aPage
.Bottom();
267 case TextContentAnchorType_AT_FRAME
:
271 case RelOrientation::FRAME
:
272 case RelOrientation::PAGE_FRAME
:
273 rRect
.Left() = aFrameAtFrame
.Left();
274 rRect
.Right() = aFrameAtFrame
.Right();
277 case RelOrientation::PRINT_AREA
:
278 case RelOrientation::PAGE_PRINT_AREA
:
279 rRect
.Left() = aFrameAtFrame
.Left() + FLYINFLY_BORDER
;
280 rRect
.Right() = aFrameAtFrame
.Right() - FLYINFLY_BORDER
;
283 case RelOrientation::PAGE_RIGHT
:
284 rRect
.Left() = aFrameAtFrame
.Left();
285 rRect
.Right() = aFrameAtFrame
.Left() + FLYINFLY_BORDER
;
288 case RelOrientation::PAGE_LEFT
:
289 rRect
.Left() = aFrameAtFrame
.Right();
290 rRect
.Right() = aFrameAtFrame
.Right() - FLYINFLY_BORDER
;
296 case RelOrientation::FRAME
:
297 case RelOrientation::PAGE_FRAME
:
298 rRect
.Top() = aFrameAtFrame
.Top();
299 rRect
.Bottom() = aFrameAtFrame
.Bottom();
302 case RelOrientation::PRINT_AREA
:
303 case RelOrientation::PAGE_PRINT_AREA
:
304 rRect
.Top() = aFrameAtFrame
.Top() + FLYINFLY_BORDER
;
305 rRect
.Bottom() = aFrameAtFrame
.Bottom() - FLYINFLY_BORDER
;
310 case TextContentAnchorType_AT_PARAGRAPH
:
311 case TextContentAnchorType_AT_CHARACTER
:
315 case RelOrientation::FRAME
:
316 rRect
.Left() = aPara
.Left();
317 rRect
.Right() = aPara
.Right();
320 case RelOrientation::PRINT_AREA
:
321 rRect
.Left() = aParaPrtArea
.Left();
322 rRect
.Right() = aParaPrtArea
.Right();
325 case RelOrientation::PAGE_LEFT
:
326 rRect
.Left() = aPage
.Left();
327 rRect
.Right() = aPagePrtArea
.Left();
330 case RelOrientation::PAGE_RIGHT
:
331 rRect
.Left() = aPagePrtArea
.Right();
332 rRect
.Right() = aPage
.Right();
335 case RelOrientation::PAGE_FRAME
:
336 rRect
.Left() = aPage
.Left();
337 rRect
.Right() = aPage
.Right();
340 case RelOrientation::PAGE_PRINT_AREA
:
341 rRect
.Left() = aPagePrtArea
.Left();
342 rRect
.Right() = aPagePrtArea
.Right();
345 case RelOrientation::FRAME_LEFT
:
346 rRect
.Left() = aPara
.Left();
347 rRect
.Right() = aParaPrtArea
.Left();
350 case RelOrientation::FRAME_RIGHT
:
351 rRect
.Left() = aParaPrtArea
.Right();
352 rRect
.Right() = aPara
.Right();
355 case RelOrientation::CHAR
:
356 rRect
.Left() = aAutoCharFrame
.Left();
357 rRect
.Right() = aAutoCharFrame
.Left();
363 case RelOrientation::FRAME
:
364 rRect
.Top() = aPara
.Top();
365 rRect
.Bottom() = aPara
.Bottom();
368 case RelOrientation::PRINT_AREA
:
369 rRect
.Top() = aParaPrtArea
.Top();
370 rRect
.Bottom() = aParaPrtArea
.Bottom();
373 case RelOrientation::CHAR
:
374 if (nVAlign
!= VertOrientation::NONE
&&
375 nVAlign
!= VertOrientation::CHAR_BOTTOM
)
376 rRect
.Top() = aAutoCharFrame
.Top();
378 rRect
.Top() = aAutoCharFrame
.Bottom();
379 rRect
.Bottom() = aAutoCharFrame
.Bottom();
381 // OD 12.11.2003 #i22341#
382 case RelOrientation::TEXT_LINE
:
383 rRect
.Top() = aAutoCharFrame
.Top();
384 rRect
.Bottom() = aAutoCharFrame
.Top();
390 case TextContentAnchorType_AS_CHARACTER
:
391 rRect
.Left() = aParaPrtArea
.Left();
392 rRect
.Right() = aParaPrtArea
.Right();
396 case VertOrientation::NONE
:
397 case VertOrientation::TOP
:
398 case VertOrientation::CENTER
:
399 case VertOrientation::BOTTOM
:
401 FontMetric
aMetric(GetFontMetric());
403 rRect
.Top() = aParaPrtArea
.Bottom() - aMetric
.GetDescent();
404 rRect
.Bottom() = rRect
.Top();
410 case VertOrientation::LINE_TOP
:
411 case VertOrientation::LINE_CENTER
:
412 case VertOrientation::LINE_BOTTOM
:
413 rRect
.Top() = aParaPrtArea
.Top();
414 rRect
.Bottom() = aDrawObj
.Bottom();
417 case VertOrientation::CHAR_TOP
:
418 case VertOrientation::CHAR_CENTER
:
419 case VertOrientation::CHAR_BOTTOM
:
420 rRect
.Top() = aParaPrtArea
.Top();
421 rRect
.Bottom() = aParaPrtArea
.Bottom();
431 Rectangle
SvxSwFrameExample::DrawInnerFrame_Impl(const Rectangle
&rRect
, const Color
&rFillColor
, const Color
&rBorderColor
)
433 DrawRect_Impl(rRect
, rFillColor
, rBorderColor
);
435 // Bereich, zu dem relativ positioniert wird, bestimmen
436 Rectangle
aRect(rRect
); // aPagePrtArea = Default
437 CalcBoundRect_Impl(aRect
);
439 if (nAnchor
== TextContentAnchorType_AT_FRAME
&& &rRect
== &aPagePrtArea
)
441 // Testabsatz zeichnen
442 Rectangle
aTxt(aTextLine
);
443 sal_Int32 nStep
= aTxt
.GetHeight() + 2;
444 sal_uInt16 nLines
= (sal_uInt16
)(aParaPrtArea
.GetHeight() / (aTextLine
.GetHeight() + 2));
446 for (sal_uInt16 i
= 0; i
< nLines
; i
++)
449 aTxt
.SetSize(Size(aTxt
.GetWidth() / 2, aTxt
.GetHeight()));
450 DrawRect_Impl(aTxt
, m_aTxtCol
, m_aTransColor
);
458 void SvxSwFrameExample::Paint(const Rectangle
&)
463 DrawRect_Impl( aPage
, m_aBgCol
, m_aBorderCol
);
466 Rectangle aRect
= DrawInnerFrame_Impl( aPagePrtArea
, m_aTransColor
, m_aPrintAreaCol
);
468 if (nAnchor
== TextContentAnchorType_AT_FRAME
)
469 aRect
= DrawInnerFrame_Impl( aFrameAtFrame
, m_aBgCol
, m_aBorderCol
);
474 // Horizontal alignment
475 if (nAnchor
!= TextContentAnchorType_AS_CHARACTER
)
479 case HoriOrientation::RIGHT
:
481 lXPos
= aRect
.Right() - aFrmSize
.Width() + 1;
484 case HoriOrientation::CENTER
:
486 lXPos
= aRect
.Left() + (aRect
.GetWidth() - aFrmSize
.Width()) / 2;
489 case HoriOrientation::NONE
:
491 lXPos
= aRect
.Left() + aRelPos
.X();
495 default: // HoriOrientation::LEFT
496 lXPos
= aRect
.Left();
501 lXPos
= aRect
.Right() + 2;
503 // Vertical Alignment
504 if (nAnchor
!= TextContentAnchorType_AS_CHARACTER
)
508 case VertOrientation::BOTTOM
:
509 case VertOrientation::LINE_BOTTOM
:
512 if ( nVRel
!= RelOrientation::TEXT_LINE
)
514 lYPos
= aRect
.Bottom() - aFrmSize
.Height() + 1;
522 case VertOrientation::CENTER
:
523 case VertOrientation::LINE_CENTER
:
525 lYPos
= aRect
.Top() + (aRect
.GetHeight() - aFrmSize
.Height()) / 2;
528 case VertOrientation::NONE
:
531 if ( nVRel
!= RelOrientation::CHAR
&& nVRel
!= RelOrientation::TEXT_LINE
)
532 lYPos
= aRect
.Top() + aRelPos
.Y();
534 lYPos
= aRect
.Top() - aRelPos
.Y();
539 if ( nVRel
!= RelOrientation::TEXT_LINE
)
545 lYPos
= aRect
.Bottom() - aFrmSize
.Height() + 1;
554 case VertOrientation::CENTER
:
555 case VertOrientation::CHAR_CENTER
:
556 case VertOrientation::LINE_CENTER
:
557 lYPos
= aRect
.Top() + (aRect
.GetHeight() - aFrmSize
.Height()) / 2;
560 case VertOrientation::TOP
:
561 case VertOrientation::CHAR_BOTTOM
:
562 case VertOrientation::LINE_BOTTOM
:
563 lYPos
= aRect
.Bottom() - aFrmSize
.Height() + 1;
567 lYPos
= aRect
.Top() - aRelPos
.Y();
572 Rectangle
aFrmRect(Point(lXPos
, lYPos
), aFrmSize
);
574 Rectangle
*pOuterFrame
= &aPage
;
576 if (nAnchor
== TextContentAnchorType_AT_FRAME
)
577 pOuterFrame
= &aFrameAtFrame
;
579 if (aFrmRect
.Left() < pOuterFrame
->Left())
580 aFrmRect
.Move(pOuterFrame
->Left() - aFrmRect
.Left(), 0);
581 if (aFrmRect
.Right() > pOuterFrame
->Right())
582 aFrmRect
.Move(pOuterFrame
->Right() - aFrmRect
.Right(), 0);
584 if (aFrmRect
.Top() < pOuterFrame
->Top())
585 aFrmRect
.Move(0, pOuterFrame
->Top() - aFrmRect
.Top());
586 if (aFrmRect
.Bottom() > pOuterFrame
->Bottom())
587 aFrmRect
.Move(0, pOuterFrame
->Bottom() - aFrmRect
.Bottom());
589 // Draw Test paragraph
590 const long nTxtLineHeight
= aTextLine
.GetHeight();
591 Rectangle
aTxt(aTextLine
);
595 if (nAnchor
== TextContentAnchorType_AT_FRAME
)
597 aTxt
.Left() = aFrameAtFrame
.Left() + FLYINFLY_BORDER
;
598 aTxt
.Right() = aFrameAtFrame
.Right() - FLYINFLY_BORDER
;
599 aTxt
.Top() = aFrameAtFrame
.Top() + FLYINFLY_BORDER
;
600 aTxt
.Bottom() = aTxt
.Top() + aTextLine
.GetHeight() - 1;
602 nStep
= aTxt
.GetHeight() + 2;
603 nLines
= (sal_uInt16
)(((aFrameAtFrame
.GetHeight() - 2 * FLYINFLY_BORDER
) * 2 / 3)
604 / (aTxt
.GetHeight() + 2));
608 nStep
= aTxt
.GetHeight() + 2;
609 nLines
= (sal_uInt16
)(aParaPrtArea
.GetHeight() / (aTextLine
.GetHeight() + 2));
612 if (nAnchor
!= TextContentAnchorType_AS_CHARACTER
)
615 const long nOldR
= aTxt
.Right();
616 const long nOldL
= aTxt
.Left();
619 const bool bIgnoreWrap
= nAnchor
== TextContentAnchorType_AT_CHARACTER
&&
620 ( nHRel
== RelOrientation::CHAR
|| nVRel
== RelOrientation::CHAR
||
621 nVRel
== RelOrientation::TEXT_LINE
);
623 for (sal_uInt16 i
= 0; i
< nLines
; ++i
)
625 if (i
== (nLines
- 1))
626 aTxt
.SetSize(Size(aTxt
.GetWidth() / 2, aTxt
.GetHeight()));
628 if (aTxt
.IsOver(aFrmRect
) && nAnchor
!= TextContentAnchorType_AS_CHARACTER
&& !bIgnoreWrap
)
632 case WrapTextMode_NONE
:
633 aTxt
.Top() = aFrmRect
.Bottom() + nTxtLineHeight
;
634 aTxt
.Bottom() = aTxt
.Top() + nTxtLineHeight
- 1;
637 case WrapTextMode_LEFT
:
638 aTxt
.Right() = aFrmRect
.Left();
641 case WrapTextMode_RIGHT
:
642 aTxt
.Left() = aFrmRect
.Right();
646 if (pOuterFrame
->IsInside(aTxt
))
647 DrawRect_Impl( aTxt
, m_aTxtCol
, m_aTransColor
);
650 aTxt
.Right() = nOldR
;
653 aTxt
.Move(0, -nStep
);
655 if (nAnchor
!= TextContentAnchorType_AT_FRAME
&& aTxt
.Bottom() > aParaPrtArea
.Bottom())
657 // Text has been replaced by frame, so adjust parameters height
658 sal_uIntPtr nDiff
= aTxt
.Bottom() - aParaPrtArea
.Bottom();
659 aParaPrtArea
.Bottom() += nDiff
;
660 aPara
.Bottom() += nDiff
;
662 CalcBoundRect_Impl(aRect
);
664 aParaPrtArea
.Bottom() -= nDiff
;
665 aPara
.Bottom() -= nDiff
;
667 if (nAnchor
== TextContentAnchorType_AT_CHARACTER
&& bIgnoreWrap
)
668 DrawText(aAutoCharFrame
, OUString('A'));
672 DrawText(aParaPrtArea
, OUString(DEMOTEXT
));
673 DrawRect_Impl(aDrawObj
, m_aBlankCol
, m_aBlankFrameCol
);
676 // Draw rectangle on which the frame is aligned:
677 DrawRect_Impl(aRect
, m_aTransColor
, m_aAlignColor
);
680 sal_Bool bDontFill
= (nAnchor
== TextContentAnchorType_AT_CHARACTER
&& aFrmRect
.IsOver(aAutoCharFrame
)) ? sal_True
: bTrans
;
681 DrawRect_Impl( aFrmRect
, bDontFill
? m_aTransColor
: m_aBgCol
, m_aFrameColor
);
684 void SvxSwFrameExample::SetRelPos(const Point
& rP
)
699 void SvxSwFrameExample::DrawRect_Impl(const Rectangle
&rRect
, const Color
&rFillColor
, const Color
&rLineColor
)
701 SetFillColor(rFillColor
);
702 SetLineColor(rLineColor
);
703 Window::DrawRect(rRect
);
708 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */