nss: upgrade to release 3.73
[LibreOffice.git] / slideshow / source / engine / shapeattributelayer.cxx
bloba688f479caec3d57aee8ffd6345627acbcc3275a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 // must be first
22 #include <tools/diagnose_ex.h>
23 #include <shapeattributelayer.hxx>
25 #include <com/sun/star/awt/FontUnderline.hpp>
26 #include <com/sun/star/awt/FontWeight.hpp>
27 #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
29 #include <rtl/math.hxx>
32 using namespace ::com::sun::star;
35 namespace slideshow::internal
37 /** Update state ids
39 This method updates all state IDs from possible
40 children. Whenever a child's state ID changed, we
41 increment ours.
43 void ShapeAttributeLayer::updateStateIds()
45 if( !haveChild() )
46 return;
48 if( mnTransformationState != mpChild->getTransformationState() )
49 ++mnTransformationState;
50 if( mnClipState != mpChild->getClipState() )
51 ++mnClipState;
52 if( mnAlphaState != mpChild->getAlphaState() )
53 ++mnAlphaState;
54 if( mnPositionState != mpChild->getPositionState() )
55 ++mnPositionState;
56 if( mnContentState != mpChild->getContentState() )
57 ++mnContentState;
58 if( mnVisibilityState != mpChild->getVisibilityState() )
59 ++mnVisibilityState;
62 /** Calc attribute value.
64 This method determines the current attribute value,
65 appropriately combining it with children values (by
66 evaluating the mnAdditiveMode member).
68 template< typename T > T ShapeAttributeLayer::calcValue( const T& rCurrValue,
69 bool bThisInstanceValid,
70 bool (ShapeAttributeLayer::*pIsValid)() const,
71 T (ShapeAttributeLayer::*pGetValue)() const ) const
73 // deviated from the (*shared_ptr).*mpFuncPtr notation
74 // here, since gcc does not seem to parse that as a member
75 // function call anymore.
76 const bool bChildInstanceValueValid( haveChild() && (mpChild.get()->*pIsValid)() );
78 if( bThisInstanceValid )
80 if( bChildInstanceValueValid )
82 // merge with child value
83 switch( mnAdditiveMode )
85 default:
86 // FALTHROUGH intended
87 case animations::AnimationAdditiveMode::NONE:
88 // FALTHROUGH intended
89 case animations::AnimationAdditiveMode::BASE:
90 // FALTHROUGH intended
91 case animations::AnimationAdditiveMode::REPLACE:
92 // TODO(F2): reverse-engineer the semantics of these
93 // values
95 // currently, treat them the same and replace
96 // the child value by our own
97 return rCurrValue;
99 case animations::AnimationAdditiveMode::SUM:
100 return rCurrValue + ((*mpChild).*pGetValue)();
102 case animations::AnimationAdditiveMode::MULTIPLY:
103 return rCurrValue * ((*mpChild).*pGetValue)();
106 else
108 // this object is the only one defining
109 // the value, so take it
110 return rCurrValue;
113 else
115 return bChildInstanceValueValid ?
116 ((*mpChild).*pGetValue)() :
117 T(); // pass on child value, regardless
118 // if it's valid or not. If not, it's
119 // a default anyway
123 ShapeAttributeLayer::ShapeAttributeLayer( const ShapeAttributeLayerSharedPtr& rChildLayer ) :
124 mpChild( rChildLayer ),
126 maSize(),
127 maPosition(),
128 maClip(),
130 maFontFamily(),
132 mnRotationAngle(),
133 mnShearXAngle(),
134 mnShearYAngle(),
135 mnAlpha(),
136 mnCharScale(),
137 mnCharWeight(),
139 meFillStyle( drawing::FillStyle_NONE ),
140 meLineStyle( drawing::LineStyle_NONE ),
141 meCharPosture( awt::FontSlant_NONE ),
142 mnUnderlineMode(),
144 maDimColor(),
145 maFillColor(),
146 maLineColor(),
147 maCharColor(),
149 mnTransformationState( rChildLayer ? rChildLayer->getTransformationState() : 0 ),
150 mnClipState( rChildLayer ? rChildLayer->getClipState() : 0),
151 mnAlphaState( rChildLayer ? rChildLayer->getAlphaState() : 0),
152 mnPositionState( rChildLayer ? rChildLayer->getPositionState() : 0 ),
153 mnContentState( rChildLayer ? rChildLayer->getContentState() : 0 ),
154 mnVisibilityState( rChildLayer ? rChildLayer->getVisibilityState() : 0 ),
156 mnAdditiveMode( animations::AnimationAdditiveMode::BASE ),
158 mbVisibility( false ),
160 mbWidthValid( false ),
161 mbHeightValid( false ),
162 mbPosXValid( false ),
163 mbPosYValid( false ),
164 mbClipValid( false ),
166 mbFontFamilyValid( false ),
168 mbRotationAngleValid( false ),
169 mbShearXAngleValid( false ),
170 mbShearYAngleValid( false ),
172 mbAlphaValid( false ),
174 mbCharScaleValid( false ),
176 mbDimColorValid( false ),
177 mbFillColorValid( false ),
178 mbLineColorValid( false ),
179 mbCharColorValid( false ),
181 mbFillStyleValid( false ),
182 mbLineStyleValid( false ),
183 mbCharWeightValid( false ),
184 mbUnderlineModeValid( false ),
185 mbCharPostureValid( false ),
186 mbVisibilityValid( false )
190 bool ShapeAttributeLayer::revokeChildLayer( const ShapeAttributeLayerSharedPtr& rChildLayer )
192 ENSURE_OR_RETURN_FALSE( rChildLayer,
193 "ShapeAttributeLayer::revokeChildLayer(): Will not remove NULL child" );
195 if( !haveChild() )
196 return false; // no children, nothing to revoke.
198 if( mpChild == rChildLayer )
200 // we have it - replace by removed child's sibling.
201 mpChild = rChildLayer->getChildLayer();
203 // if we're now the first one, defensively increment _all_
204 // state ids: possibly all underlying attributes have now
205 // changed to default
206 if( !haveChild() )
208 // TODO(P1): Check whether it pays off to check more
209 // detailed, which attributes really change
210 ++mnTransformationState;
211 ++mnClipState;
212 ++mnAlphaState;
213 ++mnPositionState;
214 ++mnContentState;
215 ++mnVisibilityState;
218 else
220 // we don't have it - pass on the request
221 if( !mpChild->revokeChildLayer( rChildLayer ) )
222 return false; // nobody has it - bail out
225 // something might have changed - update ids.
226 updateStateIds();
228 return true;
231 const ShapeAttributeLayerSharedPtr& ShapeAttributeLayer::getChildLayer() const
233 return mpChild;
236 void ShapeAttributeLayer::setAdditiveMode( sal_Int16 nMode )
238 if( mnAdditiveMode != nMode )
240 // TODO(P1): Check whether it pays off to check more
241 // detailed, which attributes really change
243 // defensively increment all states - possibly each of them
244 // will change with different additive mode
245 ++mnTransformationState;
246 ++mnClipState;
247 ++mnAlphaState;
248 ++mnPositionState;
249 ++mnContentState;
250 ++mnVisibilityState;
253 mnAdditiveMode = nMode;
256 bool ShapeAttributeLayer::isWidthValid() const
258 return mbWidthValid || (haveChild() && mpChild->isWidthValid());
261 double ShapeAttributeLayer::getWidth() const
263 return calcValue< double >(
264 maSize.getX(),
265 mbWidthValid,
266 &ShapeAttributeLayer::isWidthValid,
267 &ShapeAttributeLayer::getWidth );
270 void ShapeAttributeLayer::setWidth( const double& rNewWidth )
272 ENSURE_OR_THROW( std::isfinite(rNewWidth),
273 "ShapeAttributeLayer::setWidth(): Invalid width" );
275 maSize.setX( rNewWidth );
276 mbWidthValid = true;
277 ++mnTransformationState;
280 bool ShapeAttributeLayer::isHeightValid() const
282 return mbHeightValid || ( haveChild() && mpChild->isHeightValid() );
285 double ShapeAttributeLayer::getHeight() const
287 return calcValue< double >(
288 maSize.getY(),
289 mbHeightValid,
290 &ShapeAttributeLayer::isHeightValid,
291 &ShapeAttributeLayer::getHeight );
294 void ShapeAttributeLayer::setHeight( const double& rNewHeight )
296 ENSURE_OR_THROW( std::isfinite(rNewHeight),
297 "ShapeAttributeLayer::setHeight(): Invalid height" );
299 maSize.setY( rNewHeight );
300 mbHeightValid = true;
301 ++mnTransformationState;
304 void ShapeAttributeLayer::setSize( const ::basegfx::B2DSize& rNewSize )
306 ENSURE_OR_THROW( std::isfinite(rNewSize.getX()) &&
307 std::isfinite(rNewSize.getY()),
308 "ShapeAttributeLayer::setSize(): Invalid size" );
310 maSize = rNewSize;
311 mbWidthValid = mbHeightValid = true;
312 ++mnTransformationState;
315 bool ShapeAttributeLayer::isPosXValid() const
317 return mbPosXValid || ( haveChild() && mpChild->isPosXValid() );
320 double ShapeAttributeLayer::getPosX() const
322 return calcValue< double >(
323 maPosition.getX(),
324 mbPosXValid,
325 &ShapeAttributeLayer::isPosXValid,
326 &ShapeAttributeLayer::getPosX );
329 void ShapeAttributeLayer::setPosX( const double& rNewX )
331 ENSURE_OR_THROW( std::isfinite(rNewX),
332 "ShapeAttributeLayer::setPosX(): Invalid position" );
334 maPosition.setX( rNewX );
335 mbPosXValid = true;
336 ++mnPositionState;
339 bool ShapeAttributeLayer::isPosYValid() const
341 return mbPosYValid || ( haveChild() && mpChild->isPosYValid() );
344 double ShapeAttributeLayer::getPosY() const
346 return calcValue< double >(
347 maPosition.getY(),
348 mbPosYValid,
349 &ShapeAttributeLayer::isPosYValid,
350 &ShapeAttributeLayer::getPosY );
353 void ShapeAttributeLayer::setPosY( const double& rNewY )
355 ENSURE_OR_THROW( std::isfinite(rNewY),
356 "ShapeAttributeLayer::setPosY(): Invalid position" );
358 maPosition.setY( rNewY );
359 mbPosYValid = true;
360 ++mnPositionState;
363 void ShapeAttributeLayer::setPosition( const ::basegfx::B2DPoint& rNewPos )
365 maPosition = rNewPos;
366 mbPosXValid = mbPosYValid = true;
367 ++mnPositionState;
370 bool ShapeAttributeLayer::isRotationAngleValid() const
372 return mbRotationAngleValid || ( haveChild() && mpChild->isRotationAngleValid() );
375 double ShapeAttributeLayer::getRotationAngle() const
377 return calcValue< double >(
378 mnRotationAngle,
379 mbRotationAngleValid,
380 &ShapeAttributeLayer::isRotationAngleValid,
381 &ShapeAttributeLayer::getRotationAngle );
384 void ShapeAttributeLayer::setRotationAngle( const double& rNewAngle )
386 ENSURE_OR_THROW( std::isfinite(rNewAngle),
387 "ShapeAttributeLayer::setRotationAngle(): Invalid angle" );
389 mnRotationAngle = rNewAngle;
390 mbRotationAngleValid = true;
391 ++mnTransformationState;
394 bool ShapeAttributeLayer::isShearXAngleValid() const
396 return mbShearXAngleValid || ( haveChild() && mpChild->isShearXAngleValid() );
399 double ShapeAttributeLayer::getShearXAngle() const
401 return calcValue( mnShearXAngle,
402 mbShearXAngleValid,
403 &ShapeAttributeLayer::isShearXAngleValid,
404 &ShapeAttributeLayer::getShearXAngle );
407 void ShapeAttributeLayer::setShearXAngle( const double& rNewAngle )
409 ENSURE_OR_THROW( std::isfinite(rNewAngle),
410 "ShapeAttributeLayer::setShearXAngle(): Invalid angle" );
412 mnShearXAngle = rNewAngle;
413 mbShearXAngleValid = true;
414 ++mnTransformationState;
417 bool ShapeAttributeLayer::isShearYAngleValid() const
419 return mbShearYAngleValid || ( haveChild() && mpChild->isShearYAngleValid() );
422 double ShapeAttributeLayer::getShearYAngle() const
424 return calcValue( mnShearYAngle,
425 mbShearYAngleValid,
426 &ShapeAttributeLayer::isShearYAngleValid,
427 &ShapeAttributeLayer::getShearYAngle );
430 void ShapeAttributeLayer::setShearYAngle( const double& rNewAngle )
432 ENSURE_OR_THROW( std::isfinite(rNewAngle),
433 "ShapeAttributeLayer::setShearYAngle(): Invalid angle" );
435 mnShearYAngle = rNewAngle;
436 mbShearYAngleValid = true;
437 ++mnTransformationState;
440 bool ShapeAttributeLayer::isAlphaValid() const
442 return mbAlphaValid || ( haveChild() && mpChild->isAlphaValid() );
445 double ShapeAttributeLayer::getAlpha() const
447 return calcValue( mnAlpha,
448 mbAlphaValid,
449 &ShapeAttributeLayer::isAlphaValid,
450 &ShapeAttributeLayer::getAlpha );
453 void ShapeAttributeLayer::setAlpha( const double& rNewValue )
455 ENSURE_OR_THROW( std::isfinite(rNewValue),
456 "ShapeAttributeLayer::setAlpha(): Invalid alpha" );
458 mnAlpha = rNewValue;
459 mbAlphaValid = true;
460 ++mnAlphaState;
463 bool ShapeAttributeLayer::isClipValid() const
465 return mbClipValid || ( haveChild() && mpChild->isClipValid() );
468 ::basegfx::B2DPolyPolygon ShapeAttributeLayer::getClip() const
470 // TODO(F1): Implement polygon algebra for additive modes
471 if( mbClipValid )
472 return maClip;
473 else if( haveChild() )
474 return mpChild->getClip();
475 else
476 return ::basegfx::B2DPolyPolygon();
479 void ShapeAttributeLayer::setClip( const ::basegfx::B2DPolyPolygon& rNewClip )
481 maClip = rNewClip;
482 mbClipValid = true;
483 ++mnClipState;
486 bool ShapeAttributeLayer::isDimColorValid() const
488 return mbDimColorValid || ( haveChild() && mpChild->isDimColorValid() );
491 RGBColor ShapeAttributeLayer::getDimColor() const
493 return calcValue( maDimColor,
494 mbDimColorValid,
495 &ShapeAttributeLayer::isDimColorValid,
496 &ShapeAttributeLayer::getDimColor );
499 void ShapeAttributeLayer::setDimColor( const RGBColor& nNewColor )
501 maDimColor = nNewColor;
502 mbDimColorValid = true;
503 ++mnContentState;
506 bool ShapeAttributeLayer::isFillColorValid() const
508 return mbFillColorValid || ( haveChild() && mpChild->isFillColorValid() );
511 RGBColor ShapeAttributeLayer::getFillColor() const
513 return calcValue( maFillColor,
514 mbFillColorValid,
515 &ShapeAttributeLayer::isFillColorValid,
516 &ShapeAttributeLayer::getFillColor );
519 void ShapeAttributeLayer::setFillColor( const RGBColor& nNewColor )
521 maFillColor = nNewColor;
522 mbFillColorValid = true;
523 ++mnContentState;
526 bool ShapeAttributeLayer::isLineColorValid() const
528 return mbLineColorValid || ( haveChild() && mpChild->isLineColorValid() );
531 RGBColor ShapeAttributeLayer::getLineColor() const
533 return calcValue( maLineColor,
534 mbLineColorValid,
535 &ShapeAttributeLayer::isLineColorValid,
536 &ShapeAttributeLayer::getLineColor );
539 void ShapeAttributeLayer::setLineColor( const RGBColor& nNewColor )
541 maLineColor = nNewColor;
542 mbLineColorValid = true;
543 ++mnContentState;
546 bool ShapeAttributeLayer::isFillStyleValid() const
548 return mbFillStyleValid || ( haveChild() && mpChild->isFillStyleValid() );
551 sal_Int16 ShapeAttributeLayer::getFillStyle() const
553 // mnAdditiveMode is ignored, cannot combine strings in
554 // any sensible way
555 if( mbFillStyleValid )
556 return sal::static_int_cast<sal_Int16>(meFillStyle);
557 else if( haveChild() )
558 return sal::static_int_cast<sal_Int16>(mpChild->getFillStyle());
559 else
560 return sal::static_int_cast<sal_Int16>(drawing::FillStyle_SOLID);
563 void ShapeAttributeLayer::setFillStyle( const sal_Int16& rStyle )
565 // TODO(Q1): Check range here.
566 meFillStyle = static_cast<drawing::FillStyle>(rStyle);
567 mbFillStyleValid = true;
568 ++mnContentState;
571 bool ShapeAttributeLayer::isLineStyleValid() const
573 return mbLineStyleValid || ( haveChild() && mpChild->isLineStyleValid() );
576 sal_Int16 ShapeAttributeLayer::getLineStyle() const
578 // mnAdditiveMode is ignored, cannot combine strings in
579 // any sensible way
580 if( mbLineStyleValid )
581 return sal::static_int_cast<sal_Int16>(meLineStyle);
582 else if( haveChild() )
583 return sal::static_int_cast<sal_Int16>(mpChild->getLineStyle());
584 else
585 return sal::static_int_cast<sal_Int16>(drawing::LineStyle_SOLID);
588 void ShapeAttributeLayer::setLineStyle( const sal_Int16& rStyle )
590 // TODO(Q1): Check range here.
591 meLineStyle = static_cast<drawing::LineStyle>(rStyle);
592 mbLineStyleValid = true;
593 ++mnContentState;
596 bool ShapeAttributeLayer::isVisibilityValid() const
598 return mbVisibilityValid || ( haveChild() && mpChild->isVisibilityValid() );
601 bool ShapeAttributeLayer::getVisibility() const
603 // mnAdditiveMode is ignored, SMIL spec requires to not combine
604 // bools in any sensible way
605 if( mbVisibilityValid )
606 return mbVisibility;
607 else if( haveChild() )
608 return mpChild->getVisibility();
609 else
610 return true; // default is always visible
613 void ShapeAttributeLayer::setVisibility( const bool& bVisible )
615 mbVisibility = bVisible;
616 mbVisibilityValid = true;
617 ++mnVisibilityState;
620 bool ShapeAttributeLayer::isCharColorValid() const
622 return mbCharColorValid || ( haveChild() && mpChild->isCharColorValid() );
625 RGBColor ShapeAttributeLayer::getCharColor() const
627 return calcValue( maCharColor,
628 mbCharColorValid,
629 &ShapeAttributeLayer::isCharColorValid,
630 &ShapeAttributeLayer::getCharColor );
633 void ShapeAttributeLayer::setCharColor( const RGBColor& nNewColor )
635 maCharColor = nNewColor;
636 mbCharColorValid = true;
637 ++mnContentState;
640 bool ShapeAttributeLayer::isCharWeightValid() const
642 return mbCharWeightValid || ( haveChild() && mpChild->isCharWeightValid() );
645 double ShapeAttributeLayer::getCharWeight() const
647 // mnAdditiveMode is ignored, cannot combine strings in
648 // any sensible way
649 if( mbCharWeightValid )
650 return mnCharWeight;
651 else if( haveChild() )
652 return mpChild->getCharWeight();
653 else
654 return awt::FontWeight::NORMAL;
657 void ShapeAttributeLayer::setCharWeight( const double& rValue )
659 // TODO(Q1): Check range here.
660 mnCharWeight = rValue;
661 mbCharWeightValid = true;
662 ++mnContentState;
665 bool ShapeAttributeLayer::isUnderlineModeValid() const
667 return mbUnderlineModeValid || ( haveChild() && mpChild->isUnderlineModeValid() );
670 sal_Int16 ShapeAttributeLayer::getUnderlineMode() const
672 // mnAdditiveMode is ignored, SMIL spec requires to not combine
673 // bools in any sensible way
674 if( mbUnderlineModeValid )
675 return mnUnderlineMode;
676 else if( haveChild() )
677 return mpChild->getUnderlineMode();
678 else
679 return awt::FontUnderline::NONE; // default is no underline
682 void ShapeAttributeLayer::setUnderlineMode( const sal_Int16& rUnderlineMode )
684 // TODO(Q1): Check range here.
685 mnUnderlineMode = rUnderlineMode;
686 mbUnderlineModeValid = true;
687 ++mnContentState;
690 bool ShapeAttributeLayer::isFontFamilyValid() const
692 return mbFontFamilyValid || ( haveChild() && mpChild->isFontFamilyValid() );
695 OUString ShapeAttributeLayer::getFontFamily() const
697 // mnAdditiveMode is ignored, cannot combine strings in
698 // any sensible way
699 if( mbFontFamilyValid )
700 return maFontFamily;
701 else if( haveChild() )
702 return mpChild->getFontFamily();
703 else
704 return OUString();
707 void ShapeAttributeLayer::setFontFamily( const OUString& rName )
709 maFontFamily = rName;
710 mbFontFamilyValid = true;
711 ++mnContentState;
714 bool ShapeAttributeLayer::isCharPostureValid() const
716 return mbCharPostureValid || ( haveChild() && mpChild->isCharPostureValid() );
719 sal_Int16 ShapeAttributeLayer::getCharPosture() const
721 // mnAdditiveMode is ignored, cannot combine strings in
722 // any sensible way
723 if( mbCharPostureValid )
724 return sal::static_int_cast<sal_Int16>(meCharPosture);
725 else if( haveChild() )
726 return sal::static_int_cast<sal_Int16>(mpChild->getCharPosture());
727 else
728 return sal::static_int_cast<sal_Int16>(awt::FontSlant_NONE);
731 void ShapeAttributeLayer::setCharPosture( const sal_Int16& rStyle )
733 // TODO(Q1): Check range here.
734 meCharPosture = static_cast<awt::FontSlant>(rStyle);
735 mbCharPostureValid = true;
736 ++mnContentState;
739 bool ShapeAttributeLayer::isCharScaleValid() const
741 return mbCharScaleValid || ( haveChild() && mpChild->isCharScaleValid() );
744 double ShapeAttributeLayer::getCharScale() const
746 return calcValue( mnCharScale,
747 mbCharScaleValid,
748 &ShapeAttributeLayer::isCharScaleValid,
749 &ShapeAttributeLayer::getCharScale );
752 void ShapeAttributeLayer::setCharScale( const double& rNewHeight )
754 ENSURE_OR_THROW( std::isfinite(rNewHeight),
755 "ShapeAttributeLayer::setCharScale(): Invalid height" );
757 mnCharScale = rNewHeight;
758 mbCharScaleValid = true;
759 ++mnContentState;
762 State::StateId ShapeAttributeLayer::getTransformationState() const
764 return haveChild() ?
765 ::std::max( mnTransformationState,
766 mpChild->getTransformationState() ) :
767 mnTransformationState;
770 State::StateId ShapeAttributeLayer::getClipState() const
772 return haveChild() ?
773 ::std::max( mnClipState,
774 mpChild->getClipState() ) :
775 mnClipState;
778 State::StateId ShapeAttributeLayer::getAlphaState() const
780 return haveChild() ?
781 ::std::max( mnAlphaState,
782 mpChild->getAlphaState() ) :
783 mnAlphaState;
786 State::StateId ShapeAttributeLayer::getPositionState() const
788 return haveChild() ?
789 ::std::max( mnPositionState,
790 mpChild->getPositionState() ) :
791 mnPositionState;
794 State::StateId ShapeAttributeLayer::getContentState() const
796 return haveChild() ?
797 ::std::max( mnContentState,
798 mpChild->getContentState() ) :
799 mnContentState;
802 State::StateId ShapeAttributeLayer::getVisibilityState() const
804 return haveChild() ?
805 ::std::max( mnVisibilityState,
806 mpChild->getVisibilityState() ) :
807 mnVisibilityState;
812 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */