Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : : #ifndef SC_TABVIEW_HXX
29 : : #define SC_TABVIEW_HXX
30 : :
31 : : #include <vcl/scrbar.hxx>
32 : :
33 : : #include <sfx2/ipclient.hxx>
34 : :
35 : : #include "hiranges.hxx"
36 : : #include "viewutil.hxx"
37 : : #include "select.hxx"
38 : :
39 : : #include <boost/noncopyable.hpp>
40 : : #include <boost/scoped_ptr.hpp>
41 : :
42 : : class ScEditEngineDefaulter;
43 : : class ScGridWindow;
44 : : class ScOutlineWindow;
45 : : class ScRowBar;
46 : : class ScColBar;
47 : : class ScTabControl;
48 : : class ScTabViewShell;
49 : : struct ScRangeFindData;
50 : : class ScDrawView;
51 : : class SvBorder;
52 : : class FuPoor;
53 : : class Splitter;
54 : : class ScTabSplitter;
55 : : class SdrView;
56 : : class SdrObject;
57 : : class ScHintWindow;
58 : : class ScPageBreakData;
59 : : class SdrHdlList;
60 : :
61 : : namespace com { namespace sun { namespace star {
62 : : namespace chart2 { namespace data {
63 : : struct HighlightedRange;
64 : : }}}}}
65 : :
66 : : #define SC_FORCEMODE_NONE 0xff
67 : :
68 : : // ---------------------------------------------------------------------------
69 : : // Help - Window
70 : :
71 : : class ScCornerButton : public Window
72 : : {
73 : : private:
74 : : ScViewData* pViewData;
75 : : bool bAdd;
76 : :
77 : : protected:
78 : : virtual void Paint( const Rectangle& rRect );
79 : : virtual void Resize();
80 : : virtual void MouseButtonDown( const MouseEvent& rMEvt );
81 : : public:
82 : : ScCornerButton( Window* pParent, ScViewData* pData, bool bAdditional );
83 : : ~ScCornerButton();
84 : :
85 : : virtual void StateChanged( StateChangedType nType );
86 : : virtual void DataChanged( const DataChangedEvent& rDCEvt );
87 : : };
88 : :
89 : :
90 : : // ---------------------------------------------------------------------------
91 : :
92 : : class ScTabView : boost::noncopyable
93 : : {
94 : : private:
95 : : enum BlockMode { None = 0, Normal = 1, Own = 2 };
96 : :
97 : : Window* pFrameWin; // First !!!
98 : : ScViewData aViewData; // must be at the front !
99 : :
100 : : ScViewSelectionEngine* pSelEngine;
101 : : ScViewFunctionSet aFunctionSet;
102 : :
103 : : ScHeaderSelectionEngine* pHdrSelEng;
104 : : ScHeaderFunctionSet aHdrFunc;
105 : :
106 : : SfxInPlaceClient* pIPClient;
107 : :
108 : : ScDrawView* pDrawView;
109 : :
110 : : Size aFrameSize; // passed on as for DoResize
111 : : Point aBorderPos;
112 : :
113 : : FuPoor* pDrawActual;
114 : : FuPoor* pDrawOld;
115 : :
116 : : ScGridWindow* pGridWin[4];
117 : : ScColBar* pColBar[2];
118 : : ScRowBar* pRowBar[2];
119 : : ScOutlineWindow* pColOutline[2];
120 : : ScOutlineWindow* pRowOutline[2];
121 : : ScTabSplitter* pHSplitter;
122 : : ScTabSplitter* pVSplitter;
123 : : ScTabControl* pTabControl;
124 : : ScrollBar aVScrollTop;
125 : : ScrollBar aVScrollBottom; // initially visible
126 : : ScrollBar aHScrollLeft; // initially visible
127 : : ScrollBar aHScrollRight;
128 : : ScCornerButton aCornerButton;
129 : : ScCornerButton aTopButton;
130 : : ScrollBarBox aScrollBarBox;
131 : :
132 : : boost::scoped_ptr<ScHintWindow> mpInputHintWindow; // popup window for data validation
133 : :
134 : : ScPageBreakData* pPageBreakData;
135 : : std::vector<ScHighlightEntry> maHighlightRanges;
136 : :
137 : : ScDocument* pBrushDocument; // cell formats for format paint brush
138 : : SfxItemSet* pDrawBrushSet; // drawing object attributes for paint brush
139 : :
140 : : Timer aScrollTimer;
141 : : ScGridWindow* pTimerWindow;
142 : : MouseEvent aTimerMEvt;
143 : :
144 : : sal_uLong nTipVisible;
145 : :
146 : : long nPrevDragPos;
147 : :
148 : : BlockMode meBlockMode; // Marks block
149 : :
150 : : SCCOL nBlockStartX;
151 : : SCCOL nBlockStartXOrig;
152 : : SCCOL nBlockEndX;
153 : :
154 : : SCROW nBlockStartY;
155 : : SCROW nBlockStartYOrig;
156 : : SCROW nBlockEndY;
157 : :
158 : : SCTAB nBlockStartZ;
159 : : SCTAB nBlockEndZ;
160 : :
161 : : SCCOL nOldCurX;
162 : : SCROW nOldCurY;
163 : :
164 : : double mfPendingTabBarWidth; // Tab bar width relative to frame window width.
165 : :
166 : : bool bMinimized:1;
167 : : bool bInUpdateHeader:1;
168 : : bool bInActivatePart:1;
169 : : bool bInZoomUpdate:1;
170 : : bool bMoveIsShift:1;
171 : : bool bDrawSelMode:1; // Only select draw objects ?
172 : : bool bLockPaintBrush:1; // keep for more than one use?
173 : : bool bDragging:1; // for scroll bars
174 : : bool bBlockNeg:1; // is no longer highlighted?
175 : : bool bBlockCols:1; // are whole columns selected?
176 : : bool bBlockRows:1; // are whole rows selected?
177 : :
178 : : void Init();
179 : :
180 : : void DoAddWin( ScGridWindow* pWin );
181 : :
182 : : void InitScrollBar( ScrollBar& rScrollBar, long nMaxVal );
183 : : DECL_LINK( ScrollHdl, ScrollBar* );
184 : : DECL_LINK( EndScrollHdl, ScrollBar* );
185 : :
186 : : DECL_LINK( SplitHdl, Splitter* );
187 : : void DoHSplit(long nSplitPos);
188 : : void DoVSplit(long nSplitPos);
189 : :
190 : : DECL_LINK( TimerHdl, void* );
191 : :
192 : : void UpdateVarZoom();
193 : :
194 : : static void SetScrollBar( ScrollBar& rScroll, long nRangeMax, long nVisible, long nPos, bool bLayoutRTL );
195 : : static long GetScrollBarPos( ScrollBar& rScroll, bool bLayoutRTL );
196 : :
197 : : void GetPageMoveEndPosition(SCsCOL nMovX, SCsROW nMovY, SCsCOL& rPageX, SCsROW& rPageY);
198 : : void GetAreaMoveEndPosition(SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode,
199 : : SCsCOL& rAreaX, SCsROW& rAreaY, ScFollowMode& rMode);
200 : :
201 : : void SkipCursorHorizontal(SCsCOL& rCurX, SCsROW& rCurY, SCsCOL nOldX, SCsROW nMovX);
202 : : void SkipCursorVertical(SCsCOL& rCurX, SCsROW& rCurY, SCsROW nOldY, SCsROW nMovY);
203 : :
204 : : /**
205 : : *
206 : : * @brief Update marks for a selected Range. This is a helper function
207 : : * for PaintRangeFinder.
208 : : *
209 : : * @param pData: Range to update for painting.
210 : : * @param nTab: Current tab.
211 : : *
212 : : **/
213 : :
214 : : void PaintRangeFinderEntry (ScRangeFindData* pData, SCTAB nTab);
215 : :
216 : : protected:
217 : : void UpdateHeaderWidth( const ScVSplitPos* pWhich = NULL,
218 : : const SCROW* pPosY = NULL );
219 : :
220 : : void HideTip();
221 : : void ShowRefTip();
222 : :
223 : : void ZoomChanged();
224 : : void UpdateShow();
225 : : void UpdateVisibleRange();
226 : : void GetBorderSize( SvBorder& rBorder, const Size& rSize );
227 : :
228 : : void ResetDrawDragMode();
229 : : bool IsDrawTextEdit() const;
230 : : void DrawEnableAnim(bool bSet);
231 : :
232 : : void MakeDrawView( sal_uInt8 nForceDesignMode = SC_FORCEMODE_NONE );
233 : :
234 : : void HideNoteMarker();
235 : :
236 : : void UpdateIMap( SdrObject* pObj );
237 : :
238 : : public:
239 : : ScTabView( Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell );
240 : : ~ScTabView();
241 : :
242 : : void MakeDrawLayer();
243 : :
244 : : void HideListBox();
245 : :
246 : : bool HasHintWindow() const;
247 : : void RemoveHintWindow();
248 : : void TestHintWindow();
249 : :
250 : :
251 : : DECL_LINK( TabBarResize, void* );
252 : : /** Sets an absolute tab bar width (in pixels). */
253 : : void SetTabBarWidth( long nNewWidth );
254 : : /** Sets a relative tab bar width.
255 : : @param fRelTabBarWidth Tab bar width relative to frame window width (0.0 ... 1.0). */
256 : : SC_DLLPUBLIC void SetRelTabBarWidth( double fRelTabBarWidth );
257 : : /** Sets a relative tab bar width. Tab bar is resized again in next DoResize().
258 : : @param fRelTabBarWidth Tab bar width relative to frame window width (0.0 ... 1.0). */
259 : : void SetPendingRelTabBarWidth( double fRelTabBarWidth );
260 : : /** Returns the current tab bar width in pixels. */
261 : : long GetTabBarWidth() const;
262 : : /** Returns the current tab bar width relative to the frame window width (0.0 ... 1.0). */
263 : : SC_DLLPUBLIC double GetRelTabBarWidth() const;
264 : : /** Returns the pending tab bar width relative to the frame window width (0.0 ... 1.0). */
265 : : double GetPendingRelTabBarWidth() const;
266 : :
267 : : void DoResize( const Point& rOffset, const Size& rSize, bool bInner = false );
268 : : void RepeatResize( bool bUpdateFix = true );
269 : : void UpdateFixPos();
270 : : Point GetGridOffset() const;
271 : :
272 : 1371 : bool IsDrawSelMode() const { return bDrawSelMode; }
273 : 1 : void SetDrawSelMode(bool bNew) { bDrawSelMode = bNew; }
274 : :
275 : 229 : void SetDrawFuncPtr(FuPoor* pFuncPtr) { pDrawActual = pFuncPtr; }
276 : 0 : void SetDrawFuncOldPtr(FuPoor* pFuncPtr) { pDrawOld = pFuncPtr; }
277 : 1196 : FuPoor* GetDrawFuncPtr() { return pDrawActual; }
278 : 0 : FuPoor* GetDrawFuncOldPtr() { return pDrawOld; }
279 : :
280 : : void DrawDeselectAll();
281 : : void DrawMarkListHasChanged();
282 : : void UpdateAnchorHandles();
283 : :
284 : 0 : ScPageBreakData* GetPageBreakData() { return pPageBreakData; }
285 : 1278 : const std::vector<ScHighlightEntry>& GetHighlightRanges() { return maHighlightRanges; }
286 : :
287 : : void UpdatePageBreakData( bool bForcePaint = false );
288 : :
289 : : void DrawMarkRect( const Rectangle& rRect );
290 : :
291 : 90501 : ScViewData* GetViewData() { return &aViewData; }
292 : 0 : const ScViewData* GetViewData() const { return &aViewData; }
293 : :
294 : 229 : ScViewFunctionSet* GetFunctionSet() { return &aFunctionSet; }
295 : 18 : ScViewSelectionEngine* GetSelEngine() { return pSelEngine; }
296 : :
297 : : bool SelMouseButtonDown( const MouseEvent& rMEvt );
298 : :
299 : 22101 : ScDrawView* GetScDrawView() { return pDrawView; }
300 : : SdrView* GetSdrView(); // gegen CLOKs
301 : :
302 : 5103 : bool IsMinimized() const { return bMinimized; }
303 : :
304 : : /**
305 : : * Called after moving, copying, inserting or deleting a sheet.
306 : : *
307 : : * @param bSameTabButMoved true if the same sheet as before is activated.
308 : : */
309 : : void TabChanged( bool bSameTabButMoved = false );
310 : : void SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll );
311 : : SC_DLLPUBLIC void RefreshZoom();
312 : : void SetPagebreakMode( bool bSet );
313 : :
314 : : void UpdateLayerLocks();
315 : :
316 : : void UpdateDrawTextOutliner();
317 : : void DigitLanguageChanged();
318 : :
319 : : void UpdateInputLine();
320 : :
321 : : void InitRefMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScRefType eType,
322 : : bool bPaint = true );
323 : : void DoneRefMode( bool bContinue = false );
324 : : void UpdateRef( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ );
325 : : void StopRefMode();
326 : :
327 : : void StopMarking();
328 : : void FakeButtonUp( ScSplitPos eWhich );
329 : :
330 : : Window* GetActiveWin();
331 : : Window* GetWindowByPos( ScSplitPos ePos );
332 : :
333 : : ScSplitPos FindWindow( Window* pWindow ) const;
334 : :
335 : : void SetActivePointer( const Pointer& rPointer );
336 : :
337 : : void ActiveGrabFocus();
338 : :
339 : : void ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl );
340 : :
341 : : SC_DLLPUBLIC void SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew = false );
342 : :
343 : : SC_DLLPUBLIC void CellContentChanged();
344 : : void SelectionChanged();
345 : : void CursorPosChanged();
346 : : void UpdateInputContext();
347 : :
348 : : void CheckSelectionTransfer();
349 : :
350 : : void InvertHorizontal( ScVSplitPos eWhich, long nDragPos );
351 : : void InvertVertical( ScHSplitPos eWhich, long nDragPos );
352 : :
353 : : Point GetInsertPos();
354 : :
355 : : Point GetChartInsertPos( const Size& rSize, const ScRange& rCellRange );
356 : : Point GetChartDialogPos( const Size& rDialogSize, const Rectangle& rLogicChart );
357 : :
358 : : void UpdateAutoFillMark();
359 : :
360 : : void ShowCursor();
361 : : void HideAllCursors();
362 : : void ShowAllCursors();
363 : :
364 : : void AlignToCursor( SCsCOL nCurX, SCsROW nCurY, ScFollowMode eMode,
365 : : const ScSplitPos* pWhich = NULL );
366 : :
367 : : SvxZoomType GetZoomType() const;
368 : : void SetZoomType( SvxZoomType eNew, bool bAll );
369 : : sal_uInt16 CalcZoom( SvxZoomType eType, sal_uInt16 nOldZoom );
370 : :
371 : : bool HasPageFieldDataAtCursor() const;
372 : : void StartDataSelect();
373 : :
374 : : // MoveCursorAbs - absolute
375 : : // MoveCursorRel - single cells
376 : : // MoveCursorPage - screen
377 : : // MoveCursorArea - Data block
378 : : // MoveCursorEnd - top left / user range
379 : :
380 : : SC_DLLPUBLIC void MoveCursorAbs( SCsCOL nCurX, SCsROW nCurY, ScFollowMode eMode,
381 : : bool bShift, bool bControl,
382 : : bool bKeepOld = false, bool bKeepSel = false );
383 : : void MoveCursorRel( SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode,
384 : : bool bShift, bool bKeepSel = false );
385 : : void MoveCursorPage( SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode,
386 : : bool bShift, bool bKeepSel = false );
387 : : void MoveCursorArea( SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode,
388 : : bool bShift, bool bKeepSel = false );
389 : : void MoveCursorEnd( SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode,
390 : : bool bShift, bool bKeepSel = false );
391 : : void MoveCursorScreen( SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode, bool bShift );
392 : :
393 : : void MoveCursorEnter( bool bShift ); // Shift for direction (select nothing)
394 : :
395 : : bool MoveCursorKeyInput( const KeyEvent& rKeyEvent );
396 : :
397 : : void FindNextUnprot( bool bShift, bool bInSelection = true );
398 : :
399 : : SC_DLLPUBLIC void SetTabNo( SCTAB nTab, bool bNew = false, bool bExtendSelection = false, bool bSameTabButMoved = false );
400 : : void SelectNextTab( short nDir, bool bExtendSelection = false );
401 : :
402 : : void ActivateView( bool bActivate, bool bFirst );
403 : : void ActivatePart( ScSplitPos eWhich );
404 : 0 : bool IsInActivatePart() const { return bInActivatePart; }
405 : :
406 : : void SetTimer( ScGridWindow* pWin, const MouseEvent& rMEvt );
407 : : void ResetTimer();
408 : :
409 : : void ScrollX( long nDeltaX, ScHSplitPos eWhich, bool bUpdBars = true );
410 : : void ScrollY( long nDeltaY, ScVSplitPos eWhich, bool bUpdBars = true );
411 : : SC_DLLPUBLIC void ScrollLines( long nDeltaX, long nDeltaY ); // active
412 : :
413 : : bool ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos );
414 : :
415 : : void ScrollToObject( SdrObject* pDrawObj );
416 : : void MakeVisible( const Rectangle& rHMMRect );
417 : :
418 : : // Zeichnen
419 : :
420 : : void PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
421 : : ScUpdateMode eMode = SC_UPDATE_ALL );
422 : :
423 : : void PaintGrid();
424 : :
425 : : void PaintTopArea( SCCOL nStartCol, SCCOL nEndCol );
426 : : void PaintTop();
427 : :
428 : : void PaintLeftArea( SCROW nStartRow, SCROW nEndRow );
429 : : void PaintLeft();
430 : :
431 : : bool PaintExtras();
432 : :
433 : : void RecalcPPT();
434 : :
435 : : void CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress);
436 : :
437 : : void UpdateCopySourceOverlay();
438 : : void UpdateSelectionOverlay();
439 : : void UpdateShrinkOverlay();
440 : : void UpdateAllOverlays();
441 : :
442 : : void UpdateFormulas();
443 : : void InterpretVisible();
444 : : void CheckNeedsRepaint();
445 : :
446 : : void PaintRangeFinder( long nNumber = -1 );
447 : : void AddHighlightRange( const ScRange& rRange, const Color& rColor );
448 : : void ClearHighlightRanges();
449 : :
450 : : void DoChartSelection( const ::com::sun::star::uno::Sequence<
451 : : ::com::sun::star::chart2::data::HighlightedRange > & rHilightRanges );
452 : :
453 : : long GetGridWidth( ScHSplitPos eWhich );
454 : : long GetGridHeight( ScVSplitPos eWhich );
455 : :
456 : : void UpdateScrollBars();
457 : : void SetNewVisArea();
458 : :
459 : : void InvalidateAttribs();
460 : :
461 : : void MakeEditView( ScEditEngineDefaulter* pEngine, SCCOL nCol, SCROW nRow );
462 : : void KillEditView( bool bNoPaint );
463 : : void UpdateEditView();
464 : :
465 : :
466 : : // Blocks
467 : :
468 : : void SelectAll( bool bContinue = false );
469 : : void SelectAllTables();
470 : : void DeselectAllTables();
471 : :
472 : : void MarkCursor( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
473 : : bool bCols = false, bool bRows = false, bool bCellSelection = false );
474 : : void InitBlockMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
475 : : bool bTestNeg = false,
476 : : bool bCols = false, bool bRows = false );
477 : : void InitOwnBlockMode();
478 : : void DoneBlockMode( bool bContinue = false );
479 : :
480 : : bool IsBlockMode() const;
481 : :
482 : : void ExpandBlock(SCsCOL nMovX, SCsROW nMovY, ScFollowMode eMode);
483 : : void ExpandBlockPage(SCsCOL nMovX, SCsROW nMovY);
484 : : void ExpandBlockArea(SCsCOL nMovX, SCsROW nMovY);
485 : :
486 : : void MarkColumns();
487 : : void MarkRows();
488 : : void MarkDataArea( bool bIncludeCursor = true );
489 : : void MarkMatrixFormula();
490 : : void Unmark();
491 : :
492 : : void MarkRange( const ScRange& rRange, bool bSetCursor = true, bool bContinue = false );
493 : :
494 : : bool IsMarking( SCCOL nCol, SCROW nRow, SCTAB nTab ) const;
495 : :
496 : : void PaintMarks( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow );
497 : : void PaintBlock( bool bReset = false );
498 : :
499 : : void SetMarkData( const ScMarkData& rNew );
500 : : void MarkDataChanged();
501 : :
502 : : void LockModifiers( sal_uInt16 nModifiers );
503 : : sal_uInt16 GetLockedModifiers() const;
504 : : void ViewOptionsHasChanged( bool bHScrollChanged,
505 : : bool bGraphicsChanged = false);
506 : :
507 : : Point GetMousePosPixel();
508 : :
509 : : void SnapSplitPos( Point& rScreenPosPixel );
510 : : void FreezeSplitters( bool bFreeze );
511 : : void RemoveSplit();
512 : : void SplitAtCursor();
513 : : void SplitAtPixel( const Point& rPixel, bool bHor, bool bVer );
514 : : void InvalidateSplit();
515 : :
516 : : void ErrorMessage( sal_uInt16 nGlobStrId );
517 : : Window* GetParentOrChild( sal_uInt16 nChildId );
518 : :
519 : : void EnableRefInput(bool bFlag = true);
520 : :
521 : 0 : Window* GetFrameWin() const { return pFrameWin; }
522 : :
523 [ + - ][ - + ]: 268 : bool HasPaintBrush() const { return pBrushDocument || pDrawBrushSet; }
524 : 231 : ScDocument* GetBrushDocument() const { return pBrushDocument; }
525 : 231 : SfxItemSet* GetDrawBrushSet() const { return pDrawBrushSet; }
526 : 0 : bool IsPaintBrushLocked() const { return bLockPaintBrush; }
527 : : void SetBrushDocument( ScDocument* pNew, bool bLock );
528 : : void SetDrawBrushSet( SfxItemSet* pNew, bool bLock );
529 : : void ResetBrushDocument();
530 : : };
531 : :
532 : :
533 : :
534 : : #endif
535 : :
536 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|