bump product version to 7.6.3.2-android
[LibreOffice.git] / xmloff / source / forms / elementimport.hxx
blobbf91ac02136c56e6d994a2add67eff3de906867d
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 .
20 #pragma once
22 #include <sal/config.h>
24 #include "propertyimport.hxx"
25 #include "controlelement.hxx"
26 #include "valueproperties.hxx"
27 #include "eventimport.hxx"
28 #include "logging.hxx"
29 #include "property_description.hxx"
31 #include <com/sun/star/text/XTextCursor.hpp>
32 #include <com/sun/star/container/XNameContainer.hpp>
33 #include <com/sun/star/form/XGridColumnFactory.hpp>
34 #include <com/sun/star/graphic/XGraphic.hpp>
35 #include <osl/diagnose.h>
37 #include <map>
38 #include <vector>
40 class XMLTextStyleContext;
41 namespace xmloff
44 class OFormLayerXMLImport_Impl;
46 //= OElementNameMap
47 const OControlElement::ElementType& operator ++(OControlElement::ElementType& _e);
49 /** helper class which allows fast translation of xml tag names into element types.
51 class OElementNameMap : public OControlElement
53 typedef std::map<OUString, ElementType> MapString2Element;
54 static std::map<sal_Int32, ElementType> s_sElementTranslations2;
56 OElementNameMap() = delete;
58 public:
59 static ElementType getElementType(sal_Int32 nToken);
62 //= OElementImport
63 /** implements common behaviour for importing forms, controls and columns
65 class OElementImport
66 :public OPropertyImport
67 ,public IEventAttacher
68 ,public OStackedLogging
70 protected:
71 OUString m_sServiceName; // the service name as extracted from the service-name attribute
72 OUString m_sName; // the name of the object (redundant, already contained in the base class' array)
73 OFormLayerXMLImport_Impl& m_rFormImport; // the form import context
74 IEventAttacherManager& m_rEventManager; // the event attacher manager
76 const XMLTextStyleContext* m_pStyleElement; // the XML element which describes the style we encountered
77 // while reading our element
79 /// the parent container to insert the new element into
80 css::uno::Reference< css::container::XNameContainer >
81 m_xParentContainer;
83 /// the element we're creating. Valid after StartElement
84 css::uno::Reference< css::beans::XPropertySet >
85 m_xElement;
86 css::uno::Reference< css::beans::XPropertySetInfo >
87 m_xInfo;
89 bool m_bImplicitGenericAttributeHandling;
91 public:
92 /** ctor
93 @param _rImport
94 the importer
95 @param _rEventManager
96 the event attacher manager for the control being imported
97 @param _rAttributeMap
98 the attribute map to be used for translating attributes into properties
99 @param _rxParentContainer
100 the container in which the new element should be inserted
102 OElementImport(
103 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
104 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer
106 virtual ~OElementImport() override;
108 protected:
109 // SvXMLImportContext overridables
110 virtual void SAL_CALL startFastElement( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
111 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
112 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
113 sal_Int32 nElement,
114 const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
116 // OPropertyImport overridables
117 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
119 // IEventAttacher
120 virtual void registerEvents(
121 const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents
122 ) override;
124 /** create the (uninitialized) element which is to represent the read data
126 <p>The default implementation uses <member>m_xORB</member> to create an object with <member>m_sServiceName</member>.
128 virtual css::uno::Reference< css::beans::XPropertySet >
129 createElement();
131 protected:
132 /** can be used to handle properties where the attribute default and the property default differ.
133 <p>In such case, if the property had the attribute default upon writing, nothing is read, so upon reading,
134 the property is still at its own default (which is not the attribute default).<p/>
135 <p>This method, if told the attribute and the property, and the (implied) attribute default, sets the
136 property value as if the attribute was encountered.</p>
137 @see encounteredAttribute
139 void simulateDefaultedAttribute(sal_Int32 nElement, const OUString& _rPropertyName, const char* _pAttributeDefault);
141 /** to be called from within handleAttribute, checks whether the given attribute is covered by our generic
142 attribute handler mechanisms
144 bool tryGenericAttribute( sal_Int32 nElement, const OUString& _rValue );
146 /** controls whether |handleAttribute| implicitly calls |tryGenericAttribute|, or whether the derived class
147 must do this explicitly at a suitable place in its own |handleAttribute|
149 void disableImplicitGenericAttributeHandling() { m_bImplicitGenericAttributeHandling = false; }
151 private:
152 OUString implGetDefaultName() const;
153 void implApplyGenericProperties();
154 void implApplySpecificProperties();
156 PropertyGroups::const_iterator impl_matchPropertyGroup( const PropertyGroups& i_propertyGroups ) const;
158 virtual OUString determineDefaultServiceName() const;
161 //= OControlImport
162 /** helper class for importing the description of a single control
164 class OControlImport
165 :public OElementImport
166 ,public OValuePropertiesMetaData
168 protected:
169 OUString m_sControlId;
170 OControlElement::ElementType m_eElementType;
172 PropertyValueArray m_aValueProperties;
173 // the value properties (value, current-value, min-value, max-value) require some special
174 // handling
176 // we fake the attributes our base class gets: we add the attributes of the outer wrapper
177 // element which encloses us
178 css::uno::Reference< css::xml::sax::XFastAttributeList >
179 m_xOuterAttributes;
181 /** the address of the calc cell which the control model should be bound to,
182 if applicable
184 OUString m_sBoundCellAddress;
186 /** name of a value binding (xforms:bind attribute) */
187 OUString m_sBindingID;
189 /** name of a list binding (form:xforms-list-source attribute) */
190 OUString m_sListBindingID;
192 /** name of a submission (xforms:submission attribute) */
193 OUString m_sSubmissionID;
195 protected:
196 // for use by derived classes only
197 OControlImport(
198 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
199 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer
202 public:
203 OControlImport(
204 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
205 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
206 OControlElement::ElementType _eType
209 // SvXMLImportContext overridables
210 virtual void SAL_CALL startFastElement( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
211 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
213 // OPropertyImport overridables
214 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
216 void addOuterAttributes(const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxOuterAttribs);
218 protected:
219 void setElementType(OControlElement::ElementType _eType) { m_eElementType = _eType; }
221 protected:
222 static void implTranslateValueProperty(
223 const css::uno::Reference< css::beans::XPropertySetInfo >& _rxPropInfo,
224 css::beans::PropertyValue& /* [in/out] */ _rPropValue);
226 virtual OUString determineDefaultServiceName() const override;
228 /** registers the given cell address as value binding address for our element
230 <p>The default implementation simply calls registerCellValueBinding at our import
231 context, but you may want to override this behaviour.</p>
233 @param _rBoundCellAddress
234 the cell address to register for our element. Must not be <NULL/>.
235 @precond
236 we have a valid element (m_xElement)
238 virtual void doRegisterCellValueBinding( const OUString& _rBoundCellAddress );
240 /** register the given XForms binding */
241 void doRegisterXFormsValueBinding( const OUString& );
243 /** register the given XForms list binding */
244 void doRegisterXFormsListBinding( const OUString& );
246 /** register the given XForms submission */
247 void doRegisterXFormsSubmission( const OUString& );
249 protected:
251 // OElementImport overridables
252 virtual css::uno::Reference< css::beans::XPropertySet >
253 createElement() override;
256 // TODO:
257 // this whole mechanism doesn't scale. Instead of deriving even more classes for every new attribute,
258 // we should have dedicated attribute handlers
259 // The rest of xmloff implements it this way - why don't we do, too?
261 //= OImagePositionImport
262 class OImagePositionImport : public OControlImport
264 css::uno::Reference<css::graphic::XGraphic> m_xGraphic;
265 sal_Int16 m_nImagePosition;
266 sal_Int16 m_nImageAlign;
267 bool m_bHaveImagePosition;
269 public:
270 OImagePositionImport(
271 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
272 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
273 OControlElement::ElementType _eType
276 protected:
277 // SvXMLImportContext overridables
278 virtual void SAL_CALL startFastElement(
279 sal_Int32 nElement,
280 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override;
282 // OPropertyImport overridables
283 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
286 //= OReferredControlImport
287 class OReferredControlImport : public OControlImport
289 OUString m_sReferringControls; // the list of ids of controls referring to the one being imported
291 public:
292 OReferredControlImport(
293 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
294 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer
297 // SvXMLImportContext overridables
298 virtual void SAL_CALL startFastElement(
299 sal_Int32 nElement,
300 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override;
302 // OPropertyImport overridables
303 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
306 //= OPasswordImport
307 class OPasswordImport : public OControlImport
309 public:
310 OPasswordImport(
311 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
312 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
313 OControlElement::ElementType _eType
316 // OPropertyImport overridables
317 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
320 //= ORadioImport
321 class ORadioImport : public OImagePositionImport
323 public:
324 ORadioImport(
325 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
326 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
327 OControlElement::ElementType _eType
330 protected:
331 // OPropertyImport overridables
332 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
335 //= OURLReferenceImport
336 /** a specialized version of the <type>OControlImport</type> class, which is able
337 to handle attributes which denote URLs (and stored relative)
339 class OURLReferenceImport : public OImagePositionImport
341 public:
342 OURLReferenceImport(
343 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
344 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
345 OControlElement::ElementType _eType
348 protected:
349 // OPropertyImport overridables
350 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
353 //= OButtonImport
354 /** A specialized version of the <type>OControlImport</type> class, which handles
355 the target frame for image and command buttons
357 class OButtonImport : public OURLReferenceImport
359 public:
360 OButtonImport(
361 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
362 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
363 OControlElement::ElementType _eType
366 protected:
367 // SvXMLImportContext overridables
368 virtual void SAL_CALL startFastElement(
369 sal_Int32 nElement,
370 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override;
373 //= OValueRangeImport
374 /** A specialized version of the <type>OControlImport</type> class, which imports
375 the value-range elements
377 class OValueRangeImport : public OControlImport
379 private:
380 sal_Int32 m_nStepSizeValue;
382 public:
383 OValueRangeImport(
384 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
385 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
386 OControlElement::ElementType _eType
389 protected:
390 // SvXMLImportContext overridables
391 virtual void SAL_CALL startFastElement(
392 sal_Int32 nElement,
393 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList ) override;
395 // OPropertyImport overridables
396 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
399 //= OTextLikeImport
400 /** A specialized version of the <type>OControlImport</type> class, which handles
401 text like controls which have the convert-empty-to-null attribute</p>
403 class OTextLikeImport : public OControlImport
405 private:
406 css::uno::Reference< css::text::XTextCursor > m_xCursor;
407 css::uno::Reference< css::text::XTextCursor > m_xOldCursor;
408 bool m_bEncounteredTextPara;
410 public:
411 OTextLikeImport(
412 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
413 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
414 OControlElement::ElementType _eType
417 // SvXMLImportContext overridables
418 virtual void SAL_CALL startFastElement(
419 sal_Int32 nElement,
420 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override;
421 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
422 sal_Int32 nElement,
423 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
424 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
426 private:
427 void adjustDefaultControlProperty();
428 void removeRedundantCurrentValue();
431 //= OListAndComboImport
432 /** A specialized version of the <type>OControlImport</type> class, which handles
433 attributes / sub elements which are special to list and combo boxes
435 class OListAndComboImport : public OControlImport
437 friend class OListOptionImport;
438 friend class OComboItemImport;
440 protected:
441 std::vector<OUString >
442 m_aListSource;
443 std::vector< OUString >
444 m_aValueList;
446 std::vector< sal_Int16 >
447 m_aSelectedSeq;
448 std::vector< sal_Int16 >
449 m_aDefaultSelectedSeq;
451 OUString m_sCellListSource; /// the cell range which acts as list source for the control
453 sal_Int32 m_nEmptyListItems; /// number of empty list items encountered during reading
454 sal_Int32 m_nEmptyValueItems; /// number of empty value items encountered during reading
456 bool m_bEncounteredLSAttrib;
457 bool m_bLinkWithIndexes; /** <TRUE/> if and only if we should use a cell value binding
458 which exchanges the selection index (instead of the selection text
461 public:
462 OListAndComboImport(
463 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
464 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
465 OControlElement::ElementType _eType
468 // SvXMLImportContext overridables
469 virtual void SAL_CALL startFastElement( sal_Int32 nElement,
470 const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override;
471 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
472 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
473 sal_Int32 nElement,
474 const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
476 // OPropertyImport overridables
477 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
479 // OControlImport overridables
480 virtual void doRegisterCellValueBinding( const OUString& _rBoundCellAddress ) override;
482 protected:
483 void implPushBackLabel(const OUString& _rLabel);
484 void implPushBackValue(const OUString& _rValue);
486 void implEmptyLabelFound();
487 void implEmptyValueFound();
489 void implSelectCurrentItem();
490 void implDefaultSelectCurrentItem();
492 typedef rtl::Reference<OListAndComboImport> OListAndComboImportRef;
494 //= OListOptionImport
495 /** helper class for importing a single &lt;form:option&gt; element.
497 class OListOptionImport
498 :public SvXMLImportContext
500 OListAndComboImportRef m_xListBoxImport;
502 public:
503 OListOptionImport(SvXMLImport& _rImport,
504 OListAndComboImportRef _xListBox);
506 virtual void SAL_CALL startFastElement( sal_Int32 nElement,
507 const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override;
510 //= OComboItemImport
511 /** helper class for importing a single &lt;form:item&gt; element.
513 class OComboItemImport
514 :public SvXMLImportContext
516 OListAndComboImportRef m_xListBoxImport;
518 public:
519 OComboItemImport(SvXMLImport& _rImport,
520 OListAndComboImportRef _xListBox);
522 protected:
523 // SvXMLImportContext overridables
524 virtual void SAL_CALL startFastElement( sal_Int32 nElement,
525 const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override;
529 //= OColumnImport
530 /** helper class importing a single grid column (without the &lt;form:column&gt; element wrapping
531 the column).
533 <p>BASE (the template argument) must be a derivee of OControlImport</p>
535 template <class BASE>
536 class OColumnImport : public BASE
538 css::uno::Reference< css::form::XGridColumnFactory >
539 m_xColumnFactory;
541 public:
542 OColumnImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
543 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
544 OControlElement::ElementType _eType);
546 protected:
547 // OElementImport overridables
548 virtual css::uno::Reference< css::beans::XPropertySet >
549 createElement() override;
552 //= OColumnWrapperImport
553 class OColumnWrapperImport : public SvXMLImportContext
555 css::uno::Reference< css::xml::sax::XFastAttributeList >
556 m_xOwnAttributes;
557 css::uno::Reference< css::container::XNameContainer >
558 m_xParentContainer;
559 OFormLayerXMLImport_Impl& m_rFormImport;
560 IEventAttacherManager& m_rEventManager;
562 public:
563 OColumnWrapperImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
564 sal_Int32 nElement,
565 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer);
567 // SvXMLImportContext overridables
568 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
569 sal_Int32 nElement,
570 const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
571 virtual void SAL_CALL startFastElement(
572 sal_Int32 nElement,
573 const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override;
574 private:
575 OControlImport* implCreateChildContext(
576 sal_Int32 nElement,
577 OControlElement::ElementType _eType);
580 /** helper class importing a single &lt;form:grid&gt; element
582 class OGridImport : public OControlImport, public ODefaultEventAttacherManager
584 public:
585 OGridImport(
586 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
587 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
588 OControlElement::ElementType _eType);
590 // SvXMLImportContext overridables
591 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
592 sal_Int32 nElement,
593 const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
594 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
596 private:
597 // OElementImport overridables
598 virtual css::uno::Reference< css::beans::XPropertySet > createElement() override;
600 css::uno::Reference< css::container::XNameContainer > m_xMeAsContainer;
603 /** helper class importing a single &lt;form:form&gt; element
605 class OFormImport : public OElementImport, public ODefaultEventAttacherManager
607 public:
608 OFormImport(
609 OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager,
610 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer
613 private:
614 // SvXMLImportContext overridables
615 virtual void SAL_CALL startFastElement(
616 sal_Int32 nElement,
617 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList) override;
618 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
620 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
621 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
623 // OPropertyImport overridables
624 virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override;
626 // OElementImport overridables
627 virtual css::uno::Reference< css::beans::XPropertySet >
628 createElement() override;
630 virtual OUString determineDefaultServiceName() const override;
631 void implTranslateStringListProperty(const OUString& _rPropertyName, const OUString& _rValue);
633 css::uno::Reference< css::container::XNameContainer > m_xMeAsContainer;
636 //= OXMLDataSourceImport
637 class OXMLDataSourceImport : public SvXMLImportContext
639 public:
640 OXMLDataSourceImport( SvXMLImport& _rImport
641 ,const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList
642 ,const css::uno::Reference< css::beans::XPropertySet >& _xElement);
645 //= OColumnImport
646 template <class BASE>
647 OColumnImport< BASE >::OColumnImport(OFormLayerXMLImport_Impl& _rImport,
648 IEventAttacherManager& _rEventManager,
649 const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer,
650 OControlElement::ElementType _eType)
651 :BASE(_rImport, _rEventManager, _rxParentContainer, _eType)
652 ,m_xColumnFactory(_rxParentContainer, css::uno::UNO_QUERY)
654 OSL_ENSURE(m_xColumnFactory.is(), "OColumnImport::OColumnImport: invalid parent container (no factory)!");
657 // OElementImport overridables
658 template <class BASE>
659 css::uno::Reference< css::beans::XPropertySet > OColumnImport< BASE >::createElement()
661 css::uno::Reference< css::beans::XPropertySet > xReturn;
662 // no call to the base class' method. We have to use the grid column factory
663 if (m_xColumnFactory.is())
665 // create the column
666 xReturn = m_xColumnFactory->createColumn(this->m_sServiceName);
667 OSL_ENSURE(xReturn.is(), "OColumnImport::createElement: the factory returned an invalid object!");
669 return xReturn;
672 } // namespace xmloff
674 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */