tdf#159372 svx: Add goto dialog

Adds a goto page/slide dialog for Impress and Draw similar to Writer.
To avoid duplicating code and ui, the dialog is created in svx/ and used in other modules.
The old goto dialog in Writer has now been replaced with this dialog.

Change-Id: I28f819f0d0734fb2bb08a7b99a628217ef66dba9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162913
Tested-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Reviewed-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
This commit is contained in:
AkshayWarrier
2024-02-10 02:05:55 +01:00
committed by Jim Raykowski
parent 9af4b5254c
commit ace21b4b25
26 changed files with 202 additions and 155 deletions
+1 -1
View File
@@ -228,7 +228,7 @@ class SvxZoomItem;
#define SID_UNPACK TypedWhichId<SfxBoolItem>(SID_SFX_START + 1662)
// (SID_SFX_START + 1663) used further down
#define SID_ATTR_TARGETLANG_STR (SID_SFX_START + 1664)
// FREE
#define SID_GO_TO_PAGE (SID_SFX_START + 1665)
#define SID_OUTPUTSTREAM TypedWhichId<SfxUnoAnyItem>(SID_SFX_START + 1666)
#define SID_IMAGE_ORIENTATION TypedWhichId<SfxImageItem>(SID_SFX_START + 1667)
@@ -17,38 +17,30 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_GOTODLG_HXX
#define INCLUDED_SW_SOURCE_UIBASE_INC_GOTODLG_HXX
#pragma once
#include <sfx2/bindings.hxx>
#include <svx/svxdllapi.h>
#include <vcl/weld.hxx>
class SwView;
class SwGotoPageDlg final : public weld::GenericDialogController
namespace svx
{
class SVX_DLLPUBLIC GotoPageDlg final : public weld::GenericDialogController
{
public:
SwGotoPageDlg(weld::Window *parent, SfxBindings& rBindings);
GotoPageDlg(weld::Window* pParent, const OUString& title, const OUString& label,
sal_uInt16 curPagePos, sal_uInt16 pageCnt);
sal_uInt16 GetPageSelection() const
{
return mxMtrPageCtrl->get_text().toUInt32();
}
sal_uInt16 GetPageSelection() const { return mxMtrPageCtrl->get_text().toUInt32(); }
private:
SwView *m_pCreateView;
SfxBindings& m_rBindings;
sal_uInt16 mnMaxPageCnt;
sal_uInt16 mnMaxPageCnt;
std::unique_ptr<weld::SpinButton> mxMtrPageCtrl;
std::unique_ptr<weld::Label> mxPageNumberLbl;
std::unique_ptr<weld::Label> mxPageLbl;
SwView* GetCreateView() const;
sal_uInt16 GetPageInfo();
DECL_LINK( PageModifiedHdl, weld::Entry&, void );
DECL_LINK(PageModifiedHdl, weld::Entry&, void);
};
#endif
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@@ -2281,6 +2281,25 @@
<value>1</value>
</prop>
</node>
<node oor:name=".uno:GotoPage" oor:op="replace">
<prop oor:name="Label" oor:type="xs:string">
<value xml:lang="en-US">Go t~o Page...</value>
</prop>
<prop oor:name="Properties" oor:type="xs:int">
<value>1</value>
</prop>
</node>
<node oor:name=".uno:GotoSlide" oor:op="replace">
<prop oor:name="Label" oor:type="xs:string">
<value xml:lang="en-US">Go t~o Slide...</value>
</prop>
<prop oor:name="TargetURL" oor:type="xs:string">
<value>.uno:GotoPage</value>
</prop>
<prop oor:name="Properties" oor:type="xs:int">
<value>1</value>
</prop>
</node>
<node oor:name=".uno:MovePageFirst" oor:op="replace">
<prop oor:name="Label" oor:type="xs:string">
<value xml:lang="en-US">Move Page to Start</value>
+2
View File
@@ -138,6 +138,8 @@
#define STR_CLICK_ACTION_NEXTPAGE NC_("STR_CLICK_ACTION_NEXTPAGE", "Go to next slide")
#define STR_CLICK_ACTION_FIRSTPAGE NC_("STR_CLICK_ACTION_FIRSTPAGE", "Go to first slide")
#define STR_CLICK_ACTION_LASTPAGE NC_("STR_CLICK_ACTION_LASTPAGE", "Go to last slide")
#define STR_GOTO_PAGE_DLG_TITLE NC_("STR_GOTO_PAGE_DLG_TITLE", "Go to Page")
#define STR_GOTO_SLIDE_DLG_TITLE NC_("STR_GOTO_SLIDE_DLG_TITLE", "Go to Slide")
#define STR_CLICK_ACTION_BOOKMARK NC_("STR_CLICK_ACTION_BOOKMARK", "Go to page or object")
#define STR_CLICK_ACTION_DOCUMENT NC_("STR_CLICK_ACTION_DOCUMENT", "Go to document")
#define STR_CLICK_ACTION_SOUND NC_("STR_CLICK_ACTION_SOUND", "Play audio")
+5
View File
@@ -2885,6 +2885,11 @@ interface DrawView
ExecMethod = ExecGoToLastPage ;
StateMethod = GetStateGoToLastPage ;
]
SID_GO_TO_PAGE
[
ExecMethod = ExecGoToPage ;
StateMethod = GetStateGoToPage ;
]
SID_CLASSIFICATION_APPLY
[
ExecMethod = FuTemporary ;
+27
View File
@@ -29,6 +29,11 @@
#include <ViewShell.hxx>
#include <slideshow.hxx>
#include <svx/svxids.hrc>
#include <svx/dialog/gotodlg.hxx>
#include <strings.hrc>
#include <sdresid.hxx>
namespace sd {
@@ -139,6 +144,28 @@ void FuNavigation::DoExecute( SfxRequest& rReq )
}
}
break;
case SID_GO_TO_PAGE:
{
if( !bSlideShow)
if(auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell ))
{
OUString sTitle = SdResId(STR_GOTO_PAGE_DLG_TITLE);
OUString sLabel = SdResId(STR_PAGE_NAME) + ":";
if (mpDoc->GetDocumentType() == DocumentType::Impress)
{
sTitle = SdResId(STR_GOTO_SLIDE_DLG_TITLE);
sLabel = SdResId(STR_SLIDE_NAME) + ":";
}
svx::GotoPageDlg aDlg(pDrawViewShell->GetFrameWeld(), sTitle, sLabel,
pDrawViewShell->GetCurPagePos() + 1,
mpDoc->GetSdPageCount(PageKind::Standard));
if (aDlg.run() == RET_OK)
pDrawViewShell->SwitchPage(aDlg.GetPageSelection() - 1);
}
}
break;
}
// Refresh toolbar icons
SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings();
+3
View File
@@ -222,6 +222,9 @@ public:
void ExecGoToLastPage (SfxRequest& rReq);
void GetStateGoToLastPage (SfxItemSet& rSet);
void ExecGoToPage (SfxRequest& rReq);
void GetStateGoToPage (SfxItemSet& rSet);
SD_DLLPUBLIC void ExecChar(SfxRequest& rReq);
void ExecuteAnnotation (SfxRequest const & rRequest);
+1 -1
View File
@@ -80,7 +80,7 @@ public:
void GetStatusBarState (SfxItemSet& rSet);
void FuPermanent (SfxRequest& rRequest);
void GetAttrState (SfxItemSet& rSet);
static void ExecStatusBar (SfxRequest& rRequest);
void ExecStatusBar (SfxRequest& rRequest);
virtual void Command (const CommandEvent& rEvent, ::sd::Window* pWindow) override;
void GetMenuState (SfxItemSet &rSet);
void GetClipboardState (SfxItemSet &rSet);
@@ -50,6 +50,7 @@
#include <sfx2/viewfrm.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/request.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/sidebar/SidebarChildWindow.hxx>
#include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
#include <svx/svxids.hrc>
@@ -430,8 +431,21 @@ void SlideSorterViewShell::GetAttrState (SfxItemSet& rSet)
mpSlideSorter->GetController().GetAttrState(rSet);
}
void SlideSorterViewShell::ExecStatusBar (SfxRequest& )
void SlideSorterViewShell::ExecStatusBar (SfxRequest& rReq)
{
// nothing is executed during a slide show!
if(HasCurrentFunction(SID_PRESENTATION))
return;
switch (rReq.GetSlot())
{
case SID_STATUS_PAGE:
{
GetViewFrame()->GetDispatcher()->Execute(SID_GO_TO_PAGE,
SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
}
break;
}
}
void SlideSorterViewShell::Paint (
+7
View File
@@ -1071,6 +1071,13 @@ void DrawViewShell::ExecStatusBar(SfxRequest& rReq)
GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION_LAYOUT, SfxCallMode::ASYNCHRON );
}
break;
case SID_STATUS_PAGE:
{
GetViewFrame()->GetDispatcher()->Execute(SID_GO_TO_PAGE,
SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
}
break;
}
}
+11
View File
@@ -867,6 +867,17 @@ void DrawViewShell::GetStateGoToLastPage (SfxItemSet& rSet)
rSet.DisableItem( SID_GO_TO_LAST_PAGE );
}
void DrawViewShell::ExecGoToPage (SfxRequest& rReq)
{
SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) );
Cancel();
}
void DrawViewShell::GetStateGoToPage (SfxItemSet& rSet)
{
if (meEditMode == EditMode::MasterPage)
rSet.DisableItem( SID_GO_TO_PAGE );
}
} // end of namespace sd
+1
View File
@@ -463,6 +463,7 @@
<menu:menuitem menu:id=".uno:PreviousPage"/>
<menu:menuitem menu:id=".uno:NextPage"/>
<menu:menuitem menu:id=".uno:LastPage"/>
<menu:menuitem menu:id=".uno:GotoPage"/>
</menu:menupopup>
</menu:menu>
</menu:menupopup>
+1
View File
@@ -569,6 +569,7 @@
<menu:menuitem menu:id=".uno:PreviousSlide"/>
<menu:menuitem menu:id=".uno:NextSlide"/>
<menu:menuitem menu:id=".uno:LastSlide"/>
<menu:menuitem menu:id=".uno:GotoSlide"/>
</menu:menupopup>
</menu:menu>
<menu:menuseparator/>
+1
View File
@@ -131,6 +131,7 @@ $(eval $(call gb_Library_add_exception_objects,svx,\
svx/source/dialog/connctrl \
svx/source/dialog/_contdlg \
svx/source/dialog/contwnd \
svx/source/dialog/gotodlg \
svx/source/dialog/compressgraphicdialog \
$(call gb_Helper_optional,BREAKPAD, \
svx/source/dialog/crashreportdlg \
+1
View File
@@ -79,6 +79,7 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\
svx/uiconfig/ui/gallerymenu2 \
svx/uiconfig/ui/genericcheckdialog \
svx/uiconfig/ui/genericcheckentry \
svx/uiconfig/ui/gotopagedialog \
svx/uiconfig/ui/grafctrlbox \
svx/uiconfig/ui/grafmodebox \
svx/uiconfig/ui/headfootformatpage \
+17
View File
@@ -12508,6 +12508,23 @@ SfxVoidItem EditQrCode SID_EDIT_QRCODE
GroupId = SfxGroupId::Edit;
]
SfxVoidItem GotoPage SID_GO_TO_PAGE
()
[
AutoUpdate = FALSE,
FastCall = FALSE,
ReadOnlyDoc = TRUE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
AccelConfig = TRUE,
MenuConfig = TRUE,
ToolBoxConfig = TRUE,
GroupId = SfxGroupId::Navigator;
]
SfxVoidItem SpellCheckIgnore SID_SPELLCHECK_IGNORE
(SfxStringItem Type FN_PARAM_1)
[
+66
View File
@@ -0,0 +1,66 @@
/* -*- 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 .
*/
#include <svx/dialog/gotodlg.hxx>
using namespace com::sun::star;
namespace svx
{
GotoPageDlg::GotoPageDlg(weld::Window* pParent, const OUString& title, const OUString& label,
sal_uInt16 curPagePos, sal_uInt16 pageCnt)
: GenericDialogController(pParent, "svx/ui/gotopagedialog.ui", "GotoPageDialog")
, mnMaxPageCnt(1)
, mxMtrPageCtrl(m_xBuilder->weld_spin_button("page"))
, mxPageNumberLbl(m_xBuilder->weld_label("page_count"))
, mxPageLbl(m_xBuilder->weld_label("page_label"))
{
set_title(title);
mxPageLbl->set_label(label);
mxMtrPageCtrl->set_value(curPagePos);
sal_uInt16 nTotalPage = pageCnt;
OUString sStr = mxPageNumberLbl->get_label();
mxPageNumberLbl->set_label(sStr.replaceFirst("$1", OUString::number(nTotalPage)));
mnMaxPageCnt = nTotalPage;
mxMtrPageCtrl->connect_changed(LINK(this, GotoPageDlg, PageModifiedHdl));
mxMtrPageCtrl->set_position(-1);
mxMtrPageCtrl->select_region(0, -1);
}
IMPL_LINK_NOARG(GotoPageDlg, PageModifiedHdl, weld::Entry&, void)
{
if (mxMtrPageCtrl->get_text().isEmpty())
return;
auto page_value = mxMtrPageCtrl->get_text().toInt32();
if (page_value <= 0)
mxMtrPageCtrl->set_value(1);
else if (page_value > mnMaxPageCnt)
mxMtrPageCtrl->set_value(mnMaxPageCnt);
else
mxMtrPageCtrl->set_value(page_value);
mxMtrPageCtrl->set_position(-1);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.4 -->
<interface domain="sw">
<interface domain="svx">
<requires lib="gtk+" version="3.20"/>
<object class="GtkDialog" id="GotoPageDialog">
<property name="can_focus">False</property>
-1
View File
@@ -784,7 +784,6 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
sw/source/uibase/utlui/initui \
sw/source/uibase/utlui/navicfg \
sw/source/uibase/utlui/navipi \
sw/source/uibase/utlui/gotodlg \
sw/source/uibase/utlui/numfmtlb \
sw/source/uibase/utlui/prcntfld \
sw/source/uibase/utlui/shdwcrsr \
-1
View File
@@ -177,7 +177,6 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/swriter,\
sw/uiconfig/swriter/ui/frmaddpage \
sw/uiconfig/swriter/ui/frmtypepage \
sw/uiconfig/swriter/ui/frmurlpage \
sw/uiconfig/swriter/ui/gotopagedialog \
sw/uiconfig/swriter/ui/hfmenubutton \
sw/uiconfig/swriter/ui/indexentry \
sw/uiconfig/swriter/ui/inputfielddialog \
-2
View File
@@ -545,8 +545,6 @@ class SwUINumRuleItem;
#define FN_UPDATE_CUR_TOX (FN_EXTRA + 54) /* update current index */
#define FN_REMOVE_CUR_TOX (FN_EXTRA + 55) /* remove the current TOX*/
#define FN_GOTO_PAGE (FN_EXTRA + 59 ) /* goto page */
#define FN_COLL_TYPE (FN_EXTRA + 98) /* type for GlobalDoc-Collection*/
#define FN_COLL_ADD (FN_EXTRA + 99)
+1
View File
@@ -1247,6 +1247,7 @@
#define STR_IMGBTN_FIELD_DOWN NC_("STR_IMGBTN_FIELD_DOWN", "Next field")
#define STR_IMGBTN_FIELD_BYTYPE_UP NC_("STR_IMGBTN_FIELD_BYTYPE_UP", "Previous '%FIELDTYPE' field")
#define STR_IMGBTN_FIELD_BYTYPE_DOWN NC_("STR_IMGBTN_FIELD_BYTYPE_DOWN", "Next '%FIELDTYPE' field")
#define STR_GOTO_PAGE_DLG_TITLE NC_("STR_GOTO_PAGE_DLG_TITLE", "Go to Page")
#define STR_REDLINE_INSERT NC_("STR_REDLINE_INSERT", "Inserted")
#define STR_REDLINE_DELETE NC_("STR_REDLINE_DELETE", "Deleted")
+1 -1
View File
@@ -93,7 +93,7 @@ interface BaseTextEditView
ExecMethod = Execute ;
StateMethod = GetState ;
]
FN_GOTO_PAGE // status(final|play)
SID_GO_TO_PAGE // status(final|play)
[
ExecMethod = Execute ;
StateMethod = NoState ;
-17
View File
@@ -1900,23 +1900,6 @@ SfxVoidItem GotoNextWrongTableFormula FN_NEXT_TBLFML_ERR
GroupId = SfxGroupId::Navigator;
]
SfxVoidItem GotoPage FN_GOTO_PAGE
()
[
AutoUpdate = FALSE,
FastCall = TRUE,
ReadOnlyDoc = TRUE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
AccelConfig = TRUE,
MenuConfig = FALSE,
ToolBoxConfig = FALSE,
GroupId = SfxGroupId::Navigator;
]
SfxVoidItem GotoPrevIndexMark FN_PREV_TOXMARK
()
[
+9 -4
View File
@@ -78,7 +78,6 @@
#include <swtypes.hxx>
#include <swwait.hxx>
#include <redlndlg.hxx>
#include <gotodlg.hxx>
#include <view.hxx>
#include <uivwimp.hxx>
#include <docsh.hxx>
@@ -162,6 +161,8 @@
#include <svx/srchdlg.hxx>
#include <o3tl/string_view.hxx>
#include <svx/dialog/gotodlg.hxx>
const char sStatusDelim[] = " : ";
using namespace sfx2;
@@ -1285,9 +1286,13 @@ void SwView::Execute(SfxRequest &rReq)
}
}
break;
case FN_GOTO_PAGE:
case SID_GO_TO_PAGE:
{
SwGotoPageDlg aDlg(GetViewFrame().GetFrameWeld(), GetViewFrame().GetBindings());
sal_uInt16 nPhyPage, nVirPage;
GetWrtShell().GetPageNum(nPhyPage, nVirPage);
svx::GotoPageDlg aDlg(GetViewFrame().GetFrameWeld(), SwResId(STR_GOTO_PAGE_DLG_TITLE),
SwResId(ST_PGE) + ":", nPhyPage, GetWrtShell().GetPageCnt());
if (aDlg.run() == RET_OK)
GetWrtShell().GotoPage(aDlg.GetPageSelection(), true);
}
@@ -2202,7 +2207,7 @@ void SwView::ExecuteStatusLine(SfxRequest &rReq)
{
case FN_STAT_PAGE:
{
GetViewFrame().GetDispatcher()->Execute( FN_GOTO_PAGE,
GetViewFrame().GetDispatcher()->Execute( SID_GO_TO_PAGE,
SfxCallMode::SYNCHRON|SfxCallMode::RECORD );
}
break;
-105
View File
@@ -1,105 +0,0 @@
/* -*- 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 .
*/
#include <swmodule.hxx>
#include <view.hxx>
#include <wrtsh.hxx>
#include <gotodlg.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
using namespace com::sun::star;
SwGotoPageDlg::SwGotoPageDlg(weld::Window* pParent, SfxBindings& rBindings)
: GenericDialogController(pParent, "modules/swriter/ui/gotopagedialog.ui", "GotoPageDialog")
, m_pCreateView(nullptr)
, m_rBindings(rBindings)
, mnMaxPageCnt(1)
, mxMtrPageCtrl(m_xBuilder->weld_spin_button("page"))
, mxPageNumberLbl(m_xBuilder->weld_label("page_count"))
{
sal_uInt16 nTotalPage = GetPageInfo();
if (nTotalPage)
{
OUString sStr = mxPageNumberLbl->get_label();
mxPageNumberLbl->set_label(sStr.replaceFirst("$1", OUString::number(nTotalPage)));
mnMaxPageCnt = nTotalPage;
}
mxMtrPageCtrl->connect_changed(LINK(this, SwGotoPageDlg, PageModifiedHdl));
mxMtrPageCtrl->set_position(-1);
mxMtrPageCtrl->select_region(0, -1);
}
IMPL_LINK_NOARG(SwGotoPageDlg, PageModifiedHdl, weld::Entry&, void)
{
if (mxMtrPageCtrl->get_text().isEmpty())
return;
int page_value = mxMtrPageCtrl->get_text().toInt32();
if (page_value <= 0)
mxMtrPageCtrl->set_value(1);
else if (page_value > mnMaxPageCnt)
mxMtrPageCtrl->set_value(mnMaxPageCnt);
else
mxMtrPageCtrl->set_value(page_value);
mxMtrPageCtrl->set_position(-1);
}
SwView* SwGotoPageDlg::GetCreateView() const
{
if (!m_pCreateView)
{
SwView* pView = SwModule::GetFirstView();
while (pView)
{
if (&pView->GetViewFrame().GetBindings() == &m_rBindings)
{
const_cast<SwGotoPageDlg*>(this)->m_pCreateView = pView;
break;
}
pView = SwModule::GetNextView(pView);
}
}
return m_pCreateView;
}
// If the page can be set here, the maximum is set.
sal_uInt16 SwGotoPageDlg::GetPageInfo()
{
SwView* pView = GetCreateView();
SwWrtShell* pSh = pView ? &pView->GetWrtShell() : nullptr;
mxMtrPageCtrl->set_value(1);
if (pSh)
{
const sal_uInt16 nPageCnt = pSh->GetPageCnt();
sal_uInt16 nPhyPage, nVirPage;
pSh->GetPageNum(nPhyPage, nVirPage);
mxMtrPageCtrl->set_max(nPageCnt);
mxMtrPageCtrl->set_value(nPhyPage);
return nPageCnt;
}
return 0;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */