lok: Hide file linking in section
[LibreOffice.git] / sw / source / ui / misc / pggrid.cxx
blobb36fba8169a470d1a4e61e69cec18677321ff37e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sfx2/app.hxx>
22 #include <cmdid.h>
23 #include <hintids.hxx>
24 #include <swtypes.hxx>
25 #include <globals.hrc>
26 #include <svx/colorbox.hxx>
27 #include <svx/xtable.hxx>
28 #include <svx/svxids.hrc>
29 #include <uitool.hxx>
30 #include <editeng/sizeitem.hxx>
31 #include <editeng/lrspitem.hxx>
32 #include <editeng/ulspitem.hxx>
33 #include <editeng/boxitem.hxx>
34 #include <editeng/frmdiritem.hxx>
35 #include <svx/ruler.hxx>
36 #include <pggrid.hxx>
37 #include <tgrditem.hxx>
39 #include <wrtsh.hxx>
40 #include <doc.hxx>
41 #include <uiitems.hxx>
42 #include <swmodule.hxx>
43 #include <view.hxx>
45 SwTextGridPage::SwTextGridPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet)
46 : SfxTabPage(pPage, pController, "modules/swriter/ui/textgridpage.ui", "TextGridPage", &rSet)
47 , m_nRubyUserValue(0)
48 , m_bRubyUserValue(false)
49 , m_aPageSize(MM50, MM50)
50 , m_bVertical(false)
51 , m_bSquaredMode(false)
52 , m_bHRulerChanged(false)
53 , m_bVRulerChanged(false)
54 , m_xNoGridRB(m_xBuilder->weld_radio_button("radioRB_NOGRID"))
55 , m_xLinesGridRB(m_xBuilder->weld_radio_button("radioRB_LINESGRID"))
56 , m_xCharsGridRB(m_xBuilder->weld_radio_button("radioRB_CHARSGRID"))
57 , m_xSnapToCharsCB(m_xBuilder->weld_check_button("checkCB_SNAPTOCHARS"))
58 , m_xExampleWN(new weld::CustomWeld(*m_xBuilder, "drawingareaWN_EXAMPLE", m_aExampleWN))
59 , m_xLayoutFL(m_xBuilder->weld_widget("frameFL_LAYOUT"))
60 , m_xLinesPerPageNF(m_xBuilder->weld_spin_button("spinNF_LINESPERPAGE"))
61 , m_xLinesRangeFT(m_xBuilder->weld_label("labelFT_LINERANGE"))
62 , m_xTextSizeMF(m_xBuilder->weld_metric_spin_button("spinMF_TEXTSIZE", FieldUnit::POINT))
63 , m_xCharsPerLineFT(m_xBuilder->weld_label("labelFT_CHARSPERLINE"))
64 , m_xCharsPerLineNF(m_xBuilder->weld_spin_button("spinNF_CHARSPERLINE"))
65 , m_xCharsRangeFT(m_xBuilder->weld_label("labelFT_CHARRANGE"))
66 , m_xCharWidthFT(m_xBuilder->weld_label("labelFT_CHARWIDTH"))
67 , m_xCharWidthMF(m_xBuilder->weld_metric_spin_button("spinMF_CHARWIDTH", FieldUnit::POINT))
68 , m_xRubySizeFT(m_xBuilder->weld_label("labelFT_RUBYSIZE"))
69 , m_xRubySizeMF(m_xBuilder->weld_metric_spin_button("spinMF_RUBYSIZE", FieldUnit::POINT))
70 , m_xRubyBelowCB(m_xBuilder->weld_check_button("checkCB_RUBYBELOW"))
71 , m_xDisplayFL(m_xBuilder->weld_widget("frameFL_DISPLAY"))
72 , m_xDisplayCB(m_xBuilder->weld_check_button("checkCB_DISPLAY"))
73 , m_xPrintCB(m_xBuilder->weld_check_button("checkCB_PRINT"))
74 , m_xColorLB(new ColorListBox(m_xBuilder->weld_menu_button("listLB_COLOR"), pController->getDialog()))
76 Link<weld::SpinButton&,void> aLink = LINK(this, SwTextGridPage, CharorLineChangedHdl);
77 m_xCharsPerLineNF->connect_value_changed(aLink);
78 m_xLinesPerPageNF->connect_value_changed(aLink);
80 Link<weld::MetricSpinButton&,void> aSizeLink = LINK(this, SwTextGridPage, TextSizeChangedHdl);
81 m_xTextSizeMF->connect_value_changed(aSizeLink);
82 m_xRubySizeMF->connect_value_changed(aSizeLink);
83 m_xCharWidthMF->connect_value_changed(aSizeLink);
85 Link<weld::ToggleButton&,void> aGridTypeHdl = LINK(this, SwTextGridPage, GridTypeHdl);
86 m_xNoGridRB->connect_toggled(aGridTypeHdl);
87 m_xLinesGridRB->connect_toggled(aGridTypeHdl);
88 m_xCharsGridRB->connect_toggled(aGridTypeHdl);
90 m_xColorLB->SetSelectHdl(LINK(this, SwTextGridPage, ColorModifyHdl));
91 m_xPrintCB->connect_toggled(LINK(this, SwTextGridPage, GridModifyClickHdl));
92 m_xRubyBelowCB->connect_toggled(LINK(this, SwTextGridPage, GridModifyClickHdl));
94 m_xDisplayCB->connect_toggled(LINK(this, SwTextGridPage, DisplayGridHdl));
96 //Get the default paper mode
97 SwView *pView = ::GetActiveView();
98 if( pView )
100 SwWrtShell* pSh = pView->GetWrtShellPtr();
101 if( pSh )
103 m_bSquaredMode = pSh->GetDoc()->IsSquaredPageMode();
106 if( m_bSquaredMode )
109 m_xRubySizeFT->show();
110 m_xRubySizeMF->show();
111 m_xRubyBelowCB->show();
112 m_xSnapToCharsCB->hide();
113 m_xCharWidthFT->hide();
114 m_xCharWidthMF->hide();
116 else
118 m_xRubySizeFT->hide();
119 m_xRubySizeMF->hide();
120 m_xRubyBelowCB->hide();
121 m_xSnapToCharsCB->show();
122 m_xCharWidthFT->show();
123 m_xCharWidthMF->show();
127 SwTextGridPage::~SwTextGridPage()
129 m_xColorLB.reset();
132 std::unique_ptr<SfxTabPage> SwTextGridPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet)
134 return std::make_unique<SwTextGridPage>(pPage, pController, *rSet);
137 bool SwTextGridPage::FillItemSet(SfxItemSet *rSet)
139 bool bRet = false;
140 if (m_xNoGridRB->get_state_changed_from_saved() ||
141 m_xLinesGridRB->get_state_changed_from_saved() ||
142 m_xLinesPerPageNF->get_value_changed_from_saved() ||
143 m_xTextSizeMF->get_value_changed_from_saved() ||
144 m_xCharsPerLineNF->get_value_changed_from_saved() ||
145 m_xSnapToCharsCB->get_state_changed_from_saved() ||
146 m_xRubySizeMF->get_value_changed_from_saved() ||
147 m_xCharWidthMF->get_value_changed_from_saved() ||
148 m_xRubyBelowCB->get_state_changed_from_saved() ||
149 m_xDisplayCB->get_state_changed_from_saved() ||
150 m_xPrintCB->get_state_changed_from_saved() ||
151 m_xColorLB->IsValueChangedFromSaved())
153 PutGridItem(*rSet);
154 bRet = true;
157 // draw ticks of ruler
158 SwView * pView = ::GetActiveView();
159 if ( m_bHRulerChanged )
160 pView->GetHRuler().DrawTicks();
161 if ( m_bVRulerChanged )
162 pView->GetVRuler().DrawTicks();
163 return bRet;
166 void SwTextGridPage::Reset(const SfxItemSet *rSet)
168 sal_Int32 nLinesPerPage = 0;
170 if(SfxItemState::DEFAULT <= rSet->GetItemState(RES_TEXTGRID))
172 const SwTextGridItem& rGridItem = rSet->Get(RES_TEXTGRID);
173 weld::RadioButton* pButton = nullptr;
174 switch(rGridItem.GetGridType())
176 case GRID_NONE : pButton = m_xNoGridRB.get(); break;
177 case GRID_LINES_ONLY : pButton = m_xLinesGridRB.get(); break;
178 default: pButton = m_xCharsGridRB.get();
180 pButton->set_active(true);
181 m_xDisplayCB->set_active(rGridItem.IsDisplayGrid());
182 GridTypeHdl(*pButton);
183 m_xSnapToCharsCB->set_active(rGridItem.IsSnapToChars());
184 nLinesPerPage = rGridItem.GetLines();
186 SetLinesOrCharsRanges(*m_xLinesRangeFT , m_xLinesPerPageNF->get_max());
187 m_nRubyUserValue = rGridItem.GetBaseHeight();
188 m_bRubyUserValue = true;
189 m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(m_nRubyUserValue), FieldUnit::TWIP);
190 m_xRubySizeMF->set_value(m_xRubySizeMF->normalize(rGridItem.GetRubyHeight()), FieldUnit::TWIP);
191 m_xCharWidthMF->set_value(m_xCharWidthMF->normalize(rGridItem.GetBaseWidth()), FieldUnit::TWIP);
192 m_xRubyBelowCB->set_active(rGridItem.IsRubyTextBelow());
193 m_xPrintCB->set_active(rGridItem.IsPrintGrid());
194 m_xColorLB->SelectEntry(rGridItem.GetColor());
196 UpdatePageSize(*rSet);
198 if (nLinesPerPage > 0)
199 m_xLinesPerPageNF->set_value(nLinesPerPage);
201 m_xNoGridRB->save_state();
202 m_xLinesGridRB->save_state();
203 m_xSnapToCharsCB->save_state();
204 m_xLinesPerPageNF->save_value();
205 m_xTextSizeMF->save_value();
206 m_xCharsPerLineNF->save_value();
207 m_xRubySizeMF->save_value();
208 m_xCharWidthMF->save_value();
209 m_xRubyBelowCB->save_state();
210 m_xDisplayCB->save_state();
211 m_xPrintCB->save_state();
212 m_xColorLB->SaveValue();
215 void SwTextGridPage::ActivatePage( const SfxItemSet& rSet )
217 m_aExampleWN.Hide();
218 m_aExampleWN.UpdateExample(rSet);
219 UpdatePageSize(rSet);
220 m_aExampleWN.Show();
221 m_aExampleWN.Invalidate();
224 DeactivateRC SwTextGridPage::DeactivatePage( SfxItemSet* )
226 return DeactivateRC::LeavePage;
229 void SwTextGridPage::PutGridItem(SfxItemSet& rSet)
231 SwTextGridItem aGridItem;
232 aGridItem.SetGridType(m_xNoGridRB->get_active() ? GRID_NONE :
233 m_xLinesGridRB->get_active() ? GRID_LINES_ONLY : GRID_LINES_CHARS );
234 aGridItem.SetSnapToChars(m_xSnapToCharsCB->get_active());
235 aGridItem.SetLines( static_cast< sal_uInt16 >(m_xLinesPerPageNF->get_value()) );
236 aGridItem.SetBaseHeight( static_cast< sal_uInt16 >(
237 m_bRubyUserValue ? m_nRubyUserValue :
238 m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP))) );
239 aGridItem.SetRubyHeight( static_cast< sal_uInt16 >(m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP))) );
240 aGridItem.SetBaseWidth( static_cast< sal_uInt16 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP))) );
241 aGridItem.SetRubyTextBelow(m_xRubyBelowCB->get_active());
242 aGridItem.SetSquaredMode(m_bSquaredMode);
243 aGridItem.SetDisplayGrid(m_xDisplayCB->get_active());
244 aGridItem.SetPrintGrid(m_xPrintCB->get_active());
245 aGridItem.SetColor(m_xColorLB->GetSelectEntryColor());
246 rSet.Put(aGridItem);
248 SwView * pView = ::GetActiveView();
249 if ( aGridItem.GetGridType() != GRID_NONE )
251 if ( aGridItem.GetGridType() == GRID_LINES_CHARS )
253 m_bHRulerChanged = true;
255 m_bVRulerChanged = true;
256 pView->GetHRuler().SetCharWidth(static_cast<long>(m_xCharWidthMF->get_value(FieldUnit::TWIP)/56.7));
257 pView->GetVRuler().SetLineHeight(static_cast<long>(m_xTextSizeMF->get_value(FieldUnit::TWIP)/56.7));
261 void SwTextGridPage::UpdatePageSize(const SfxItemSet& rSet)
263 if( SfxItemState::UNKNOWN != rSet.GetItemState( RES_FRAMEDIR ))
265 const SvxFrameDirectionItem& rDirItem =
266 rSet.Get(RES_FRAMEDIR);
267 m_bVertical = rDirItem.GetValue() == SvxFrameDirection::Vertical_RL_TB||
268 rDirItem.GetValue() == SvxFrameDirection::Vertical_LR_TB;
271 if( SfxItemState::SET != rSet.GetItemState( SID_ATTR_PAGE_SIZE ))
272 return;
274 const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE);
275 const SvxLRSpaceItem& rLRSpace = rSet.Get( RES_LR_SPACE );
276 const SvxULSpaceItem& rULSpace = rSet.Get( RES_UL_SPACE );
277 const SvxBoxItem& rBox = rSet.Get(RES_BOX);
278 sal_Int32 nDistanceLR = rLRSpace.GetLeft() + rLRSpace.GetRight();
279 sal_Int32 nDistanceUL = rULSpace.GetUpper() + rULSpace.GetLower();
281 sal_Int32 nValue1 = rSize.GetSize().Height() - nDistanceUL -
282 rBox.GetDistance(SvxBoxItemLine::TOP) -
283 rBox.GetDistance(SvxBoxItemLine::BOTTOM);
284 sal_Int32 nValue2 = rSize.GetSize().Width() - nDistanceLR -
285 rBox.GetDistance(SvxBoxItemLine::LEFT) -
286 rBox.GetDistance(SvxBoxItemLine::RIGHT);
287 if(m_bVertical)
289 m_aPageSize.setWidth( nValue1 );
290 m_aPageSize.setHeight( nValue2 );
292 else
294 m_aPageSize.setWidth( nValue2 );
295 m_aPageSize.setHeight( nValue1 );
298 sal_Int32 nTextSize = static_cast< sal_Int32 >(m_bRubyUserValue ?
299 m_nRubyUserValue :
300 m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)));
302 if ( m_bSquaredMode )
304 sal_Int32 nCharsPerLine = m_aPageSize.Width() / nTextSize;
305 m_xCharsPerLineNF->set_max(nCharsPerLine);
306 m_xCharsPerLineNF->set_value(nCharsPerLine);
307 m_xLinesPerPageNF->set_max(m_aPageSize.Height() /
308 ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) +
309 m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP))));
310 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
311 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
313 else
315 sal_Int32 nTextWidth = static_cast< sal_Int32 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP)));
316 m_xLinesPerPageNF->set_value(m_aPageSize.Height() / nTextSize);
317 if (nTextWidth)
318 m_xCharsPerLineNF->set_value(m_aPageSize.Width() / nTextWidth);
319 else
320 m_xCharsPerLineNF->set_value(45);
321 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
322 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
326 void SwTextGridPage::SetLinesOrCharsRanges(weld::Label& rField, const sal_Int32 nValue )
328 OUString aFieldStr = "( 1 -" + OUString::number(nValue) + " )";
329 rField.set_label(aFieldStr);
332 const sal_uInt16* SwTextGridPage::GetRanges()
334 static const sal_uInt16 aPageRg[] = {
335 RES_TEXTGRID, RES_TEXTGRID,
337 return aPageRg;
340 IMPL_LINK(SwTextGridPage, CharorLineChangedHdl, weld::SpinButton&, rField, void)
342 //if in squared mode
343 if ( m_bSquaredMode )
345 if (m_xCharsPerLineNF.get() == &rField)
347 auto nValue = m_xCharsPerLineNF->get_value();
348 assert(nValue && "div-by-zero");
349 auto nWidth = m_aPageSize.Width() / nValue;
350 m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(nWidth), FieldUnit::TWIP);
351 //prevent rounding errors in the MetricField by saving the used value
352 m_nRubyUserValue = nWidth;
353 m_bRubyUserValue = true;
356 //set maximum line per page
358 sal_Int32 nMaxLines = static_cast< sal_Int32 >(m_aPageSize.Height() /
359 ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) +
360 m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP))));
361 m_xLinesPerPageNF->set_max(nMaxLines);
363 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
364 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
366 else//in normal mode
368 if (m_xLinesPerPageNF.get() == &rField)
370 auto nValue = m_xLinesPerPageNF->get_value();
371 assert(nValue && "div-by-zero");
372 auto nHeight = m_aPageSize.Height() / nValue;
373 m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(nHeight), FieldUnit::TWIP);
374 m_xRubySizeMF->set_value(0, FieldUnit::TWIP);
375 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
377 m_nRubyUserValue = nHeight;
378 m_bRubyUserValue = true;
380 else if (m_xCharsPerLineNF.get() == &rField)
382 auto nValue = m_xCharsPerLineNF->get_value();
383 assert(nValue && "div-by-zero");
384 auto nWidth = m_aPageSize.Width() / nValue;
385 m_xCharWidthMF->set_value(m_xCharWidthMF->normalize(nWidth), FieldUnit::TWIP);
386 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
389 GridModifyHdl();
392 IMPL_LINK(SwTextGridPage, TextSizeChangedHdl, weld::MetricSpinButton&, rField, void)
394 //if in squared mode
395 if( m_bSquaredMode )
397 if (m_xTextSizeMF.get() == &rField)
399 m_bRubyUserValue = false;
401 // fdo#50941: set maximum characters per line
402 sal_Int32 nTextSize = static_cast< sal_Int32 >(m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)));
403 if (nTextSize > 0)
405 sal_Int32 nMaxChars = m_aPageSize.Width() / nTextSize;
406 m_xCharsPerLineNF->set_value(nMaxChars);
407 m_xCharsPerLineNF->set_max(nMaxChars);
408 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
411 //set maximum line per page
413 sal_Int32 nMaxLines = static_cast< sal_Int32 >(m_aPageSize.Height() /
414 ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) +
415 m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP))));
416 m_xLinesPerPageNF->set_max(nMaxLines);
417 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
420 else
422 if (m_xTextSizeMF.get() == &rField)
424 sal_Int32 nTextSize = static_cast< sal_Int32 >(m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)));
425 m_xLinesPerPageNF->set_value(m_aPageSize.Height() / nTextSize);
426 m_bRubyUserValue = false;
427 SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() );
429 else if (m_xCharWidthMF.get() == &rField)
431 sal_Int32 nTextWidth = static_cast< sal_Int32 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP)));
432 sal_Int32 nMaxChar = 45 ;
433 if (nTextWidth)
434 nMaxChar = m_aPageSize.Width() / nTextWidth;
435 m_xCharsPerLineNF->set_value( nMaxChar );
436 SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() );
438 //rubySize is disabled
440 GridModifyHdl();
443 IMPL_LINK(SwTextGridPage, GridTypeHdl, weld::ToggleButton&, rButton, void)
445 bool bEnable = m_xNoGridRB.get() != &rButton;
446 m_xLayoutFL->set_sensitive(bEnable);
447 m_xDisplayFL->set_sensitive(bEnable);
449 //one special case
450 if (bEnable)
451 DisplayGridHdl(*m_xDisplayCB);
453 bEnable = m_xCharsGridRB.get() == &rButton;
454 m_xSnapToCharsCB->set_sensitive(bEnable);
456 bEnable = m_xLinesGridRB.get() == &rButton;
457 if (bEnable && !m_bSquaredMode)
459 m_xCharsPerLineFT->set_sensitive(false);
460 m_xCharsPerLineNF->set_sensitive(false);
461 m_xCharsRangeFT->set_sensitive(false);
462 m_xCharWidthFT->set_sensitive(false);
463 m_xCharWidthMF->set_sensitive(false);
466 GridModifyHdl();
469 IMPL_LINK_NOARG(SwTextGridPage, DisplayGridHdl, weld::ToggleButton&, void)
471 bool bChecked = m_xDisplayCB->get_active();
472 m_xPrintCB->set_sensitive(bChecked);
473 m_xPrintCB->set_active(bChecked);
476 IMPL_LINK_NOARG(SwTextGridPage, GridModifyClickHdl, weld::ToggleButton&, void)
478 GridModifyHdl();
481 IMPL_LINK_NOARG(SwTextGridPage, ColorModifyHdl, ColorListBox&, void)
483 GridModifyHdl();
486 void SwTextGridPage::GridModifyHdl()
488 const SfxItemSet& rOldSet = GetItemSet();
489 SfxItemSet aSet(rOldSet);
490 const SfxItemSet* pExSet = GetDialogExampleSet();
491 if(pExSet)
492 aSet.Put(*pExSet);
493 PutGridItem(aSet);
494 m_aExampleWN.UpdateExample(aSet);
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */