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 #include <svx/svxdlg.hxx>
21 #include <svx/dialogs.hrc>
24 #include <svx/fontwork.hxx>
25 #include <svx/svdpage.hxx>
26 #include <sfx2/app.hxx>
27 #include <sfx2/objface.hxx>
28 #include <sfx2/objsh.hxx>
29 #include <sfx2/request.hxx>
30 #include <sfx2/dispatch.hxx>
31 #include <sfx2/viewfrm.hxx>
32 #include <svl/whiter.hxx>
35 #include <drwlayer.hxx>
36 #include <strings.hrc>
37 #include <viewdata.hxx>
38 #include <document.hxx>
39 #include <drawview.hxx>
40 #include <scresid.hxx>
41 #include <svx/svdobj.hxx>
42 #include <tabvwsh.hxx>
43 #include <gridwin.hxx>
44 #include <sfx2/bindings.hxx>
46 #define ShellClass_ScDrawShell
47 #include <scslots.hxx>
49 #include <userdat.hxx>
50 #include <svl/macitem.hxx>
51 #include <sfx2/evntconf.hxx>
52 #include <sfx2/viewsh.hxx>
53 #include <com/sun/star/util/XModifiable.hpp>
55 #include <svx/xlnwtit.hxx>
56 #include <svx/chrtitem.hxx>
57 #include <svx/xflgrit.hxx>
58 #include <tools/UnitConversion.hxx>
59 #include <comphelper/lok.hxx>
60 #include <vcl/unohelp2.hxx>
64 SFX_IMPL_INTERFACE(ScDrawShell
, SfxShell
)
68 void lcl_convertStringArguments(SfxItemSet
& rArgs
)
70 if (const SvxDoubleItem
* pWidthItem
= rArgs
.GetItemIfSet(SID_ATTR_LINE_WIDTH_ARG
, false))
72 double fValue
= pWidthItem
->GetValue();
73 // FIXME: different units...
75 int nValue
= fValue
* nPow
;
77 XLineWidthItem
aItem(nValue
);
80 if (const SfxStringItem
* pJSON
= rArgs
.GetItemIfSet(SID_FILL_GRADIENT_JSON
, false))
82 basegfx::BGradient aGradient
= basegfx::BGradient::fromJSON(pJSON
->GetValue());
83 XFillGradientItem
aItem(aGradient
);
89 void ScDrawShell::InitInterface_Impl()
91 GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT
,
92 SfxVisibilityFlags::Standard
| SfxVisibilityFlags::Server
,
93 ToolbarId::Draw_Objectbar
);
95 GetStaticInterface()->RegisterPopupMenu(u
"draw"_ustr
);
97 GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId());
100 // disable the unwanted Accelerators
102 void ScDrawShell::StateDisableItems( SfxItemSet
&rSet
)
104 SfxWhichIter
aIter(rSet
);
105 sal_uInt16 nWhich
= aIter
.FirstWhich();
109 rSet
.DisableItem( nWhich
);
110 nWhich
= aIter
.NextWhich();
114 void ScDrawShell::setModified()
116 const SfxObjectShell
* pShell
= GetObjectShell();
119 css::uno::Reference
< css::util::XModifiable
> xModif( pShell
->GetModel(), css::uno::UNO_QUERY
);
121 xModif
->setModified( true );
125 static void lcl_invalidateTransformAttr(const ScTabViewShell
* pViewShell
)
127 SfxBindings
& rBindings
=pViewShell
->GetViewFrame().GetBindings();
128 rBindings
.Invalidate(SID_ATTR_TRANSFORM_WIDTH
);
129 rBindings
.Invalidate(SID_ATTR_TRANSFORM_HEIGHT
);
130 rBindings
.Invalidate(SID_ATTR_TRANSFORM_POS_X
);
131 rBindings
.Invalidate(SID_ATTR_TRANSFORM_POS_Y
);
132 rBindings
.Invalidate(SID_ATTR_TRANSFORM_ANGLE
);
133 rBindings
.Invalidate(SID_ATTR_TRANSFORM_ROT_X
);
134 rBindings
.Invalidate(SID_ATTR_TRANSFORM_ROT_Y
);
135 rBindings
.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH
);
136 rBindings
.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT
);
139 void ScDrawShell::ExecDrawAttr( SfxRequest
& rReq
)
141 sal_uInt16 nSlot
= rReq
.GetSlot();
142 vcl::Window
* pWin
= rViewData
.GetActiveWin();
143 ScDrawView
* pView
= rViewData
.GetScDrawView();
144 SdrModel
* pDoc
= rViewData
.GetDocument().GetDrawLayer();
146 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
147 const size_t nMarkCount
= rMarkList
.GetMarkCount();
148 SdrObject
* pSingleSelectedObj
= nullptr;
149 if ( nMarkCount
> 0 )
150 pSingleSelectedObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
154 case SID_ASSIGNMACRO
:
156 if ( pSingleSelectedObj
)
157 ExecuteMacroAssign(pSingleSelectedObj
, pWin
? pWin
->GetFrameWeld() : nullptr);
161 case SID_CELL_FORMAT_RESET
:
162 case SID_TEXT_STANDARD
:
164 SfxItemSetFixed
<SDRATTR_TEXT_MINFRAMEHEIGHT
, SDRATTR_TEXT_MINFRAMEHEIGHT
,
165 SDRATTR_TEXT_MAXFRAMEHEIGHT
, SDRATTR_TEXT_MAXFRAMEWIDTH
> aEmptyAttr(GetPool());
167 if (ScDrawLayer::IsNoteCaption(pSingleSelectedObj
))
168 aEmptyAttr
.Put(pView
->GetAttrFromMarked(true));
170 pView
->SetAttributes(aEmptyAttr
, true);
173 case SID_MOVE_SHAPE_HANDLE
:
175 const SfxItemSet
*pArgs
= rReq
.GetArgs ();
176 if (pArgs
&& pArgs
->Count () >= 3)
178 const SfxUInt32Item
* handleNumItem
= rReq
.GetArg
<SfxUInt32Item
>(FN_PARAM_1
);
179 const SfxUInt32Item
* newPosXTwips
= rReq
.GetArg
<SfxUInt32Item
>(FN_PARAM_2
);
180 const SfxUInt32Item
* newPosYTwips
= rReq
.GetArg
<SfxUInt32Item
>(FN_PARAM_3
);
181 const SfxInt32Item
* OrdNum
= rReq
.GetArg
<SfxInt32Item
>(FN_PARAM_4
);
183 const sal_uLong handleNum
= handleNumItem
->GetValue();
184 const sal_uLong newPosX
= convertTwipToMm100(newPosXTwips
->GetValue());
185 const sal_uLong newPosY
= convertTwipToMm100(newPosYTwips
->GetValue());
187 bool bNegateX
= comphelper::LibreOfficeKit::isActive() && rViewData
.GetDocument().IsLayoutRTL(rViewData
.GetTabNo());
188 pView
->MoveShapeHandle(handleNum
, Point(bNegateX
? -static_cast<tools::Long
>(newPosX
) : newPosX
, newPosY
), OrdNum
? OrdNum
->GetValue() : -1);
193 case SID_ATTR_LINE_STYLE
:
194 case SID_ATTR_LINEEND_STYLE
:
195 case SID_ATTR_LINE_START
:
196 case SID_ATTR_LINE_END
:
197 case SID_ATTR_LINE_DASH
:
198 case SID_ATTR_LINE_WIDTH
:
199 case SID_ATTR_LINE_COLOR
:
200 case SID_ATTR_LINE_TRANSPARENCE
:
201 case SID_ATTR_LINE_JOINT
:
202 case SID_ATTR_LINE_CAP
:
203 case SID_ATTR_FILL_STYLE
:
204 case SID_ATTR_FILL_COLOR
:
205 case SID_ATTR_FILL_GRADIENT
:
206 case SID_ATTR_FILL_HATCH
:
207 case SID_ATTR_FILL_BITMAP
:
208 case SID_ATTR_FILL_TRANSPARENCE
:
209 case SID_ATTR_FILL_FLOATTRANSPARENCE
:
212 case SID_ATTR_FILL_SHADOW
:
213 case SID_ATTR_SHADOW_TRANSPARENCE
:
214 case SID_ATTR_SHADOW_COLOR
:
215 case SID_ATTR_SHADOW_XDISTANCE
:
216 case SID_ATTR_SHADOW_YDISTANCE
:
218 // if toolbar is vertical :
219 if ( !rReq
.GetArgs() )
223 case SID_ATTR_LINE_STYLE
:
224 case SID_ATTR_LINE_DASH
:
225 case SID_ATTR_LINE_WIDTH
:
226 case SID_ATTR_LINE_COLOR
:
227 case SID_ATTR_LINE_TRANSPARENCE
:
228 case SID_ATTR_LINE_JOINT
:
229 case SID_ATTR_LINE_CAP
:
230 ExecuteLineDlg( rReq
);
233 case SID_ATTR_FILL_STYLE
:
234 case SID_ATTR_FILL_COLOR
:
235 case SID_ATTR_FILL_GRADIENT
:
236 case SID_ATTR_FILL_HATCH
:
237 case SID_ATTR_FILL_BITMAP
:
238 case SID_ATTR_FILL_TRANSPARENCE
:
239 case SID_ATTR_FILL_FLOATTRANSPARENCE
:
242 case SID_ATTR_FILL_SHADOW
:
243 case SID_ATTR_SHADOW_TRANSPARENCE
:
244 case SID_ATTR_SHADOW_COLOR
:
245 case SID_ATTR_SHADOW_XDISTANCE
:
246 case SID_ATTR_SHADOW_YDISTANCE
:
247 ExecuteAreaDlg( rReq
);
258 if( rMarkList
.GetMarkCount() != 0 )
260 std::unique_ptr
<SfxItemSet
> pNewArgs
= rReq
.GetArgs()->Clone();
261 lcl_convertStringArguments(*pNewArgs
);
262 pView
->SetAttrToMarked(*pNewArgs
, false);
265 pView
->SetDefaultAttr( *rReq
.GetArgs(), false);
266 pView
->InvalidateAttribs();
270 case SID_ATTRIBUTES_LINE
:
271 ExecuteLineDlg( rReq
);
274 case SID_ATTRIBUTES_AREA
:
275 ExecuteAreaDlg( rReq
);
278 case SID_MEASURE_DLG
:
279 ExecuteMeasureDlg( rReq
);
282 case SID_DRAWTEXT_ATTR_DLG
:
283 ExecuteTextAttrDlg( rReq
);
286 case SID_EDIT_HYPERLINK
:
287 if ( pSingleSelectedObj
)
288 rViewData
.GetDispatcher().Execute( SID_HYPERLINK_DIALOG
);
291 case SID_REMOVE_HYPERLINK
:
292 if ( pSingleSelectedObj
)
294 pSingleSelectedObj
->setHyperlink(OUString());
299 case SID_OPEN_HYPERLINK
:
300 case SID_COPY_HYPERLINK_LOCATION
:
301 if ( nMarkCount
== 1 )
303 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
304 if ( pObj
->IsGroupObject() )
306 SdrPageView
* pPV
= nullptr;
307 SdrObject
* pHit
= pView
->PickObj(pWin
->PixelToLogic(rViewData
.GetMousePosPixel()), pView
->getHitTolLog(), pPV
, SdrSearchOptions::DEEP
);
312 if (!pObj
->getHyperlink().isEmpty())
314 if (nSlot
== SID_OPEN_HYPERLINK
)
316 ScGlobal::OpenURL(pObj
->getHyperlink(), OUString(), true);
318 else if (nSlot
== SID_COPY_HYPERLINK_LOCATION
)
320 uno::Reference
<datatransfer::clipboard::XClipboard
> xClipboard
321 = GetViewShell()->GetWindow()->GetClipboard();
322 vcl::unohelper::TextDataObject::CopyStringTo(pObj
->getHyperlink(), xClipboard
);
328 case SID_ATTR_TRANSFORM
:
330 if ( rMarkList
.GetMarkCount() != 0 )
332 const SfxItemSet
* pArgs
= rReq
.GetArgs();
336 if( rMarkList
.GetMark(0) != nullptr )
338 SdrObject
* pObj
= rMarkList
.GetMark(0)->GetMarkedSdrObj();
339 std::shared_ptr
<SfxRequest
> pRequest
= std::make_shared
<SfxRequest
>(rReq
);
341 if( pObj
->GetObjIdentifier() == SdrObjKind::Caption
)
344 SfxItemSet
aNewAttr(pDoc
->GetItemPool());
345 pView
->GetAttributes(aNewAttr
);
346 // Size and Position Itemset
347 SfxItemSet
aNewGeoAttr(pView
->GetGeoAttrFromMarked());
349 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
350 VclPtr
<SfxAbstractTabDialog
> pDlg(pFact
->CreateCaptionDialog(pWin
? pWin
->GetFrameWeld() : nullptr, pView
));
352 const WhichRangesContainer aRange
= pDlg
->GetInputRanges( *aNewAttr
.GetPool() );
353 SfxItemSet
aCombSet( *aNewAttr
.GetPool(), aRange
);
354 aCombSet
.Put( aNewAttr
);
355 aCombSet
.Put( aNewGeoAttr
);
356 pDlg
->SetInputSet( &aCombSet
);
358 pDlg
->StartExecuteAsync([pDlg
, pRequest
=std::move(pRequest
), pView
, this](
360 if (nResult
== RET_OK
)
362 pRequest
->Done(*(pDlg
->GetOutputItemSet()));
363 pView
->SetAttributes(*pDlg
->GetOutputItemSet());
364 pView
->SetGeoAttrToMarked(*pDlg
->GetOutputItemSet());
367 lcl_invalidateTransformAttr(rViewData
.GetViewShell());
373 SfxItemSet
aNewAttr(pView
->GetGeoAttrFromMarked());
374 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
375 VclPtr
<SfxAbstractTabDialog
> pDlg(pFact
->CreateSvxTransformTabDialog(pWin
? pWin
->GetFrameWeld() : nullptr, &aNewAttr
, pView
));
377 pDlg
->StartExecuteAsync([pDlg
, pRequest
=std::move(pRequest
), pView
, this](
379 if (nResult
== RET_OK
)
381 pRequest
->Done(*(pDlg
->GetOutputItemSet()));
382 pView
->SetGeoAttrToMarked(*pDlg
->GetOutputItemSet());
385 lcl_invalidateTransformAttr(rViewData
.GetViewShell());
393 pView
->SetGeoAttrToMarked( *pArgs
);
396 lcl_invalidateTransformAttr(rViewData
.GetViewShell());
400 case SID_ATTR_GLOW_COLOR
:
401 case SID_ATTR_GLOW_RADIUS
:
402 case SID_ATTR_GLOW_TRANSPARENCY
:
403 case SID_ATTR_GLOW_TEXT_COLOR
:
404 case SID_ATTR_GLOW_TEXT_RADIUS
:
405 case SID_ATTR_GLOW_TEXT_TRANSPARENCY
:
406 case SID_ATTR_SOFTEDGE_RADIUS
:
407 case SID_ATTR_TEXTCOLUMNS_NUMBER
:
408 case SID_ATTR_TEXTCOLUMNS_SPACING
:
409 if (const SfxItemSet
* pNewArgs
= rReq
.GetArgs())
410 pView
->SetAttrToMarked(*pNewArgs
, false);
419 void ScDrawShell::ExecuteMacroAssign(SdrObject
* pObj
, weld::Window
* pWin
)
421 SvxMacroItem
aItem ( SfxGetpApp()->GetPool().GetWhichIDFromSlotID( SID_ATTR_MACROITEM
) );
422 ScMacroInfo
* pInfo
= ScDrawLayer::GetMacroInfo( pObj
, true );
423 if ( !pInfo
->GetMacro().isEmpty() )
425 SvxMacroTableDtor aTab
;
426 const OUString
& sMacro
= pInfo
->GetMacro();
427 aTab
.Insert(SvMacroItemId::OnClick
, SvxMacro(sMacro
, OUString()));
428 aItem
.SetMacroTable( aTab
);
431 // create empty itemset for macro-dlg
432 auto xItemSet
= std::make_unique
<SfxItemSetFixed
<SID_ATTR_MACROITEM
, SID_ATTR_MACROITEM
, SID_EVENTCONFIG
, SID_EVENTCONFIG
>>( SfxGetpApp()->GetPool() );
433 xItemSet
->Put ( aItem
);
435 SfxEventNamesItem
aNamesItem(SID_EVENTCONFIG
);
436 aNamesItem
.AddEvent( ScResId(RID_SCSTR_ONCLICK
), OUString(), SvMacroItemId::OnClick
);
437 xItemSet
->Put( aNamesItem
);
439 css::uno::Reference
< css::frame::XFrame
> xFrame
;
441 xFrame
= GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
443 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
444 VclPtr
<SfxAbstractDialog
> pMacroDlg(pFact
->CreateEventConfigDialog( pWin
, std::move(xItemSet
), xFrame
));
445 pMacroDlg
->StartExecuteAsync(
446 [this, pMacroDlg
, pObj
, pInfo
] (sal_Int32 nResult
) mutable -> void
448 ScopedVclPtr
<SfxAbstractDialog
> pDlgDisposer(std::move(pMacroDlg
));
449 if (nResult
!= RET_OK
)
452 const SfxItemSet
* pOutSet
= pDlgDisposer
->GetOutputItemSet();
453 const SvxMacroItem
* pItem
= pOutSet
->GetItemIfSet( SID_ATTR_MACROITEM
, false );
458 const SvxMacro
* pMacro
= pItem
->GetMacroTable().Get( SvMacroItemId::OnClick
);
460 sMacro
= pMacro
->GetMacName();
462 if ( pObj
->IsGroupObject() )
464 SdrObjList
* pOL
= pObj
->GetSubList();
465 for (const rtl::Reference
<SdrObject
>& pChildObj
: *pOL
)
467 pInfo
= ScDrawLayer::GetMacroInfo( pChildObj
.get(), true );
468 pInfo
->SetMacro( sMacro
);
472 pInfo
->SetMacro( sMacro
);
479 void ScDrawShell::ExecuteLineDlg( const SfxRequest
& rReq
)
481 ScDrawView
* pView
= rViewData
.GetScDrawView();
482 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
483 bool bHasMarked
= rMarkList
.GetMarkCount() != 0;
484 const SdrObject
* pObj
= nullptr;
486 std::shared_ptr
<SfxRequest
> xRequest
= std::make_shared
<SfxRequest
>(rReq
);
488 if( rMarkList
.GetMarkCount() == 1 )
489 pObj
= rMarkList
.GetMark(0)->GetMarkedSdrObj();
491 SfxItemSet
aNewAttr( pView
->GetDefaultAttr() );
493 pView
->MergeAttrFromMarked( aNewAttr
, false );
495 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
496 VclPtr
<SfxAbstractTabDialog
> pDlg(pFact
->CreateSvxLineTabDialog( rViewData
.GetDialogParent(),
498 rViewData
.GetDocument().GetDrawLayer(),
502 pDlg
->StartExecuteAsync([pDlg
, xRequest
=std::move(xRequest
), pView
, bHasMarked
](sal_Int32 nResult
){
503 if ( nResult
== RET_OK
)
506 pView
->SetAttrToMarked( *pDlg
->GetOutputItemSet(), false );
508 pView
->SetDefaultAttr( *pDlg
->GetOutputItemSet(), false );
510 pView
->InvalidateAttribs();
517 void ScDrawShell::ExecuteAreaDlg( const SfxRequest
& rReq
)
519 ScDrawView
* pView
= rViewData
.GetScDrawView();
520 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
521 bool bHasMarked
= rMarkList
.GetMarkCount() != 0;
523 std::shared_ptr
<SfxRequest
> xRequest
= std::make_shared
<SfxRequest
>(rReq
);
525 SfxItemSet
aNewAttr( pView
->GetDefaultAttr() );
527 pView
->MergeAttrFromMarked( aNewAttr
, false );
529 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
530 weld::Window
* pWin
= rViewData
.GetDialogParent();
531 VclPtr
<AbstractSvxAreaTabDialog
> pDlg(pFact
->CreateSvxAreaTabDialog(
533 rViewData
.GetDocument().GetDrawLayer(), true, false));
535 pDlg
->StartExecuteAsync([pDlg
, xRequest
=std::move(xRequest
), pView
, bHasMarked
](sal_Int32 nResult
){
536 if ( nResult
== RET_OK
)
539 pView
->SetAttrToMarked( *pDlg
->GetOutputItemSet(), false );
541 pView
->SetDefaultAttr( *pDlg
->GetOutputItemSet(), false );
543 pView
->InvalidateAttribs();
550 void ScDrawShell::ExecuteTextAttrDlg( SfxRequest
& rReq
)
552 ScDrawView
* pView
= rViewData
.GetScDrawView();
553 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
554 bool bHasMarked
= rMarkList
.GetMarkCount() != 0;
555 SfxItemSet
aNewAttr ( pView
->GetDefaultAttr() );
558 pView
->MergeAttrFromMarked( aNewAttr
, false );
560 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
561 weld::Window
* pWin
= rViewData
.GetDialogParent();
562 ScopedVclPtr
<SfxAbstractTabDialog
> pDlg(pFact
->CreateTextTabDialog(pWin
, &aNewAttr
, pView
));
564 sal_uInt16 nResult
= pDlg
->Execute();
566 if ( RET_OK
== nResult
)
569 pView
->SetAttributes( *pDlg
->GetOutputItemSet() );
571 pView
->SetDefaultAttr( *pDlg
->GetOutputItemSet(), false );
573 pView
->InvalidateAttribs();
578 void ScDrawShell::ExecuteMeasureDlg( SfxRequest
& rReq
)
580 ScDrawView
* pView
= rViewData
.GetScDrawView();
581 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
582 bool bHasMarked
= rMarkList
.GetMarkCount() != 0;
583 SfxItemSet
aNewAttr ( pView
->GetDefaultAttr() );
586 pView
->MergeAttrFromMarked( aNewAttr
, false );
588 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
589 weld::Window
* pWin
= rViewData
.GetDialogParent();
590 ScopedVclPtr
<SfxAbstractDialog
> pDlg(pFact
->CreateSfxDialog(pWin
, aNewAttr
, pView
, RID_SVXPAGE_MEASURE
));
592 sal_uInt16 nResult
= pDlg
->Execute();
594 if ( RET_OK
== nResult
)
597 pView
->SetAttrToMarked( *pDlg
->GetOutputItemSet(), false );
599 pView
->SetDefaultAttr( *pDlg
->GetOutputItemSet(), false );
601 pView
->InvalidateAttribs();
606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */