xref: /core/vcl/inc/svdata.hxx (revision e041602d59947fbf5690104d9c34c5f93a624951)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #pragma once
21 
22 #include <sal/config.h>
23 
24 #include <o3tl/lru_map.hxx>
25 #include <o3tl/hash_combine.hxx>
26 #include <o3tl/sorted_vector.hxx>
27 #include <osl/conditn.hxx>
28 #include <tools/fldunit.hxx>
29 #include <unotools/options.hxx>
30 #include <vcl/bitmapex.hxx>
31 #include <vcl/cvtgrf.hxx>
32 #include <vcl/dropcache.hxx>
33 #include <vcl/image.hxx>
34 #include <vcl/settings.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/print.hxx>
37 #include <vcl/uitest/logger.hxx>
38 #include <vcl/virdev.hxx>
39 #include <vcl/wrkwin.hxx>
40 #include <vcl/window.hxx>
41 #include <vcl/task.hxx>
42 #include <LibreOfficeKit/LibreOfficeKitTypes.h>
43 #include <unotools/resmgr.hxx>
44 
45 #include <com/sun/star/lang/XComponent.hpp>
46 #include <com/sun/star/i18n/XCharacterClassification.hpp>
47 #include "vcleventlisteners.hxx"
48 #include "print.h"
49 #include "salwtype.hxx"
50 #include "windowdev.hxx"
51 #include "displayconnectiondispatch.hxx"
52 
53 #include <atomic>
54 #include <mutex>
55 #include <optional>
56 #include <vector>
57 #include <unordered_map>
58 #include "schedulerimpl.hxx"
59 #include <basegfx/DrawCommands.hxx>
60 
61 struct ImplPostEventData;
62 struct ImplTimerData;
63 struct ImplIdleData;
64 struct ImplConfigData;
65 namespace rtl
66 {
67     class OStringBuffer;
68 }
69 namespace vcl::font
70 {
71     class DirectFontSubstitution;
72     class PhysicalFontCollection;
73 }
74 struct BlendFrameCache;
75 struct ImplHotKey;
76 struct ImplEventHook;
77 class Point;
78 class ImplAccelManager;
79 class ImplFontCache;
80 class HelpTextWindow;
81 class ImplTBDragMgr;
82 class ImplIdleMgr;
83 class FloatingWindow;
84 class AllSettings;
85 class NotifyEvent;
86 class Timer;
87 class AutoTimer;
88 class Idle;
89 class Help;
90 class PopupMenu;
91 class Application;
92 class OutputDevice;
93 class SvFileStream;
94 class SystemWindow;
95 class WorkWindow;
96 class Dialog;
97 class VirtualDevice;
98 class Printer;
99 class SalFrame;
100 class SalInstance;
101 class SalSystem;
102 class ImplPrnQueueList;
103 class UnoWrapperBase;
104 class GraphicConverter;
105 class ImplWheelWindow;
106 class SalTimer;
107 class DockingManager;
108 class VclEventListeners2;
109 class SalData;
110 class OpenGLContext;
111 class UITestLogger;
112 
113 #define SV_ICON_ID_OFFICE                               1
114 #define SV_ICON_ID_TEXT                                 2
115 #define SV_ICON_ID_TEXT_TEMPLATE                        3
116 #define SV_ICON_ID_SPREADSHEET                          4
117 #define SV_ICON_ID_SPREADSHEET_TEMPLATE                 5
118 #define SV_ICON_ID_DRAWING                              6
119 #define SV_ICON_ID_PRESENTATION                         8
120 #define SV_ICON_ID_MASTER_DOCUMENT                     10
121 #define SV_ICON_ID_TEMPLATE                            11
122 #define SV_ICON_ID_DATABASE                            12
123 #define SV_ICON_ID_FORMULA                             13
124 
125 const FloatWinPopupFlags LISTBOX_FLOATWINPOPUPFLAGS = FloatWinPopupFlags::Down |
126     FloatWinPopupFlags::NoHorzPlacement | FloatWinPopupFlags::AllMouseButtonClose;
127 
128 namespace com::sun::star::datatransfer::clipboard { class XClipboard; }
129 
130 namespace vcl
131 {
132     class DisplayConnectionDispatch;
133     class Window;
134 }
135 
136 namespace basegfx
137 {
138     class SystemDependentDataManager;
139 }
140 
141 class LocaleConfigurationListener final : public utl::ConfigurationListener
142 {
143 public:
144     virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
145 };
146 
147 typedef std::pair<VclPtr<vcl::Window>, ImplPostEventData *> ImplPostEventPair;
148 
149 struct ImplSVAppData
150 {
151     ImplSVAppData();
152     ~ImplSVAppData();
153 
154     std::optional<AllSettings> mxSettings;           // Application settings
155     LocaleConfigurationListener* mpCfgListener = nullptr;
156     VclEventListeners       maEventListeners;     // listeners for vcl events (eg, extended toolkit)
157     std::vector<Link<VclWindowEvent&,bool> >
158                             maKeyListeners;       // listeners for key events only (eg, extended toolkit)
159     std::vector<ImplPostEventPair> maPostedEventList;
160     ImplAccelManager*       mpAccelMgr = nullptr; // Accelerator Manager
161     std::optional<OUString> mxAppName;            // Application name
162     std::optional<OUString> mxAppFileName;        // Abs. Application FileName
163     std::optional<OUString> mxDisplayName;        // Application Display Name
164     std::optional<OUString> mxToolkitName;        // Toolkit Name
165     Help*                   mpHelp = nullptr;               // Application help
166     VclPtr<PopupMenu>       mpActivePopupMenu;              // Actives Popup-Menu (in Execute)
167     VclPtr<ImplWheelWindow> mpWheelWindow;                  // WheelWindow
168     sal_uInt64              mnLastInputTime = 0;            // GetLastInputTime()
169     sal_uInt16              mnDispatchLevel = 0;            // DispatchLevel
170     sal_uInt16              mnModalMode = 0;                // ModalMode Count
171     SystemWindowFlags       mnSysWinMode = SystemWindowFlags(0); // Mode, when SystemWindows should be created
172     bool                    mbInAppMain = false;            // is Application::Main() on stack
173     bool                    mbInAppExecute = false;         // is Application::Execute() on stack
174     std::atomic<bool>       mbAppQuit = false;              // is Application::Quit() called, volatile because we read/write from different threads
175     bool                    mbSettingsInit = false;         // true: Settings are initialized
176     DialogCancelMode meDialogCancel = DialogCancelMode::Off; // true: All Dialog::Execute() calls will be terminated immediately with return false
177     bool mbRenderToBitmaps = false; // set via svp / headless plugin
178     bool m_bUseSystemLoop = false;
179 
180     DECL_STATIC_LINK(ImplSVAppData, ImplQuitMsg, void*, void);
181 };
182 
183 /// Cache multiple scalings for the same bitmap
184 struct ScaleCacheKey {
185     SalBitmap *mpBitmap;
186     Size       maDestSize;
ScaleCacheKeyScaleCacheKey187     ScaleCacheKey(SalBitmap *pBitmap, const Size &aDestSize)
188     {
189         mpBitmap = pBitmap;
190         maDestSize = aDestSize;
191     }
ScaleCacheKeyScaleCacheKey192     ScaleCacheKey(const ScaleCacheKey &key)
193     {
194         mpBitmap = key.mpBitmap;
195         maDestSize = key.maDestSize;
196     }
operator ==ScaleCacheKey197     bool operator==(ScaleCacheKey const& rOther) const
198     {
199         return mpBitmap == rOther.mpBitmap && maDestSize == rOther.maDestSize;
200     }
201 };
202 
203 namespace std
204 {
205 template <> struct hash<ScaleCacheKey>
206 {
operator ()std::hash207     std::size_t operator()(ScaleCacheKey const& k) const noexcept
208     {
209         std::size_t seed = 0;
210         o3tl::hash_combine(seed, k.mpBitmap);
211         o3tl::hash_combine(seed, k.maDestSize.getWidth());
212         o3tl::hash_combine(seed, k.maDestSize.getHeight());
213         return seed;
214     }
215 };
216 
217 } // end std namespace
218 
219 typedef o3tl::lru_map<ScaleCacheKey, Bitmap> lru_scale_cache;
220 
221 struct ImplSVGDIData
222 {
223     ~ImplSVGDIData();
224 
225     VclPtr<vcl::WindowOutputDevice> mpFirstWinGraphics;     // First OutputDevice with a Frame Graphics
226     VclPtr<vcl::WindowOutputDevice> mpLastWinGraphics;      // Last OutputDevice with a Frame Graphics
227     VclPtr<OutputDevice>    mpFirstVirGraphics;             // First OutputDevice with a VirtualDevice Graphics
228     VclPtr<OutputDevice>    mpLastVirGraphics;              // Last OutputDevice with a VirtualDevice Graphics
229     VclPtr<Printer>         mpFirstPrnGraphics;             // First OutputDevice with an InfoPrinter Graphics
230     VclPtr<Printer>         mpLastPrnGraphics;              // Last OutputDevice with an InfoPrinter Graphics
231     VclPtr<VirtualDevice>   mpFirstVirDev;                  // First VirtualDevice
232     OpenGLContext*          mpLastContext = nullptr;        // Last OpenGLContext
233     VclPtr<Printer>         mpFirstPrinter;                 // First Printer
234     std::unique_ptr<ImplPrnQueueList> mpPrinterQueueList;   // List of all printer queue
235     std::shared_ptr<vcl::font::PhysicalFontCollection> mxScreenFontList; // Screen-Font-List
236     std::shared_ptr<ImplFontCache> mxScreenFontCache;       // Screen-Font-Cache
237     lru_scale_cache         maScaleCache = lru_scale_cache(10); // Cache for scaled images
238     vcl::font::DirectFontSubstitution* mpDirectFontSubst = nullptr; // Font-Substitutions defined in Tools->Options->Fonts
239     std::unique_ptr<GraphicConverter> mxGrfConverter;       // Converter for graphics
240     tools::Long                    mnAppFontX = 0;                 // AppFont X-Numenator for 40/tel Width
241     tools::Long                    mnAppFontY = 0;                 // AppFont Y-Numenator for 80/tel Height
242     bool                    mbFontSubChanged = false;       // true: FontSubstitution was changed between Begin/End
243 
244     o3tl::lru_map<OUString, Bitmap> maThemeImageCache = o3tl::lru_map<OUString, Bitmap>(10);
245     o3tl::lru_map<OUString, gfx::DrawRoot> maThemeDrawCommandsCache = o3tl::lru_map<OUString, gfx::DrawRoot>(50);
246 };
247 
248 struct ImplSVFrameData
249 {
250     ~ImplSVFrameData();
251     VclPtr<vcl::Window>     mpFirstFrame;                   // First FrameWindow
252     VclPtr<vcl::Window>     mpActiveApplicationFrame;       // the last active application frame, can be used as DefModalDialogParent if no focuswin set
253     VclPtr<WorkWindow>      mpAppWin;                       // Application-Window
254 
255     std::unique_ptr<UITestLogger> m_pUITestLogger;
256 };
257 
258 struct ImplSVWinData
259 {
260     ~ImplSVWinData();
261     VclPtr<vcl::Window>     mpFocusWin;                     // window, that has the focus
262     VclPtr<vcl::Window>     mpCaptureWin;                   // window, that has the mouse capture
263     VclPtr<vcl::Window>     mpLastDeacWin;                  // Window, that need a deactivate (FloatingWindow-Handling)
264     VclPtr<FloatingWindow>  mpFirstFloat;                   // First FloatingWindow in PopupMode
265     std::vector<VclPtr<Dialog>> mpExecuteDialogs;           ///< Stack of dialogs that are Execute()'d - the last one is the top most one.
266     VclPtr<vcl::Window>     mpExtTextInputWin;              // Window, which is in ExtTextInput
267     VclPtr<vcl::Window>     mpTrackWin;                     // window, that is in tracking mode
268     std::unique_ptr<AutoTimer> mpTrackTimer;                // tracking timer
269     std::vector<Image>      maMsgBoxImgList;                // ImageList for MessageBox
270     VclPtr<vcl::Window>     mpAutoScrollWin;                // window, that is in AutoScrollMode mode
271     VclPtr<vcl::Window>     mpLastWheelWindow;              // window, that last received a mouse wheel event
272     SalWheelMouseEvent      maLastWheelEvent;               // the last received mouse wheel event
273 
274     StartTrackingFlags      mnTrackFlags = StartTrackingFlags::NONE; // tracking flags
275     StartAutoScrollFlags    mnAutoScrollFlags = StartAutoScrollFlags::NONE; // auto scroll flags
276     bool                    mbNoDeactivate = false;         // true: do not execute Deactivate
277     bool                    mbNoSaveFocus = false;          // true: menus must not save/restore focus
278     bool                    mbIsLiveResize = false;         // true: skip waiting for events and low priority timers
279     bool                    mbIsWaitingForNativeEvent = false; // true: code is executing via a native callback while waiting for the next native event
280 };
281 
282 typedef std::vector< std::pair< OUString, FieldUnit > > FieldUnitStringList;
283 
284 struct ImplSVCtrlData
285 {
286     std::vector<Image>      maCheckImgList;                 // ImageList for CheckBoxes
287     std::vector<Image>      maRadioImgList;                 // ImageList for RadioButtons
288     std::optional<Image>    moDisclosurePlus;
289     std::optional<Image>    moDisclosureMinus;
290     ImplTBDragMgr*          mpTBDragMgr = nullptr;          // DragMgr for ToolBox
291     sal_uInt16              mnCheckStyle = 0;               // CheckBox-Style for ImageList-Update
292     sal_uInt16              mnRadioStyle = 0;               // Radio-Style for ImageList-Update
293     Color                   mnLastCheckFColor;              // Last FaceColor for CheckImage
294     Color                   mnLastCheckWColor;              // Last WindowColor for CheckImage
295     Color                   mnLastCheckLColor;              // Last LightColor for CheckImage
296     Color                   mnLastRadioFColor;              // Last FaceColor for RadioImage
297     Color                   mnLastRadioWColor;              // Last WindowColor for RadioImage
298     Color                   mnLastRadioLColor;              // Last LightColor for RadioImage
299     FieldUnitStringList     maFieldUnitStrings;   // list with field units
300     FieldUnitStringList     maCleanUnitStrings;   // same list but with some "fluff" like spaces removed
301 };
302 
303 struct ImplSVHelpData
304 {
305     ~ImplSVHelpData();
306     bool                    mbContextHelp = false;          // is ContextHelp enabled
307     bool                    mbExtHelp = false;              // is ExtendedHelp enabled
308     bool                    mbExtHelpMode = false;          // is in ExtendedHelp Mode
309     bool                    mbOldBalloonMode = false;       // BalloonMode, before ExtHelpMode started
310     bool                    mbBalloonHelp = false;          // is BalloonHelp enabled
311     bool                    mbQuickHelp = false;            // is QuickHelp enabled
312     bool                    mbSetKeyboardHelp = false;      // tiphelp was activated by keyboard
313     bool                    mbKeyboardHelp = false;         // tiphelp was activated by keyboard
314     bool                    mbRequestingHelp = false;       // In Window::RequestHelp
315     VclPtr<HelpTextWindow>  mpHelpWin;                      // HelpWindow
316     sal_uInt64              mnLastHelpHideTime = 0;         // ticks of last show
317 };
318 
319 // "NWF" means "Native Widget Framework" and was the term used for the
320 // idea that StarView/OOo "widgets" should *look* (and feel) like the
321 // "native widgets" on each platform, even if not at all implemented
322 // using them. See http://people.redhat.com/dcbw/ooo-nwf.html .
323 
324 struct ImplSVNWFData
325 {
326     int                     mnStatusBarLowerRightOffset = 0; // amount in pixel to avoid in the lower righthand corner
327     int                     mnMenuFormatBorderX = 0;        // horizontal inner popup menu border
328     int                     mnMenuFormatBorderY = 0;        // vertical inner popup menu border
329     ::Color                 maMenuBarHighlightTextColor = COL_TRANSPARENT; // override highlight text color
330                                                             // in menubar if not transparent
331     bool                    mbMenuBarDockingAreaCommonBG = false; // e.g. WinXP default theme
332     bool                    mbDockingAreaSeparateTB = false; // individual toolbar backgrounds
333                                                             // instead of one for docking area
334     bool                    mbDockingAreaAvoidTBFrames = false; ///< don't draw frames around the individual toolbars if mbDockingAreaSeparateTB is false
335     bool                    mbFlatMenu = false;             // no popup 3D border
336     bool                    mbNoFocusRects = false;         // on Aqua/Gtk3 use native focus rendering, except for flat buttons
337     bool                    mbNoFocusRectsForFlatButtons = false; // on Gtk3 native focusing is also preferred for flat buttons
338     bool                    mbNoFrameJunctionForPopups = false; // on Gtk4 popups are done via popovers and a toolbar menu won't align to its toolitem, so
339                                                                 // omit the effort the creation a visual junction
340     bool                    mbCenteredTabs = false;         // on Aqua, tabs are centered
341     bool                    mbNoActiveTabTextRaise = false; // on Aqua the text for the selected tab
342                                                             // should not "jump up" a pixel
343     bool                    mbProgressNeedsErase = false;   // set true for platforms that should draw the
344                                                             // window background before drawing the native
345                                                             // progress bar
346     bool                    mbCanDrawWidgetAnySize = false; // set to true currently on gtk
347 
348     /// entire drop down listbox resembles a button, no textarea/button parts (as currently on Windows)
349     bool                    mbDDListBoxNoTextArea = false;
350     bool                    mbAutoAccel = false;            // whether accelerators are only shown when Alt is held down
351     bool                    mbRolloverMenubar = false;      // theming engine supports rollover in menubar
352     // gnome#768128 I cannot see a route under wayland at present to support
353     // floating toolbars that can be redocked because there's no way to track
354     // that the toolbar is over a dockable area.
355     bool                    mbCanDetermineWindowPosition = true;
356 
357     int mnListBoxEntryMargin = 0;
358 };
359 
360 struct ImplSchedulerContext
361 {
362     ImplSchedulerData*      mpFirstSchedulerData[PRIO_COUNT] = { nullptr, }; ///< list of all active tasks per priority
363     ImplSchedulerData*      mpLastSchedulerData[PRIO_COUNT] = { nullptr, };  ///< last item of each mpFirstSchedulerData list
364     ImplSchedulerData*      mpSchedulerStack = nullptr;     ///< stack of invoked tasks
365     ImplSchedulerData*      mpSchedulerStackTop = nullptr;  ///< top most stack entry to detect needed rescheduling during pop
366     SalTimer*               mpSalTimer = nullptr;           ///< interface to sal event loop / system timer
367     sal_uInt64              mnTimerStart = 0;               ///< start time of the timer
368     sal_uInt64              mnTimerPeriod = SAL_MAX_UINT64; ///< current timer period
369     std::mutex              maMutex;                        ///< the "scheduler mutex" (see
370                                                             ///< vcl/README.scheduler)
371     bool                    mbActive = true;                ///< is the scheduler active?
372     oslInterlockedCount     mnIdlesLockCount = 0;           ///< temporary ignore idles
373 };
374 
375 struct ImplSVData
376 {
377     ImplSVData();
378     ~ImplSVData();
379     SalData*                mpSalData = nullptr;
380     SalInstance*            mpDefInst = nullptr;            // Default SalInstance
381     Application*            mpApp = nullptr;                // pApp
382     VclPtr<WorkWindow>      mpDefaultWin;                   // Default-Window
383     bool                    mbDeInit = false;               // Is VCL deinitializing
384     std::unique_ptr<SalSystem> mpSalSystem;                 // SalSystem interface
385     bool                    mbResLocaleSet = false;         // SV-Resource-Manager
386     std::locale             maResLocale;                    // Resource locale
387     ImplSchedulerContext    maSchedCtx;                     // Data for class Scheduler
388     ImplSVAppData           maAppData;                      // Data for class Application
389     ImplSVGDIData           maGDIData;                      // Data for Output classes
390     ImplSVFrameData         maFrameData;                    // Data for Frame classes
391     ImplSVWinData*          mpWinData = nullptr;            // Data for per-view Windows classes
392     ImplSVCtrlData          maCtrlData;                     // Data for Control classes
393     ImplSVHelpData*         mpHelpData;                     // Data for Help classes
394     ImplSVNWFData           maNWFData;
395     UnoWrapperBase*         mpUnoWrapper = nullptr;
396     VclPtr<vcl::Window>     mpIntroWindow;                  // the splash screen
397     std::unique_ptr<DockingManager> mpDockingManager;
398     std::unique_ptr<BlendFrameCache> mpBlendFrameCache;
399 
400     oslThreadIdentifier     mnMainThreadId = 0;
401     rtl::Reference< vcl::DisplayConnectionDispatch > mxDisplayConnection;
402 
403     css::uno::Reference< css::lang::XComponent > mxAccessBridge;
404     std::unordered_map< int, OUString > maPaperNames;
405     o3tl::sorted_vector<CacheOwner*> maCacheOwners;
406 
407     css::uno::Reference<css::i18n::XCharacterClassification> m_xCharClass;
408 
409 #if defined _WIN32
410     css::uno::Reference<css::datatransfer::clipboard::XClipboard> m_xSystemClipboard;
411 #endif
412 
413     osl::Condition m_inExecuteCondtion; // Set when code returns to Application::Execute,
414                                         // i.e. no nested message loops run
415 
416     Link<LinkParamNone*,void> maDeInitHook;
417 
418     // LOK & headless backend specific hooks
419     LibreOfficeKitPollCallback mpPollCallback = nullptr;
420     LibreOfficeKitWakeCallback mpWakeCallback = nullptr;
421     void *mpPollClosure = nullptr;
422 
423     void registerCacheOwner(CacheOwner&);
424     void deregisterCacheOwner(CacheOwner&);
425     void dropCaches();
426     void dumpState(rtl::OStringBuffer &rState);
427 };
428 
429 css::uno::Reference<css::i18n::XCharacterClassification> const& ImplGetCharClass();
430 
431 void        ImplDeInitSVData();
432 VCL_PLUGIN_PUBLIC basegfx::SystemDependentDataManager& ImplGetSystemDependentDataManager();
433 VCL_PLUGIN_PUBLIC vcl::Window* ImplGetDefaultWindow();
434 vcl::Window* ImplGetDefaultContextWindow();
435 const std::locale& ImplGetResLocale();
436 VCL_PLUGIN_PUBLIC OUString VclResId(TranslateId sContextAndId);
437 DockingManager*     ImplGetDockingManager();
438 void GenerateAutoMnemonicsOnHierarchy(const vcl::Window* pWindow);
439 
440 VCL_PLUGIN_PUBLIC ImplSVHelpData& ImplGetSVHelpData();
441 
442 VCL_DLLPUBLIC bool        ImplCallPreNotify( NotifyEvent& rEvt );
443 
444 VCL_PLUGIN_PUBLIC ImplSVData* ImplGetSVData();
445 VCL_PLUGIN_PUBLIC void ImplHideSplash();
446 
447 const FieldUnitStringList& ImplGetFieldUnits();
448 const FieldUnitStringList& ImplGetCleanedFieldUnits();
449 
450 struct ImplSVEvent
451 {
452     void*               mpData;
453     Link<void*,void>    maLink;
454     VclPtr<vcl::Window> mpInstanceRef;
455     VclPtr<vcl::Window> mpWindow;
456     bool                mbCall;
457 };
458 
459 extern int nImplSysDialog;
460 
GetSalData()461 inline SalData* GetSalData() { return ImplGetSVData()->mpSalData; }
SetSalData(SalData * pData)462 inline void SetSalData(SalData* pData) { ImplGetSVData()->mpSalData = pData; }
GetSalInstance()463 inline SalInstance* GetSalInstance() { return ImplGetSVData()->mpDefInst; }
464 
465 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
466