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 #ifndef INCLUDED_XMLOFF_TXTPARAE_HXX
21 #define INCLUDED_XMLOFF_TXTPARAE_HXX
23 #include <sal/config.h>
24 #include <rtl/ref.hxx>
25 #include <xmloff/dllapi.h>
26 #include <rtl/ustring.hxx>
27 #include <com/sun/star/uno/Reference.h>
28 #include <xmloff/maptype.hxx>
29 #include <xmloff/styleexp.hxx>
30 #include <xmloff/xmltoken.hxx>
31 #include <xmloff/SinglePropertySetInfoCache.hxx>
32 #include <xmloff/XMLTextListAutoStylePool.hxx>
33 #include <o3tl/sorted_vector.hxx>
38 class XMLTextListsHelper
;
40 class SvXMLAutoStylePoolP
;
41 class XMLTextFieldExport
;
42 class XMLTextNumRuleInfo
;
43 class XMLSectionExport
;
44 class XMLIndexMarkExport
;
45 class XMLRedlineExport
;
46 class MultiPropertySetHelper
;
47 enum class XMLShapeExportFlags
;
48 class SvXMLExportPropertyMapper
;
50 namespace com::sun::star
52 namespace beans
{ class XPropertySet
; class XPropertyState
;
53 class XPropertySetInfo
; }
54 namespace container
{ class XEnumeration
; class XIndexAccess
; class XNameReplace
; }
55 namespace drawing
{ class XShape
; }
56 namespace text
{ class XTextContent
; class XTextRange
; class XText
;
57 class XFootnote
; class XTextFrame
; class XTextSection
;
63 class OFormLayerXMLExport
;
79 class XMLOFF_DLLPUBLIC XMLTextParagraphExport
: public XMLStyleExport
82 std::unique_ptr
<Impl
> m_xImpl
;
84 // SvXMLExport& rExport;
85 SvXMLAutoStylePoolP
& m_rAutoStylePool
;
86 rtl::Reference
< SvXMLExportPropertyMapper
> m_xParaPropMapper
;
87 rtl::Reference
< SvXMLExportPropertyMapper
> m_xTextPropMapper
;
88 rtl::Reference
< SvXMLExportPropertyMapper
> m_xFramePropMapper
;
89 rtl::Reference
< SvXMLExportPropertyMapper
> m_xAutoFramePropMapper
;
90 rtl::Reference
< SvXMLExportPropertyMapper
> m_xSectionPropMapper
;
91 rtl::Reference
< SvXMLExportPropertyMapper
> m_xRubyPropMapper
;
93 const ::std::unique_ptr
< ::xmloff::BoundFrameSets
> m_pBoundFrameSets
;
94 std::unique_ptr
<XMLTextFieldExport
> m_pFieldExport
;
95 std::vector
<OUString
> maListElements
;
96 XMLTextListAutoStylePool maListAutoPool
;
97 std::unique_ptr
<XMLSectionExport
> m_pSectionExport
;
98 std::unique_ptr
<XMLIndexMarkExport
> m_pIndexMarkExport
;
100 /// may be NULL (if no redlines should be exported; e.g. in block mode)
101 std::unique_ptr
<XMLRedlineExport
> m_pRedlineExport
;
107 // keep track of open rubies
108 OUString m_sOpenRubyText
;
109 OUString m_sOpenRubyCharStyle
;
112 XMLTextListsHelper
* mpTextListsHelper
;
113 ::std::vector
< std::unique_ptr
<XMLTextListsHelper
> > maTextListsHelperStack
;
115 struct DocumentListNodes
;
116 std::unique_ptr
<DocumentListNodes
> mpDocumentListNodes
;
117 std::vector
<sal_Int32
> maDocumentNodeOrder
;
118 bool bInDocumentNodeOrderCollection
= false;
120 o3tl::sorted_vector
<css::uno::Reference
<css::text::XTextFrame
>> maFrameRecurseGuard
;
121 o3tl::sorted_vector
<css::uno::Reference
<css::drawing::XShape
>> maShapeRecurseGuard
;
125 enum class FrameType
{ Text
, Graphic
, Embedded
, Shape
};
128 enum FieldmarkType
{ NONE
, TEXT
, CHECK
}; // Used for simulating fieldmarks in OpenDocument 1.n Strict (for n <= 2). CHECK currently ignored.
131 void exportTextRangeSpan(
132 const css::uno::Reference
< css::text::XTextRange
> & rTextRange
,
133 css::uno::Reference
< css::beans::XPropertySet
> const & xPropSet
,
134 css::uno::Reference
< css::beans::XPropertySetInfo
> & xPropSetInfo
,
135 const bool bIsUICharStyle
,
136 const bool bHasAutoStyle
,
137 const OUString
& sStyle
,
138 bool& rPrevCharIsSpace
,
139 FieldmarkType
& openFieldMark
);
143 // Implement Title/Description Elements UI (#i73249#)
144 static constexpr OUString gsAnchorCharStyleName
= u
"AnchorCharStyleName"_ustr
;
145 static constexpr OUString gsBeginNotice
= u
"BeginNotice"_ustr
;
146 static constexpr OUString gsCategory
= u
"Category"_ustr
;
147 static constexpr OUString gsCharStyleName
= u
"CharStyleName"_ustr
;
148 static constexpr OUString gsCharStyleNames
= u
"CharStyleNames"_ustr
;
149 static constexpr OUString gsEndNotice
= u
"EndNotice"_ustr
;
150 static constexpr OUString gsFootnote
= u
"Footnote"_ustr
;
151 static constexpr OUString gsFootnoteCounting
= u
"FootnoteCounting"_ustr
;
152 static constexpr OUString gsNumberingType
= u
"NumberingType"_ustr
;
153 static constexpr OUString gsPageDescName
= u
"PageDescName"_ustr
;
154 static constexpr OUString gsPageStyleName
= u
"PageStyleName"_ustr
;
155 static constexpr OUString gsParaStyleName
= u
"ParaStyleName"_ustr
;
156 static constexpr OUString gsPositionEndOfDoc
= u
"PositionEndOfDoc"_ustr
;
157 static constexpr OUString gsPrefix
= u
"Prefix"_ustr
;
158 static constexpr OUString gsReferenceId
= u
"ReferenceId"_ustr
;
159 static constexpr OUString gsStartAt
= u
"StartAt"_ustr
;
160 static constexpr OUString gsSuffix
= u
"Suffix"_ustr
;
161 static constexpr OUString gsTextEndnoteService
= u
"com.sun.star.text.Endnote"_ustr
;
162 static constexpr OUString gsTextSection
= u
"TextSection"_ustr
;
165 static constexpr OUString gsFrameStyleName
= u
"FrameStyleName"_ustr
;
166 SinglePropertySetInfoCache m_aCharStyleNamesPropInfoCache
;
168 SvXMLAutoStylePoolP
& GetAutoStylePool() { return m_rAutoStylePool
; }
169 const SvXMLAutoStylePoolP
& GetAutoStylePool() const { return m_rAutoStylePool
; }
172 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetParaPropMapper() const
174 return m_xParaPropMapper
;
177 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetTextPropMapper() const
179 return m_xTextPropMapper
;
182 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetAutoFramePropMapper() const
184 return m_xAutoFramePropMapper
;
186 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetSectionPropMapper() const
188 return m_xSectionPropMapper
;
190 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetRubyPropMapper() const
192 return m_xRubyPropMapper
;
195 OUString
FindTextStyle(
196 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
197 bool& rbHasCharStyle
,
198 bool& rbHasAutoStyle
,
199 const XMLPropertyState
** pAddState
= nullptr) const;
201 void exportTextRangeEnumeration(
202 const css::uno::Reference
< css::container::XEnumeration
> & rRangeEnum
,
203 bool bAutoStyles
, bool bProgress
, bool & rPrevCharIsSpace
);
207 XMLShapeExportFlags
addTextFrameAttributes(
208 const css::uno::Reference
< css::beans::XPropertySet
>& rPropSet
,
210 basegfx::B2DPoint
* pCenter
= nullptr,
211 OUString
*pMinHeightValue
= nullptr,
212 OUString
*pMinWidthValue
= nullptr );
214 virtual void exportStyleAttributes(
215 const css::uno::Reference
< css::style::XStyle
> & rStyle
) override final
;
217 void exportPageFrames( bool bProgress
);
218 void exportFrameFrames( bool bAutoStyles
, bool bProgress
,
219 const css::uno::Reference
< css::text::XTextFrame
>& rParentTxtFrame
);
221 void exportNumStyles( bool bUsed
);
224 const css::uno::Reference
<
225 css::text::XText
> & rText
,
226 bool bAutoStyles
, bool bProgress
, bool bExportParagraph
, TextPNS eExtensionNS
= TextPNS::ODF
);
229 const css::uno::Reference
< css::text::XText
> & rText
,
230 const css::uno::Reference
< css::text::XTextSection
> & rBaseSection
,
231 bool bAutoStyles
, bool bProgress
, bool bExportParagraph
);
233 void exportTextContentEnumeration(
234 const css::uno::Reference
< css::container::XEnumeration
> & rContentEnum
,
236 const css::uno::Reference
< css::text::XTextSection
> & rBaseSection
,
238 bool bExportParagraph
= true,
239 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
= nullptr,
240 TextPNS eExtensionNS
= TextPNS::ODF
);
241 void exportParagraph(
242 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
243 bool bAutoStyles
, bool bProgress
,
244 bool bExportParagraph
,
245 MultiPropertySetHelper
& rPropSetHelper
,
246 TextPNS eExtensionNS
);
248 virtual void exportTable(
249 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
250 bool bAutoStyles
, bool bProgress
);
252 void exportTextField(
253 const css::uno::Reference
< css::text::XTextRange
> & rTextRange
,
254 bool bAutoStyles
, bool bProgress
, bool * pPrevCharIsSpace
);
256 void exportTextField(
257 const css::uno::Reference
< css::text::XTextField
> & xTextField
,
258 const bool bAutoStyles
, const bool bProgress
,
259 const bool bRecursive
, bool * pPrevCharIsSpace
);
261 void exportAnyTextFrame(
262 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
264 bool bAutoStyles
, bool bProgress
, bool bExportContent
,
265 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
);
266 void _exportTextFrame(
267 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
268 const css::uno::Reference
< css::beans::XPropertySetInfo
> & rPropSetInfo
,
270 inline void exportTextFrame(
271 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
272 bool bAutoStyles
, bool bProgress
, bool bExportContent
,
273 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
= nullptr );
274 inline void exportShape(
275 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
277 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
= nullptr );
280 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
281 const css::uno::Reference
< css::beans::XPropertySetInfo
> & rPropSetInfo
);
282 void _exportTextGraphic(
283 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
284 const css::uno::Reference
< css::beans::XPropertySetInfo
> & rPropSetInfo
);
285 inline void exportTextGraphic(
286 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
288 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
= nullptr );
290 virtual void _collectTextEmbeddedAutoStyles(
291 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
);
292 virtual void _exportTextEmbedded(
293 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
294 const css::uno::Reference
< css::beans::XPropertySetInfo
> & rPropSetInfo
);
295 inline void exportTextEmbedded(
296 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
298 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
= nullptr );
300 /// export a footnote and styles
301 void exportTextFootnote(
302 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
303 const OUString
& sString
,
304 bool bAutoStyles
, bool bProgress
);
306 /// helper for exportTextFootnote
307 void exportTextFootnoteHelper(
308 const css::uno::Reference
< css::text::XFootnote
> & rPropSet
,
309 const css::uno::Reference
< css::text::XText
> & rText
,
310 const OUString
& sString
,
312 bool bIsEndnote
, bool bProgress
);
314 /// export footnote and endnote configuration elements
315 void exportTextFootnoteConfiguration();
317 void exportTextFootnoteConfigurationHelper(
318 const css::uno::Reference
< css::beans::XPropertySet
> & rFootnoteSupplier
,
322 const css::uno::Reference
< css::beans::XPropertySet
> & xPropSet
,
323 const OUString
& rProperty
,
324 const enum ::xmloff::token::XMLTokenEnum pElements
[],
327 void exportSoftPageBreak();
329 void exportTextLineBreak(const css::uno::Reference
<css::beans::XPropertySet
>& xPropSet
);
331 void exportTextRange(
332 const css::uno::Reference
< css::text::XTextRange
> & rTextRange
,
334 bool& rPrevCharWasSpace
,
335 FieldmarkType
& openFieldmarkType
);
337 void exportListChange( const XMLTextNumRuleInfo
& rPrvInfo
,
338 const XMLTextNumRuleInfo
& rNextInfo
);
340 /// check if current section or current list has changed;
341 /// calls exportListChange as appropriate
342 void exportListAndSectionChange(
343 css::uno::Reference
< css::text::XTextSection
> & rOldSection
,
344 const css::uno::Reference
< css::text::XTextSection
> & rNewSection
,
345 const XMLTextNumRuleInfo
& rOldList
,
346 const XMLTextNumRuleInfo
& rNewList
,
349 /// overload for exportListAndSectionChange;
350 /// takes new content rather than new section.
351 void exportListAndSectionChange(
352 css::uno::Reference
< css::text::XTextSection
> & rOldSection
,
353 const css::uno::Reference
< css::text::XTextContent
> & rNewContent
,
354 const XMLTextNumRuleInfo
& rOldList
,
355 const XMLTextNumRuleInfo
& rNewList
,
357 void exportListAndSectionChange(
358 css::uno::Reference
< css::text::XTextSection
> & rOldSection
,
359 MultiPropertySetHelper
& rPropSetHelper
,
360 sal_Int16 nTextSectionId
,
361 const css::uno::Reference
< css::text::XTextContent
> & rNewContent
,
362 const XMLTextNumRuleInfo
& rOldList
,
363 const XMLTextNumRuleInfo
& rNewList
,
368 const css::uno::Reference
< css::beans::XPropertySet
> & rPortionPropSet
,
371 /// export a text:meta
373 const css::uno::Reference
< css::beans::XPropertySet
> & i_xPortion
,
374 bool i_bAutoStyles
, bool i_isProgress
, bool & rPrevCharIsSpace
);
376 /// Exports a <loext:content-control> element.
377 void ExportContentControl(const css::uno::Reference
<css::beans::XPropertySet
>& xPortion
,
378 bool bAutoStyles
, bool isProgress
, bool& rPrevCharIsSpace
);
380 bool isAutoStylesCollected() const { return mbCollected
; }
382 virtual void exportTableAutoStyles();
386 XMLTextParagraphExport(
388 SvXMLAutoStylePoolP
& rASP
390 virtual ~XMLTextParagraphExport() override
;
392 /// add autostyle for specified family
394 XmlStyleFamily nFamily
,
395 MultiPropertySetHelper
& rPropSetHelper
,
396 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
);
398 XmlStyleFamily nFamily
,
399 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
400 std::span
<const XMLPropertyState
> aAddStates
= {}, bool bDontSeek
= false );
402 /// find style name for specified family and parent
404 XmlStyleFamily nFamily
,
405 const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
406 const OUString
& rParent
,
407 const std::span
<const XMLPropertyState
> aAddStates
= {} ) const;
409 static SvXMLExportPropertyMapper
*CreateShapeExtPropMapper(
410 SvXMLExport
& rExport
);
411 static SvXMLExportPropertyMapper
*CreateCharExtPropMapper(
412 SvXMLExport
& rExport
);
413 static SvXMLExportPropertyMapper
*CreateParaExtPropMapper(
414 SvXMLExport
& rExport
);
415 static SvXMLExportPropertyMapper
*CreateParaDefaultExtPropMapper(
416 SvXMLExport
& rExport
);
418 // This methods exports all (or all used) styles
419 void exportTextStyles( bool bUsed
, bool bProg
);
421 /// This method exports (text field) declarations etc.
422 void exportTextDeclarations();
424 /// export the (text field) declarations for a particular XText
425 void exportTextDeclarations(
426 const css::uno::Reference
< css::text::XText
> & rText
);
428 /// export all declarations
429 void exportUsedDeclarations();
431 /// Export the list of change information (enclosed by <tracked-changes>)
432 /// (or the necessary automatic styles)
433 void exportTrackedChanges(bool bAutoStyle
);
435 /// Export the list of change information (enclosed by <tracked-changes>)
436 /// (or the necessary automatic styles)
437 void exportTrackedChanges(const css::uno::Reference
< css::text::XText
> & rText
,
440 /// Record tracked changes for this particular XText
441 /// (empty reference stop recording)
442 /// This should be used if tracked changes for e.g. footers are to
443 /// be exported separately via the exportTrackedChanges(bool,
444 /// Reference<XText>) method.
445 void recordTrackedChangesForXText(
446 const css::uno::Reference
< css::text::XText
> & rText
);
449 /// Stop recording tracked changes.
450 /// This is the same as calling recordTrackedChanges(...) with an
452 void recordTrackedChangesNoXText();
455 // This method exports the given OUString
456 void exportCharacterData(
457 const OUString
& rText
,
458 bool& rPrevCharWasSpace
);
460 // This method collects all automatic styles for the given XText
461 void collectTextAutoStyles(
462 const css::uno::Reference
< css::text::XText
> & rText
,
463 bool bIsProgress
= false,
464 bool bExportParagraph
= true )
466 exportText( rText
, true, bIsProgress
, bExportParagraph
);
469 void collectTextAutoStyles(
470 const css::uno::Reference
< css::text::XText
> & rText
,
471 const css::uno::Reference
< css::text::XTextSection
> & rBaseSection
,
474 exportText( rText
, rBaseSection
, true, bIsProgress
, true/*bExportParagraph*/ );
477 void collectTextAutoStylesAndNodeExportOrder(bool bIsProgress
);
479 // This method exports all automatic styles that have been collected.
480 void exportTextAutoStyles();
482 void exportEvents( const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
);
484 // Implement Title/Description Elements UI (#i73249#)
485 void exportTitleAndDescription( const css::uno::Reference
< css::beans::XPropertySet
> & rPropSet
,
486 const css::uno::Reference
< css::beans::XPropertySetInfo
> & rPropSetInfo
);
488 // This method exports the given XText
490 const css::uno::Reference
< css::text::XText
> & rText
,
491 bool bIsProgress
= false,
492 bool bExportParagraph
= true, TextPNS eExtensionNS
= TextPNS::ODF
)
494 exportText( rText
, false, bIsProgress
, bExportParagraph
, eExtensionNS
);
498 const css::uno::Reference
< css::text::XText
> & rText
,
499 const css::uno::Reference
< css::text::XTextSection
> & rBaseSection
,
502 exportText( rText
, rBaseSection
, false, bIsProgress
, true/*bExportParagraph*/ );
505 void exportFramesBoundToPage( bool bIsProgress
)
507 exportPageFrames( bIsProgress
);
509 inline const XMLTextListAutoStylePool
& GetListAutoStylePool() const;
511 void SetBlockMode( bool bSet
) { m_bBlock
= bSet
; }
512 bool IsBlockMode() const { return m_bBlock
; }
515 const rtl::Reference
< SvXMLExportPropertyMapper
>& GetParagraphPropertyMapper() const
517 return m_xParaPropMapper
;
521 /** exclude form controls which are in mute sections.
523 * This method is necessary to prevent the form layer export from exporting
524 * control models whose controls are not represented in the document. To
525 * achieve this, this method iterates over all shapes, checks to see if
526 * they are control shapes, and if so, whether they should be exported or
527 * not. If not, the form layer export will be notified accordingly.
529 * The reason this method is located here is that it needs to access the
530 * XMLSectionExport, which is only available here.
532 void PreventExportOfControlsInMuteSections(
533 const css::uno::Reference
< css::container::XIndexAccess
> & rShapes
,
534 const rtl::Reference
<xmloff::OFormLayerXMLExport
>& xFormExport
);
536 SinglePropertySetInfoCache
& GetCharStyleNamesPropInfoCache() { return m_aCharStyleNamesPropInfoCache
; }
538 void PushNewTextListsHelper();
540 void PopTextListsHelper();
543 void RecordNodeIndex(const css::uno::Reference
<css::text::XTextContent
>& xTextContent
);
544 bool ShouldSkipListId(const css::uno::Reference
<css::text::XTextContent
>& xTextContent
);
545 bool ExportListId() const;
547 XMLTextParagraphExport(XMLTextParagraphExport
const &) = delete;
551 inline const XMLTextListAutoStylePool
&
552 XMLTextParagraphExport::GetListAutoStylePool() const
554 return maListAutoPool
;
557 inline void XMLTextParagraphExport::exportTextFrame(
558 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
559 bool bAutoStyles
, bool bIsProgress
, bool bExportContent
,
560 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
)
562 exportAnyTextFrame( rTextContent
, FrameType::Text
, bAutoStyles
, bIsProgress
,
563 bExportContent
, pRangePropSet
);
566 inline void XMLTextParagraphExport::exportTextGraphic(
567 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
569 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
)
571 exportAnyTextFrame( rTextContent
, FrameType::Graphic
, bAutoStyles
, false,
572 true, pRangePropSet
);
575 inline void XMLTextParagraphExport::exportTextEmbedded(
576 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
578 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
)
580 exportAnyTextFrame( rTextContent
, FrameType::Embedded
, bAutoStyles
, false,
581 true, pRangePropSet
);
584 inline void XMLTextParagraphExport::exportShape(
585 const css::uno::Reference
< css::text::XTextContent
> & rTextContent
,
587 const css::uno::Reference
< css::beans::XPropertySet
> *pRangePropSet
)
589 exportAnyTextFrame( rTextContent
, FrameType::Shape
, bAutoStyles
, false,
590 true, pRangePropSet
);
595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */