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 .
21 #include <hintids.hxx>
22 #include <svl/eitem.hxx>
23 #include <tools/fract.hxx>
24 #include <editeng/lrspitem.hxx>
25 #include <editeng/ulspitem.hxx>
26 #include <editeng/sizeitem.hxx>
27 #include <svx/pageitem.hxx>
28 #include <editeng/brushitem.hxx>
29 #include <editeng/frmdiritem.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/settings.hxx>
32 #include <tgrditem.hxx>
33 #include <viewopt.hxx>
36 #include <svx/unobrushitemhelper.hxx>
38 void SwPageExample::UpdateExample( const SfxItemSet
& rSet
)
40 if (SfxItemState::DEFAULT
<= rSet
.GetItemState(RES_FRAMEDIR
))
42 const SvxFrameDirectionItem
& rDirItem
= rSet
.Get(RES_FRAMEDIR
);
43 m_bVertical
= rDirItem
.GetValue() == SvxFrameDirection::Vertical_RL_TB
||
44 rDirItem
.GetValue() == SvxFrameDirection::Vertical_LR_TB
;
47 SfxItemPool
* pPool
= rSet
.GetPool();
48 sal_uInt16 nWhich
= pPool
->GetWhich( SID_ATTR_PAGE
);
49 if ( rSet
.GetItemState( nWhich
, false ) == SfxItemState::SET
)
52 const SvxPageItem
& rPage
= static_cast<const SvxPageItem
&>(rSet
.Get(nWhich
));
53 SetUsage(rPage
.GetPageUsage());
56 nWhich
= pPool
->GetWhich( SID_ATTR_PAGE_SIZE
);
58 if ( rSet
.GetItemState( nWhich
, false ) == SfxItemState::SET
)
60 // orientation and size from PageItem
61 const SvxSizeItem
& rSize
= static_cast<const SvxSizeItem
&>(rSet
.Get( nWhich
));
62 SetSize( rSize
.GetSize() );
64 nWhich
= RES_LR_SPACE
;
65 if ( rSet
.GetItemState( nWhich
, false ) == SfxItemState::SET
)
67 // set left and right border
68 const SvxLRSpaceItem
& rLRSpace
= static_cast<const SvxLRSpaceItem
&>(rSet
.Get( nWhich
));
70 SetLeft( rLRSpace
.GetLeft() );
71 SetRight( rLRSpace
.GetRight() );
79 nWhich
= RES_UL_SPACE
;
81 if ( rSet
.GetItemState( nWhich
, false ) == SfxItemState::SET
)
83 // set upper and lower border
84 const SvxULSpaceItem
& rULSpace
= static_cast<const SvxULSpaceItem
&>(rSet
.Get( nWhich
));
86 SetTop( rULSpace
.GetUpper() );
87 SetBottom( rULSpace
.GetLower() );
95 // evaluate header-attributes
96 const SfxPoolItem
* pItem
;
97 if( SfxItemState::SET
== rSet
.GetItemState( pPool
->GetWhich( SID_ATTR_PAGE_HEADERSET
),
100 const SfxItemSet
& rHeaderSet
= static_cast<const SvxSetItem
*>(pItem
)->GetItemSet();
101 const SfxBoolItem
& rHeaderOn
=
102 static_cast<const SfxBoolItem
&>(rHeaderSet
.Get( pPool
->GetWhich( SID_ATTR_PAGE_ON
) ) );
104 if ( rHeaderOn
.GetValue() )
106 const SvxSizeItem
& rSize
=
107 static_cast<const SvxSizeItem
&>(rHeaderSet
.Get(pPool
->GetWhich(SID_ATTR_PAGE_SIZE
)));
109 const SvxULSpaceItem
& rUL
= static_cast<const SvxULSpaceItem
&>(rHeaderSet
.Get(
110 pPool
->GetWhich(SID_ATTR_ULSPACE
)));
111 const SvxLRSpaceItem
& rLR
= static_cast<const SvxLRSpaceItem
&>(rHeaderSet
.Get(
112 pPool
->GetWhich(SID_ATTR_LRSPACE
)));
114 SetHdHeight( rSize
.GetSize().Height() - rUL
.GetLower());
115 SetHdDist( rUL
.GetLower() );
116 SetHdLeft( rLR
.GetLeft() );
117 SetHdRight( rLR
.GetRight() );
120 if(SfxItemState::SET
== rHeaderSet
.GetItemState(RES_BACKGROUND
))
122 // create FillAttributes from SvxBrushItem //SetHdColor(rItem.GetColor());
123 const SvxBrushItem
& rItem
= rHeaderSet
.Get(RES_BACKGROUND
);
124 SfxItemSet
aTempSet(*rHeaderSet
.GetPool(), svl::Items
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>{});
126 setSvxBrushItemAsFillAttributesToTargetSet(rItem
, aTempSet
);
127 setHeaderFillAttributes(
128 std::make_shared
<drawinglayer::attribute::SdrAllFillAttributesHelper
>(
136 if( SfxItemState::SET
== rSet
.GetItemState( pPool
->GetWhich( SID_ATTR_PAGE_FOOTERSET
),
139 const SfxItemSet
& rFooterSet
= static_cast<const SvxSetItem
*>(pItem
)->GetItemSet();
140 const SfxBoolItem
& rFooterOn
= rFooterSet
.Get( SID_ATTR_PAGE_ON
);
142 if ( rFooterOn
.GetValue() )
144 const SvxSizeItem
& rSize
=
145 static_cast<const SvxSizeItem
&>(rFooterSet
.Get( pPool
->GetWhich( SID_ATTR_PAGE_SIZE
) ));
147 const SvxULSpaceItem
& rUL
= static_cast<const SvxULSpaceItem
&>(rFooterSet
.Get(
148 pPool
->GetWhich( SID_ATTR_ULSPACE
) ));
149 const SvxLRSpaceItem
& rLR
= static_cast<const SvxLRSpaceItem
&>(rFooterSet
.Get(
150 pPool
->GetWhich( SID_ATTR_LRSPACE
) ));
152 SetFtHeight( rSize
.GetSize().Height() - rUL
.GetUpper());
153 SetFtDist( rUL
.GetUpper() );
154 SetFtLeft( rLR
.GetLeft() );
155 SetFtRight( rLR
.GetRight() );
158 if( rFooterSet
.GetItemState( RES_BACKGROUND
) == SfxItemState::SET
)
160 // create FillAttributes from SvxBrushItem //SetFtColor(rItem.GetColor());
161 const SvxBrushItem
& rItem
= rFooterSet
.Get(RES_BACKGROUND
);
162 SfxItemSet
aTempSet(*rFooterSet
.GetPool(), svl::Items
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>{});
164 setSvxBrushItemAsFillAttributesToTargetSet(rItem
, aTempSet
);
165 setFooterFillAttributes(
166 std::make_shared
<drawinglayer::attribute::SdrAllFillAttributesHelper
>(
174 if(SfxItemState::SET
== rSet
.GetItemState(RES_BACKGROUND
, false, &pItem
))
176 // create FillAttributes from SvxBrushItem
177 const SvxBrushItem
& rItem
= static_cast< const SvxBrushItem
& >(*pItem
);
178 SfxItemSet
aTempSet(*rSet
.GetPool(), svl::Items
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>{});
180 setSvxBrushItemAsFillAttributesToTargetSet(rItem
, aTempSet
);
181 setPageFillAttributes(
182 std::make_shared
<drawinglayer::attribute::SdrAllFillAttributesHelper
>(
189 void SwColExample::DrawPage(vcl::RenderContext
& rRenderContext
, const Point
& rOrg
,
190 const bool bSecond
, const bool bEnabled
)
192 SwPageExample::DrawPage(rRenderContext
, rOrg
, bSecond
, bEnabled
);
195 sal_uInt16 nColumnCount
= pColMgr
->GetCount();
199 tools::Long nL
= GetLeft();
200 tools::Long nR
= GetRight();
202 if (GetUsage() == SvxPageUsage::Mirror
&& !bSecond
)
209 rRenderContext
.SetFillColor(COL_LIGHTGRAY
);
210 tools::Rectangle aRect
;
211 aRect
.SetRight( rOrg
.X() + GetSize().Width() - nR
);
212 aRect
.SetLeft( rOrg
.X() + nL
);
213 aRect
.SetTop( rOrg
.Y() + GetTop() + GetHdHeight() + GetHdDist() );
214 aRect
.SetBottom( rOrg
.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist() );
215 rRenderContext
.DrawRect(aRect
);
217 const tools::Rectangle
aDefineRect(aRect
);
218 const drawinglayer::attribute::SdrAllFillAttributesHelperPtr
& rFillAttributes
= getPageFillAttributes();
220 if (!rFillAttributes
|| !rFillAttributes
->isUsed())
222 // If there is no fill, use fallback color
223 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
224 const Color
& rFieldColor
= rStyleSettings
.GetFieldColor();
226 setPageFillAttributes(
227 std::make_shared
<drawinglayer::attribute::SdrAllFillAttributesHelper
>(
231 // #97495# make sure that the automatic column width's are always equal
232 bool bAutoWidth
= pColMgr
->IsAutoWidth();
233 sal_Int32 nAutoColWidth
= 0;
236 sal_Int32 nColumnWidthSum
= 0;
237 for (sal_uInt16 i
= 0; i
< nColumnCount
; ++i
)
238 nColumnWidthSum
+= pColMgr
->GetColWidth( i
);
239 nAutoColWidth
= nColumnWidthSum
/ nColumnCount
;
242 for (sal_uInt16 i
= 0; i
< nColumnCount
; ++i
)
245 nAutoColWidth
= pColMgr
->GetColWidth(i
);
248 aRect
.SetRight( aRect
.Left() + nAutoColWidth
);
250 aRect
.SetBottom( aRect
.Top() + nAutoColWidth
);
252 // use primitive draw command
253 drawFillAttributes(rRenderContext
, getPageFillAttributes(), aRect
, aDefineRect
);
255 if (i
< nColumnCount
- 1)
258 aRect
.SetLeft( aRect
.Right() + pColMgr
->GetGutterWidth(i
) );
260 aRect
.SetTop( aRect
.Bottom() + pColMgr
->GetGutterWidth(i
) );
263 if (!pColMgr
->HasLine())
266 Point
aUp(rOrg
.X() + nL
, rOrg
.Y() + GetTop());
267 Point
aDown(rOrg
.X() + nL
,
268 rOrg
.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist());
270 if (pColMgr
->GetLineHeightPercent() != 100)
272 tools::Long nLength
= !m_bVertical
? aDown
.Y() - aUp
.Y() : aDown
.X() - aUp
.X();
273 nLength
-= nLength
* pColMgr
->GetLineHeightPercent() / 100;
274 switch (pColMgr
->GetAdjust())
278 aUp
.AdjustY(nLength
);
280 aUp
.AdjustX(nLength
);
284 aDown
.AdjustY( -nLength
);
286 aDown
.AdjustX( -nLength
);
291 aUp
.AdjustY(nLength
/ 2 );
292 aDown
.AdjustY( -(nLength
/ 2) );
296 aUp
.AdjustX(nLength
/ 2 );
297 aDown
.AdjustX( -(nLength
/ 2) );
301 break; // prevent warning
305 for (sal_uInt16 i
= 0; i
< nColumnCount
- 1; ++i
)
307 int nGutter
= pColMgr
->GetGutterWidth(i
);
308 int nDist
= pColMgr
->GetColWidth( i
) + nGutter
;
309 nDist
-= (i
== 0) ? nGutter
/ 2 : 0;
313 aDown
.AdjustX(nDist
);
318 aDown
.AdjustY(nDist
);
321 rRenderContext
.DrawLine(aUp
, aDown
);
325 SwColumnOnlyExample::SwColumnOnlyExample()
326 : m_aFrameSize(SvxPaperInfo::GetPaperSize(PAPER_A4
)) // DIN A4
328 ::FitToActualSize(m_aCols
, static_cast<sal_uInt16
>(m_aFrameSize
.Width()));
331 void SwColumnOnlyExample::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rRect*/)
333 rRenderContext
.Push(PushFlags::MAPMODE
);
335 Fraction
aScale(m_aWinSize
.Height(), m_aFrameSize
.Height());
336 MapMode
aMapMode(MapUnit::MapTwip
);
337 aMapMode
.SetScaleX(aScale
);
338 aMapMode
.SetScaleY(aScale
);
339 rRenderContext
.SetMapMode(aMapMode
);
341 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
342 const Color
& rFieldColor
= rStyleSettings
.GetFieldColor();
343 const Color
& rDlgColor
= rStyleSettings
.GetDialogColor();
344 const Color
& rFieldTextColor
= SwViewOption::GetFontColor();
345 Color
aGrayColor(COL_LIGHTGRAY
);
346 if (rFieldColor
== aGrayColor
)
349 Size
aLogSize(rRenderContext
.PixelToLogic(GetOutputSizePixel()));
350 tools::Rectangle
aCompleteRect(Point(0,0), aLogSize
);
351 rRenderContext
.SetLineColor(rDlgColor
);
352 rRenderContext
.SetFillColor(rDlgColor
);
353 rRenderContext
.DrawRect(aCompleteRect
);
355 rRenderContext
.SetLineColor(rFieldTextColor
);
356 Point
aTL((aLogSize
.Width() - m_aFrameSize
.Width()) / 2,
357 (aLogSize
.Height() - m_aFrameSize
.Height()) / 2);
358 tools::Rectangle
aRect(aTL
, m_aFrameSize
);
360 //draw a shadow rectangle
361 rRenderContext
.SetFillColor(COL_GRAY
);
362 tools::Rectangle
aShadowRect(aRect
);
363 aShadowRect
.Move(aTL
.Y(), aTL
.Y());
364 rRenderContext
.DrawRect(aShadowRect
);
366 rRenderContext
.SetFillColor(rFieldColor
);
367 rRenderContext
.DrawRect(aRect
);
369 rRenderContext
.SetFillColor(aGrayColor
);
372 tools::Long nLength
= aLogSize
.Height() - 2 * aTL
.Y();
374 Point
aDown(aTL
.X(), nLength
);
376 if (m_aCols
.GetLineAdj() != COLADJ_NONE
)
380 sal_uInt16 nPercent
= m_aCols
.GetLineHeight();
383 nLength
-= nLength
* nPercent
/ 100;
384 switch(m_aCols
.GetLineAdj())
386 case COLADJ_BOTTOM
: aUp
.AdjustY(nLength
); break;
387 case COLADJ_TOP
: aDown
.AdjustY( -nLength
); break;
389 aUp
.AdjustY(nLength
/ 2 );
390 aDown
.AdjustY( -(nLength
/ 2) );
393 break; //prevent warning
398 const SwColumns
& rCols
= m_aCols
.GetColumns();
399 sal_uInt16 nColCount
= rCols
.size();
402 rRenderContext
.DrawRect(aRect
);
403 rRenderContext
.SetFillColor(rFieldColor
);
404 tools::Rectangle
aFrameRect(aTL
, m_aFrameSize
);
405 tools::Long nSum
= aTL
.X();
406 for (sal_uInt16 i
= 0; i
< nColCount
; i
++)
408 const SwColumn
* pCol
= &rCols
[i
];
409 aFrameRect
.SetLeft( nSum
+ pCol
->GetLeft() ); //nSum + pCol->GetLeft() + aTL.X();
410 nSum
+= pCol
->GetWishWidth();
411 aFrameRect
.SetRight( nSum
- pCol
->GetRight() );
412 rRenderContext
.DrawRect(aFrameRect
);
417 for (sal_uInt16 i
= 0; i
< nColCount
- 1; i
++)
419 nSum
+= rCols
[i
].GetWishWidth();
422 rRenderContext
.DrawLine(aUp
, aDown
);
426 rRenderContext
.Pop();
429 void SwColumnOnlyExample::SetColumns(const SwFormatCol
& rCol
)
432 sal_uInt16 nWishSum
= m_aCols
.GetWishWidth();
433 tools::Long nFrameWidth
= m_aFrameSize
.Width();
434 SwColumns
& rCols
= m_aCols
.GetColumns();
435 sal_uInt16 nColCount
= rCols
.size();
437 for(sal_uInt16 i
= 0; i
< nColCount
; i
++)
439 SwColumn
* pCol
= &rCols
[i
];
440 tools::Long nWish
= pCol
->GetWishWidth();
441 nWish
*= nFrameWidth
;
443 pCol
->SetWishWidth(static_cast<sal_uInt16
>(nWish
));
444 tools::Long nLeft
= pCol
->GetLeft();
445 nLeft
*= nFrameWidth
;
447 pCol
->SetLeft(static_cast<sal_uInt16
>(nLeft
));
448 tools::Long nRight
= pCol
->GetRight();
449 nRight
*= nFrameWidth
;
451 pCol
->SetRight(static_cast<sal_uInt16
>(nRight
));
453 // #97495# make sure that the automatic column width's are always equal
454 if(!(nColCount
&& m_aCols
.IsOrtho()))
457 sal_Int32 nColumnWidthSum
= 0;
459 for(i
= 0; i
< nColCount
; ++i
)
461 SwColumn
* pCol
= &rCols
[i
];
462 nColumnWidthSum
+= pCol
->GetWishWidth();
463 nColumnWidthSum
-= (pCol
->GetRight() + pCol
->GetLeft());
465 nColumnWidthSum
/= nColCount
;
466 for(i
= 0; i
< nColCount
; ++i
)
468 SwColumn
* pCol
= &rCols
[i
];
469 pCol
->SetWishWidth( static_cast< sal_uInt16
>(nColumnWidthSum
+ pCol
->GetRight() + pCol
->GetLeft()));
473 void SwColumnOnlyExample::SetDrawingArea(weld::DrawingArea
* pDrawingArea
)
475 weld::CustomWidgetController::SetDrawingArea(pDrawingArea
);
476 OutputDevice
& rRefDevice
= pDrawingArea
->get_ref_device();
477 Size
aPrefSize(rRefDevice
.LogicToPixel(Size(75, 46), MapMode(MapUnit::MapAppFont
)));
478 pDrawingArea
->set_size_request(aPrefSize
.Width(), aPrefSize
.Height());
481 void SwColumnOnlyExample::Resize()
483 OutputDevice
& rRefDevice
= GetDrawingArea()->get_ref_device();
484 rRefDevice
.Push(PushFlags::MAPMODE
);
485 rRefDevice
.SetMapMode(MapMode(MapUnit::MapTwip
));
486 m_aWinSize
= GetOutputSizePixel();
487 m_aWinSize
.AdjustHeight( -4 );
488 m_aWinSize
.AdjustWidth( -4 );
489 m_aWinSize
= rRefDevice
.PixelToLogic(m_aWinSize
);
494 SwPageGridExample::SwPageGridExample()
498 void SwPageGridExample::DrawPage(vcl::RenderContext
& rRenderContext
, const Point
& rOrg
,
499 const bool bSecond
, const bool bEnabled
)
501 SwPageExample::DrawPage(rRenderContext
, rOrg
, bSecond
, bEnabled
);
503 if (!pGridItem
|| !pGridItem
->GetGridType())
507 Color aLineColor
= pGridItem
->GetColor();
508 if (aLineColor
== COL_AUTO
)
510 aLineColor
= rRenderContext
.GetFillColor();
513 rRenderContext
.SetLineColor(aLineColor
);
514 tools::Long nL
= GetLeft();
515 tools::Long nR
= GetRight();
517 if (GetUsage() == SvxPageUsage::Mirror
&& !bSecond
)
519 // rotate for mirrored
524 tools::Rectangle aRect
;
525 aRect
.SetRight( rOrg
.X() + GetSize().Width() - nR
);
526 aRect
.SetLeft( rOrg
.X() + nL
);
527 aRect
.SetTop( rOrg
.Y() + GetTop() + GetHdHeight() + GetHdDist() );
528 aRect
.SetBottom( rOrg
.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist() );
530 //increase the values to get a 'viewable' preview
531 sal_Int32 nBaseHeight
= pGridItem
->GetBaseHeight() * 3;
532 sal_Int32 nRubyHeight
= pGridItem
->GetRubyHeight() * 3;
534 //detect height of rectangles
535 tools::Rectangle
aRubyRect(aRect
.TopLeft(),
537 Size(nRubyHeight
, aRect
.GetHeight()) :
538 Size(aRect
.GetWidth(), nRubyHeight
));
539 tools::Rectangle
aCharRect(aRect
.TopLeft(),
541 Size(nBaseHeight
, aRect
.GetHeight()) :
542 Size(aRect
.GetWidth(), nBaseHeight
));
544 sal_Int32 nLineHeight
= nBaseHeight
+ nRubyHeight
;
546 //detect count of rectangles
547 sal_Int32 nLines
= (m_bVertical
? aRect
.GetWidth(): aRect
.GetHeight()) / nLineHeight
;
548 if (nLines
> pGridItem
->GetLines())
549 nLines
= pGridItem
->GetLines();
551 // determine start position
554 sal_Int16 nXStart
= static_cast<sal_Int16
>(aRect
.GetWidth() / 2 - nLineHeight
* nLines
/2);
555 aRubyRect
.Move(nXStart
, 0);
556 aCharRect
.Move(nXStart
, 0);
560 sal_Int16 nYStart
= static_cast<sal_Int16
>(aRect
.GetHeight() / 2 - nLineHeight
* nLines
/2);
561 aRubyRect
.Move(0, nYStart
);
562 aCharRect
.Move(0, nYStart
);
565 if (pGridItem
->IsRubyTextBelow())
566 m_bVertical
? aRubyRect
.Move(nBaseHeight
, 0) : aRubyRect
.Move(0, nBaseHeight
);
568 m_bVertical
? aCharRect
.Move(nRubyHeight
, 0) : aCharRect
.Move(0, nRubyHeight
);
571 bool bBothLines
= pGridItem
->GetGridType() == GRID_LINES_CHARS
;
572 rRenderContext
.SetFillColor(COL_TRANSPARENT
);
573 sal_Int32 nXMove
= m_bVertical
? nLineHeight
: 0;
574 sal_Int32 nYMove
= m_bVertical
? 0 : nLineHeight
;
575 for (sal_Int32 nLine
= 0; nLine
< nLines
; nLine
++)
577 rRenderContext
.DrawRect(aRubyRect
);
578 rRenderContext
.DrawRect(aCharRect
);
581 Point aStart
= aCharRect
.TopLeft();
582 Point aEnd
= m_bVertical
? aCharRect
.TopRight() : aCharRect
.BottomLeft();
583 while (m_bVertical
? aStart
.Y() < aRect
.Bottom(): aStart
.X() < aRect
.Right())
585 rRenderContext
.DrawLine(aStart
, aEnd
);
587 aStart
.setY( aEnd
.AdjustY(nBaseHeight
) );
589 aStart
.setX( aEnd
.AdjustX(nBaseHeight
) );
592 aRubyRect
.Move(nXMove
, nYMove
);
593 aCharRect
.Move(nXMove
, nYMove
);
598 void SwPageGridExample::UpdateExample( const SfxItemSet
& rSet
)
601 //get the grid information
602 if (SfxItemState::DEFAULT
<= rSet
.GetItemState(RES_TEXTGRID
))
603 pGridItem
.reset(rSet
.Get(RES_TEXTGRID
).Clone());
604 SwPageExample::UpdateExample(rSet
);
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */