1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#ifndef INCLUDED_SVX_SVDPNTV_HXX
#define INCLUDED_SVX_SVDPNTV_HXX
#include <svl/SfxBroadcaster.hxx>
#include <svl/lstner.hxx>
#include <svl/undo.hxx>
#include <svx/svddrag.hxx>
#include <svx/svdlayer.hxx>
#include <vcl/window.hxx>
#include <svtools/colorcfg.hxx>
#include <com/sun/star/awt/XControlContainer.hpp>
#include <svl/itemset.hxx>
#include <vcl/timer.hxx>
#include <svx/svxdllapi.h>
#include <svtools/optionsdrawinglayer.hxx>
#include <unotools/options.hxx>
#include <vcl/idle.hxx>
// Pre defines
class SdrPageWindow;
namespace com { namespace sun { namespace star { namespace awt {
class XControlContainer;
}}}}
class SdrPage;
class SdrView;
class SfxItemSet;
class SfxStyleSheet;
class SdrOle2Obj;
class SdrModel;
class SdrObject;
enum class GraphicManagerDrawFlags;
#ifdef DBG_UTIL
class SdrItemBrowser;
#endif
namespace sdr { namespace contact {
class ViewObjectContactRedirector;
}}
// Defines for AnimationMode
enum class SdrAnimationMode
{
Animate,
Disable
};
// Typedefs and defines
typedef unsigned char SDR_TRISTATE;
#define FUZZY (2)
#define SDR_ANYFORMAT (0xFFFFFFFF)
#define SDR_ANYITEM (0xFFFF)
#define SDRVIEWWIN_NOTFOUND (0xFFFF)
class SdrPaintView;
namespace sdr
{
namespace contact
{
class ViewObjectContactRedirector;
}
}
class SVX_DLLPUBLIC SvxViewChangedHint : public SfxHint
{
public:
explicit SvxViewChangedHint();
};
/// Typedefs for a list of SdrPaintWindows
class SdrPaintWindow;
typedef ::std::vector< SdrPaintWindow* > SdrPaintWindowVector;
/**
* Helper to convert any GDIMetaFile to a good quality BitmapEx,
* using default parameters and graphic::XPrimitive2DRenderer
*/
BitmapEx SVX_DLLPUBLIC convertMetafileToBitmapEx(
const GDIMetaFile& rMtf,
const basegfx::B2DRange& rTargetRange,
const sal_uInt32 nMaximumQuadraticPixels);
class SVX_DLLPUBLIC SdrPaintView : public SfxListener, public SfxRepeatTarget, public SfxBroadcaster, public ::utl::ConfigurationListener
{
friend class SdrPageView;
friend class SdrGrafObj;
SdrPageView* mpPageView;
protected:
SdrModel* mpModel;
#ifdef DBG_UTIL
VclPtr<SdrItemBrowser> mpItemBrowser;
#endif
VclPtr<OutputDevice> mpActualOutDev; // Only for comparison
VclPtr<OutputDevice> mpDragWin;
SfxStyleSheet* mpDefaultStyleSheet;
OUString maActualLayer; // Current drawing layer
OUString maMeasureLayer; // Current layer for measurements
// Container aPagV; // List of SdrPageViews
// All windows this view is displayed on
SdrPaintWindowVector maPaintWindows;
Size maGridBig; // FIXME: We need to get rid of this eventually
Size maGridFin; // FIXME: We need to get rid of this eventually
SdrDragStat maDragStat;
Rectangle maMaxWorkArea;
SfxItemSet maDefaultAttr;
Idle maComeBackIdle;
SdrAnimationMode meAnimationMode;
sal_uInt16 mnHitTolPix;
sal_uInt16 mnMinMovPix;
sal_uInt16 mnHitTolLog;
sal_uInt16 mnMinMovLog;
GraphicManagerDrawFlags mnGraphicManagerDrawMode;
// Hold an incarnation of Drawinglayer configuration options
SvtOptionsDrawinglayer maDrawinglayerOpt;
bool mbPageVisible : 1;
bool mbPageShadowVisible : 1;
bool mbPageBorderVisible : 1;
bool mbBordVisible : 1;
bool mbGridVisible : 1;
bool mbGridFront : 1;
bool mbHlplVisible : 1;
bool mbHlplFront : 1;
bool mbGlueVisible : 1; // Persistent; show glue points
bool mbGlueVisible2 : 1; // Also show glue points for GluePointEdit
bool mbGlueVisible3 : 1; // Also show glue points for EdgeTool
bool mbGlueVisible4 : 1; // Show glue points, if one edge is selected
bool mbSomeObjChgdFlag : 1;
bool mbSwapAsynchron : 1;
bool mbPrintPreview : 1;
// These bools manage, the status that is displayed
//
bool mbAnimationPause : 1;
// Flag which decides if buffered output for this view is allowed. When
// set, PreRendering for PageView rendering will be used. Default is sal_False
bool mbBufferedOutputAllowed : 1;
// Flag which decides if buffered overlay for this view is allowed. When
// set, the output will be buffered in an overlay vdev. When not, overlay is
// directly painted to OutDev. Default is sal_False.
bool mbBufferedOverlayAllowed : 1;
// Allow page painting at all?
bool mbPagePaintingAllowed : 1;
// Is this a preview renderer?
bool mbPreviewRenderer : 1;
// Flags for calc and sw for suppressing OLE, CHART or DRAW objects
bool mbHideOle : 1;
bool mbHideChart : 1;
bool mbHideDraw : 1; // hide draw objects other than form controls
bool mbHideFormControl : 1; // hide form controls only
public:
// Interface for PagePaintingAllowed flag
bool IsBufferedOutputAllowed() const;
void SetBufferedOutputAllowed(bool bNew);
// Interface for BufferedOverlayAllowed flag
bool IsBufferedOverlayAllowed() const;
void SetBufferedOverlayAllowed(bool bNew);
// Allow page painting at all?
bool IsPagePaintingAllowed() const { return mbPagePaintingAllowed;}
void SetPagePaintingAllowed(bool bNew);
protected:
svtools::ColorConfig maColorConfig;
Color maGridColor;
// Interface to SdrPaintWindow
protected:
void AppendPaintWindow(SdrPaintWindow& rNew);
void RemovePaintWindow(SdrPaintWindow& rOld);
void ConfigurationChanged( ::utl::ConfigurationBroadcaster*, sal_uInt32 ) override;
public:
sal_uInt32 PaintWindowCount() const { return maPaintWindows.size(); }
SdrPaintWindow* FindPaintWindow(const OutputDevice& rOut) const;
SdrPaintWindow* GetPaintWindow(sal_uInt32 nIndex) const;
// Replacement for GetWin(0), may return 0L (!)
OutputDevice* GetFirstOutputDevice() const;
private:
SVX_DLLPRIVATE void ImpClearVars();
DECL_LINK_TYPED(ImpComeBackHdl, Idle*, void);
protected:
sal_uInt16 ImpGetMinMovLogic(short nMinMov, const OutputDevice* pOut) const;
sal_uInt16 ImpGetHitTolLogic(short nHitTol, const OutputDevice* pOut) const;
// If one does not want to wait for the IdleState of the system (cheated as const)
void FlushComeBackTimer() const;
void TheresNewMapMode();
void ImpSetGlueVisible2(bool bOn) { if (mbGlueVisible2!=bOn) { mbGlueVisible2=bOn; if (!mbGlueVisible && !mbGlueVisible3 && !mbGlueVisible4) GlueInvalidate(); } }
void ImpSetGlueVisible3(bool bOn) { if (mbGlueVisible3!=bOn) { mbGlueVisible3=bOn; if (!mbGlueVisible && !mbGlueVisible2 && !mbGlueVisible4) GlueInvalidate(); } }
void ImpSetGlueVisible4(bool bOn) { if (mbGlueVisible4!=bOn) { mbGlueVisible4=bOn; if (!mbGlueVisible && !mbGlueVisible2 && !mbGlueVisible3) GlueInvalidate(); } }
public:
bool ImpIsGlueVisible() { return mbGlueVisible || mbGlueVisible2 || mbGlueVisible3 || mbGlueVisible4; }
protected:
virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
void GlueInvalidate() const;
// ModelHasChanged is called, as soon as the system is idle again after many SdrHintKind::ObjectChange.
//
// Any sub-class override this method, MUST call the base class' ModelHasChanged() method
virtual void ModelHasChanged();
protected:
// #i71538# make constructors of SdrView sub-components protected to avoid incomplete incarnations which may get casted to SdrView
SdrPaintView(SdrModel* pModel1, OutputDevice* pOut = nullptr);
virtual ~SdrPaintView() override;
public:
virtual void ClearPageView();
SdrModel* GetModel() const { return mpModel; }
virtual bool IsAction() const;
virtual void MovAction(const Point& rPnt);
virtual void EndAction();
virtual void BckAction();
virtual void BrkAction(); // Cancel all Actions (e.g. cancel dragging)
virtual void TakeActionRect(Rectangle& rRect) const;
// Info about TextEdit. Default is sal_False.
virtual bool IsTextEdit() const;
// Info about TextEditPageView. Default is 0L.
virtual SdrPageView* GetTextEditPageView() const;
// Must be called for every Window change as well as MapMode (Scaling) change:
// If the SdrView is shown in multiple windows at the same time (e.g.
// using the split pane), so that I can convert my pixel values to logical
// values.
void SetActualWin(const OutputDevice* pWin);
void SetMinMoveDistancePixel(sal_uInt16 nVal) { mnMinMovPix=nVal; TheresNewMapMode(); }
void SetHitTolerancePixel(sal_uInt16 nVal) { mnHitTolPix=nVal; TheresNewMapMode(); }
sal_uInt16 GetHitTolerancePixel() const { return (sal_uInt16)mnHitTolPix; }
// Data read access on logic HitTolerance and MinMoveTolerance
sal_uInt16 getHitTolLog() const { return mnHitTolLog; }
// Using the DragState we can tell e.g. which distance was
// already dragged
const SdrDragStat& GetDragStat() const { return maDragStat; }
// Registering/de-registering a PageView at a View
//
// The same Page cannot be registered multiple times.
//
// Methods ending in PgNum expect being passed a Page number.
// Methods ending in PvNum, instead, expect the number of the
// PageView at the SdrView (iterating over all registered Pages).
virtual SdrPageView* ShowSdrPage(SdrPage* pPage);
virtual void HideSdrPage();
// Iterate over all registered PageViews
SdrPageView* GetSdrPageView() const { return mpPageView; }
// A SdrView can be displayed on multiple Windows at the same time
virtual void AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window* pWindow);
virtual void DeleteWindowFromPaintView(OutputDevice* pOldWin);
void SetLayerVisible(const OUString& rName, bool bShow);
bool IsLayerVisible(const OUString& rName) const;
void SetLayerLocked(const OUString& rName, bool bLock=true);
bool IsLayerLocked(const OUString& rName) const;
void SetLayerPrintable(const OUString& rName, bool bPrn);
bool IsLayerPrintable(const OUString& rName) const;
// PrePaint call forwarded from app windows
void PrePaint();
// Used internally for Draw/Impress/sch/chart2
virtual void CompleteRedraw(OutputDevice* pOut, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr);
// #i72889# used from CompleteRedraw() implementation internally, added to be able to do a complete redraw in single steps
// BeginCompleteRedraw returns (or even creates) a SdrPaintWindow which will then be used as the
// target for paints. Since paints may be buffered, use its GetTargetOutputDevice() method which will
// return the buffer in case it's buffered.
//
// DoCompleteRedraw then draws the DrawingLayer hierarchy
// EndCompleteRedraw does the necessary refreshes, paints text edit and overlay as well as destroys the
// SdrPaintWindow again, if needed.
// This means: the SdrPaintWindow is no longer safe after this closing call.
virtual SdrPaintWindow* BeginCompleteRedraw(OutputDevice* pOut);
void DoCompleteRedraw(SdrPaintWindow& rPaintWindow, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr);
virtual void EndCompleteRedraw(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer);
// Used for the other applications basctl/sc/sw which call DrawLayer at PageViews
// #i74769# Interface change to use common BeginCompleteRedraw/EndCompleteRedraw
// #i76114# bDisableIntersect disables intersecting rReg with the Window's paint region
SdrPaintWindow* BeginDrawLayers(OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect = false);
// Used when the region passed to BeginDrawLayers needs to be changed
void UpdateDrawLayersRegion(OutputDevice* pOut, const vcl::Region& rReg);
void EndDrawLayers(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer);
protected:
// Used to paint the form layer after the PreRender device is flushed (painted) to the window.
void ImpFormLayerDrawing( SdrPaintWindow& rPaintWindow );
static vcl::Region OptimizeDrawLayersRegion(OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect);
public:
/// Draw Page as a white area or not
bool IsPageVisible() const { return mbPageVisible; }
/// Draw Page shadow or not
bool IsPageShadowVisible() const { return mbPageShadowVisible; }
/// Draw Page as a white area or not
bool IsPageBorderVisible() const { return mbPageBorderVisible; }
/// Draw Border line or not
bool IsBordVisible() const { return mbBordVisible; }
/// Draw Grid or not
bool IsGridVisible() const { return mbGridVisible; }
/// Draw Grid in front of objects or behind them
bool IsGridFront() const { return mbGridFront ; }
/// Draw Help line of the Page or not
bool IsHlplVisible() const { return mbHlplVisible; }
/// Draw Help line in fron of the objects or beging them
bool IsHlplFront() const { return mbHlplFront ; }
const Color& GetGridColor() const { return maGridColor;}
void SetPageVisible(bool bOn = true) { mbPageVisible=bOn; InvalidateAllWin(); }
void SetPageShadowVisible(bool bOn) { mbPageShadowVisible=bOn; InvalidateAllWin(); }
void SetPageBorderVisible(bool bOn = true) { mbPageBorderVisible=bOn; InvalidateAllWin(); }
void SetBordVisible(bool bOn = true) { mbBordVisible=bOn; InvalidateAllWin(); }
void SetGridVisible(bool bOn) { mbGridVisible=bOn; InvalidateAllWin(); }
void SetGridFront(bool bOn) { mbGridFront =bOn; InvalidateAllWin(); }
void SetHlplVisible(bool bOn = true) { mbHlplVisible=bOn; InvalidateAllWin(); }
void SetHlplFront(bool bOn) { mbHlplFront =bOn; InvalidateAllWin(); }
void SetGlueVisible(bool bOn = true) { if (mbGlueVisible!=bOn) { mbGlueVisible=bOn; if (!mbGlueVisible2 && !mbGlueVisible3 && !mbGlueVisible4) GlueInvalidate(); } }
void SetGridColor( Color aColor );
bool IsPreviewRenderer() const { return mbPreviewRenderer; }
void SetPreviewRenderer(bool bOn) { mbPreviewRenderer=bOn; }
// Access methods for calc and sw hide object modes
bool getHideOle() const { return mbHideOle; }
bool getHideChart() const { return mbHideChart; }
bool getHideDraw() const { return mbHideDraw; }
bool getHideFormControl() const { return mbHideFormControl; }
void setHideOle(bool bNew) { if(bNew != (bool)mbHideOle) mbHideOle = bNew; }
void setHideChart(bool bNew) { if(bNew != (bool)mbHideChart) mbHideChart = bNew; }
void setHideDraw(bool bNew) { if(bNew != (bool)mbHideDraw) mbHideDraw = bNew; }
void setHideFormControl(bool bNew) { if(bNew != (bool)mbHideFormControl) mbHideFormControl = bNew; }
void SetGridCoarse(const Size& rSiz) { maGridBig=rSiz; }
void SetGridFine(const Size& rSiz) {
maGridFin=rSiz;
if (maGridFin.Height()==0) maGridFin.Height()=maGridFin.Width();
if (mbGridVisible) InvalidateAllWin();
}
const Size& GetGridCoarse() const { return maGridBig; }
const Size& GetGridFine() const { return maGridFin; }
void InvalidateAllWin();
void InvalidateAllWin(const Rectangle& rRect);
/// If the View should not call Invalidate() on the windows, override
/// the following 2 methods and do something else.
virtual void InvalidateOneWin(vcl::Window& rWin);
virtual void InvalidateOneWin(vcl::Window& rWin, const Rectangle& rRect);
void SetActiveLayer(const OUString& rName) { maActualLayer=rName; }
const OUString& GetActiveLayer() const { return maActualLayer; }
/// Leave an object group of all visible Pages (like `chdir ..` in MSDOS)
void LeaveOneGroup();
/// Leave all entered object groups of all visible Pages (like `chdir \` in MSDOS)
void LeaveAllGroup();
/// Determine, whether Leave is useful or not
bool IsGroupEntered() const;
/// Default attributes at the View
/// Newly created objects are assigned these attributes by default when they are created.
void SetDefaultAttr(const SfxItemSet& rAttr, bool bReplaceAll);
const SfxItemSet& GetDefaultAttr() const { return maDefaultAttr; }
void SetDefaultStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr);
SfxStyleSheet* GetDefaultStyleSheet() const { return mpDefaultStyleSheet; }
void SetNotPersistDefaultAttr(const SfxItemSet& rAttr, bool bReplaceAll);
void MergeNotPersistDefaultAttr(SfxItemSet& rAttr, bool bOnlyHardAttr) const;
/// Execute a swap-in of e.g. graphics asynchronously.
/// This does not reload all graphics like Paint does, but kicks off
/// the loading there. When such an object is done loading, it is displayed.
/// TODO: Only works at the moment, if SwapGraphics is enabled in the model.
/// The default = false flag is non-persistent
bool IsSwapAsynchron() const { return mbSwapAsynchron; }
void SetSwapAsynchron(bool bJa=true) { mbSwapAsynchron=bJa; }
virtual bool KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin);
virtual bool MouseButtonDown(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/) { return false; }
virtual bool MouseButtonUp(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/) { return false; }
virtual bool MouseMove(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/) { return false; }
virtual bool Command(const CommandEvent& /*rCEvt*/, vcl::Window* /*pWin*/) { return false; }
bool GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const;
bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll);
SfxStyleSheet* GetStyleSheet() const; // SfxStyleSheet* GetStyleSheet(bool& rOk) const;
bool SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr);
virtual void MakeVisible(const Rectangle& rRect, vcl::Window& rWin);
/// For Plugins
/// Is called by the Paint of the OLE object
virtual void DoConnect(SdrOle2Obj* pOleObj);
/// Enable/disable animations for ::Paint
/// Is used by e.g. SdrGrafObj, if it contains an animation
/// Preventing automatic animation is needed for e.g. the presentation view
bool IsAnimationEnabled() const { return ( SdrAnimationMode::Animate == meAnimationMode ); }
void SetAnimationEnabled( bool bEnable=true );
/// Set/unset pause state for animations
void SetAnimationPause( bool bSet );
/// Mode when starting an animation in the Paint Handler:
/// 1. SdrAnimationMode::Animate (default): start animation normally
/// 2. SDR_ANIMATION_DONT_ANIMATE: only show the replacement picture
/// 3. SdrAnimationMode::Disable: don't start and don't show any replacement
void SetAnimationMode( const SdrAnimationMode eMode );
/// The Browser is destroyed for bShow=false
#ifdef DBG_UTIL
void ShowItemBrowser(bool bShow);
bool IsItemBrowserVisible() const { return mpItemBrowser!=nullptr && GetItemBrowser()->IsVisible(); }
vcl::Window* GetItemBrowser() const;
#endif
/// Must be called by the App when scrolling etc. in order for
/// an active FormularControl to be moved too
void VisAreaChanged(const OutputDevice* pOut=nullptr);
void VisAreaChanged(const SdrPageWindow& rWindow);
bool IsPrintPreview() const { return mbPrintPreview; }
void SetPrintPreview(bool bOn = true) { mbPrintPreview=bOn; }
const svtools::ColorConfig& getColorConfig() const { return maColorConfig;}
void onChangeColorConfig();
// #103834# Set background color for svx at SdrPageViews
void SetApplicationBackgroundColor(Color aBackgroundColor);
// #103911# Set document color for svx at SdrPageViews
void SetApplicationDocumentColor(Color aDocumentColor);
// #i38135#
// Sets the timer for Object animations and restarts.
void SetAnimationTimer(sal_uInt32 nTime);
// Access to Drawinglayer configuration options
const SvtOptionsDrawinglayer& getOptionsDrawinglayer() const { return maDrawinglayerOpt; }
};
#endif // INCLUDED_SVX_SVDPNTV_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|