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/.
10 #include <osl/module.hxx>
11 #include <sal/log.hxx>
12 #include <unotools/configmgr.hxx>
13 #include <vcl/builder.hxx>
14 #include <vcl/button.hxx>
15 #include <vcl/dialog.hxx>
16 #include <vcl/edit.hxx>
17 #include <vcl/field.hxx>
18 #include <vcl/fixed.hxx>
19 #include <vcl/fixedhyper.hxx>
20 #include <vcl/layout.hxx>
21 #include <vcl/lstbox.hxx>
22 #include <vcl/menubtn.hxx>
23 #include <vcl/mnemonic.hxx>
24 #include <vcl/prgsbar.hxx>
25 #include <vcl/scrbar.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/tabctrl.hxx>
28 #include <vcl/tabpage.hxx>
29 #include <vcl/toolbox.hxx>
30 #include <vcl/vclmedit.hxx>
35 using namespace com::sun::star
;
37 #ifdef DISABLE_DYNLOADING
38 #include <dlfcn.h> // For RTLD_DEFAULT
43 sal_uInt16
mapStockToImageResource(OString sType
)
46 if (sType
== "gtk-index")
47 nRet
= SV_RESID_BITMAP_INDEX
;
48 else if (sType
== "gtk-refresh")
49 nRet
= SV_RESID_BITMAP_REFRESH
;
53 SymbolType
mapStockToSymbol(OString sType
)
55 SymbolType eRet
= SYMBOL_NOSYMBOL
;
56 if (sType
== "gtk-media-next")
58 else if (sType
== "gtk-media-previous")
60 else if (sType
== "gtk-goto-first")
62 else if (sType
== "gtk-goto-last")
64 else if (sType
== "gtk-go-back")
65 eRet
= SYMBOL_ARROW_LEFT
;
66 else if (sType
== "gtk-go-forward")
67 eRet
= SYMBOL_ARROW_RIGHT
;
68 else if (sType
== "gtk-go-up")
69 eRet
= SYMBOL_ARROW_UP
;
70 else if (sType
== "gtk-go-down")
71 eRet
= SYMBOL_ARROW_DOWN
;
72 else if (sType
== "gtk-missing-image")
74 else if (sType
== "gtk-help")
76 else if (sType
== "gtk-close")
78 else if (mapStockToImageResource(sType
))
84 void VclBuilder::loadTranslations(const com::sun::star::lang::Locale
&rLocale
, const OUString
& rUri
)
86 for (int i
= rLocale
.Country
.isEmpty() ? 1 : 0; i
< 2; ++i
)
88 OUStringBuffer aTransBuf
;
89 sal_Int32 nLastSlash
= rUri
.lastIndexOf('/');
91 aTransBuf
.append(rUri
.copy(0, nLastSlash
));
94 aTransBuf
.append('.');
97 aTransBuf
.append("/res/").append(rLocale
.Language
);
101 aTransBuf
.append('-').append(rLocale
.Country
);
106 sal_Int32 nEndName
= rUri
.lastIndexOf('.');
108 nEndName
= rUri
.getLength();
109 aTransBuf
.append(rUri
.copy(nLastSlash
, nEndName
-nLastSlash
));
111 OUString sTransUri
= aTransBuf
.makeStringAndClear();
114 xmlreader::XmlReader
reader(sTransUri
);
115 handleTranslations(reader
);
118 catch (const ::com::sun::star::uno::Exception
&)
124 #if defined SAL_LOG_WARN
127 bool isButtonType(WindowType nType
)
129 return nType
== WINDOW_PUSHBUTTON
||
130 nType
== WINDOW_OKBUTTON
||
131 nType
== WINDOW_CANCELBUTTON
||
132 nType
== WINDOW_HELPBUTTON
||
133 nType
== WINDOW_IMAGEBUTTON
||
134 nType
== WINDOW_MENUBUTTON
||
135 nType
== WINDOW_MOREBUTTON
||
136 nType
== WINDOW_SPINBUTTON
;
141 VclBuilder::VclBuilder(Window
*pParent
, OUString sUIDir
, OUString sUIFile
, OString sID
, const uno::Reference
<frame::XFrame
>& rFrame
)
143 , m_sHelpRoot(OUStringToOString(sUIFile
, RTL_TEXTENCODING_UTF8
))
144 , m_pStringReplace(ResMgr::GetReadStringHook())
146 , m_bToplevelParentFound(false)
147 , m_pParserState(new ParserState
)
150 m_bToplevelHasDeferredInit
= (pParent
&& pParent
->IsDialog()) ? ((Dialog
*)pParent
)->isDeferredInit() : false;
151 m_bToplevelHasDeferredProperties
= m_bToplevelHasDeferredInit
;
153 sal_Int32 nIdx
= m_sHelpRoot
.lastIndexOf('.');
155 m_sHelpRoot
= m_sHelpRoot
.copy(0, nIdx
);
156 m_sHelpRoot
= m_sHelpRoot
+ OString('/');
158 OUString sUri
= sUIDir
+ sUIFile
;
160 com::sun::star::lang::Locale aLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
161 bool bEN_US
= aLocale
.Language
== "en" && aLocale
.Country
== "US" && aLocale
.Variant
.isEmpty();
163 loadTranslations(aLocale
, sUri
);
165 xmlreader::XmlReader
reader(sUri
);
167 handleChild(pParent
, reader
);
169 //Set Mnemonic widgets when everything has been imported
170 for (std::vector
<MnemonicWidgetMap
>::iterator aI
= m_pParserState
->m_aMnemonicWidgetMaps
.begin(),
171 aEnd
= m_pParserState
->m_aMnemonicWidgetMaps
.end(); aI
!= aEnd
; ++aI
)
173 FixedText
*pOne
= get
<FixedText
>(aI
->m_sID
);
174 Window
*pOther
= get
<Window
>(aI
->m_sValue
);
175 SAL_WARN_IF(!pOne
|| !pOther
, "vcl", "missing member of Mnemonic Widget Mapping");
177 pOne
->set_mnemonic_widget(pOther
);
180 //Set a11y relations when everything has been imported
181 for (AtkMap::iterator aI
= m_pParserState
->m_aAtkInfo
.begin(),
182 aEnd
= m_pParserState
->m_aAtkInfo
.end(); aI
!= aEnd
; ++aI
)
184 Window
*pSource
= aI
->first
;
185 const stringmap
&rMap
= aI
->second
;
187 for (stringmap::const_iterator aP
= rMap
.begin(),
188 aEndP
= rMap
.end(); aP
!= aEndP
; ++aP
)
190 const OString
&rTarget
= aP
->second
;
191 Window
*pTarget
= get
<Window
>(rTarget
);
192 SAL_WARN_IF(!pTarget
, "vcl", "missing member of a11y relation: "
193 << rTarget
.getStr());
196 const OString
&rType
= aP
->first
;
197 if (rType
== "labelled-by")
198 pSource
->SetAccessibleRelationLabeledBy(pTarget
);
199 else if (rType
== "label-for")
200 pSource
->SetAccessibleRelationLabelFor(pTarget
);
201 else if (rType
== "member-of")
202 pSource
->SetAccessibleRelationMemberOf(pTarget
);
205 SAL_INFO("vcl.layout", "unhandled a11y relation :" << rType
.getStr());
210 //Set radiobutton groups when everything has been imported
211 for (std::vector
<RadioButtonGroupMap
>::iterator aI
= m_pParserState
->m_aGroupMaps
.begin(),
212 aEnd
= m_pParserState
->m_aGroupMaps
.end(); aI
!= aEnd
; ++aI
)
214 RadioButton
*pOne
= get
<RadioButton
>(aI
->m_sID
);
215 RadioButton
*pOther
= get
<RadioButton
>(aI
->m_sValue
);
216 SAL_WARN_IF(!pOne
|| !pOther
, "vcl", "missing member of radiobutton group");
218 pOne
->group(*pOther
);
221 //Set ComboBox models when everything has been imported
222 for (std::vector
<ComboBoxModelMap
>::iterator aI
= m_pParserState
->m_aModelMaps
.begin(),
223 aEnd
= m_pParserState
->m_aModelMaps
.end(); aI
!= aEnd
; ++aI
)
225 ListBox
*pTarget
= get
<ListBox
>(aI
->m_sID
);
226 const ListStore
*pStore
= get_model_by_name(aI
->m_sValue
);
227 SAL_WARN_IF(!pTarget
|| !pStore
, "vcl", "missing elements of combobox/liststore");
228 if (pTarget
&& pStore
)
229 mungeModel(*pTarget
, *pStore
, aI
->m_nActiveId
);
232 //Set TextView buffers when everything has been imported
233 for (std::vector
<TextBufferMap
>::iterator aI
= m_pParserState
->m_aTextBufferMaps
.begin(),
234 aEnd
= m_pParserState
->m_aTextBufferMaps
.end(); aI
!= aEnd
; ++aI
)
236 VclMultiLineEdit
*pTarget
= get
<VclMultiLineEdit
>(aI
->m_sID
);
237 const TextBuffer
*pBuffer
= get_buffer_by_name(aI
->m_sValue
);
238 SAL_WARN_IF(!pTarget
|| !pBuffer
, "vcl", "missing elements of textview/textbuffer");
239 if (pTarget
&& pBuffer
)
240 mungeTextBuffer(*pTarget
, *pBuffer
);
243 //Set SpinButton adjustments when everything has been imported
244 for (std::vector
<WidgetAdjustmentMap
>::iterator aI
= m_pParserState
->m_aNumericFormatterAdjustmentMaps
.begin(),
245 aEnd
= m_pParserState
->m_aNumericFormatterAdjustmentMaps
.end(); aI
!= aEnd
; ++aI
)
247 NumericFormatter
*pTarget
= dynamic_cast<NumericFormatter
*>(get
<Window
>(aI
->m_sID
));
248 const Adjustment
*pAdjustment
= get_adjustment_by_name(aI
->m_sValue
);
249 SAL_WARN_IF(!pTarget
|| !pAdjustment
, "vcl", "missing elements of spinbutton/adjustment");
250 if (pTarget
&& pAdjustment
)
251 mungeAdjustment(*pTarget
, *pAdjustment
);
254 for (std::vector
<WidgetAdjustmentMap
>::iterator aI
= m_pParserState
->m_aTimeFormatterAdjustmentMaps
.begin(),
255 aEnd
= m_pParserState
->m_aTimeFormatterAdjustmentMaps
.end(); aI
!= aEnd
; ++aI
)
257 TimeField
*pTarget
= dynamic_cast<TimeField
*>(get
<Window
>(aI
->m_sID
));
258 const Adjustment
*pAdjustment
= get_adjustment_by_name(aI
->m_sValue
);
259 SAL_WARN_IF(!pTarget
|| !pAdjustment
, "vcl", "missing elements of spinbutton/adjustment");
260 if (pTarget
&& pAdjustment
)
261 mungeAdjustment(*pTarget
, *pAdjustment
);
264 for (std::vector
<WidgetAdjustmentMap
>::iterator aI
= m_pParserState
->m_aDateFormatterAdjustmentMaps
.begin(),
265 aEnd
= m_pParserState
->m_aDateFormatterAdjustmentMaps
.end(); aI
!= aEnd
; ++aI
)
267 DateField
*pTarget
= dynamic_cast<DateField
*>(get
<Window
>(aI
->m_sID
));
268 const Adjustment
*pAdjustment
= get_adjustment_by_name(aI
->m_sValue
);
269 SAL_WARN_IF(!pTarget
|| !pAdjustment
, "vcl", "missing elements of spinbutton/adjustment");
270 if (pTarget
&& pAdjustment
)
271 mungeAdjustment(*pTarget
, *pAdjustment
);
274 //Set ScrollBar adjustments when everything has been imported
275 for (std::vector
<WidgetAdjustmentMap
>::iterator aI
= m_pParserState
->m_aScrollAdjustmentMaps
.begin(),
276 aEnd
= m_pParserState
->m_aScrollAdjustmentMaps
.end(); aI
!= aEnd
; ++aI
)
278 ScrollBar
*pTarget
= get
<ScrollBar
>(aI
->m_sID
);
279 const Adjustment
*pAdjustment
= get_adjustment_by_name(aI
->m_sValue
);
280 SAL_WARN_IF(!pTarget
|| !pAdjustment
, "vcl", "missing elements of scrollbar/adjustment");
281 if (pTarget
&& pAdjustment
)
282 mungeAdjustment(*pTarget
, *pAdjustment
);
285 //Set size-groups when all widgets have been imported
286 for (std::vector
<SizeGroup
>::iterator aI
= m_pParserState
->m_aSizeGroups
.begin(),
287 aEnd
= m_pParserState
->m_aSizeGroups
.end(); aI
!= aEnd
; ++aI
)
289 boost::shared_ptr
< VclSizeGroup
> xGroup(new VclSizeGroup
);
291 for (stringmap::iterator aP
= aI
->m_aProperties
.begin(),
292 aEndP
= aI
->m_aProperties
.end(); aP
!= aEndP
; ++aP
)
294 const OString
&rKey
= aP
->first
;
295 const OString
&rValue
= aP
->second
;
296 xGroup
->set_property(rKey
, rValue
);
299 for (std::vector
<OString
>::iterator aW
= aI
->m_aWidgets
.begin(),
300 aEndW
= aI
->m_aWidgets
.end(); aW
!= aEndW
; ++aW
)
302 Window
* pWindow
= get
<Window
>(aW
->getStr());
303 pWindow
->add_to_size_group(xGroup
);
307 //Set button images when everything has been imported
308 std::set
<OString
> aImagesToBeRemoved
;
309 for (std::vector
<ButtonImageWidgetMap
>::iterator aI
= m_pParserState
->m_aButtonImageWidgetMaps
.begin(),
310 aEnd
= m_pParserState
->m_aButtonImageWidgetMaps
.end(); aI
!= aEnd
; ++aI
)
312 PushButton
*pTargetButton
= NULL
;
313 RadioButton
*pTargetRadio
= NULL
;
314 Button
*pTarget
= NULL
;
318 pTargetButton
= get
<PushButton
>(aI
->m_sID
);
319 pTarget
= pTargetButton
;
323 pTargetRadio
= get
<RadioButton
>(aI
->m_sID
);
324 pTarget
= pTargetRadio
;
327 FixedImage
*pImage
= get
<FixedImage
>(aI
->m_sValue
);
328 SAL_WARN_IF(!pTarget
|| !pImage
,
329 "vcl", "missing elements of button/image/stock");
330 if (!pTarget
|| !pImage
)
332 aImagesToBeRemoved
.insert(aI
->m_sValue
);
334 VclBuilder::StockMap::iterator aFind
= m_pParserState
->m_aStockMap
.find(aI
->m_sValue
);
335 if (aFind
== m_pParserState
->m_aStockMap
.end())
338 pTargetButton
->SetModeImage(pImage
->GetImage());
340 pTargetRadio
->SetModeRadioImage(pImage
->GetImage());
344 const stockinfo
&rImageInfo
= aFind
->second
;
345 SymbolType eType
= mapStockToSymbol(rImageInfo
.m_sStock
);
346 SAL_WARN_IF(eType
== SYMBOL_NOSYMBOL
, "vcl", "missing stock image element for button");
347 if (eType
== SYMBOL_NOSYMBOL
)
350 pTargetButton
->SetSymbol(eType
);
352 SAL_WARN_IF(eType
!= SYMBOL_IMAGE
, "vcl.layout", "inimplemented symbol type for radiobuttons");
353 if (eType
== SYMBOL_IMAGE
)
355 Bitmap
aBitmap(VclResId(mapStockToImageResource(rImageInfo
.m_sStock
)));
357 pTargetButton
->SetModeImage(aBitmap
);
359 pTargetRadio
->SetModeRadioImage(aBitmap
);
361 switch (rImageInfo
.m_nSize
)
364 pTarget
->SetSmallSymbol();
369 SAL_WARN("vcl.layout", "unsupported image size " << rImageInfo
.m_nSize
);
375 //There may be duplicate use of an Image, so we used a set to collect and
376 //now we can remove them from the tree after their final munge
377 for (std::set
<OString
>::iterator aI
= aImagesToBeRemoved
.begin(),
378 aEnd
= aImagesToBeRemoved
.end(); aI
!= aEnd
; ++aI
)
383 //Set button menus when everything has been imported
384 for (std::vector
<ButtonMenuMap
>::iterator aI
= m_pParserState
->m_aButtonMenuMaps
.begin(),
385 aEnd
= m_pParserState
->m_aButtonMenuMaps
.end(); aI
!= aEnd
; ++aI
)
387 MenuButton
*pTarget
= get
<MenuButton
>(aI
->m_sID
);
388 PopupMenu
*pMenu
= get_menu(aI
->m_sValue
);
389 SAL_WARN_IF(!pTarget
|| !pMenu
,
390 "vcl", "missing elements of button/menu");
391 if (!pTarget
|| !pMenu
)
393 pTarget
->SetPopupMenu(pMenu
);
396 //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
398 for (std::map
<Window
*, Window
*>::iterator aI
= m_pParserState
->m_aRedundantParentWidgets
.begin(),
399 aEnd
= m_pParserState
->m_aRedundantParentWidgets
.end(); aI
!= aEnd
; ++aI
)
401 delete_by_window(aI
->first
);
404 //fdo#67378 merge the label into the disclosure button
405 for (std::vector
<VclExpander
*>::iterator aI
= m_pParserState
->m_aExpanderWidgets
.begin(),
406 aEnd
= m_pParserState
->m_aExpanderWidgets
.end(); aI
!= aEnd
; ++aI
)
408 VclExpander
*pOne
= *aI
;
410 Window
*pChild
= pOne
->get_child();
411 Window
* pLabel
= pOne
->GetWindow(WINDOW_LASTCHILD
);
412 if (pLabel
&& pLabel
!= pChild
&& pLabel
->GetType() == WINDOW_FIXEDTEXT
)
414 FixedText
*pLabelWidget
= static_cast<FixedText
*>(pLabel
);
415 pOne
->set_label(pLabelWidget
->GetText());
416 delete_by_window(pLabel
);
420 //drop maps, etc. that we don't need again
421 delete m_pParserState
;
423 SAL_WARN_IF(!m_sID
.isEmpty() && (!m_bToplevelParentFound
&& !get_by_name(m_sID
)), "vcl.layout",
424 "Requested top level widget \"" << m_sID
.getStr() <<
425 "\" not found in " << sUIFile
);
427 #if defined SAL_LOG_WARN
428 if (m_bToplevelParentFound
&& m_pParent
->IsDialog())
431 bool bHasDefButton
= false;
432 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
433 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
435 if (isButtonType(aI
->m_pWindow
->GetType()))
438 if (aI
->m_pWindow
->GetStyle() & WB_DEFBUTTON
)
440 bHasDefButton
= true;
445 SAL_WARN_IF(nButtons
&& !bHasDefButton
, "vcl.layout", "No default button defined");
450 VclBuilder::~VclBuilder()
452 for (std::vector
<WinAndId
>::reverse_iterator aI
= m_aChildren
.rbegin(),
453 aEnd
= m_aChildren
.rend(); aI
!= aEnd
; ++aI
)
455 delete aI
->m_pWindow
;
458 for (std::vector
<MenuAndId
>::reverse_iterator aI
= m_aMenus
.rbegin(),
459 aEnd
= m_aMenus
.rend(); aI
!= aEnd
; ++aI
)
465 void VclBuilder::handleTranslations(xmlreader::XmlReader
&reader
)
467 xmlreader::Span name
;
470 OString sID
, sProperty
;
474 xmlreader::XmlReader::Result res
= reader
.nextItem(
475 xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
477 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
479 if (name
.equals("e"))
481 while (reader
.nextAttribute(&nsId
, &name
))
483 if (name
.equals("g"))
485 name
= reader
.getAttributeValue(false);
486 sID
= OString(name
.begin
, name
.length
);
487 sal_Int32 nDelim
= sID
.indexOf(':');
489 sID
= sID
.copy(nDelim
);
491 else if (name
.equals("i"))
493 name
= reader
.getAttributeValue(false);
494 sProperty
= OString(name
.begin
, name
.length
);
500 if (res
== xmlreader::XmlReader::RESULT_TEXT
&& !sID
.isEmpty())
502 OString
sTranslation(name
.begin
, name
.length
);
503 m_pParserState
->m_aTranslations
[sID
][sProperty
] = sTranslation
;
506 if (res
== xmlreader::XmlReader::RESULT_END
)
509 if (res
== xmlreader::XmlReader::RESULT_DONE
)
514 OString
VclBuilder::extractCustomProperty(VclBuilder::stringmap
&rMap
)
516 OString sCustomProperty
;
517 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("customproperty"));
518 if (aFind
!= rMap
.end())
520 sCustomProperty
= aFind
->second
;
523 return sCustomProperty
;
528 bool extractResizable(VclBuilder::stringmap
&rMap
)
530 bool bResizable
= true;
531 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("resizable"));
532 if (aFind
!= rMap
.end())
534 bResizable
= toBool(aFind
->second
);
540 bool extractEntry(VclBuilder::stringmap
&rMap
)
542 bool bHasEntry
= false;
543 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("has-entry"));
544 if (aFind
!= rMap
.end())
546 bHasEntry
= toBool(aFind
->second
);
552 bool extractOrientation(VclBuilder::stringmap
&rMap
)
554 bool bVertical
= false;
555 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("orientation"));
556 if (aFind
!= rMap
.end())
558 bVertical
= aFind
->second
.equalsIgnoreAsciiCase("vertical");
564 bool extractInconsistent(VclBuilder::stringmap
&rMap
)
566 bool bInconsistent
= false;
567 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("inconsistent"));
568 if (aFind
!= rMap
.end())
570 bInconsistent
= toBool(aFind
->second
);
573 return bInconsistent
;
576 OUString
getStockText(const OString
&rType
)
578 if (rType
== "gtk-ok")
579 return (VclResId(SV_BUTTONTEXT_OK
).toString());
580 else if (rType
== "gtk-cancel")
581 return (VclResId(SV_BUTTONTEXT_CANCEL
).toString());
582 else if (rType
== "gtk-help")
583 return (VclResId(SV_BUTTONTEXT_HELP
).toString());
584 else if (rType
== "gtk-close")
585 return (VclResId(SV_BUTTONTEXT_CLOSE
).toString());
586 else if (rType
== "gtk-revert-to-saved")
587 return (VclResId(SV_BUTTONTEXT_RESET
).toString());
588 else if (rType
== "gtk-add")
589 return (VclResId(SV_BUTTONTEXT_ADD
).toString());
590 else if (rType
== "gtk-delete")
591 return (VclResId(SV_BUTTONTEXT_DELETE
).toString());
592 else if (rType
== "gtk-remove")
593 return (VclResId(SV_BUTTONTEXT_REMOVE
).toString());
594 else if (rType
== "gtk-new")
595 return (VclResId(SV_BUTTONTEXT_NEW
).toString());
596 else if (rType
== "gtk-edit")
597 return (VclResId(SV_BUTTONTEXT_EDIT
).toString());
598 else if (rType
== "gtk-apply")
599 return (VclResId(SV_BUTTONTEXT_APPLY
).toString());
600 else if (rType
== "gtk-save")
601 return (VclResId(SV_BUTTONTEXT_SAVE
).toString());
602 else if (rType
== "gtk-undo")
603 return (VclResId(SV_BUTTONTEXT_UNDO
).toString());
604 else if (rType
== "gtk-paste")
605 return (VclResId(SV_BUTTONTEXT_PASTE
).toString());
606 else if (rType
== "gtk-media-next")
607 return (VclResId(SV_BUTTONTEXT_NEXT
).toString());
608 else if (rType
== "gtk-go-up")
609 return (VclResId(SV_BUTTONTEXT_GO_UP
).toString());
610 else if (rType
== "gtk-go-down")
611 return (VclResId(SV_BUTTONTEXT_GO_DOWN
).toString());
612 SAL_WARN("vcl.layout", "unknown stock type: " << rType
.getStr());
616 bool extractStock(VclBuilder::stringmap
&rMap
)
618 bool bIsStock
= false;
619 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("use-stock"));
620 if (aFind
!= rMap
.end())
622 bIsStock
= toBool(aFind
->second
);
628 OString
extractLabel(VclBuilder::stringmap
&rMap
)
631 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("label"));
632 if (aFind
!= rMap
.end())
634 sType
= aFind
->second
;
640 OString
extractActionName(VclBuilder::stringmap
&rMap
)
643 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("action-name"));
644 if (aFind
!= rMap
.end())
646 sActionName
= aFind
->second
;
652 Window
* extractStockAndBuildPushButton(Window
*pParent
, VclBuilder::stringmap
&rMap
)
654 WinBits nBits
= WB_CENTER
|WB_VCENTER
|WB_3DLOOK
;
656 bool bIsStock
= extractStock(rMap
);
658 Window
*pWindow
= NULL
;
662 OString sType
= extractLabel(rMap
);
663 if (sType
== "gtk-ok")
664 pWindow
= new OKButton(pParent
, nBits
);
665 else if (sType
== "gtk-cancel")
666 pWindow
= new CancelButton(pParent
, nBits
);
667 else if (sType
== "gtk-close")
668 pWindow
= new CloseButton(pParent
, nBits
);
669 else if (sType
== "gtk-help")
670 pWindow
= new HelpButton(pParent
, nBits
);
673 pWindow
= new PushButton(pParent
, nBits
);
674 pWindow
->SetText(getStockText(sType
));
679 pWindow
= new PushButton(pParent
, nBits
);
683 Window
* extractStockAndBuildMenuButton(Window
*pParent
, VclBuilder::stringmap
&rMap
)
685 WinBits nBits
= WB_CENTER
|WB_VCENTER
|WB_3DLOOK
;
687 Window
*pWindow
= new MenuButton(pParent
, nBits
);
689 if (extractStock(rMap
))
691 pWindow
->SetText(getStockText(extractLabel(rMap
)));
697 FieldUnit
detectMetricUnit(OString sUnit
)
699 FieldUnit eUnit
= FUNIT_NONE
;
703 else if (sUnit
== "cm")
705 else if (sUnit
== "m")
707 else if (sUnit
== "km")
709 else if ((sUnit
== "twips") || (sUnit
== "twip"))
711 else if (sUnit
== "pt")
713 else if (sUnit
== "pc")
715 else if (sUnit
== "\"" || (sUnit
== "in") || (sUnit
== "inch"))
717 else if ((sUnit
== "'") || (sUnit
== "ft") || (sUnit
== "foot") || (sUnit
== "feet"))
719 else if (sUnit
== "mile" || (sUnit
== "miles"))
721 else if (sUnit
== "ch")
723 else if (sUnit
== "line")
725 else if (sUnit
== "%")
726 eUnit
= FUNIT_PERCENT
;
728 // if lack of unit is not intentional
730 assert(eUnit
!= FUNIT_NONE
); //unknown unit
736 void VclBuilder::ensureDefaultWidthChars(VclBuilder::stringmap
&rMap
)
738 OString
sWidthChars("width-chars");
739 VclBuilder::stringmap::iterator aFind
= rMap
.find(sWidthChars
);
740 if (aFind
== rMap
.end())
741 rMap
[sWidthChars
] = "25";
744 bool VclBuilder::extractGroup(const OString
&id
, stringmap
&rMap
)
746 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("group"));
747 if (aFind
!= rMap
.end())
749 OString sID
= aFind
->second
;
750 sal_Int32 nDelim
= sID
.indexOf(':');
752 sID
= sID
.copy(0, nDelim
);
753 m_pParserState
->m_aGroupMaps
.push_back(RadioButtonGroupMap(id
, sID
));
760 void VclBuilder::connectNumericFormatterAdjustment(const OString
&id
, const OString
&rAdjustment
)
762 if (!rAdjustment
.isEmpty())
763 m_pParserState
->m_aNumericFormatterAdjustmentMaps
.push_back(WidgetAdjustmentMap(id
, rAdjustment
));
766 void VclBuilder::connectTimeFormatterAdjustment(const OString
&id
, const OString
&rAdjustment
)
768 if (!rAdjustment
.isEmpty())
769 m_pParserState
->m_aTimeFormatterAdjustmentMaps
.push_back(WidgetAdjustmentMap(id
, rAdjustment
));
772 void VclBuilder::connectDateFormatterAdjustment(const OString
&id
, const OString
&rAdjustment
)
774 if (!rAdjustment
.isEmpty())
775 m_pParserState
->m_aDateFormatterAdjustmentMaps
.push_back(WidgetAdjustmentMap(id
, rAdjustment
));
778 bool VclBuilder::extractScrollAdjustment(const OString
&id
, VclBuilder::stringmap
&rMap
)
780 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("adjustment"));
781 if (aFind
!= rMap
.end())
783 m_pParserState
->m_aScrollAdjustmentMaps
.push_back(WidgetAdjustmentMap(id
, aFind
->second
));
792 sal_Int32
extractActive(VclBuilder::stringmap
&rMap
)
794 sal_Int32 nActiveId
= 0;
795 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("active"));
796 if (aFind
!= rMap
.end())
798 nActiveId
= aFind
->second
.toInt32();
804 bool extractSelectable(VclBuilder::stringmap
&rMap
)
806 bool bSelectable
= false;
807 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("selectable"));
808 if (aFind
!= rMap
.end())
810 bSelectable
= toBool(aFind
->second
);
816 OString
extractAdjustment(VclBuilder::stringmap
&rMap
)
819 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("adjustment"));
820 if (aFind
!= rMap
.end())
822 sAdjustment
= aFind
->second
;
830 bool VclBuilder::extractModel(const OString
&id
, stringmap
&rMap
)
832 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("model"));
833 if (aFind
!= rMap
.end())
835 m_pParserState
->m_aModelMaps
.push_back(ComboBoxModelMap(id
, aFind
->second
,
836 extractActive(rMap
)));
843 bool VclBuilder::extractBuffer(const OString
&id
, stringmap
&rMap
)
845 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("buffer"));
846 if (aFind
!= rMap
.end())
848 m_pParserState
->m_aTextBufferMaps
.push_back(TextBufferMap(id
, aFind
->second
));
855 bool VclBuilder::extractStock(const OString
&id
, stringmap
&rMap
)
857 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("stock"));
858 if (aFind
!= rMap
.end())
861 aInfo
.m_sStock
= aFind
->second
;
863 aFind
= rMap
.find(OString("icon-size"));
864 if (aFind
!= rMap
.end())
866 aInfo
.m_nSize
= aFind
->second
.toInt32();
869 m_pParserState
->m_aStockMap
[id
] = aInfo
;
875 bool VclBuilder::extractButtonImage(const OString
&id
, stringmap
&rMap
, bool bRadio
)
877 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("image"));
878 if (aFind
!= rMap
.end())
880 m_pParserState
->m_aButtonImageWidgetMaps
.push_back(ButtonImageWidgetMap(id
, aFind
->second
, bRadio
));
887 void VclBuilder::extractMnemonicWidget(const OString
&rLabelID
, stringmap
&rMap
)
889 VclBuilder::stringmap::iterator aFind
= rMap
.find(OString("mnemonic-widget"));
890 if (aFind
!= rMap
.end())
892 OString sID
= aFind
->second
;
893 sal_Int32 nDelim
= sID
.indexOf(':');
895 sID
= sID
.copy(0, nDelim
);
896 m_pParserState
->m_aMnemonicWidgetMaps
.push_back(MnemonicWidgetMap(rLabelID
, sID
));
901 Window
* VclBuilder::prepareWidgetOwnScrolling(Window
*pParent
, WinBits
&rWinStyle
)
903 //For Widgets that manage their own scrolling, if one appears as a child of
904 //a scrolling window shoehorn that scrolling settings to this widget and
905 //return the real parent to use
906 if (pParent
&& pParent
->GetType() == WINDOW_SCROLLWINDOW
)
908 WinBits nScrollBits
= pParent
->GetStyle();
909 nScrollBits
&= (WB_AUTOHSCROLL
|WB_HSCROLL
|WB_AUTOVSCROLL
|WB_VSCROLL
);
910 rWinStyle
|= nScrollBits
;
911 pParent
= pParent
->GetParent();
917 void VclBuilder::cleanupWidgetOwnScrolling(Window
*pScrollParent
, Window
*pWindow
, stringmap
&rMap
)
919 //remove the redundant scrolling parent
920 sal_Int32 nWidthReq
= pScrollParent
->get_width_request();
921 rMap
[OString("width-request")] = OString::valueOf(nWidthReq
);
922 sal_Int32 nHeightReq
= pScrollParent
->get_height_request();
923 rMap
[OString("height-request")] = OString::valueOf(nHeightReq
);
925 m_pParserState
->m_aRedundantParentWidgets
[pScrollParent
] = pWindow
;
928 #ifndef DISABLE_DYNLOADING
929 extern "C" { static void SAL_CALL
thisModule() {} }
932 Window
*VclBuilder::makeObject(Window
*pParent
, const OString
&name
, const OString
&id
,
933 stringmap
&rMap
, const std::vector
<OString
> &rItems
)
935 bool bIsPlaceHolder
= name
.isEmpty();
936 bool bVertical
= false;
938 if (pParent
&& pParent
->GetType() == WINDOW_TABCONTROL
)
940 //We have to add a page
942 //make default pageid == position
943 TabControl
*pTabControl
= static_cast<TabControl
*>(pParent
);
944 sal_uInt16 nNewPageCount
= pTabControl
->GetPageCount()+1;
945 sal_uInt16 nNewPageId
= nNewPageCount
;
946 pTabControl
->InsertPage(nNewPageId
, OUString());
947 pTabControl
->SetCurPageId(nNewPageId
);
951 TabPage
* pPage
= new TabPage(pTabControl
);
954 //Make up a name for it
955 OString sTabPageId
= get_by_window(pParent
) +
957 OString::number(nNewPageCount
);
958 m_aChildren
.push_back(WinAndId(sTabPageId
, pPage
, false));
959 pPage
->SetHelpId(m_sHelpRoot
+ sTabPageId
);
961 //And give the page one container as a child to make it a layout enabled
963 VclBin
* pContainer
= new VclBin(pPage
);
965 m_aChildren
.push_back(WinAndId(OString(), pContainer
, false));
966 pContainer
->SetHelpId(m_sHelpRoot
+ sTabPageId
+ OString("-bin"));
967 pParent
= pContainer
;
969 pTabControl
->SetTabPage(nNewPageId
, pPage
);
973 if (bIsPlaceHolder
|| name
== "GtkTreeSelection")
976 extractButtonImage(id
, rMap
, name
== "GtkRadioButton");
978 Window
*pWindow
= NULL
;
979 if (name
== "GtkDialog")
981 WinBits nBits
= WB_MOVEABLE
|WB_3DLOOK
|WB_CLOSEABLE
;
982 if (extractResizable(rMap
))
983 nBits
|= WB_SIZEABLE
;
984 pWindow
= new Dialog(pParent
, nBits
);
986 else if (name
== "GtkMessageDialog")
988 WinBits nBits
= WB_MOVEABLE
|WB_3DLOOK
|WB_CLOSEABLE
;
989 if (extractResizable(rMap
))
990 nBits
|= WB_SIZEABLE
;
991 pWindow
= new MessageDialog(pParent
, nBits
);
993 else if (name
== "GtkBox")
995 bVertical
= extractOrientation(rMap
);
997 pWindow
= new VclVBox(pParent
);
999 pWindow
= new VclHBox(pParent
);
1001 else if (name
== "GtkHBox")
1002 pWindow
= new VclHBox(pParent
);
1003 else if (name
== "GtkVBox")
1004 pWindow
= new VclVBox(pParent
);
1005 else if (name
== "GtkButtonBox")
1007 bVertical
= extractOrientation(rMap
);
1009 pWindow
= new VclVButtonBox(pParent
);
1011 pWindow
= new VclHButtonBox(pParent
);
1013 else if (name
== "GtkHButtonBox")
1014 pWindow
= new VclHButtonBox(pParent
);
1015 else if (name
== "GtkVButtonBox")
1016 pWindow
= new VclVButtonBox(pParent
);
1017 else if (name
== "GtkGrid")
1018 pWindow
= new VclGrid(pParent
);
1019 else if (name
== "GtkFrame")
1020 pWindow
= new VclFrame(pParent
);
1021 else if (name
== "GtkExpander")
1023 VclExpander
*pExpander
= new VclExpander(pParent
);
1024 m_pParserState
->m_aExpanderWidgets
.push_back(pExpander
);
1025 pWindow
= pExpander
;
1027 else if (name
== "GtkAlignment")
1028 pWindow
= new VclAlignment(pParent
);
1029 else if (name
== "GtkButton")
1031 OString sMenu
= extractCustomProperty(rMap
);
1032 if (sMenu
.isEmpty())
1033 pWindow
= extractStockAndBuildPushButton(pParent
, rMap
);
1036 pWindow
= extractStockAndBuildMenuButton(pParent
, rMap
);
1037 m_pParserState
->m_aButtonMenuMaps
.push_back(ButtonMenuMap(id
, sMenu
));
1040 else if (name
== "GtkRadioButton")
1042 extractGroup(id
, rMap
);
1043 WinBits nBits
= WB_CENTER
|WB_VCENTER
|WB_3DLOOK
;
1044 OString sWrap
= extractCustomProperty(rMap
);
1045 if (!sWrap
.isEmpty())
1046 nBits
|= WB_WORDBREAK
;
1047 pWindow
= new RadioButton(pParent
, nBits
);
1049 else if (name
== "GtkCheckButton")
1051 WinBits nBits
= WB_CENTER
|WB_VCENTER
|WB_3DLOOK
;
1052 OString sWrap
= extractCustomProperty(rMap
);
1053 if (!sWrap
.isEmpty())
1054 nBits
|= WB_WORDBREAK
;
1055 //maybe always import as TriStateBox and enable/disable tristate
1056 bool bIsTriState
= extractInconsistent(rMap
);
1057 CheckBox
*pCheckBox
= bIsTriState
?
1058 new TriStateBox(pParent
, nBits
) :
1059 new CheckBox(pParent
, nBits
);
1061 pCheckBox
->SetState(STATE_DONTKNOW
);
1062 pWindow
= pCheckBox
;
1064 else if (name
== "GtkSpinButton")
1066 OString sAdjustment
= extractAdjustment(rMap
);
1067 OString sPattern
= extractCustomProperty(rMap
);
1068 OString sUnit
= sPattern
;
1070 for (sal_Int32 i
= 0; i
< sPattern
.getLength(); ++i
)
1072 if (sPattern
[i
] != '.' && sPattern
[i
] != ',' && sPattern
[i
] != '0')
1074 sUnit
= sPattern
.copy(i
);
1079 WinBits nBits
= WB_LEFT
|WB_BORDER
|WB_3DLOOK
;
1080 if (!id
.endsWith("-nospin"))
1081 nBits
|= WB_SPIN
| WB_REPEAT
;
1083 if (sPattern
.isEmpty())
1085 connectNumericFormatterAdjustment(id
, sAdjustment
);
1086 SAL_INFO("vcl.layout", "making numeric field for " << name
.getStr() << " " << sUnit
.getStr());
1087 pWindow
= new NumericField(pParent
, nBits
);
1091 if (sPattern
== "hh:mm")
1093 connectTimeFormatterAdjustment(id
, sAdjustment
);
1094 SAL_INFO("vcl.layout", "making time field for " << name
.getStr() << " " << sUnit
.getStr());
1095 TimeField
*pField
= new TimeField(pParent
, nBits
);
1098 else if (sPattern
== "yy:mm:dd")
1100 connectDateFormatterAdjustment(id
, sAdjustment
);
1101 SAL_INFO("vcl.layout", "making date field for " << name
.getStr() << " " << sUnit
.getStr());
1102 DateField
*pField
= new DateField(pParent
, nBits
);
1107 connectNumericFormatterAdjustment(id
, sAdjustment
);
1108 FieldUnit eUnit
= detectMetricUnit(sUnit
);
1109 SAL_INFO("vcl.layout", "making metric field for " << name
.getStr() << " " << sUnit
.getStr());
1110 MetricField
*pField
= new MetricField(pParent
, nBits
);
1111 pField
->SetUnit(eUnit
);
1116 else if (name
== "GtkLinkButton")
1117 pWindow
= new FixedHyperlink(pParent
, WB_CENTER
|WB_VCENTER
|WB_3DLOOK
|WB_NOLABEL
);
1118 else if ((name
== "GtkComboBox") || (name
== "GtkComboBoxText") || (name
== "VclComboBoxText"))
1120 extractModel(id
, rMap
);
1121 if (extractEntry(rMap
))
1123 ComboBox
* pComboBox
= new ComboBox(pParent
, WB_LEFT
|WB_DROPDOWN
|WB_VCENTER
|WB_3DLOOK
);
1124 pComboBox
->EnableAutoSize(true);
1125 if (!rItems
.empty())
1127 sal_uInt16 nActiveId
= extractActive(rMap
);
1128 for (std::vector
<OString
>::const_iterator aI
= rItems
.begin(), aEnd
= rItems
.end(); aI
!= aEnd
; ++aI
)
1129 pComboBox
->InsertEntry(OStringToOUString(*aI
, RTL_TEXTENCODING_UTF8
));
1130 if (nActiveId
< rItems
.size())
1131 pComboBox
->SelectEntryPos(nActiveId
);
1133 pWindow
= pComboBox
;
1137 ListBox
*pListBox
= new ListBox(pParent
, WB_LEFT
|WB_DROPDOWN
|WB_VCENTER
|WB_3DLOOK
|WB_SIMPLEMODE
);
1138 pListBox
->EnableAutoSize(true);
1139 if (!rItems
.empty())
1141 sal_uInt16 nActiveId
= extractActive(rMap
);
1142 for (std::vector
<OString
>::const_iterator aI
= rItems
.begin(), aEnd
= rItems
.end(); aI
!= aEnd
; ++aI
)
1143 pListBox
->InsertEntry(OStringToOUString(*aI
, RTL_TEXTENCODING_UTF8
));
1144 if (nActiveId
< rItems
.size())
1145 pListBox
->SelectEntryPos(nActiveId
);
1150 else if (name
== "VclComboBoxNumeric")
1152 NumericBox
* pComboBox
= new NumericBox(pParent
, WB_LEFT
|WB_DROPDOWN
|WB_VCENTER
|WB_3DLOOK
);
1153 pComboBox
->EnableAutoSize(true);
1154 pWindow
= pComboBox
;
1156 else if (name
== "GtkTreeView")
1159 //a) move svtools SvTreeViewBox into vcl
1160 //b) make that the default target for GtkTreeView
1161 //c) remove the non-drop down mode of ListBox and convert
1162 // everything over to SvTreeViewBox
1163 //d) remove the users of makeSvTreeViewBox
1164 extractModel(id
, rMap
);
1165 WinBits nWinStyle
= WB_LEFT
|WB_VCENTER
|WB_3DLOOK
|WB_SIMPLEMODE
;
1166 OString sBorder
= extractCustomProperty(rMap
);
1167 if (!sBorder
.isEmpty())
1168 nWinStyle
|= WB_BORDER
;
1169 //ListBox manages its own scrolling,
1170 Window
*pRealParent
= prepareWidgetOwnScrolling(pParent
, nWinStyle
);
1171 pWindow
= new ListBox(pRealParent
, nWinStyle
);
1172 if (pRealParent
!= pParent
)
1173 cleanupWidgetOwnScrolling(pParent
, pWindow
, rMap
);
1175 else if (name
== "GtkLabel")
1177 extractMnemonicWidget(id
, rMap
);
1178 if (extractSelectable(rMap
))
1179 pWindow
= new SelectableFixedText(pParent
, WB_CENTER
|WB_VCENTER
|WB_3DLOOK
);
1181 pWindow
= new FixedText(pParent
, WB_CENTER
|WB_VCENTER
|WB_3DLOOK
);
1183 else if (name
== "GtkImage")
1185 extractStock(id
, rMap
);
1186 pWindow
= new FixedImage(pParent
, WB_CENTER
|WB_VCENTER
|WB_3DLOOK
);
1187 //such parentless GtkImages are temps used to set icons on buttons
1188 //default them to hidden to stop e.g. insert->index entry flicking temp
1189 //full screen windows
1192 rMap
["visible"] = "false";
1196 else if (name
== "GtkSeparator")
1198 bVertical
= extractOrientation(rMap
);
1200 pWindow
= new FixedLine(pParent
, WB_VERT
);
1202 pWindow
= new FixedLine(pParent
, WB_HORZ
);
1204 else if (name
== "GtkScrollbar")
1206 extractScrollAdjustment(id
, rMap
);
1207 bVertical
= extractOrientation(rMap
);
1209 pWindow
= new ScrollBar(pParent
, WB_VERT
);
1211 pWindow
= new ScrollBar(pParent
, WB_HORZ
);
1213 else if (name
== "GtkProgressBar")
1215 extractScrollAdjustment(id
, rMap
);
1216 bVertical
= extractOrientation(rMap
);
1218 pWindow
= new ProgressBar(pParent
, WB_VERT
);
1220 pWindow
= new ProgressBar(pParent
, WB_HORZ
);
1222 else if (name
== "GtkScrolledWindow")
1224 pWindow
= new VclScrolledWindow(pParent
);
1226 else if (name
== "GtkEventBox")
1228 pWindow
= new VclEventBox(pParent
);
1230 else if (name
== "GtkEntry")
1232 pWindow
= new Edit(pParent
, WB_LEFT
|WB_VCENTER
|WB_BORDER
|WB_3DLOOK
);
1233 ensureDefaultWidthChars(rMap
);
1235 else if (name
== "GtkNotebook")
1236 pWindow
= new TabControl(pParent
, WB_STDTABCONTROL
|WB_3DLOOK
);
1237 else if (name
== "GtkDrawingArea")
1239 OString sBorder
= extractCustomProperty(rMap
);
1240 pWindow
= new Window(pParent
, sBorder
.isEmpty() ? 0 : WB_BORDER
);
1242 else if (name
== "GtkTextView")
1244 extractBuffer(id
, rMap
);
1246 WinBits nWinStyle
= WB_LEFT
;
1247 OString sBorder
= extractCustomProperty(rMap
);
1248 if (!sBorder
.isEmpty())
1249 nWinStyle
|= WB_BORDER
;
1250 //VclMultiLineEdit manages its own scrolling,
1251 Window
*pRealParent
= prepareWidgetOwnScrolling(pParent
, nWinStyle
);
1252 pWindow
= new VclMultiLineEdit(pRealParent
, nWinStyle
);
1253 if (pRealParent
!= pParent
)
1254 cleanupWidgetOwnScrolling(pParent
, pWindow
, rMap
);
1256 else if (name
== "GtkToolbar")
1258 pWindow
= new ToolBox(pParent
, WB_3DLOOK
| WB_TABSTOP
);
1260 else if (name
== "GtkToolButton" || name
== "GtkMenuToolButton")
1262 ToolBox
*pToolBox
= dynamic_cast<ToolBox
*>(pParent
);
1265 OUString
aCommand(OStringToOUString(extractActionName(rMap
), RTL_TEXTENCODING_UTF8
));
1267 ToolBoxItemBits nBits
= 0;
1268 if (name
== "GtkMenuToolButton")
1269 nBits
|= TIB_DROPDOWN
;
1271 if (!aCommand
.isEmpty())
1272 pToolBox
->InsertItem(aCommand
, m_xFrame
, nBits
);
1274 return NULL
; // no widget to be created
1279 sal_Int32 nDelim
= name
.indexOf('-');
1282 #ifndef DISABLE_DYNLOADING
1283 OUStringBuffer sModuleBuf
;
1284 #ifdef SAL_DLLPREFIX
1285 sModuleBuf
.append(SAL_DLLPREFIX
);
1287 sModuleBuf
.append(OStringToOUString(name
.copy(0, nDelim
), RTL_TEXTENCODING_UTF8
));
1288 #ifdef SAL_DLLEXTENSION
1289 sModuleBuf
.append(SAL_DLLEXTENSION
);
1292 OUString
sFunction(OStringToOUString(OString("make") + name
.copy(nDelim
+1), RTL_TEXTENCODING_UTF8
));
1293 #ifndef DISABLE_DYNLOADING
1294 OUString sModule
= sModuleBuf
.makeStringAndClear();
1295 ModuleMap::iterator aI
= m_aModuleMap
.find(sModule
);
1296 osl::Module
* pModule
= NULL
;
1297 if (aI
== m_aModuleMap
.end())
1299 pModule
= new osl::Module
;
1300 pModule
->loadRelative(&thisModule
, sModule
);
1301 aI
= m_aModuleMap
.insert(sModule
, pModule
).first
;
1303 customMakeWidget pFunction
= (customMakeWidget
)aI
->second
->getFunctionSymbol(sFunction
);
1305 customMakeWidget pFunction
= (customMakeWidget
)osl_getFunctionSymbol((oslModule
) RTLD_DEFAULT
, sFunction
.pData
);
1308 pWindow
= (*pFunction
)(pParent
, rMap
);
1311 SAL_WARN_IF(!pWindow
, "vcl.layout", "probably need to implement " << name
.getStr() << " or add a make" << name
.getStr() << " function");
1314 pWindow
->SetHelpId(m_sHelpRoot
+ id
);
1315 SAL_INFO("vcl.layout", "for " << name
.getStr() <<
1316 ", created " << pWindow
<< " child of " <<
1317 pParent
<< "(" << pWindow
->mpWindowImpl
->mpParent
<< "/" <<
1318 pWindow
->mpWindowImpl
->mpRealParent
<< "/" <<
1319 pWindow
->mpWindowImpl
->mpBorderWindow
<< ") with helpid " <<
1320 pWindow
->GetHelpId().getStr());
1321 m_aChildren
.push_back(WinAndId(id
, pWindow
, bVertical
));
1328 //return true for window types which exist in vcl but are not themselves
1329 //represented in the .ui format, i.e. only their children exist.
1330 bool isConsideredGtkPseudo(Window
*pWindow
)
1332 return pWindow
->GetType() == WINDOW_TABPAGE
;
1336 //Any properties from .ui load we couldn't set because of potential virtual methods
1337 //during ctor are applied here
1338 void VclBuilder::setDeferredProperties()
1340 if (!m_bToplevelHasDeferredProperties
)
1342 stringmap aDeferredProperties
;
1343 aDeferredProperties
.swap(m_aDeferredProperties
);
1344 m_bToplevelHasDeferredProperties
= false;
1345 set_properties(m_pParent
, aDeferredProperties
);
1348 void VclBuilder::set_properties(Window
*pWindow
, const stringmap
&rProps
)
1350 for (stringmap::const_iterator aI
= rProps
.begin(), aEnd
= rProps
.end(); aI
!= aEnd
; ++aI
)
1352 const OString
&rKey
= aI
->first
;
1353 const OString
&rValue
= aI
->second
;
1354 pWindow
->set_property(rKey
, rValue
);
1358 Window
*VclBuilder::insertObject(Window
*pParent
, const OString
&rClass
,
1359 const OString
&rID
, stringmap
&rProps
, stringmap
&rPango
,
1361 std::vector
<OString
> &rItems
)
1363 Window
*pCurrentChild
= NULL
;
1365 if (m_pParent
&& !isConsideredGtkPseudo(m_pParent
) && !m_sID
.isEmpty() && rID
.equals(m_sID
))
1367 pCurrentChild
= m_pParent
;
1368 //toplevels default to resizable
1369 if (pCurrentChild
->IsDialog())
1371 Dialog
*pDialog
= (Dialog
*)pCurrentChild
;
1372 pDialog
->doDeferredInit(extractResizable(rProps
));
1373 m_bToplevelHasDeferredInit
= false;
1375 if (pCurrentChild
->GetHelpId().isEmpty())
1377 pCurrentChild
->SetHelpId(m_sHelpRoot
+ m_sID
);
1378 SAL_INFO("vcl.layout", "for toplevel dialog " << this << " " <<
1379 rID
.getStr() << ", set helpid " <<
1380 pCurrentChild
->GetHelpId().getStr());
1382 m_bToplevelParentFound
= true;
1386 //if we're being inserting under a toplevel dialog whose init is
1387 //deferred due to waiting to encounter it in this .ui, and it hasn't
1388 //been seen yet, then make unattached widgets parent-less toplevels
1389 if (pParent
== m_pParent
&& m_bToplevelHasDeferredInit
)
1391 pCurrentChild
= makeObject(pParent
, rClass
, rID
, rProps
, rItems
);
1396 if (pCurrentChild
== m_pParent
&& m_bToplevelHasDeferredProperties
)
1397 m_aDeferredProperties
= rProps
;
1399 set_properties(pCurrentChild
, rProps
);
1401 for (stringmap::iterator aI
= rPango
.begin(), aEnd
= rPango
.end(); aI
!= aEnd
; ++aI
)
1403 const OString
&rKey
= aI
->first
;
1404 const OString
&rValue
= aI
->second
;
1405 pCurrentChild
->set_font_attribute(rKey
, rValue
);
1408 m_pParserState
->m_aAtkInfo
[pCurrentChild
] = rAtk
;
1417 pCurrentChild
= m_aChildren
.empty() ? pParent
: m_aChildren
.back().m_pWindow
;
1418 return pCurrentChild
;
1421 void VclBuilder::reorderWithinParent(Window
&rWindow
, sal_uInt16 nNewPosition
)
1423 if (rWindow
.mpWindowImpl
->mpParent
!= rWindow
.mpWindowImpl
->mpRealParent
)
1425 assert(rWindow
.mpWindowImpl
->mpBorderWindow
==
1426 rWindow
.mpWindowImpl
->mpParent
);
1427 assert(rWindow
.mpWindowImpl
->mpBorderWindow
->mpWindowImpl
->mpParent
==
1428 rWindow
.mpWindowImpl
->mpRealParent
);
1429 reorderWithinParent(*rWindow
.mpWindowImpl
->mpBorderWindow
, nNewPosition
);
1432 rWindow
.reorderWithinParent(nNewPosition
);
1435 void VclBuilder::handleTabChild(Window
*pParent
, xmlreader::XmlReader
&reader
)
1440 stringmap aProperties
;
1443 xmlreader::Span name
;
1446 xmlreader::XmlReader::Result res
= reader
.nextItem(
1447 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1449 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1452 if (name
.equals("object"))
1454 while (reader
.nextAttribute(&nsId
, &name
))
1456 if (name
.equals("id"))
1458 name
= reader
.getAttributeValue(false);
1459 sID
= OString(name
.begin
, name
.length
);
1460 sal_Int32 nDelim
= sID
.indexOf(':');
1463 OString sPattern
= sID
.copy(nDelim
+1);
1464 aProperties
[OString("customproperty")] = sPattern
;
1465 sID
= sID
.copy(0, nDelim
);
1470 else if (name
.equals("property"))
1471 collectProperty(reader
, sID
, aProperties
);
1474 if (res
== xmlreader::XmlReader::RESULT_END
)
1480 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1484 TabControl
*pTabControl
= static_cast<TabControl
*>(pParent
);
1485 VclBuilder::stringmap::iterator aFind
= aProperties
.find(OString("label"));
1486 if (aFind
!= aProperties
.end())
1488 sal_uInt16 nPageId
= pTabControl
->GetCurPageId();
1489 pTabControl
->SetPageText(nPageId
,
1490 OStringToOUString(aFind
->second
, RTL_TEXTENCODING_UTF8
));
1491 pTabControl
->SetPageName(nPageId
, sID
);
1494 pTabControl
->RemovePage(pTabControl
->GetCurPageId());
1497 //so that tabbing between controls goes in a visually sensible sequence
1498 //we sort these into a best-tab-order sequence
1499 bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const Window
*pA
, const Window
*pB
) const
1501 //sort child order within parent list by grid position
1502 sal_Int32 nTopA
= pA
->get_grid_top_attach();
1503 sal_Int32 nTopB
= pB
->get_grid_top_attach();
1508 sal_Int32 nLeftA
= pA
->get_grid_left_attach();
1509 sal_Int32 nLeftB
= pB
->get_grid_left_attach();
1510 if (nLeftA
< nLeftB
)
1512 if (nLeftA
> nLeftB
)
1514 //sort into two groups of pack start and pack end
1515 VclPackType ePackA
= pA
->get_pack_type();
1516 VclPackType ePackB
= pB
->get_pack_type();
1517 if (ePackA
< ePackB
)
1519 if (ePackA
> ePackB
)
1521 bool bVerticalContainer
= m_pBuilder
->get_window_packing_data(pA
->GetParent()).m_bVerticalOrient
;
1522 bool bPackA
= pA
->get_secondary();
1523 bool bPackB
= pB
->get_secondary();
1524 if (!bVerticalContainer
)
1526 //for horizontal boxes group secondaries before primaries
1527 if (bPackA
> bPackB
)
1529 if (bPackA
< bPackB
)
1534 //for vertical boxes group secondaries after primaries
1535 if (bPackA
< bPackB
)
1537 if (bPackA
> bPackB
)
1540 //honour relative box positions with pack group
1541 bPackA
= m_pBuilder
->get_window_packing_data(pA
).m_nPosition
;
1542 bPackB
= m_pBuilder
->get_window_packing_data(pB
).m_nPosition
;
1543 if (bPackA
< bPackB
)
1545 if (bPackA
> bPackB
)
1547 //sort labels of Frames before body
1548 if (pA
->GetParent() == pB
->GetParent())
1550 const VclFrame
*pFrameParent
= dynamic_cast<const VclFrame
*>(pA
->GetParent());
1553 const Window
*pLabel
= pFrameParent
->get_label_widget();
1554 int nFramePosA
= (pA
== pLabel
) ? 0 : 1;
1555 int nFramePosB
= (pB
== pLabel
) ? 0 : 1;
1556 return nFramePosA
< nFramePosB
;
1562 void VclBuilder::handleChild(Window
*pParent
, xmlreader::XmlReader
&reader
)
1564 Window
*pCurrentChild
= NULL
;
1566 xmlreader::Span name
;
1568 OString sType
, sInternalChild
;
1570 while (reader
.nextAttribute(&nsId
, &name
))
1572 if (name
.equals("type"))
1574 name
= reader
.getAttributeValue(false);
1575 sType
= OString(name
.begin
, name
.length
);
1577 else if (name
.equals("internal-child"))
1579 name
= reader
.getAttributeValue(false);
1580 sInternalChild
= OString(name
.begin
, name
.length
);
1584 if (sType
.equals("tab"))
1586 handleTabChild(pParent
, reader
);
1593 xmlreader::XmlReader::Result res
= reader
.nextItem(
1594 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1596 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1598 if (name
.equals("object") || name
.equals("placeholder"))
1600 pCurrentChild
= handleObject(pParent
, reader
);
1602 bool bObjectInserted
= pCurrentChild
&& pParent
!= pCurrentChild
;
1604 if (bObjectInserted
)
1606 //Internal-children default in glade to not having their visible bits set
1607 //even though they are visible (generally anyway)
1608 if (!sInternalChild
.isEmpty())
1609 pCurrentChild
->Show();
1611 //Select the first page if its a notebook
1612 if (pCurrentChild
->GetType() == WINDOW_TABCONTROL
)
1614 TabControl
*pTabControl
= static_cast<TabControl
*>(pCurrentChild
);
1615 pTabControl
->SetCurPageId(pTabControl
->GetPageId(0));
1617 //To-Do add reorder capability to the TabControl
1621 // We want to sort labels before contents of frames
1622 // for key board traversal, especially if there
1623 // are multiple widgets using the same mnemonic
1624 if (sType
.equals("label"))
1626 if (VclFrame
*pFrameParent
= dynamic_cast<VclFrame
*>(pParent
))
1627 pFrameParent
->designate_label(pCurrentChild
);
1629 if (sInternalChild
.equals("vbox") || sInternalChild
.equals("messagedialog-vbox"))
1631 if (Dialog
*pBoxParent
= dynamic_cast<Dialog
*>(pParent
))
1632 pBoxParent
->set_content_area(static_cast<VclBox
*>(pCurrentChild
));
1634 else if (sInternalChild
.equals("action_area") || sInternalChild
.equals("messagedialog-action_area"))
1636 Window
*pContentArea
= pCurrentChild
->GetParent();
1637 assert(pContentArea
&& pContentArea
->GetType() == WINDOW_CONTAINER
);
1638 if (Dialog
*pBoxParent
= dynamic_cast<Dialog
*>(pContentArea
? pContentArea
->GetParent() : NULL
))
1640 pBoxParent
->set_action_area(static_cast<VclButtonBox
*>(pCurrentChild
));
1644 //To-Do make reorder a virtual in Window, move this foo
1645 //there and see above
1646 std::vector
<Window
*> aChilds
;
1647 for (Window
* pChild
= pCurrentChild
->GetWindow(WINDOW_FIRSTCHILD
); pChild
;
1648 pChild
= pChild
->GetWindow(WINDOW_NEXT
))
1650 aChilds
.push_back(pChild
);
1653 bool bIsButtonBox
= dynamic_cast<VclButtonBox
*>(pCurrentChild
) != NULL
;
1655 //sort child order within parent so that tabbing
1656 //between controls goes in a visually sensible sequence
1657 std::stable_sort(aChilds
.begin(), aChilds
.end(), sortIntoBestTabTraversalOrder(this));
1658 reorderWithinParent(aChilds
, bIsButtonBox
);
1662 else if (name
.equals("packing"))
1664 handlePacking(pCurrentChild
, reader
);
1670 if (res
== xmlreader::XmlReader::RESULT_END
)
1676 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1681 void VclBuilder::reorderWithinParent(std::vector
<Window
*>& rChilds
, bool bIsButtonBox
)
1683 for (size_t i
= 0; i
< rChilds
.size(); ++i
)
1685 reorderWithinParent(*rChilds
[i
], i
);
1690 //The first member of the group for legacy code needs WB_GROUP set and the
1692 WinBits nBits
= rChilds
[i
]->GetStyle();
1696 rChilds
[i
]->SetStyle(nBits
);
1700 void VclBuilder::collectPangoAttribute(xmlreader::XmlReader
&reader
, stringmap
&rMap
)
1702 xmlreader::Span span
;
1708 while (reader
.nextAttribute(&nsId
, &span
))
1710 if (span
.equals("name"))
1712 span
= reader
.getAttributeValue(false);
1713 sProperty
= OString(span
.begin
, span
.length
);
1715 else if (span
.equals("value"))
1717 span
= reader
.getAttributeValue(false);
1718 sValue
= OString(span
.begin
, span
.length
);
1722 if (!sProperty
.isEmpty())
1723 rMap
[sProperty
] = sValue
;
1726 void VclBuilder::collectAtkAttribute(xmlreader::XmlReader
&reader
, stringmap
&rMap
)
1728 xmlreader::Span span
;
1734 while (reader
.nextAttribute(&nsId
, &span
))
1736 if (span
.equals("type"))
1738 span
= reader
.getAttributeValue(false);
1739 sProperty
= OString(span
.begin
, span
.length
);
1741 else if (span
.equals("target"))
1743 span
= reader
.getAttributeValue(false);
1744 sValue
= OString(span
.begin
, span
.length
);
1745 sal_Int32 nDelim
= sValue
.indexOf(':');
1747 sValue
= sValue
.copy(0, nDelim
);
1751 if (!sProperty
.isEmpty())
1752 rMap
[sProperty
] = sValue
;
1755 void VclBuilder::handleAdjustment(const OString
&rID
, stringmap
&rProperties
)
1757 m_pParserState
->m_aAdjustments
[rID
] = rProperties
;
1760 void VclBuilder::handleTextBuffer(const OString
&rID
, stringmap
&rProperties
)
1762 m_pParserState
->m_aTextBuffers
[rID
] = rProperties
;
1765 void VclBuilder::handleRow(xmlreader::XmlReader
&reader
, const OString
&rID
, sal_Int32 nRowIndex
)
1769 ListStore::row aRow
;
1773 xmlreader::Span name
;
1776 xmlreader::XmlReader::Result res
= reader
.nextItem(
1777 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1779 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1782 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1785 if (name
.equals("col"))
1787 bool bTranslated
= false;
1791 while (reader
.nextAttribute(&nsId
, &name
))
1793 if (name
.equals("id"))
1795 name
= reader
.getAttributeValue(false);
1796 nId
= OString(name
.begin
, name
.length
).toInt32();
1798 else if (nId
== 0 && name
.equals("translatable") && reader
.getAttributeValue(false).equals("yes"))
1800 sValue
= getTranslation(rID
, OString::valueOf(nRowIndex
));
1801 bTranslated
= !sValue
.isEmpty();
1806 xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
1809 sValue
= OString(name
.begin
, name
.length
);
1811 if (aRow
.size() < nId
+1)
1817 if (res
== xmlreader::XmlReader::RESULT_END
)
1826 m_pParserState
->m_aModels
[rID
].m_aEntries
.push_back(aRow
);
1829 void VclBuilder::handleListStore(xmlreader::XmlReader
&reader
, const OString
&rID
)
1832 sal_Int32 nRowIndex
= 0;
1836 xmlreader::Span name
;
1839 xmlreader::XmlReader::Result res
= reader
.nextItem(
1840 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1842 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1845 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1847 if (name
.equals("row"))
1848 handleRow(reader
, rID
, nRowIndex
++);
1853 if (res
== xmlreader::XmlReader::RESULT_END
)
1863 void VclBuilder::handleAtkObject(xmlreader::XmlReader
&reader
, const OString
&rID
, Window
*pWindow
)
1869 stringmap aProperties
;
1873 xmlreader::Span name
;
1876 xmlreader::XmlReader::Result res
= reader
.nextItem(
1877 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1879 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1882 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1885 if (name
.equals("property"))
1886 collectProperty(reader
, rID
, aProperties
);
1889 if (res
== xmlreader::XmlReader::RESULT_END
)
1898 for (stringmap::iterator aI
= aProperties
.begin(), aEnd
= aProperties
.end(); aI
!= aEnd
; ++aI
)
1900 const OString
&rKey
= aI
->first
;
1901 const OString
&rValue
= aI
->second
;
1903 if (rKey
.match("AtkObject::"))
1904 pWindow
->set_property(rKey
.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue
);
1906 SAL_WARN("vcl.layout", "unhandled atk prop: " << rKey
.getStr());
1910 std::vector
<OString
> VclBuilder::handleItems(xmlreader::XmlReader
&reader
, const OString
&rID
)
1914 std::vector
<OString
> aItems
;
1915 sal_Int32 nItemIndex
= 0;
1919 xmlreader::Span name
;
1922 xmlreader::XmlReader::Result res
= reader
.nextItem(
1923 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1925 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1928 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1931 if (name
.equals("item"))
1933 bool bTranslated
= false;
1936 while (reader
.nextAttribute(&nsId
, &name
))
1938 if (name
.equals("translatable") && reader
.getAttributeValue(false).equals("yes"))
1940 sValue
= getTranslation(rID
, OString::valueOf(nItemIndex
));
1941 bTranslated
= !sValue
.isEmpty();
1946 xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
1949 sValue
= OString(name
.begin
, name
.length
);
1951 if (m_pStringReplace
)
1953 OUString sTmp
= (*m_pStringReplace
)(OStringToOUString(sValue
, RTL_TEXTENCODING_UTF8
));
1954 sValue
= OUStringToOString(sTmp
, RTL_TEXTENCODING_UTF8
);
1957 aItems
.push_back(sValue
);
1962 if (res
== xmlreader::XmlReader::RESULT_END
)
1974 void VclBuilder::handleMenu(xmlreader::XmlReader
&reader
, const OString
&rID
)
1976 PopupMenu
*pCurrentMenu
= new PopupMenu
;
1980 stringmap aProperties
;
1984 xmlreader::Span name
;
1987 xmlreader::XmlReader::Result res
= reader
.nextItem(
1988 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
1990 if (res
== xmlreader::XmlReader::RESULT_DONE
)
1993 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
1995 if (name
.equals("child"))
1997 handleMenuChild(pCurrentMenu
, reader
);
2002 if (name
.equals("property"))
2003 collectProperty(reader
, rID
, aProperties
);
2007 if (res
== xmlreader::XmlReader::RESULT_END
)
2016 m_aMenus
.push_back(MenuAndId(rID
, pCurrentMenu
));
2019 void VclBuilder::handleMenuChild(PopupMenu
*pParent
, xmlreader::XmlReader
&reader
)
2021 xmlreader::Span name
;
2027 xmlreader::XmlReader::Result res
= reader
.nextItem(
2028 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
2030 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
2032 if (name
.equals("object") || name
.equals("placeholder"))
2034 handleMenuObject(pParent
, reader
);
2040 if (res
== xmlreader::XmlReader::RESULT_END
)
2046 if (res
== xmlreader::XmlReader::RESULT_DONE
)
2051 void VclBuilder::handleMenuObject(PopupMenu
*pParent
, xmlreader::XmlReader
&reader
)
2055 OString sCustomProperty
;
2057 xmlreader::Span name
;
2060 while (reader
.nextAttribute(&nsId
, &name
))
2062 if (name
.equals("class"))
2064 name
= reader
.getAttributeValue(false);
2065 sClass
= OString(name
.begin
, name
.length
);
2067 else if (name
.equals("id"))
2069 name
= reader
.getAttributeValue(false);
2070 sID
= OString(name
.begin
, name
.length
);
2071 sal_Int32 nDelim
= sID
.indexOf(':');
2074 sCustomProperty
= sID
.copy(nDelim
+1);
2075 sID
= sID
.copy(0, nDelim
);
2082 stringmap aProperties
, aAccelerators
;
2084 if (!sCustomProperty
.isEmpty())
2085 aProperties
[OString("customproperty")] = sCustomProperty
;
2089 xmlreader::XmlReader::Result res
= reader
.nextItem(
2090 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
2092 if (res
== xmlreader::XmlReader::RESULT_DONE
)
2095 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
2098 if (name
.equals("property"))
2099 collectProperty(reader
, sID
, aProperties
);
2100 else if (name
.equals("accelerator"))
2101 collectAccelerator(reader
, aAccelerators
);
2104 if (res
== xmlreader::XmlReader::RESULT_END
)
2113 insertMenuObject(pParent
, sClass
, sID
, aProperties
, aAccelerators
);
2116 void VclBuilder::handleSizeGroup(xmlreader::XmlReader
&reader
, const OString
&rID
)
2118 m_pParserState
->m_aSizeGroups
.push_back(SizeGroup(rID
));
2119 SizeGroup
&rSizeGroup
= m_pParserState
->m_aSizeGroups
.back();
2125 xmlreader::Span name
;
2128 xmlreader::XmlReader::Result res
= reader
.nextItem(
2129 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
2131 if (res
== xmlreader::XmlReader::RESULT_DONE
)
2134 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
2137 if (name
.equals("widget"))
2139 while (reader
.nextAttribute(&nsId
, &name
))
2141 if (name
.equals("name"))
2143 name
= reader
.getAttributeValue(false);
2144 OString sWidget
= OString(name
.begin
, name
.length
);
2145 sal_Int32 nDelim
= sWidget
.indexOf(':');
2147 sWidget
= sWidget
.copy(0, nDelim
);
2148 rSizeGroup
.m_aWidgets
.push_back(sWidget
);
2154 if (name
.equals("property"))
2155 collectProperty(reader
, rID
, rSizeGroup
.m_aProperties
);
2159 if (res
== xmlreader::XmlReader::RESULT_END
)
2169 OString
VclBuilder::convertMnemonicMarkup(const OString
&rIn
)
2171 OStringBuffer
aRet(rIn
);
2172 for (sal_Int32 nI
= 0; nI
< aRet
.getLength(); ++nI
)
2174 if (aRet
[nI
] == '_')
2176 if (aRet
[nI
+1] != '_')
2177 aRet
[nI
] = MNEMONIC_CHAR
;
2183 return aRet
.makeStringAndClear();
2188 KeyCode
makeKeyCode(const OString
&rKey
)
2190 if (rKey
== "Insert")
2191 return KeyCode(KEY_INSERT
);
2192 else if (rKey
== "Delete")
2193 return KeyCode(KEY_DELETE
);
2195 assert (rKey
.getLength() == 1);
2196 sal_Char cChar
= rKey
.toChar();
2198 if (cChar
>= 'a' && cChar
<= 'z')
2199 return KeyCode(KEY_A
+ (cChar
- 'a'));
2200 else if (cChar
>= 'A' && cChar
<= 'Z')
2201 return KeyCode(KEY_A
+ (cChar
- 'A'));
2202 else if (cChar
>= '0' && cChar
<= '9')
2203 return KeyCode(KEY_0
+ (cChar
- 'A'));
2205 return KeyCode(cChar
);
2209 void VclBuilder::insertMenuObject(PopupMenu
*pParent
, const OString
&rClass
, const OString
&rID
,
2210 stringmap
&rProps
, stringmap
&rAccels
)
2212 sal_uInt16 nOldCount
= pParent
->GetItemCount();
2213 sal_uInt16 nNewId
= nOldCount
+ 1;
2215 if (rClass
== "GtkMenuItem")
2217 OUString
sLabel(OStringToOUString(convertMnemonicMarkup(extractLabel(rProps
)), RTL_TEXTENCODING_UTF8
));
2218 pParent
->InsertItem(nNewId
, sLabel
, MIB_TEXT
, rID
);
2220 else if (rClass
== "GtkSeparatorMenuItem")
2222 pParent
->InsertSeparator(rID
);
2225 SAL_WARN_IF(nOldCount
== pParent
->GetItemCount(), "vcl.layout", "probably need to implement " << rClass
.getStr());
2227 if (nOldCount
!= pParent
->GetItemCount())
2229 pParent
->SetHelpId(nNewId
, m_sHelpRoot
+ rID
);
2231 for (stringmap::iterator aI
= rProps
.begin(), aEnd
= rProps
.end(); aI
!= aEnd
; ++aI
)
2233 const OString
&rKey
= aI
->first
;
2234 const OString
&rValue
= aI
->second
;
2236 if (rKey
== "tooltip-markup")
2237 pParent
->SetTipHelpText(nNewId
, OStringToOUString(rValue
, RTL_TEXTENCODING_UTF8
));
2238 else if (rKey
== "tooltip-text")
2239 pParent
->SetTipHelpText(nNewId
, OStringToOUString(rValue
, RTL_TEXTENCODING_UTF8
));
2240 else if (rKey
== "visible")
2241 pParent
->ShowItem(nNewId
, toBool(rValue
));
2242 else if (rKey
== "has-default" && toBool(rValue
))
2243 pParent
->SetSelectedEntry(nNewId
);
2245 SAL_INFO("vcl.layout", "unhandled property: " << rKey
.getStr());
2248 for (stringmap::iterator aI
= rAccels
.begin(), aEnd
= rAccels
.end(); aI
!= aEnd
; ++aI
)
2250 const OString
&rSignal
= aI
->first
;
2251 const OString
&rValue
= aI
->second
;
2253 if (rSignal
== "activate")
2254 pParent
->SetAccelKey(nNewId
, makeKeyCode(rValue
));
2256 SAL_INFO("vcl.layout", "unhandled accelerator for: " << rSignal
.getStr());
2263 Window
* VclBuilder::handleObject(Window
*pParent
, xmlreader::XmlReader
&reader
)
2267 OString sCustomProperty
;
2269 xmlreader::Span name
;
2272 while (reader
.nextAttribute(&nsId
, &name
))
2274 if (name
.equals("class"))
2276 name
= reader
.getAttributeValue(false);
2277 sClass
= OString(name
.begin
, name
.length
);
2279 else if (name
.equals("id"))
2281 name
= reader
.getAttributeValue(false);
2282 sID
= OString(name
.begin
, name
.length
);
2283 sal_Int32 nDelim
= sID
.indexOf(':');
2286 sCustomProperty
= sID
.copy(nDelim
+1);
2287 sID
= sID
.copy(0, nDelim
);
2292 if (sClass
== "GtkListStore")
2294 handleListStore(reader
, sID
);
2297 else if (sClass
== "GtkMenu")
2299 handleMenu(reader
, sID
);
2302 else if (sClass
== "GtkSizeGroup")
2304 handleSizeGroup(reader
, sID
);
2307 else if (sClass
== "AtkObject")
2309 handleAtkObject(reader
, sID
, pParent
);
2315 stringmap aProperties
, aPangoAttributes
, aAtkAttributes
;
2316 std::vector
<OString
> aItems
;
2318 if (!sCustomProperty
.isEmpty())
2319 aProperties
[OString("customproperty")] = sCustomProperty
;
2321 Window
*pCurrentChild
= NULL
;
2324 xmlreader::XmlReader::Result res
= reader
.nextItem(
2325 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
2327 if (res
== xmlreader::XmlReader::RESULT_DONE
)
2330 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
2332 if (name
.equals("child"))
2336 pCurrentChild
= insertObject(pParent
, sClass
, sID
,
2337 aProperties
, aPangoAttributes
, aAtkAttributes
, aItems
);
2339 handleChild(pCurrentChild
, reader
);
2341 else if (name
.equals("items"))
2342 aItems
= handleItems(reader
, sID
);
2346 if (name
.equals("property"))
2347 collectProperty(reader
, sID
, aProperties
);
2348 else if (name
.equals("attribute"))
2349 collectPangoAttribute(reader
, aPangoAttributes
);
2350 else if (name
.equals("relation"))
2351 collectAtkAttribute(reader
, aAtkAttributes
);
2352 else if (name
.equals("action-widget"))
2353 handleActionWidget(reader
);
2357 if (res
== xmlreader::XmlReader::RESULT_END
)
2366 if (sClass
== "GtkAdjustment")
2368 handleAdjustment(sID
, aProperties
);
2371 else if (sClass
== "GtkTextBuffer")
2373 handleTextBuffer(sID
, aProperties
);
2379 pCurrentChild
= insertObject(pParent
, sClass
, sID
, aProperties
,
2380 aPangoAttributes
, aAtkAttributes
, aItems
);
2383 return pCurrentChild
;
2386 void VclBuilder::handlePacking(Window
*pCurrent
, xmlreader::XmlReader
&reader
)
2388 xmlreader::Span name
;
2395 xmlreader::XmlReader::Result res
= reader
.nextItem(
2396 xmlreader::XmlReader::TEXT_NONE
, &name
, &nsId
);
2398 if (res
== xmlreader::XmlReader::RESULT_DONE
)
2401 if (res
== xmlreader::XmlReader::RESULT_BEGIN
)
2404 if (name
.equals("property"))
2405 applyPackingProperty(pCurrent
, reader
);
2408 if (res
== xmlreader::XmlReader::RESULT_END
)
2418 void VclBuilder::applyPackingProperty(Window
*pCurrent
,
2419 xmlreader::XmlReader
&reader
)
2424 xmlreader::Span name
;
2427 if (pCurrent
->GetType() == WINDOW_SCROLLWINDOW
)
2429 std::map
<Window
*, Window
*>::iterator aFind
= m_pParserState
->m_aRedundantParentWidgets
.find(pCurrent
);
2430 if (aFind
!= m_pParserState
->m_aRedundantParentWidgets
.end())
2432 pCurrent
= aFind
->second
;
2437 while (reader
.nextAttribute(&nsId
, &name
))
2439 if (name
.equals("name"))
2441 name
= reader
.getAttributeValue(false);
2442 OString
sKey(name
.begin
, name
.length
);
2443 sKey
= sKey
.replace('_', '-');
2445 xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
2446 OString
sValue(name
.begin
, name
.length
);
2448 if (sKey
== "expand")
2450 bool bTrue
= (sValue
[0] == 't' || sValue
[0] == 'T' || sValue
[0] == '1');
2451 pCurrent
->set_expand(bTrue
);
2453 else if (sKey
== "fill")
2455 bool bTrue
= (sValue
[0] == 't' || sValue
[0] == 'T' || sValue
[0] == '1');
2456 pCurrent
->set_fill(bTrue
);
2458 else if (sKey
== "pack-type")
2460 VclPackType ePackType
= (sValue
[0] == 'e' || sValue
[0] == 'E') ? VCL_PACK_END
: VCL_PACK_START
;
2461 pCurrent
->set_pack_type(ePackType
);
2463 else if (sKey
== "left-attach")
2465 pCurrent
->set_grid_left_attach(sValue
.toInt32());
2467 else if (sKey
== "top-attach")
2469 pCurrent
->set_grid_top_attach(sValue
.toInt32());
2471 else if (sKey
== "width")
2473 pCurrent
->set_grid_width(sValue
.toInt32());
2475 else if (sKey
== "height")
2477 pCurrent
->set_grid_height(sValue
.toInt32());
2479 else if (sKey
== "padding")
2481 pCurrent
->set_padding(sValue
.toInt32());
2483 else if (sKey
== "position")
2485 set_window_packing_position(pCurrent
, sValue
.toInt32());
2487 else if (sKey
== "secondary")
2489 pCurrent
->set_secondary(toBool(sValue
));
2493 SAL_WARN("vcl.layout", "unknown packing: " << sKey
.getStr());
2499 OString
VclBuilder::getTranslation(const OString
&rID
, const OString
&rProperty
) const
2501 Translations::const_iterator aWidgetFind
= m_pParserState
->m_aTranslations
.find(rID
);
2502 if (aWidgetFind
!= m_pParserState
->m_aTranslations
.end())
2504 const WidgetTranslations
&rWidgetTranslations
= aWidgetFind
->second
;
2505 WidgetTranslations::const_iterator aPropertyFind
= rWidgetTranslations
.find(rProperty
);
2506 if (aPropertyFind
!= rWidgetTranslations
.end())
2507 return aPropertyFind
->second
;
2512 void VclBuilder::collectProperty(xmlreader::XmlReader
&reader
, const OString
&rID
, stringmap
&rMap
)
2514 xmlreader::Span name
;
2520 bool bTranslated
= false;
2522 while (reader
.nextAttribute(&nsId
, &name
))
2524 if (name
.equals("name"))
2526 name
= reader
.getAttributeValue(false);
2527 sProperty
= OString(name
.begin
, name
.length
);
2529 else if (name
.equals("translatable") && reader
.getAttributeValue(false).equals("yes"))
2531 sValue
= getTranslation(rID
, sProperty
);
2532 bTranslated
= !sValue
.isEmpty();
2537 reader
.nextItem(xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
2539 sValue
= OString(name
.begin
, name
.length
);
2541 if (!sProperty
.isEmpty())
2543 sProperty
= sProperty
.replace('_', '-');
2544 if (m_pStringReplace
)
2546 OUString sTmp
= (*m_pStringReplace
)(OStringToOUString(sValue
, RTL_TEXTENCODING_UTF8
));
2547 rMap
[sProperty
] = OUStringToOString(sTmp
, RTL_TEXTENCODING_UTF8
);
2551 rMap
[sProperty
] = sValue
;
2556 void VclBuilder::handleActionWidget(xmlreader::XmlReader
&reader
)
2558 xmlreader::Span name
;
2563 while (reader
.nextAttribute(&nsId
, &name
))
2565 if (name
.equals("response"))
2567 name
= reader
.getAttributeValue(false);
2568 sResponse
= OString(name
.begin
, name
.length
);
2572 reader
.nextItem(xmlreader::XmlReader::TEXT_RAW
, &name
, &nsId
);
2573 OString sID
= OString(name
.begin
, name
.length
);
2574 sal_Int32 nDelim
= sID
.indexOf(':');
2576 sID
= sID
.copy(0, nDelim
);
2577 set_response(sID
, sResponse
.toInt32());
2581 void VclBuilder::collectAccelerator(xmlreader::XmlReader
&reader
, stringmap
&rMap
)
2583 xmlreader::Span name
;
2589 while (reader
.nextAttribute(&nsId
, &name
))
2591 if (name
.equals("key"))
2593 name
= reader
.getAttributeValue(false);
2594 sValue
= OString(name
.begin
, name
.length
);
2596 else if (name
.equals("signal"))
2598 name
= reader
.getAttributeValue(false);
2599 sProperty
= OString(name
.begin
, name
.length
);
2604 if (!sProperty
.isEmpty() && !sValue
.isEmpty())
2606 rMap
[sProperty
] = sValue
;
2610 Window
*VclBuilder::get_widget_root()
2612 return m_aChildren
.empty() ? NULL
: m_aChildren
[0].m_pWindow
;
2615 Window
*VclBuilder::get_by_name(OString sID
)
2617 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
2618 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2620 if (aI
->m_sID
.equals(sID
))
2621 return aI
->m_pWindow
;
2627 PopupMenu
*VclBuilder::get_menu(OString sID
)
2629 for (std::vector
<MenuAndId
>::iterator aI
= m_aMenus
.begin(),
2630 aEnd
= m_aMenus
.end(); aI
!= aEnd
; ++aI
)
2632 if (aI
->m_sID
.equals(sID
))
2639 short VclBuilder::get_response(const Window
*pWindow
) const
2641 for (std::vector
<WinAndId
>::const_iterator aI
= m_aChildren
.begin(),
2642 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2644 if (aI
->m_pWindow
== pWindow
)
2646 return aI
->m_nResponseId
;
2650 //how did we not find sID ?
2655 void VclBuilder::set_response(OString sID
, short nResponse
)
2657 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
2658 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2660 if (aI
->m_sID
.equals(sID
))
2662 aI
->m_nResponseId
= nResponse
;
2667 //how did we not find sID ?
2671 void VclBuilder::delete_by_name(OString sID
)
2673 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
2674 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2676 if (aI
->m_sID
.equals(sID
))
2678 delete aI
->m_pWindow
;
2679 m_aChildren
.erase(aI
);
2685 void VclBuilder::delete_by_window(const Window
*pWindow
)
2687 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
2688 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2690 if (aI
->m_pWindow
== pWindow
)
2692 delete aI
->m_pWindow
;
2693 m_aChildren
.erase(aI
);
2699 OString
VclBuilder::get_by_window(const Window
*pWindow
) const
2701 for (std::vector
<WinAndId
>::const_iterator aI
= m_aChildren
.begin(),
2702 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2704 if (aI
->m_pWindow
== pWindow
)
2711 VclBuilder::PackingData
VclBuilder::get_window_packing_data(const Window
*pWindow
) const
2713 //We've stored the return of new Control, some of these get
2714 //border windows placed around them which are what you get
2715 //from GetChild, so scoot up a level if necessary to get the
2716 //window whose position value we have
2717 const Window
*pPropHolder
= pWindow
->mpWindowImpl
->mpClientWindow
?
2718 pWindow
->mpWindowImpl
->mpClientWindow
: pWindow
;
2720 for (std::vector
<WinAndId
>::const_iterator aI
= m_aChildren
.begin(),
2721 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2723 if (aI
->m_pWindow
== pPropHolder
)
2724 return aI
->m_aPackingData
;
2727 return PackingData();
2730 void VclBuilder::set_window_packing_position(const Window
*pWindow
, sal_Int32 nPosition
)
2732 for (std::vector
<WinAndId
>::iterator aI
= m_aChildren
.begin(),
2733 aEnd
= m_aChildren
.end(); aI
!= aEnd
; ++aI
)
2735 if (aI
->m_pWindow
== pWindow
)
2736 aI
->m_aPackingData
.m_nPosition
= nPosition
;
2740 const VclBuilder::ListStore
*VclBuilder::get_model_by_name(OString sID
) const
2742 std::map
<OString
, ListStore
>::const_iterator aI
= m_pParserState
->m_aModels
.find(sID
);
2743 if (aI
!= m_pParserState
->m_aModels
.end())
2744 return &(aI
->second
);
2748 const VclBuilder::TextBuffer
*VclBuilder::get_buffer_by_name(OString sID
) const
2750 std::map
<OString
, TextBuffer
>::const_iterator aI
= m_pParserState
->m_aTextBuffers
.find(sID
);
2751 if (aI
!= m_pParserState
->m_aTextBuffers
.end())
2752 return &(aI
->second
);
2756 const VclBuilder::Adjustment
*VclBuilder::get_adjustment_by_name(OString sID
) const
2758 std::map
<OString
, Adjustment
>::const_iterator aI
= m_pParserState
->m_aAdjustments
.find(sID
);
2759 if (aI
!= m_pParserState
->m_aAdjustments
.end())
2760 return &(aI
->second
);
2764 void VclBuilder::mungeModel(ListBox
&rTarget
, const ListStore
&rStore
, sal_uInt16 nActiveId
)
2766 for (std::vector
<ListStore::row
>::const_iterator aI
= rStore
.m_aEntries
.begin(), aEnd
= rStore
.m_aEntries
.end();
2769 const ListStore::row
&rRow
= *aI
;
2770 sal_uInt16 nEntry
= rTarget
.InsertEntry(OStringToOUString(rRow
[0], RTL_TEXTENCODING_UTF8
));
2771 if (rRow
.size() > 1)
2773 sal_IntPtr nValue
= rRow
[1].toInt32();
2774 rTarget
.SetEntryData(nEntry
, (void*)nValue
);
2777 if (nActiveId
< rStore
.m_aEntries
.size())
2778 rTarget
.SelectEntryPos(nActiveId
);
2781 void VclBuilder::mungeAdjustment(NumericFormatter
&rTarget
, const Adjustment
&rAdjustment
)
2783 int nMul
= rtl_math_pow10Exp(1, rTarget
.GetDecimalDigits());
2785 for (stringmap::const_iterator aI
= rAdjustment
.begin(), aEnd
= rAdjustment
.end(); aI
!= aEnd
; ++aI
)
2787 const OString
&rKey
= aI
->first
;
2788 const OString
&rValue
= aI
->second
;
2790 if (rKey
== "upper")
2792 sal_Int64 nUpper
= rValue
.toDouble() * nMul
;
2793 rTarget
.SetMax(nUpper
);
2794 rTarget
.SetLast(nUpper
);
2796 else if (rKey
== "lower")
2798 sal_Int64 nLower
= rValue
.toDouble() * nMul
;
2799 rTarget
.SetMin(nLower
);
2800 rTarget
.SetFirst(nLower
);
2802 else if (rKey
== "value")
2804 sal_Int64 nValue
= rValue
.toDouble() * nMul
;
2805 rTarget
.SetValue(nValue
);
2807 else if (rKey
== "step-increment")
2809 sal_Int64 nSpinSize
= rValue
.toDouble() * nMul
;
2810 rTarget
.SetSpinSize(nSpinSize
);
2814 SAL_INFO("vcl.layout", "unhandled property :" << rKey
.getStr());
2819 void VclBuilder::mungeAdjustment(TimeField
&rTarget
, const Adjustment
&rAdjustment
)
2821 for (stringmap::const_iterator aI
= rAdjustment
.begin(), aEnd
= rAdjustment
.end(); aI
!= aEnd
; ++aI
)
2823 const OString
&rKey
= aI
->first
;
2824 const OString
&rValue
= aI
->second
;
2826 if (rKey
== "upper")
2828 Time
aUpper(rValue
.toInt32());
2829 rTarget
.SetMax(aUpper
);
2830 rTarget
.SetLast(aUpper
);
2832 else if (rKey
== "lower")
2834 Time
aLower(rValue
.toInt32());
2835 rTarget
.SetMin(aLower
);
2836 rTarget
.SetFirst(aLower
);
2838 else if (rKey
== "value")
2840 Time
aValue(rValue
.toInt32());
2841 rTarget
.SetTime(aValue
);
2845 SAL_INFO("vcl.layout", "unhandled property :" << rKey
.getStr());
2850 void VclBuilder::mungeAdjustment(DateField
&rTarget
, const Adjustment
&rAdjustment
)
2852 for (stringmap::const_iterator aI
= rAdjustment
.begin(), aEnd
= rAdjustment
.end(); aI
!= aEnd
; ++aI
)
2854 const OString
&rKey
= aI
->first
;
2855 const OString
&rValue
= aI
->second
;
2857 if (rKey
== "upper")
2859 Date
aUpper(rValue
.toInt32());
2860 rTarget
.SetMax(aUpper
);
2861 rTarget
.SetLast(aUpper
);
2863 else if (rKey
== "lower")
2865 Date
aLower(rValue
.toInt32());
2866 rTarget
.SetMin(aLower
);
2867 rTarget
.SetFirst(aLower
);
2869 else if (rKey
== "value")
2871 Date
aValue(rValue
.toInt32());
2872 rTarget
.SetDate(aValue
);
2876 SAL_INFO("vcl.layout", "unhandled property :" << rKey
.getStr());
2881 void VclBuilder::mungeAdjustment(ScrollBar
&rTarget
, const Adjustment
&rAdjustment
)
2883 for (stringmap::const_iterator aI
= rAdjustment
.begin(), aEnd
= rAdjustment
.end(); aI
!= aEnd
; ++aI
)
2885 const OString
&rKey
= aI
->first
;
2886 const OString
&rValue
= aI
->second
;
2888 if (rKey
== "upper")
2889 rTarget
.SetRangeMax(rValue
.toInt32());
2890 else if (rKey
== "lower")
2891 rTarget
.SetRangeMin(rValue
.toInt32());
2892 else if (rKey
== "value")
2893 rTarget
.SetThumbPos(rValue
.toInt32());
2894 else if (rKey
== "step-increment")
2895 rTarget
.SetLineSize(rValue
.toInt32());
2896 else if (rKey
== "page-increment")
2897 rTarget
.SetPageSize(rValue
.toInt32());
2900 SAL_INFO("vcl.layout", "unhandled property :" << rKey
.getStr());
2905 void VclBuilder::mungeTextBuffer(VclMultiLineEdit
&rTarget
, const TextBuffer
&rTextBuffer
)
2907 for (stringmap::const_iterator aI
= rTextBuffer
.begin(), aEnd
= rTextBuffer
.end(); aI
!= aEnd
; ++aI
)
2909 const OString
&rKey
= aI
->first
;
2910 const OString
&rValue
= aI
->second
;
2913 rTarget
.SetText(OStringToOUString(rValue
, RTL_TEXTENCODING_UTF8
));
2916 SAL_INFO("vcl.layout", "unhandled property :" << rKey
.getStr());
2921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */