summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2019-08-05 17:26:53 +0100
committerCaolán McNamara <caolanm@redhat.com>2019-08-22 16:37:16 +0200
commit69057064d8957804c76e623d57c103c3413b7cbc (patch)
tree8026201f2750300a3b9f831a04dcc34016d54a95 /vcl
parente724f245e9652230d4c1f58c353be150006affcd (diff)
weld ODbTypeWizDialogSetup
split up RoadmapWizard to sit its wizard logic on top of a a native GtkAssistant a) awkwardly GtkAssistant is not a GtkDialog, but derives directly from GtkWindow so some shuffling around required due to that b) hidden/unused pages are shuffled to the end of the list of pages and their titles turned off in order to hide them from the roadmap c) some nonstandard hackery required to get the gtk roadmap titles to wrap Change-Id: I0d2346c489fef744136a2785f33c846d97bd8dc6 Reviewed-on: https://gerrit.libreoffice.org/76876 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolanm@redhat.com> Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r--vcl/source/app/salvtables.cxx293
-rw-r--r--vcl/source/control/button.cxx3
-rw-r--r--vcl/source/control/roadmapwizard.cxx356
-rw-r--r--vcl/source/control/wizardmachine.cxx598
-rw-r--r--vcl/unx/gtk3/gtk3gtkinst.cxx399
5 files changed, 1588 insertions, 61 deletions
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 66b5e3d1bbe2..e0f3ad3f35c3 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -50,6 +50,7 @@
#include <vcl/menubtn.hxx>
#include <vcl/prgsbar.hxx>
#include <vcl/ptrstyle.hxx>
+#include <vcl/roadmapwizard.hxx>
#include <vcl/slider.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/svimpbox.hxx>
@@ -966,6 +967,35 @@ class SalInstanceContainer : public SalInstanceWidget, public virtual weld::Cont
{
private:
VclPtr<vcl::Window> m_xContainer;
+
+ void implResetDefault(const vcl::Window* _pWindow)
+ {
+ vcl::Window* pChildLoop = _pWindow->GetWindow(GetWindowType::FirstChild);
+ while (pChildLoop)
+ {
+ // does the window participate in the tabbing order?
+ if (pChildLoop->GetStyle() & WB_DIALOGCONTROL)
+ implResetDefault(pChildLoop);
+
+ // is it a button?
+ WindowType eType = pChildLoop->GetType();
+ if ( (WindowType::PUSHBUTTON == eType)
+ || (WindowType::OKBUTTON == eType)
+ || (WindowType::CANCELBUTTON == eType)
+ || (WindowType::HELPBUTTON == eType)
+ || (WindowType::IMAGEBUTTON == eType)
+ || (WindowType::MENUBUTTON == eType)
+ || (WindowType::MOREBUTTON == eType)
+ )
+ {
+ pChildLoop->SetStyle(pChildLoop->GetStyle() & ~WB_DEFBUTTON);
+ }
+
+ // the next one ...
+ pChildLoop = pChildLoop->GetWindow(GetWindowType::Next);
+ }
+ }
+
public:
SalInstanceContainer(vcl::Window* pContainer, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
: SalInstanceWidget(pContainer, pBuilder, bTakeOwnership)
@@ -980,6 +1010,10 @@ public:
assert(!pNewParent || pNewVclParent);
pVclWidget->getWidget()->SetParent(pNewVclParent ? pNewVclParent->getWidget() : nullptr);
}
+ virtual void recursively_unset_default_buttons() override
+ {
+ implResetDefault(m_xContainer.get());
+ }
};
std::unique_ptr<weld::Container> SalInstanceWidget::weld_parent() const
@@ -1358,7 +1392,7 @@ public:
return m_xDialog->IsModalInputMode();
}
- virtual weld::Button* get_widget_for_response(int nResponse) override;
+ virtual weld::Button* weld_widget_for_response(int nResponse) override;
virtual void set_default_response(int nResponse) override
{
@@ -1448,6 +1482,218 @@ public:
}
};
+class SalInstanceAssistant : public SalInstanceDialog, public virtual weld::Assistant
+{
+private:
+ VclPtr<vcl::RoadmapWizard> m_xWizard;
+ std::vector<std::unique_ptr<SalInstanceContainer>> m_aPages;
+ std::vector<VclPtr<TabPage>> m_aAddedPages;
+ std::vector<int> m_aIds;
+ std::vector<VclPtr<VclGrid>> m_aAddedGrids;
+ Idle m_aUpdateRoadmapIdle;
+
+ int find_page(const OString& rIdent) const
+ {
+ for (size_t i = 0; i < m_aAddedPages.size(); ++i)
+ {
+ if (m_aAddedPages[i]->get_id().toUtf8() == rIdent)
+ return i;
+ }
+ return -1;
+ }
+
+ int find_id(int nId) const
+ {
+ for (size_t i = 0; i < m_aIds.size(); ++i)
+ {
+ if (nId == m_aIds[i])
+ return i;
+ }
+ return -1;
+ }
+
+ DECL_LINK(OnRoadmapItemSelected, LinkParamNone*, void);
+ DECL_LINK(UpdateRoadmap_Hdl, Timer*, void);
+
+public:
+ SalInstanceAssistant(vcl::RoadmapWizard* pDialog, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
+ : SalInstanceDialog(pDialog, pBuilder, bTakeOwnership)
+ , m_xWizard(pDialog)
+ {
+ m_xWizard->SetItemSelectHdl(LINK(this, SalInstanceAssistant, OnRoadmapItemSelected));
+
+ m_aUpdateRoadmapIdle.SetInvokeHandler(LINK(this, SalInstanceAssistant, UpdateRoadmap_Hdl));
+ m_aUpdateRoadmapIdle.SetPriority(TaskPriority::HIGHEST);
+ }
+
+ virtual int get_current_page() const override
+ {
+ return find_id(m_xWizard->GetCurLevel());
+ }
+
+ virtual int get_n_pages() const override
+ {
+ return m_aAddedPages.size();
+ }
+
+ virtual OString get_page_ident(int nPage) const override
+ {
+ return m_aAddedPages[nPage]->get_id().toUtf8();
+ }
+
+ virtual OString get_current_page_ident() const override
+ {
+ return get_page_ident(get_current_page());
+ }
+
+ virtual void set_current_page(int nPage) override
+ {
+ disable_notify_events();
+
+ // take the first shown page as the size for all pages
+ if (m_xWizard->GetPageSizePixel().Width() == 0)
+ {
+ TabPage* pPage = m_xWizard->GetPage(m_aIds[nPage]);
+ assert(pPage);
+ Size aPageSize(pPage->get_preferred_size());
+ m_xWizard->SetPageSizePixel(aPageSize);
+ }
+
+ m_xWizard->ShowPage(m_aIds[nPage]);
+ enable_notify_events();
+ }
+
+ virtual void set_current_page(const OString& rIdent) override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return;
+ set_current_page(nIndex);
+ }
+
+ virtual void set_page_index(const OString& rIdent, int nNewIndex) override
+ {
+ int nOldIndex = find_page(rIdent);
+
+ if (nOldIndex == -1)
+ return;
+
+ if (nOldIndex == nNewIndex)
+ return;
+
+ disable_notify_events();
+
+ auto entry = std::move(m_aAddedPages[nOldIndex]);
+ m_aAddedPages.erase(m_aAddedPages.begin() + nOldIndex);
+ m_aAddedPages.insert(m_aAddedPages.begin() + nNewIndex, std::move(entry));
+
+ int nId = m_aIds[nOldIndex];
+ m_aIds.erase(m_aIds.begin() + nOldIndex);
+ m_aIds.insert(m_aIds.begin() + nNewIndex, nId);
+
+ m_aUpdateRoadmapIdle.Start();
+
+ enable_notify_events();
+ }
+
+ virtual weld::Container* append_page(const OString& rIdent) override
+ {
+ VclPtrInstance<TabPage> xPage(m_xWizard);
+ VclPtrInstance<VclGrid> xGrid(xPage);
+ xPage->set_id(OUString::fromUtf8(rIdent));
+ xPage->Show();
+ xGrid->set_hexpand(true);
+ xGrid->set_vexpand(true);
+ xGrid->Show();
+ m_xWizard->AddPage(xPage);
+ m_aIds.push_back(m_aAddedPages.size());
+ m_xWizard->SetPage(m_aIds.back(), xPage);
+ m_aAddedPages.push_back(xPage);
+ m_aAddedGrids.push_back(xGrid);
+
+ m_aUpdateRoadmapIdle.Start();
+
+ m_aPages.emplace_back(new SalInstanceContainer(xGrid, m_pBuilder, false));
+ return m_aPages.back().get();
+ }
+
+ virtual OUString get_page_title(const OString& rIdent) const override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return OUString();
+ return m_aAddedPages[nIndex]->GetText();
+ }
+
+ virtual void set_page_title(const OString& rIdent, const OUString& rTitle) override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return;
+ if (m_aAddedPages[nIndex]->GetText() != rTitle)
+ {
+ disable_notify_events();
+ m_aAddedPages[nIndex]->SetText(rTitle);
+ m_aUpdateRoadmapIdle.Start();
+ enable_notify_events();
+ }
+ }
+
+ virtual void set_page_sensitive(const OString& rIdent, bool bSensitive) override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return;
+ if (m_aAddedPages[nIndex]->IsEnabled() != bSensitive)
+ {
+ disable_notify_events();
+ m_aAddedPages[nIndex]->Enable(bSensitive);
+ m_aUpdateRoadmapIdle.Start();
+ enable_notify_events();
+ }
+ }
+
+ weld::Button* weld_widget_for_response(int nResponse) override;
+
+ virtual ~SalInstanceAssistant() override
+ {
+ for (auto &rGrid : m_aAddedGrids)
+ rGrid.disposeAndClear();
+ for (auto &rPage : m_aAddedPages)
+ rPage.disposeAndClear();
+ }
+};
+
+IMPL_LINK_NOARG(SalInstanceAssistant, OnRoadmapItemSelected, LinkParamNone*, void)
+{
+ if (notify_events_disabled())
+ return;
+ int nPageIndex(find_id(m_xWizard->GetCurrentRoadmapItemID()));
+ if (!signal_jump_page(get_page_ident(nPageIndex)))
+ m_xWizard->SelectRoadmapItemByID(m_xWizard->GetCurLevel());
+}
+
+IMPL_LINK_NOARG(SalInstanceAssistant, UpdateRoadmap_Hdl, Timer*, void)
+{
+ disable_notify_events();
+
+ m_xWizard->DeleteRoadmapItems();
+
+ int nPos = 0;
+ for (size_t i = 0; i < m_aAddedPages.size(); ++i)
+ {
+ const OUString& rLabel = m_aAddedPages[i]->GetText();
+ bool bSensitive = m_aAddedPages[i]->IsEnabled();
+ if (rLabel.isEmpty())
+ continue;
+ m_xWizard->InsertRoadmapItem(nPos++, rLabel, m_aIds[i], bSensitive);
+ }
+
+ m_xWizard->SelectRoadmapItemByID(m_aIds[get_current_page()]);
+
+ enable_notify_events();
+}
+
class SalInstanceFrame : public SalInstanceContainer, public virtual weld::Frame
{
private:
@@ -1978,12 +2224,30 @@ IMPL_LINK(SalInstanceButton, ClickHdl, ::Button*, pButton, void)
signal_clicked();
}
-weld::Button* SalInstanceDialog::get_widget_for_response(int nResponse)
+weld::Button* SalInstanceDialog::weld_widget_for_response(int nResponse)
{
PushButton* pButton = dynamic_cast<PushButton*>(m_xDialog->get_widget_for_response(nResponse));
return pButton ? new SalInstanceButton(pButton, nullptr, false) : nullptr;
}
+weld::Button* SalInstanceAssistant::weld_widget_for_response(int nResponse)
+{
+ PushButton* pButton = nullptr;
+ if (nResponse == static_cast<int>(WizardButtonFlags::NEXT))
+ pButton = m_xWizard->m_pNextPage;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::PREVIOUS))
+ pButton = m_xWizard->m_pPrevPage;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::FINISH))
+ pButton = m_xWizard->m_pFinish;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::CANCEL))
+ pButton = m_xWizard->m_pCancel;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::HELP))
+ pButton = m_xWizard->m_pHelp;
+ if (pButton)
+ return new SalInstanceButton(pButton, nullptr, false);
+ return nullptr;
+}
+
class SalInstanceMenuButton : public SalInstanceButton, public virtual weld::MenuButton
{
private:
@@ -5286,6 +5550,19 @@ public:
return pRet;
}
+ virtual std::unique_ptr<weld::Assistant> weld_assistant(const OString &id, bool bTakeOwnership) override
+ {
+ vcl::RoadmapWizard* pDialog = m_xBuilder->get<vcl::RoadmapWizard>(id);
+ std::unique_ptr<weld::Assistant> pRet(pDialog ? new SalInstanceAssistant(pDialog, this, false) : nullptr);
+ if (bTakeOwnership && pDialog)
+ {
+ assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed");
+ m_aOwnedToplevel.set(pDialog);
+ m_xBuilder->drop_ownership(pDialog);
+ }
+ return pRet;
+ }
+
virtual std::unique_ptr<weld::Window> weld_window(const OString &id, bool bTakeOwnership) override
{
SystemWindow* pWindow = m_xBuilder->get<SystemWindow>(id);
@@ -5676,6 +5953,18 @@ namespace weld
}
}
+ AssistantController::AssistantController(weld::Widget* pParent, const OUString &rUIFile, const OString& rDialogId)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIFile))
+ , m_xAssistant(m_xBuilder->weld_assistant(rDialogId))
+ {
+ }
+
+ Dialog* AssistantController::getDialog() { return m_xAssistant.get(); }
+
+ AssistantController::~AssistantController()
+ {
+ }
+
void TriStateEnabled::ButtonToggled(weld::ToggleButton& rToggle)
{
if (bTriStateEnabled)
diff --git a/vcl/source/control/button.cxx b/vcl/source/control/button.cxx
index 8a076fadceb9..8669e655479f 100644
--- a/vcl/source/control/button.cxx
+++ b/vcl/source/control/button.cxx
@@ -145,6 +145,9 @@ OUString Button::GetStandardText(StandardButtonType eButton)
SV_BUTTONTEXT_IGNORE,
SV_BUTTONTEXT_ABORT,
SV_BUTTONTEXT_LESS,
+ STR_WIZDLG_PREVIOUS,
+ STR_WIZDLG_NEXT,
+ STR_WIZDLG_FINISH,
};
return VclResId(aResIdAry[static_cast<sal_uInt16>(eButton)]);
diff --git a/vcl/source/control/roadmapwizard.cxx b/vcl/source/control/roadmapwizard.cxx
index ec27bb6dae82..2d29a71aa47a 100644
--- a/vcl/source/control/roadmapwizard.cxx
+++ b/vcl/source/control/roadmapwizard.cxx
@@ -124,6 +124,13 @@ namespace vcl
impl_construct();
}
+ RoadmapWizardMachine::RoadmapWizardMachine(weld::Window* pParent)
+ : WizardMachine(pParent, WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP)
+ , m_pImpl( new RoadmapWizardImpl )
+ {
+ m_xAssistant->connect_jump_page(LINK(this, RoadmapWizardMachine, OnRoadmapItemSelected));
+ }
+
void RoadmapWizard::impl_construct()
{
SetLeftAlignedButtonCount( 1 );
@@ -143,12 +150,15 @@ namespace vcl
m_pImpl->pRoadmap->Show();
}
-
RoadmapWizard::~RoadmapWizard()
{
disposeOnce();
}
+ RoadmapWizardMachine::~RoadmapWizardMachine()
+ {
+ }
+
void RoadmapWizard::dispose()
{
m_pImpl.reset();
@@ -160,12 +170,15 @@ namespace vcl
m_pImpl->pRoadmap->SetHelpId( _rId );
}
-
void RoadmapWizard::SetRoadmapInteractive( bool _bInteractive )
{
m_pImpl->pRoadmap->SetRoadmapInteractive( _bInteractive );
}
+ void RoadmapWizardMachine::SetRoadmapInteractive( bool _bInteractive )
+ {
+ m_pImpl->pRoadmap->SetRoadmapInteractive( _bInteractive );
+ }
void RoadmapWizard::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
{
@@ -179,6 +192,17 @@ namespace vcl
implUpdateRoadmap( );
}
+ void RoadmapWizardMachine::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
+ {
+
+ m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
+
+ if ( m_pImpl->aPaths.size() == 1 )
+ // the very first path -> activate it
+ activatePath( _nPathId );
+ else
+ implUpdateRoadmap( );
+ }
void RoadmapWizard::describeState( WizardState _nState, const OUString& _rStateDisplayName, RoadmapPageFactory _pPageFactory )
{
@@ -187,7 +211,6 @@ namespace vcl
m_pImpl->aStateDescriptors[ _nState ] = StateDescriptions::mapped_type( _rStateDisplayName, _pPageFactory );
}
-
void RoadmapWizard::activatePath( PathId _nPathId, bool _bDecideForIt )
{
@@ -231,6 +254,47 @@ namespace vcl
implUpdateRoadmap( );
}
+ void RoadmapWizardMachine::activatePath( PathId _nPathId, bool _bDecideForIt )
+ {
+ if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
+ // nothing to do
+ return;
+
+ // does the given path exist?
+ Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
+ DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
+ if ( aNewPathPos == m_pImpl->aPaths.end() )
+ return;
+
+ // determine the index of the current state in the current path
+ sal_Int32 nCurrentStatePathIndex = -1;
+ if ( m_pImpl->nActivePath != -1 )
+ nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
+
+ DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
+ "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
+ // If this asserts, this for instance means that we are already in state number, say, 5
+ // of our current path, and the caller tries to activate a path which has less than 5
+ // states
+ if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
+ return;
+
+ // assert that the current and the new path are equal, up to nCurrentStatePathIndex
+ Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
+ if ( aActivePathPos != m_pImpl->aPaths.end() )
+ {
+ if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
+ {
+ OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
+ return;
+ }
+ }
+
+ m_pImpl->nActivePath = _nPathId;
+ m_pImpl->bActivePathIsDefinite = _bDecideForIt;
+
+ implUpdateRoadmap( );
+ }
void RoadmapWizard::implUpdateRoadmap( )
{
@@ -337,6 +401,110 @@ namespace vcl
m_pImpl->pRoadmap->SetRoadmapComplete( !bIncompletePath );
}
+ void RoadmapWizardMachine::implUpdateRoadmap( )
+ {
+
+ DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
+ "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
+ const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
+
+ sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
+ if (nCurrentStatePathIndex < 0)
+ return;
+
+ // determine up to which index (in the new path) we have to display the items
+ RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
+ if ( !m_pImpl->bActivePathIsDefinite )
+ {
+ for (auto const& path : m_pImpl->aPaths)
+ {
+ if ( path.first == m_pImpl->nActivePath )
+ // it's the path we are just activating -> no need to check anything
+ continue;
+ // the index from which on both paths differ
+ sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
+ if ( nDivergenceIndex <= nCurrentStatePathIndex )
+ // they differ in an index which we have already left behind us
+ // -> this is no conflict anymore
+ continue;
+
+ // the path conflicts with our new path -> don't activate the
+ // *complete* new path, but only up to the step which is unambiguous
+ nUpperStepBoundary = nDivergenceIndex;
+ }
+ }
+
+ // can we advance from the current page?
+ bool bCurrentPageCanAdvance = true;
+ TabPage* pCurrentPage = GetPage( getCurrentState() );
+ if ( pCurrentPage )
+ {
+ const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
+ OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
+ bCurrentPageCanAdvance = !pController || pController->canAdvance();
+ }
+
+ // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
+ // path, up to (excluding) nUpperStepBoundary
+ RoadmapTypes::ItemIndex nRoadmapItems = m_xAssistant->get_n_pages();
+ RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, nRoadmapItems );
+ for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
+ {
+ bool bExistentItem = ( nItemIndex < nRoadmapItems );
+ bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
+
+ bool bInsertItem = false;
+ if ( bExistentItem )
+ {
+ if ( !bNeedItem )
+ {
+ int nPages = nRoadmapItems;
+ for (int i = nPages - 1; i >= nItemIndex; --i)
+ {
+ m_xAssistant->set_page_title(m_xAssistant->get_page_ident(i), "");
+ --nRoadmapItems;
+ }
+ break;
+ }
+ else
+ {
+ // there is an item with this index in the roadmap - does it match what is requested by
+ // the respective state in the active path?
+ RoadmapTypes::ItemId nPresentItemId = m_xAssistant->get_page_ident(nItemIndex).toInt32();
+ WizardState nRequiredState = rActivePath[ nItemIndex ];
+ if ( nPresentItemId != nRequiredState )
+ {
+ m_xAssistant->set_page_title(OString::number(nPresentItemId), "");
+ bInsertItem = true;
+ }
+ }
+ }
+ else
+ {
+ DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
+ bInsertItem = bNeedItem;
+ }
+
+ WizardState nState( rActivePath[ nItemIndex ] );
+
+ if ( bInsertItem )
+ {
+ GetOrCreatePage(nState);
+ }
+
+ OString sIdent(OString::number(nState));
+ m_xAssistant->set_page_index(sIdent, nItemIndex);
+ m_xAssistant->set_page_title(sIdent, getStateDisplayName(nState));
+
+ // if the item is *after* the current state, but the current page does not
+ // allow advancing, the disable the state. This relieves derived classes
+ // from disabling all future states just because the current state does not
+ // (yet) allow advancing.
+ const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
+ const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
+ m_xAssistant->set_page_sensitive(sIdent, bEnable);
+ }
+ }
WizardTypes::WizardState RoadmapWizard::determineNextState( WizardState _nCurrentState ) const
{
@@ -367,6 +535,33 @@ namespace vcl
return aActivePathPos->second[ nNextStateIndex ];
}
+ WizardTypes::WizardState RoadmapWizardMachine::determineNextState( WizardState _nCurrentState ) const
+ {
+ sal_Int32 nCurrentStatePathIndex = -1;
+
+ Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
+ if ( aActivePathPos != m_pImpl->aPaths.end() )
+ nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
+
+ DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
+ if ( nCurrentStatePathIndex == -1 )
+ return WZS_INVALID_STATE;
+
+ sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
+
+ while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
+ && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
+ )
+ {
+ ++nNextStateIndex;
+ }
+
+ if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
+ // there is no next state in the current path (at least none which is enabled)
+ return WZS_INVALID_STATE;
+
+ return aActivePathPos->second[ nNextStateIndex ];
+ }
bool RoadmapWizard::canAdvance() const
{
@@ -398,6 +593,35 @@ namespace vcl
return *rPath.rbegin() != getCurrentState();
}
+ bool RoadmapWizardMachine::canAdvance() const
+ {
+ if ( !m_pImpl->bActivePathIsDefinite )
+ {
+ // check how many paths are still allowed
+ const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
+ sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
+
+ size_t nPossiblePaths(0);
+ for (auto const& path : m_pImpl->aPaths)
+ {
+ // the index from which on both paths differ
+ sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
+
+ if ( nDivergenceIndex > nCurrentStatePathIndex )
+ // this path is still a possible path
+ nPossiblePaths += 1;
+ }
+
+ // if we have more than one path which is still possible, then we assume
+ // to always have a next state. Though there might be scenarios where this
+ // is not true, but this is too sophisticated (means not really needed) right now.
+ if ( nPossiblePaths > 1 )
+ return true;
+ }
+
+ const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
+ return *rPath.rbegin() != getCurrentState();
+ }
void RoadmapWizard::updateTravelUI()
{
@@ -421,6 +645,27 @@ namespace vcl
implUpdateRoadmap();
}
+ void RoadmapWizardMachine::updateTravelUI()
+ {
+ WizardMachine::updateTravelUI();
+
+ // disable the "Previous" button if all states in our history are disabled
+ ::std::vector< WizardState > aHistory;
+ getStateHistory( aHistory );
+ bool bHaveEnabledState = false;
+ for (auto const& state : aHistory)
+ {
+ if ( isStateEnabled(state) )
+ {
+ bHaveEnabledState = true;
+ break;
+ }
+ }
+
+ enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
+
+ implUpdateRoadmap();
+ }
IMPL_LINK_NOARG(RoadmapWizard, OnRoadmapItemSelected, LinkParamNone*, void)
{
@@ -463,10 +708,48 @@ namespace vcl
m_pImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
}
+ IMPL_LINK(RoadmapWizardMachine, OnRoadmapItemSelected, const OString&, rCurItemId, bool)
+ {
+ int nCurItemId = rCurItemId.toInt32();
+
+ if ( nCurItemId == getCurrentState() )
+ // nothing to do
+ return false;
+
+ if ( isTravelingSuspended() )
+ return false;
+
+ WizardTravelSuspension aTravelGuard( *this );
+
+ sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
+ sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nCurItemId, m_pImpl->nActivePath );
+
+ DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
+ "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
+ if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
+ {
+ return false;
+ }
+
+ bool bResult = true;
+ if ( nNewIndex > nCurrentIndex )
+ {
+ bResult = skipUntil( static_cast<WizardState>(nCurItemId) );
+ WizardState nTemp = static_cast<WizardState>(nCurItemId);
+ while( nTemp )
+ {
+ if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
+ removePageFromHistory( nTemp );
+ }
+ }
+ else
+ bResult = skipBackwardUntil( static_cast<WizardState>(nCurItemId) );
+
+ return bResult;
+ }
void RoadmapWizard::enterState( WizardState _nState )
{
-
OWizardMachine::enterState( _nState );
// synchronize the roadmap
@@ -474,6 +757,13 @@ namespace vcl
m_pImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
}
+ void RoadmapWizardMachine::enterState( WizardState _nState )
+ {
+ WizardMachine::enterState( _nState );
+
+ // synchronize the roadmap
+ implUpdateRoadmap();
+ }
OUString RoadmapWizard::getStateDisplayName( WizardState _nState ) const
{
@@ -488,6 +778,18 @@ namespace vcl
return sDisplayName;
}
+ OUString RoadmapWizardMachine::getStateDisplayName( WizardState _nState ) const
+ {
+ OUString sDisplayName;
+
+ StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
+ OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
+ "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
+ if ( pos != m_pImpl->aStateDescriptors.end() )
+ sDisplayName = pos->second.first;
+
+ return sDisplayName;
+ }
VclPtr<TabPage> RoadmapWizard::createPage( WizardState _nState )
{
@@ -505,7 +807,6 @@ namespace vcl
return pPage;
}
-
void RoadmapWizard::enableState( WizardState _nState, bool _bEnable )
{
@@ -522,6 +823,20 @@ namespace vcl
m_pImpl->pRoadmap->EnableRoadmapItem( static_cast<RoadmapTypes::ItemId>(_nState), _bEnable );
}
+ void RoadmapWizardMachine::enableState( WizardState _nState, bool _bEnable )
+ {
+ // remember this (in case the state appears in the roadmap later on)
+ if ( _bEnable )
+ m_pImpl->aDisabledStates.erase( _nState );
+ else
+ {
+ m_pImpl->aDisabledStates.insert( _nState );
+ removePageFromHistory( _nState );
+ }
+
+ // if the state is currently in the roadmap, reflect it's new status
+ m_xAssistant->set_page_sensitive(OString::number(_nState), _bEnable);
+ }
bool RoadmapWizard::knowsState( WizardState i_nState ) const
{
@@ -541,6 +856,11 @@ namespace vcl
return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
}
+ bool RoadmapWizardMachine::isStateEnabled( WizardState _nState ) const
+ {
+ return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
+ }
+
void RoadmapWizard::updateRoadmapItemLabel( WizardState _nState )
{
const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
@@ -569,6 +889,32 @@ namespace vcl
}
}
+ void RoadmapWizard::InsertRoadmapItem(int nItemIndex, const OUString& rText, int nItemId, bool bEnable)
+ {
+ m_pImpl->pRoadmap->InsertRoadmapItem(nItemIndex, rText, nItemId, bEnable);
+ }
+
+ void RoadmapWizard::SelectRoadmapItemByID(int nItemId)
+ {
+ m_pImpl->pRoadmap->SelectRoadmapItemByID(nItemId);
+ }
+
+ void RoadmapWizard::DeleteRoadmapItems()
+ {
+ while (m_pImpl->pRoadmap->GetItemCount())
+ m_pImpl->pRoadmap->DeleteRoadmapItem(0);
+ }
+
+ void RoadmapWizard::SetItemSelectHdl( const Link<LinkParamNone*,void>& _rHdl )
+ {
+ m_pImpl->pRoadmap->SetItemSelectHdl(_rHdl);
+ }
+
+ int RoadmapWizard::GetCurrentRoadmapItemID() const
+ {
+ return m_pImpl->pRoadmap->GetCurrentRoadmapItemID();
+ }
+
} // namespace vcl
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/control/wizardmachine.cxx b/vcl/source/control/wizardmachine.cxx
index f42c509bff37..8a1a92a707f2 100644
--- a/vcl/source/control/wizardmachine.cxx
+++ b/vcl/source/control/wizardmachine.cxx
@@ -18,7 +18,6 @@
*/
#include <vcl/wizardmachine.hxx>
-#include <svtools/helpids.h>
#include <tools/debug.hxx>
#include <tools/diagnose_ex.h>
#include <vcl/svapp.hxx>
@@ -26,6 +25,15 @@
#include <svdata.hxx>
#include <stack>
+#define HID_WIZARD_NEXT "SVT_HID_WIZARD_NEXT"
+#define HID_WIZARD_PREVIOUS "SVT_HID_WIZARD_PREVIOUS"
+
+struct ImplWizPageData
+{
+ ImplWizPageData* mpNext;
+ VclPtr<TabPage> mpPage;
+};
+
namespace vcl
{
//= WizardPageImplData
@@ -360,14 +368,13 @@ namespace vcl
m_pCancel->Enable(_bEnable);
}
-
void OWizardMachine::enterState(WizardState _nState)
{
// tell the page
IWizardPageController* pController = getPageController( GetPage( _nState ) );
- OSL_ENSURE( pController, "OWizardMachine::enterState: no controller for the given page!" );
- if ( pController )
- pController->initializePage();
+ if (!pController)
+ return;
+ pController->initializePage();
if ( isAutomaticNextButtonStateEnabled() )
enableButtons( WizardButtonFlags::NEXT, canAdvance() );
@@ -378,7 +385,6 @@ namespace vcl
implUpdateTitle();
}
-
bool OWizardMachine::leaveState(WizardState)
{
// no need to ask the page here.
@@ -522,7 +528,6 @@ namespace vcl
// all fine
}
-
bool OWizardMachine::travelNext()
{
// allowed to leave the current page?
@@ -679,7 +684,586 @@ namespace vcl
m_pImpl->m_bTravelingSuspended = false;
}
+ WizardMachine::WizardMachine(weld::Window* pParent, WizardButtonFlags nButtonFlags)
+ : AssistantController(pParent, "dbaccess/ui/databasewizard.ui", "DatabaseWizard")
+ , m_nCurState(0)
+ , m_pFirstPage(nullptr)
+ , m_xFinish(m_xAssistant->weld_widget_for_response(static_cast<int>(WizardButtonFlags::FINISH)))
+ , m_xCancel(m_xAssistant->weld_widget_for_response(static_cast<int>(WizardButtonFlags::CANCEL)))
+ , m_xNextPage(m_xAssistant->weld_widget_for_response(static_cast<int>(WizardButtonFlags::NEXT)))
+ , m_xPrevPage(m_xAssistant->weld_widget_for_response(static_cast<int>(WizardButtonFlags::PREVIOUS)))
+ , m_xHelp(m_xAssistant->weld_widget_for_response(static_cast<int>(WizardButtonFlags::HELP)))
+ , m_pImpl(new WizardMachineImplData)
+ {
+ implConstruct(nButtonFlags);
+ }
+
+ void WizardMachine::implConstruct(const WizardButtonFlags nButtonFlags)
+ {
+ m_pImpl->sTitleBase = m_xAssistant->get_title();
+
+ // create the buttons according to the wizard button flags
+ // the help button
+ if (nButtonFlags & WizardButtonFlags::HELP)
+ {
+ m_xHelp->show();
+ }
+
+ // the previous button
+ if (nButtonFlags & WizardButtonFlags::PREVIOUS)
+ {
+ m_xPrevPage->set_help_id( HID_WIZARD_PREVIOUS );
+ m_xPrevPage->show();
+
+ m_xPrevPage->connect_clicked( LINK( this, WizardMachine, OnPrevPage ) );
+ }
+
+ // the next button
+ if (nButtonFlags & WizardButtonFlags::NEXT)
+ {
+ m_xNextPage->set_help_id( HID_WIZARD_NEXT );
+ m_xNextPage->show();
+
+ m_xNextPage->connect_clicked( LINK( this, WizardMachine, OnNextPage ) );
+ }
+
+ // the finish button
+ if (nButtonFlags & WizardButtonFlags::FINISH)
+ {
+ m_xFinish->show();
+
+ m_xFinish->connect_clicked( LINK( this, WizardMachine, OnFinish ) );
+ }
+
+ // the cancel button
+ if (nButtonFlags & WizardButtonFlags::CANCEL)
+ {
+ m_xCancel->show();
+ m_xCancel->connect_clicked( LINK( this, WizardMachine, OnCancel ) );
+ }
+ }
+
+ WizardMachine::~WizardMachine()
+ {
+ if (m_pImpl)
+ {
+ for (WizardState i = 0; i < m_pImpl->nFirstUnknownPage; ++i)
+ {
+ TabPage *pPage = GetPage(i);
+ if (pPage)
+ pPage->disposeOnce();
+ }
+ m_pImpl.reset();
+ }
+ }
+
+ void WizardMachine::implUpdateTitle()
+ {
+ OUString sCompleteTitle(m_pImpl->sTitleBase);
+
+ // append the page title
+ TabPage* pCurrentPage = GetPage(getCurrentState());
+ if ( pCurrentPage && !pCurrentPage->GetText().isEmpty() )
+ {
+ sCompleteTitle += " - " + pCurrentPage->GetText();
+ }
+
+ m_xAssistant->set_title(sCompleteTitle);
+ }
+
+ void WizardMachine::setTitleBase(const OUString& _rTitleBase)
+ {
+ m_pImpl->sTitleBase = _rTitleBase;
+ implUpdateTitle();
+ }
+
+ TabPage* WizardMachine::GetOrCreatePage( const WizardState i_nState )
+ {
+ if ( nullptr == GetPage( i_nState ) )
+ {
+ VclPtr<TabPage> pNewPage = createPage( i_nState );
+ DBG_ASSERT( pNewPage, "WizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
+
+ // fill up the page sequence of our base class (with dummies)
+ while ( m_pImpl->nFirstUnknownPage < i_nState )
+ {
+ AddPage( nullptr );
+ ++m_pImpl->nFirstUnknownPage;
+ }
+
+ if ( m_pImpl->nFirstUnknownPage == i_nState )
+ {
+ // encountered this page number the first time
+ AddPage( pNewPage );
+ ++m_pImpl->nFirstUnknownPage;
+ }
+ else
+ // already had this page - just change it
+ SetPage( i_nState, pNewPage );
+ }
+ return GetPage( i_nState );
+ }
+
+ void WizardMachine::ActivatePage()
+ {
+ WizardState nCurrentLevel = m_nCurState;
+ GetOrCreatePage( nCurrentLevel );
+
+ enterState( nCurrentLevel );
+ }
+
+ bool WizardMachine::DeactivatePage()
+ {
+ WizardState nCurrentState = getCurrentState();
+ return leaveState(nCurrentState);
+ }
+
+ void WizardMachine::defaultButton(WizardButtonFlags _nWizardButtonFlags)
+ {
+ // the new default button
+ weld::Button* pNewDefButton = nullptr;
+ if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
+ pNewDefButton = m_xFinish.get();
+ if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
+ pNewDefButton = m_xNextPage.get();
+ if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
+ pNewDefButton = m_xPrevPage.get();
+ if (_nWizardButtonFlags & WizardButtonFlags::HELP)
+ pNewDefButton = m_xHelp.get();
+ if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
+ pNewDefButton = m_xCancel.get();
+
+ if ( pNewDefButton )
+ defaultButton( pNewDefButton );
+ else
+ m_xAssistant->recursively_unset_default_buttons();
+ }
+ void WizardMachine::defaultButton(weld::Button* _pNewDefButton)
+ {
+ // loop through all (direct and indirect) descendants which participate in our tabbing order, and
+ // reset the WB_DEFBUTTON for every window which is a button
+ m_xAssistant->recursively_unset_default_buttons();
+
+ // set its new style
+ if (_pNewDefButton)
+ _pNewDefButton->set_has_default(true);
+ }
+
+ void WizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
+ {
+ if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
+ m_xFinish->set_sensitive(_bEnable);
+ if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
+ m_xNextPage->set_sensitive(_bEnable);
+ if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
+ m_xPrevPage->set_sensitive(_bEnable);
+ if (_nWizardButtonFlags & WizardButtonFlags::HELP)
+ m_xHelp->set_sensitive(_bEnable);
+ if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
+ m_xCancel->set_sensitive(_bEnable);
+ }
+
+ void WizardMachine::enterState(WizardState _nState)
+ {
+ // tell the page
+ IWizardPageController* pController = getPageController( GetPage( _nState ) );
+ OSL_ENSURE( pController, "WizardMachine::enterState: no controller for the given page!" );
+ if ( pController )
+ pController->initializePage();
+
+ if ( isAutomaticNextButtonStateEnabled() )
+ enableButtons( WizardButtonFlags::NEXT, canAdvance() );
+
+ enableButtons( WizardButtonFlags::PREVIOUS, !m_pImpl->aStateHistory.empty() );
+
+ // set the new title - it depends on the current page (i.e. state)
+ implUpdateTitle();
+ }
+
+ bool WizardMachine::leaveState(WizardState)
+ {
+ // no need to ask the page here.
+ // If we reach this point, we already gave the current page the chance to commit it's data,
+ // and it was allowed to commit it's data
+
+ return true;
+ }
+
+ bool WizardMachine::onFinish()
+ {
+ return Finish( RET_OK );
+ }
+
+ IMPL_LINK_NOARG(WizardMachine, OnFinish, weld::Button&, void)
+ {
+ if ( isTravelingSuspended() )
+ return;
+ WizardTravelSuspension aTravelGuard( *this );
+ if ( !prepareLeaveCurrentState( eFinish ) )
+ {
+ return;
+ }
+ onFinish();
+ }
+
+ IMPL_LINK_NOARG(WizardMachine, OnCancel, weld::Button&, void)
+ {
+ m_xAssistant->response(static_cast<int>(WizardButtonFlags::CANCEL));
+ }
+
+ WizardMachine::WizardState WizardMachine::determineNextState( WizardState _nCurrentState ) const
+ {
+ return _nCurrentState + 1;
+ }
+
+ bool WizardMachine::prepareLeaveCurrentState( CommitPageReason _eReason )
+ {
+ IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
+ ENSURE_OR_RETURN( pController != nullptr, "WizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
+ return pController->commitPage( _eReason );
+ }
+
+
+ bool WizardMachine::skipBackwardUntil( WizardState _nTargetState )
+ {
+ // allowed to leave the current page?
+ if ( !prepareLeaveCurrentState( eTravelBackward ) )
+ return false;
+
+ // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
+ ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
+ ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
+
+ WizardState nCurrentRollbackState = getCurrentState();
+ while ( nCurrentRollbackState != _nTargetState )
+ {
+ DBG_ASSERT( !aTravelVirtually.empty(), "WizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
+ nCurrentRollbackState = aTravelVirtually.top();
+ aTravelVirtually.pop();
+ }
+ m_pImpl->aStateHistory = aTravelVirtually;
+ if ( !ShowPage( _nTargetState ) )
+ {
+ m_pImpl->aStateHistory = aOldStateHistory;
+ return false;
+ }
+ return true;
+ }
+
+
+ bool WizardMachine::skipUntil( WizardState _nTargetState )
+ {
+ WizardState nCurrentState = getCurrentState();
+
+ // allowed to leave the current page?
+ if ( !prepareLeaveCurrentState( nCurrentState < _nTargetState ? eTravelForward : eTravelBackward ) )
+ return false;
+
+ // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
+ ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
+ ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
+ while ( nCurrentState != _nTargetState )
+ {
+ WizardState nNextState = determineNextState( nCurrentState );
+ if ( WZS_INVALID_STATE == nNextState )
+ {
+ OSL_FAIL( "WizardMachine::skipUntil: the given target state does not exist!" );
+ return false;
+ }
+
+ // remember the skipped state in the history
+ aTravelVirtually.push( nCurrentState );
+
+ // get the next state
+ nCurrentState = nNextState;
+ }
+ m_pImpl->aStateHistory = aTravelVirtually;
+ // show the target page
+ if ( !ShowPage( nCurrentState ) )
+ {
+ // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
+ // but ShowPage doesn't? Somebody behaves very strange here ....
+ OSL_FAIL( "WizardMachine::skipUntil: very unpolite ...." );
+ m_pImpl->aStateHistory = aOldStateHistory;
+ return false;
+ }
+ return true;
+ }
+
+
+ void WizardMachine::skip()
+ {
+ // allowed to leave the current page?
+ if ( !prepareLeaveCurrentState( eTravelForward ) )
+ return;
+
+ WizardState nCurrentState = getCurrentState();
+ WizardState nNextState = determineNextState(nCurrentState);
+
+ if (WZS_INVALID_STATE == nNextState)
+ return;
+
+ // remember the skipped state in the history
+ m_pImpl->aStateHistory.push(nCurrentState);
+
+ // get the next state
+ nCurrentState = nNextState;
+
+ // show the (n+1)th page
+ if (!ShowPage(nCurrentState))
+ {
+ // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
+ // Perhaps we should rollback the skipping here ....
+ OSL_FAIL("WizardMachine::skip: very unpolite ....");
+ // if somebody does a skip and then does not allow to leave ...
+ // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
+ // somebody behaves really strange ...)
+ return;
+ }
+
+ // all fine
+ }
+
+ bool WizardMachine::travelNext()
+ {
+ // allowed to leave the current page?
+ if ( !prepareLeaveCurrentState( eTravelForward ) )
+ return false;
+
+ // determine the next state to travel to
+ WizardState nCurrentState = getCurrentState();
+ WizardState nNextState = determineNextState(nCurrentState);
+ if (WZS_INVALID_STATE == nNextState)
+ return false;
+
+ // the state history is used by the enterState method
+ // all fine
+ m_pImpl->aStateHistory.push(nCurrentState);
+ if (!ShowPage(nNextState))
+ {
+ m_pImpl->aStateHistory.pop();
+ return false;
+ }
+
+ return true;
+ }
+
+ bool WizardMachine::ShowPage(WizardState nState)
+ {
+ if (DeactivatePage())
+ {
+ TabPage* pOldTabPage = m_xCurTabPage;
+
+ m_nCurState = nState;
+ ActivatePage();
+
+ if (pOldTabPage)
+ pOldTabPage->DeactivatePage();
+
+ m_xAssistant->set_current_page(OString::number(nState));
+
+ m_xCurTabPage = GetPage(m_nCurState);
+ m_xCurTabPage->ActivatePage();
+
+ return true;
+ }
+ return false;
+ }
+
+ bool WizardMachine::travelPrevious()
+ {
+ DBG_ASSERT(!m_pImpl->aStateHistory.empty(), "WizardMachine::travelPrevious: have no previous page!");
+
+ // allowed to leave the current page?
+ if ( !prepareLeaveCurrentState( eTravelBackward ) )
+ return false;
+
+ // the next state to switch to
+ WizardState nPreviousState = m_pImpl->aStateHistory.top();
+
+ // the state history is used by the enterState method
+ m_pImpl->aStateHistory.pop();
+ // show this page
+ if (!ShowPage(nPreviousState))
+ {
+ m_pImpl->aStateHistory.push(nPreviousState);
+ return false;
+ }
+
+ // all fine
+ return true;
+ }
+
+
+ void WizardMachine::removePageFromHistory( WizardState nToRemove )
+ {
+
+ ::std::stack< WizardState > aTemp;
+ while(!m_pImpl->aStateHistory.empty())
+ {
+ WizardState nPreviousState = m_pImpl->aStateHistory.top();
+ m_pImpl->aStateHistory.pop();
+ if(nPreviousState != nToRemove)
+ aTemp.push( nPreviousState );
+ else
+ break;
+ }
+ while(!aTemp.empty())
+ {
+ m_pImpl->aStateHistory.push( aTemp.top() );
+ aTemp.pop();
+ }
+ }
+
+
+ void WizardMachine::enableAutomaticNextButtonState()
+ {
+ m_pImpl->m_bAutoNextButtonState = true;
+ }
+
+
+ bool WizardMachine::isAutomaticNextButtonStateEnabled() const
+ {
+ return m_pImpl->m_bAutoNextButtonState;
+ }
+
+ IMPL_LINK_NOARG(WizardMachine, OnPrevPage, weld::Button&, void)
+ {
+ if ( isTravelingSuspended() )
+ return;
+ WizardTravelSuspension aTravelGuard( *this );
+ travelPrevious();
+ }
+
+ IMPL_LINK_NOARG(WizardMachine, OnNextPage, weld::Button&, void)
+ {
+ if ( isTravelingSuspended() )
+ return;
+ WizardTravelSuspension aTravelGuard( *this );
+ travelNext();
+ }
+
+ IWizardPageController* WizardMachine::getPageController( TabPage* _pCurrentPage ) const
+ {
+ IWizardPageController* pController = dynamic_cast< IWizardPageController* >( _pCurrentPage );
+ return pController;
+ }
+
+
+ void WizardMachine::getStateHistory( ::std::vector< WizardState >& _out_rHistory )
+ {
+ ::std::stack< WizardState > aHistoryCopy( m_pImpl->aStateHistory );
+ while ( !aHistoryCopy.empty() )
+ {
+ _out_rHistory.push_back( aHistoryCopy.top() );
+ aHistoryCopy.pop();
+ }
+ }
+
+
+ bool WizardMachine::canAdvance() const
+ {
+ return WZS_INVALID_STATE != determineNextState( getCurrentState() );
+ }
+
+
+ void WizardMachine::updateTravelUI()
+ {
+ const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
+ OSL_ENSURE( pController != nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
+
+ bool bCanAdvance =
+ ( !pController || pController->canAdvance() ) // the current page allows to advance
+ && canAdvance(); // the dialog as a whole allows to advance
+ enableButtons( WizardButtonFlags::NEXT, bCanAdvance );
+ }
+
+
+ bool WizardMachine::isTravelingSuspended() const
+ {
+ return m_pImpl->m_bTravelingSuspended;
+ }
+
+
+ void WizardMachine::suspendTraveling( AccessGuard )
+ {
+ DBG_ASSERT( !m_pImpl->m_bTravelingSuspended, "WizardMachine::suspendTraveling: already suspended!" );
+ m_pImpl->m_bTravelingSuspended = true;
+ }
+
+ void WizardMachine::resumeTraveling( AccessGuard )
+ {
+ DBG_ASSERT( m_pImpl->m_bTravelingSuspended, "WizardMachine::resumeTraveling: nothing to resume!" );
+ m_pImpl->m_bTravelingSuspended = false;
+ }
+
+ bool WizardMachine::Finish(short nResult)
+ {
+ if ( DeactivatePage() )
+ {
+ if (m_xCurTabPage)
+ m_xCurTabPage->DeactivatePage();
+
+ m_xAssistant->response(nResult);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ void WizardMachine::AddPage( TabPage* pPage )
+ {
+ ImplWizPageData* pNewPageData = new ImplWizPageData;
+ pNewPageData->mpNext = nullptr;
+ pNewPageData->mpPage = pPage;
+
+ if ( !m_pFirstPage )
+ m_pFirstPage = pNewPageData;
+ else
+ {
+ ImplWizPageData* pPageData = m_pFirstPage;
+ while ( pPageData->mpNext )
+ pPageData = pPageData->mpNext;
+ pPageData->mpNext = pNewPageData;
+ }
+ }
+
+ void WizardMachine::SetPage(WizardState nLevel, TabPage* pPage)
+ {
+ sal_uInt16 nTempLevel = 0;
+ ImplWizPageData* pPageData = m_pFirstPage;
+ while ( pPageData )
+ {
+ if ( (nTempLevel == nLevel) || !pPageData->mpNext )
+ break;
+
+ nTempLevel++;
+ pPageData = pPageData->mpNext;
+ }
+
+ if ( pPageData )
+ {
+ if ( pPageData->mpPage == m_xCurTabPage )
+ m_xCurTabPage = nullptr;
+ pPageData->mpPage = pPage;
+ }
+ }
+
+ TabPage* WizardMachine::GetPage(WizardState nLevel) const
+ {
+ sal_uInt16 nTempLevel = 0;
+
+ for (ImplWizPageData* pPageData = m_pFirstPage; pPageData;
+ pPageData = pPageData->mpNext)
+ {
+ if ( nTempLevel == nLevel )
+ return pPageData->mpPage;
+ nTempLevel++;
+ }
+
+ return nullptr;
+ }
} // namespace svt
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 10e3331ee355..45f6ae5a9813 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -38,6 +38,7 @@
#include <unotools/resmgr.hxx>
#include <unx/gstsink.hxx>
#include <vcl/ImageTree.hxx>
+#include <vcl/button.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/quickselectionengine.hxx>
#include <vcl/mnemonic.hxx>
@@ -2619,6 +2620,15 @@ class GtkInstanceContainer : public GtkInstanceWidget, public virtual weld::Cont
{
private:
GtkContainer* m_pContainer;
+
+ static void implResetDefault(GtkWidget *pWidget, gpointer user_data)
+ {
+ if (GTK_IS_BUTTON(pWidget))
+ g_object_set(G_OBJECT(pWidget), "has-default", false, nullptr);
+ if (GTK_IS_CONTAINER(pWidget))
+ gtk_container_forall(GTK_CONTAINER(pWidget), implResetDefault, user_data);
+ }
+
public:
GtkInstanceContainer(GtkContainer* pContainer, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
: GtkInstanceWidget(GTK_WIDGET(pContainer), pBuilder, bTakeOwnership)
@@ -2642,6 +2652,11 @@ public:
gtk_container_add(pNewGtkParent->getContainer(), pChild);
g_object_unref(pChild);
}
+
+ virtual void recursively_unset_default_buttons() override
+ {
+ implResetDefault(GTK_WIDGET(m_pContainer), nullptr);
+ }
};
std::unique_ptr<weld::Container> GtkInstanceWidget::weld_parent() const
@@ -2964,21 +2979,21 @@ class GtkInstanceDialog;
struct DialogRunner
{
- GtkDialog *m_pDialog;
+ GtkWindow* m_pDialog;
GtkInstanceDialog *m_pInstance;
gint m_nResponseId;
GMainLoop *m_pLoop;
VclPtr<vcl::Window> m_xFrameWindow;
int m_nModalDepth;
- DialogRunner(GtkDialog* pDialog, GtkInstanceDialog* pInstance)
+ DialogRunner(GtkWindow* pDialog, GtkInstanceDialog* pInstance)
: m_pDialog(pDialog)
, m_pInstance(pInstance)
, m_nResponseId(GTK_RESPONSE_NONE)
, m_pLoop(nullptr)
, m_nModalDepth(0)
{
- GtkWindow* pParent = gtk_window_get_transient_for(GTK_WINDOW(m_pDialog));
+ GtkWindow* pParent = gtk_window_get_transient_for(m_pDialog);
GtkSalFrame* pFrame = pParent ? GtkSalFrame::getFromWindow(pParent) : nullptr;
m_xFrameWindow = pFrame ? pFrame->GetWindow() : nullptr;
}
@@ -2995,6 +3010,7 @@ struct DialogRunner
}
static void signal_response(GtkDialog*, gint nResponseId, gpointer data);
+ static void signal_cancel(GtkDialog*, gpointer data);
static gboolean signal_delete(GtkDialog*, GdkEventAny*, gpointer data)
{
@@ -3038,14 +3054,15 @@ struct DialogRunner
inc_modal_count();
- bool bWasModal = gtk_window_get_modal(GTK_WINDOW(m_pDialog));
+ bool bWasModal = gtk_window_get_modal(m_pDialog);
if (!bWasModal)
- gtk_window_set_modal(GTK_WINDOW(m_pDialog), true);
+ gtk_window_set_modal(m_pDialog, true);
if (!gtk_widget_get_visible(GTK_WIDGET(m_pDialog)))
gtk_widget_show(GTK_WIDGET(m_pDialog));
- gulong nSignalResponseId = g_signal_connect(m_pDialog, "response", G_CALLBACK(signal_response), this);
+ gulong nSignalResponseId = GTK_IS_DIALOG(m_pDialog) ? g_signal_connect(m_pDialog, "response", G_CALLBACK(signal_response), this) : 0;
+ gulong nSignalCancelId = GTK_IS_ASSISTANT(m_pDialog) ? g_signal_connect(m_pDialog, "cancel", G_CALLBACK(signal_cancel), this) : 0;
gulong nSignalDeleteId = g_signal_connect(m_pDialog, "delete-event", G_CALLBACK(signal_delete), this);
gulong nSignalDestroyId = g_signal_connect(m_pDialog, "destroy", G_CALLBACK(signal_destroy), this);
@@ -3061,9 +3078,12 @@ struct DialogRunner
m_pLoop = nullptr;
if (!bWasModal)
- gtk_window_set_modal(GTK_WINDOW(m_pDialog), false);
+ gtk_window_set_modal(m_pDialog, false);
- g_signal_handler_disconnect(m_pDialog, nSignalResponseId);
+ if (nSignalResponseId)
+ g_signal_handler_disconnect(m_pDialog, nSignalResponseId);
+ if (nSignalCancelId)
+ g_signal_handler_disconnect(m_pDialog, nSignalCancelId);
g_signal_handler_disconnect(m_pDialog, nSignalDeleteId);
g_signal_handler_disconnect(m_pDialog, nSignalDestroyId);
@@ -3125,7 +3145,7 @@ class GtkInstanceButton;
class GtkInstanceDialog : public GtkInstanceWindow, public virtual weld::Dialog
{
private:
- GtkDialog* m_pDialog;
+ GtkWindow* m_pDialog;
DialogRunner m_aDialogRun;
std::shared_ptr<weld::DialogController> m_xDialogController;
// Used to keep ourself alive during a runAsync(when doing runAsync without a DialogController)
@@ -3164,7 +3184,7 @@ private:
return true; /* Do not destroy */
}
- static int GtkToVcl(int ret)
+ virtual int GtkToVcl(int ret)
{
if (ret == GTK_RESPONSE_OK)
ret = RET_OK;
@@ -3181,11 +3201,28 @@ private:
return ret;
}
+ virtual int VclToGtk(int nResponse)
+ {
+ if (nResponse == RET_OK)
+ return GTK_RESPONSE_OK;
+ else if (nResponse == RET_CANCEL)
+ return GTK_RESPONSE_CANCEL;
+ else if (nResponse == RET_CLOSE)
+ return GTK_RESPONSE_CLOSE;
+ else if (nResponse == RET_YES)
+ return GTK_RESPONSE_YES;
+ else if (nResponse == RET_NO)
+ return GTK_RESPONSE_NO;
+ else if (nResponse == RET_HELP)
+ return GTK_RESPONSE_HELP;
+ return nResponse;
+ }
+
void asyncresponse(gint ret);
public:
- GtkInstanceDialog(GtkDialog* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
- : GtkInstanceWindow(GTK_WINDOW(pDialog), pBuilder, bTakeOwnership)
+ GtkInstanceDialog(GtkWindow* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
+ : GtkInstanceWindow(pDialog, pBuilder, bTakeOwnership)
, m_pDialog(pDialog)
, m_aDialogRun(pDialog, this)
, m_nCloseSignalId(g_signal_connect(m_pDialog, "close", G_CALLBACK(signalClose), this))
@@ -3243,7 +3280,8 @@ public:
{
if (gtk_widget_get_visible(m_pWidget))
return;
- sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog)));
+ if (GTK_IS_DIALOG(m_pDialog))
+ sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog))));
gtk_widget_show(m_pWidget);
}
@@ -3272,42 +3310,30 @@ public:
}
}
- static int VclToGtk(int nResponse)
- {
- if (nResponse == RET_OK)
- return GTK_RESPONSE_OK;
- else if (nResponse == RET_CANCEL)
- return GTK_RESPONSE_CANCEL;
- else if (nResponse == RET_CLOSE)
- return GTK_RESPONSE_CLOSE;
- else if (nResponse == RET_YES)
- return GTK_RESPONSE_YES;
- else if (nResponse == RET_NO)
- return GTK_RESPONSE_NO;
- else if (nResponse == RET_HELP)
- return GTK_RESPONSE_HELP;
- return nResponse;
- }
-
virtual void response(int nResponse) override;
virtual void add_button(const OUString& rText, int nResponse, const OString& rHelpId) override
{
- GtkWidget* pWidget = gtk_dialog_add_button(m_pDialog, MapToGtkAccelerator(rText).getStr(), VclToGtk(nResponse));
+ GtkWidget* pWidget = gtk_dialog_add_button(GTK_DIALOG(m_pDialog), MapToGtkAccelerator(rText).getStr(), VclToGtk(nResponse));
if (!rHelpId.isEmpty())
::set_help_id(pWidget, rHelpId);
}
virtual void set_default_response(int nResponse) override
{
- gtk_dialog_set_default_response(m_pDialog, VclToGtk(nResponse));
+ gtk_dialog_set_default_response(GTK_DIALOG(m_pDialog), VclToGtk(nResponse));
}
- virtual weld::Button* get_widget_for_response(int nResponse) override;
+ virtual GtkButton* get_widget_for_response(int nGtkResponse)
+ {
+ return GTK_BUTTON(gtk_dialog_get_widget_for_response(GTK_DIALOG(m_pDialog), nGtkResponse));
+ }
+
+ virtual weld::Button* weld_widget_for_response(int nVclResponse) override;
virtual Container* weld_content_area() override
{
- return new GtkInstanceContainer(GTK_CONTAINER(gtk_dialog_get_content_area(m_pDialog)), m_pBuilder, false);
+ return new GtkInstanceContainer(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(m_pDialog))), m_pBuilder, false);
}
virtual void collapse(weld::Widget* pEdit, weld::Widget* pButton) override
@@ -3327,7 +3353,7 @@ public:
//mark widgets we want to be visible, starting with pRefEdit
//and all its direct parents.
winset aVisibleWidgets;
- GtkWidget *pContentArea = gtk_dialog_get_content_area(m_pDialog);
+ GtkWidget *pContentArea = gtk_dialog_get_content_area(GTK_DIALOG(m_pDialog));
for (GtkWidget *pCandidate = pRefEdit;
pCandidate && pCandidate != pContentArea && gtk_widget_get_visible(pCandidate);
pCandidate = gtk_widget_get_parent(pCandidate))
@@ -3350,7 +3376,7 @@ public:
gtk_widget_set_size_request(pRefEdit, m_nOldEditWidth, -1);
m_nOldBorderWidth = gtk_container_get_border_width(GTK_CONTAINER(m_pDialog));
gtk_container_set_border_width(GTK_CONTAINER(m_pDialog), 0);
- if (GtkWidget* pActionArea = gtk_dialog_get_action_area(m_pDialog))
+ if (GtkWidget* pActionArea = gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog)))
gtk_widget_hide(pActionArea);
// calc's insert->function is springing back to its original size if the ref-button
@@ -3385,7 +3411,7 @@ public:
gtk_widget_set_size_request(m_pRefEdit, m_nOldEditWidthReq, -1);
m_pRefEdit = nullptr;
gtk_container_set_border_width(GTK_CONTAINER(m_pDialog), m_nOldBorderWidth);
- if (GtkWidget* pActionArea = gtk_dialog_get_action_area(m_pDialog))
+ if (GtkWidget* pActionArea = gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog)))
gtk_widget_show(pActionArea);
resize_to_request();
present();
@@ -3430,13 +3456,21 @@ void DialogRunner::signal_response(GtkDialog*, gint nResponseId, gpointer data)
pThis->loop_quit();
}
+void DialogRunner::signal_cancel(GtkDialog*, gpointer data)
+{
+ DialogRunner* pThis = static_cast<DialogRunner*>(data);
+
+ // make esc in an assistant act as if cancel button was pressed
+ pThis->m_pInstance->close(false);
+}
+
class GtkInstanceMessageDialog : public GtkInstanceDialog, public virtual weld::MessageDialog
{
private:
GtkMessageDialog* m_pMessageDialog;
public:
GtkInstanceMessageDialog(GtkMessageDialog* pMessageDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
- : GtkInstanceDialog(GTK_DIALOG(pMessageDialog), pBuilder, bTakeOwnership)
+ : GtkInstanceDialog(GTK_WINDOW(pMessageDialog), pBuilder, bTakeOwnership)
, m_pMessageDialog(pMessageDialog)
{
}
@@ -3475,7 +3509,7 @@ private:
std::unique_ptr<utl::TempFile> mxBackgroundImage;
public:
GtkInstanceAboutDialog(GtkAboutDialog* pAboutDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
- : GtkInstanceDialog(GTK_DIALOG(pAboutDialog), pBuilder, bTakeOwnership)
+ : GtkInstanceDialog(GTK_WINDOW(pAboutDialog), pBuilder, bTakeOwnership)
, m_pAboutDialog(pAboutDialog)
, m_pCssProvider(nullptr)
{
@@ -3564,6 +3598,260 @@ public:
}
};
+class GtkInstanceAssistant : public GtkInstanceDialog, public virtual weld::Assistant
+{
+private:
+ GtkAssistant* m_pAssistant;
+ GtkWidget* m_pSidebar;
+ GtkButtonBox* m_pButtonBox;
+ GtkButton* m_pHelp;
+ GtkButton* m_pBack;
+ GtkButton* m_pNext;
+ GtkButton* m_pFinish;
+ GtkButton* m_pCancel;
+ std::vector<std::unique_ptr<GtkInstanceContainer>> m_aPages;
+
+ int find_page(const OString& rIdent) const
+ {
+ int nPages = gtk_assistant_get_n_pages(m_pAssistant);
+ for (int i = 0; i < nPages; ++i)
+ {
+ GtkWidget* pPage = gtk_assistant_get_nth_page(m_pAssistant, i);
+ const gchar* pStr = gtk_buildable_get_name(GTK_BUILDABLE(pPage));
+ if (g_strcmp0(pStr, rIdent.getStr()) == 0)
+ return i;
+ }
+ return -1;
+ }
+
+ virtual int GtkToVcl(int ret) override
+ {
+ if (ret == GTK_RESPONSE_OK)
+ ret = static_cast<int>(WizardButtonFlags::FINISH);
+ else if (ret == GTK_RESPONSE_CANCEL)
+ ret = static_cast<int>(WizardButtonFlags::CANCEL);
+ else if (ret == GTK_RESPONSE_DELETE_EVENT)
+ ret = static_cast<int>(WizardButtonFlags::CANCEL);
+ else if (ret == GTK_RESPONSE_CLOSE)
+ ret = static_cast<int>(WizardButtonFlags::CANCEL);
+ else if (ret == GTK_RESPONSE_ACCEPT)
+ ret = static_cast<int>(WizardButtonFlags::NEXT);
+ else if (ret == GTK_RESPONSE_REJECT)
+ ret = static_cast<int>(WizardButtonFlags::PREVIOUS);
+ else if (ret == GTK_RESPONSE_HELP)
+ ret = static_cast<int>(WizardButtonFlags::HELP);
+ return ret;
+ }
+
+ virtual int VclToGtk(int nResponse) override
+ {
+ if (nResponse == static_cast<int>(WizardButtonFlags::NEXT))
+ return GTK_RESPONSE_ACCEPT;
+ if (nResponse == static_cast<int>(WizardButtonFlags::PREVIOUS))
+ return GTK_RESPONSE_REJECT;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::FINISH))
+ return GTK_RESPONSE_OK;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::CANCEL))
+ return GTK_RESPONSE_CANCEL;
+ else if (nResponse == static_cast<int>(WizardButtonFlags::HELP))
+ return GTK_RESPONSE_HELP;
+ return nResponse;
+ }
+
+ static void wrap_sidebar_label(GtkWidget *pWidget, gpointer /*user_data*/)
+ {
+ if (GTK_IS_LABEL(pWidget))
+ {
+ gtk_label_set_line_wrap(GTK_LABEL(pWidget), true);
+ gtk_label_set_width_chars(GTK_LABEL(pWidget), 22);
+ gtk_label_set_max_width_chars(GTK_LABEL(pWidget), 22);
+ }
+ }
+
+ static void find_sidebar(GtkWidget *pWidget, gpointer user_data)
+ {
+ if (g_strcmp0(gtk_buildable_get_name(GTK_BUILDABLE(pWidget)), "sidebar") == 0)
+ {
+ GtkWidget **ppSidebar = static_cast<GtkWidget**>(user_data);
+ *ppSidebar = pWidget;
+ }
+ if (GTK_IS_CONTAINER(pWidget))
+ gtk_container_forall(GTK_CONTAINER(pWidget), find_sidebar, user_data);
+ }
+
+public:
+ GtkInstanceAssistant(GtkAssistant* pAssistant, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
+ : GtkInstanceDialog(GTK_WINDOW(pAssistant), pBuilder, bTakeOwnership)
+ , m_pAssistant(pAssistant)
+ , m_pSidebar(nullptr)
+ {
+ m_pButtonBox = GTK_BUTTON_BOX(gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL));
+ gtk_button_box_set_layout(m_pButtonBox, GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(m_pButtonBox), 6);
+
+ m_pBack = GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(Button::GetStandardText(StandardButtonType::Back)).getStr()));
+ gtk_widget_set_can_default(GTK_WIDGET(m_pBack), true);
+ gtk_buildable_set_name(GTK_BUILDABLE(m_pBack), "previous");
+ gtk_box_pack_end(GTK_BOX(m_pButtonBox), GTK_WIDGET(m_pBack), false, false, 0);
+
+ m_pNext = GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(Button::GetStandardText(StandardButtonType::Next)).getStr()));
+ gtk_widget_set_can_default(GTK_WIDGET(m_pNext), true);
+ gtk_buildable_set_name(GTK_BUILDABLE(m_pNext), "next");
+ gtk_box_pack_end(GTK_BOX(m_pButtonBox), GTK_WIDGET(m_pNext), false, false, 0);
+
+ m_pCancel = GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(Button::GetStandardText(StandardButtonType::Cancel)).getStr()));
+ gtk_widget_set_can_default(GTK_WIDGET(m_pCancel), true);
+ gtk_box_pack_end(GTK_BOX(m_pButtonBox), GTK_WIDGET(m_pCancel), false, false, 0);
+
+ m_pFinish = GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(Button::GetStandardText(StandardButtonType::Finish)).getStr()));
+ gtk_widget_set_can_default(GTK_WIDGET(m_pFinish), true);
+ gtk_buildable_set_name(GTK_BUILDABLE(m_pFinish), "finish");
+ gtk_box_pack_end(GTK_BOX(m_pButtonBox), GTK_WIDGET(m_pFinish), false, false, 0);
+
+ m_pHelp = GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(Button::GetStandardText(StandardButtonType::Help)).getStr()));
+ gtk_widget_set_can_default(GTK_WIDGET(m_pHelp), true);
+ gtk_box_pack_end(GTK_BOX(m_pButtonBox), GTK_WIDGET(m_pHelp), false, false, 0);
+
+ gtk_assistant_add_action_widget(pAssistant, GTK_WIDGET(m_pButtonBox));
+ gtk_button_box_set_child_secondary(m_pButtonBox, GTK_WIDGET(m_pHelp), true);
+ gtk_widget_set_hexpand(GTK_WIDGET(m_pButtonBox), true);
+
+ GtkWidget* pParent = gtk_widget_get_parent(GTK_WIDGET(m_pButtonBox));
+ gtk_container_child_set(GTK_CONTAINER(pParent), GTK_WIDGET(m_pButtonBox), "expand", true, "fill", true, nullptr);
+ gtk_widget_set_halign(pParent, GTK_ALIGN_FILL);
+
+ // Hide the built-in ones early so we get a nice optimal size for the width without
+ // including the unused contents
+ GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pParent));
+ for (GList* pChild = g_list_first(pChildren); pChild; pChild = g_list_next(pChild))
+ {
+ GtkWidget* pWidget = static_cast<GtkWidget*>(pChild->data);
+ gtk_widget_hide(pWidget);
+ }
+ g_list_free(pChildren);
+
+ gtk_widget_show_all(GTK_WIDGET(m_pButtonBox));
+
+ find_sidebar(GTK_WIDGET(m_pAssistant), &m_pSidebar);
+ }
+
+ virtual int get_current_page() const override
+ {
+ return gtk_assistant_get_current_page(m_pAssistant);
+ }
+
+ virtual int get_n_pages() const override
+ {
+ return gtk_assistant_get_n_pages(m_pAssistant);
+ }
+
+ virtual OString get_page_ident(int nPage) const override
+ {
+ const GtkWidget* pWidget = gtk_assistant_get_nth_page(m_pAssistant, nPage);
+ const gchar* pStr = gtk_buildable_get_name(GTK_BUILDABLE(pWidget));
+ return OString(pStr, pStr ? strlen(pStr) : 0);
+ }
+
+ virtual OString get_current_page_ident() const override
+ {
+ return get_page_ident(get_current_page());
+ }
+
+ virtual void set_current_page(int nPage) override
+ {
+ gtk_assistant_set_current_page(m_pAssistant, nPage);
+ }
+
+ virtual void set_current_page(const OString& rIdent) override
+ {
+ int nPage = find_page(rIdent);
+ if (nPage == -1)
+ return;
+ set_current_page(nPage);
+ }
+
+ virtual void set_page_title(const OString& rIdent, const OUString& rTitle) override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return;
+ GtkWidget* pPage = gtk_assistant_get_nth_page(m_pAssistant, nIndex);
+ gtk_assistant_set_page_title(m_pAssistant, pPage,
+ OUStringToOString(rTitle, RTL_TEXTENCODING_UTF8).getStr());
+ gtk_container_forall(GTK_CONTAINER(m_pSidebar), wrap_sidebar_label, nullptr);
+ }
+
+ virtual OUString get_page_title(const OString& rIdent) const override
+ {
+ int nIndex = find_page(rIdent);
+ if (nIndex == -1)
+ return OUString();
+ GtkWidget* pPage = gtk_assistant_get_nth_page(m_pAssistant, nIndex);
+ const gchar* pStr = gtk_assistant_get_page_title(m_pAssistant, pPage);
+ return OUString(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8);
+ }
+
+ virtual void set_page_sensitive(const OString& /*rIdent*/, bool /*bSensitive*/) override
+ {
+ // seeing as the GtkAssistant doesn't have clickable roadmap entries
+ // sensitive vs insensitive is moot
+ }
+
+ virtual void set_page_index(const OString& rIdent, int nNewIndex) override
+ {
+ int nOldIndex = find_page(rIdent);
+ if (nOldIndex == -1)
+ return;
+
+ if (nOldIndex == nNewIndex)
+ return;
+
+ GtkWidget* pPage = gtk_assistant_get_nth_page(m_pAssistant, nOldIndex);
+
+ g_object_ref(pPage);
+ OString sTitle(gtk_assistant_get_page_title(m_pAssistant, pPage));
+ gtk_assistant_remove_page(m_pAssistant, nOldIndex);
+ gtk_assistant_insert_page(m_pAssistant, pPage, nNewIndex);
+ gtk_assistant_set_page_type(m_pAssistant, pPage, GTK_ASSISTANT_PAGE_CUSTOM);
+ gtk_assistant_set_page_title(m_pAssistant, pPage, sTitle.getStr());
+ gtk_container_forall(GTK_CONTAINER(m_pSidebar), wrap_sidebar_label, nullptr);
+ g_object_unref(pPage);
+ }
+
+ virtual weld::Container* append_page(const OString& rIdent) override
+ {
+ disable_notify_events();
+
+ GtkWidget *pChild = gtk_grid_new();
+ gtk_buildable_set_name(GTK_BUILDABLE(pChild), rIdent.getStr());
+ gtk_assistant_append_page(m_pAssistant, pChild);
+ gtk_assistant_set_page_type(m_pAssistant, pChild, GTK_ASSISTANT_PAGE_CUSTOM);
+ gtk_widget_show(pChild);
+
+ enable_notify_events();
+
+ m_aPages.emplace_back(new GtkInstanceContainer(GTK_CONTAINER(pChild), m_pBuilder, false));
+
+ return m_aPages.back().get();
+ }
+
+ virtual GtkButton* get_widget_for_response(int nGtkResponse) override
+ {
+ GtkButton* pButton = nullptr;
+ if (nGtkResponse == GTK_RESPONSE_ACCEPT)
+ pButton = m_pNext;
+ else if (nGtkResponse == GTK_RESPONSE_REJECT)
+ pButton = m_pBack;
+ else if (nGtkResponse == GTK_RESPONSE_OK)
+ pButton = m_pFinish;
+ else if (nGtkResponse == GTK_RESPONSE_CANCEL)
+ pButton = m_pCancel;
+ else if (nGtkResponse == GTK_RESPONSE_HELP)
+ pButton = m_pHelp;
+ return pButton;
+ }
+};
+
class GtkInstanceFrame : public GtkInstanceContainer, public virtual weld::Frame
{
private:
@@ -4869,7 +5157,8 @@ void GtkInstanceDialog::asyncresponse(gint ret)
int GtkInstanceDialog::run()
{
- sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog)));
+ if (GTK_IS_DIALOG(m_pDialog))
+ sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog))));
int ret;
while (true)
{
@@ -4887,9 +5176,9 @@ int GtkInstanceDialog::run()
return GtkToVcl(ret);
}
-weld::Button* GtkInstanceDialog::get_widget_for_response(int nResponse)
+weld::Button* GtkInstanceDialog::weld_widget_for_response(int nVclResponse)
{
- GtkButton* pButton = GTK_BUTTON(gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse)));
+ GtkButton* pButton = get_widget_for_response(VclToGtk(nVclResponse));
if (!pButton)
return nullptr;
return new GtkInstanceButton(pButton, m_pBuilder, false);
@@ -4897,15 +5186,22 @@ weld::Button* GtkInstanceDialog::get_widget_for_response(int nResponse)
void GtkInstanceDialog::response(int nResponse)
{
+ int nGtkResponse = VclToGtk(nResponse);
//unblock this response now when activated through code
- if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse)))
+ if (GtkButton* pWidget = get_widget_for_response(nGtkResponse))
{
void* pData = g_object_get_data(G_OBJECT(pWidget), "g-lo-GtkInstanceButton");
GtkInstanceButton* pButton = static_cast<GtkInstanceButton*>(pData);
if (pButton)
pButton->clear_click_handler();
}
- gtk_dialog_response(m_pDialog, VclToGtk(nResponse));
+ if (GTK_IS_DIALOG(m_pDialog))
+ gtk_dialog_response(GTK_DIALOG(m_pDialog), nGtkResponse);
+ else if (GTK_IS_ASSISTANT(m_pDialog))
+ {
+ m_aDialogRun.m_nResponseId = nGtkResponse;
+ m_aDialogRun.loop_quit();
+ }
}
void GtkInstanceDialog::close(bool bCloseSignal)
@@ -4928,7 +5224,7 @@ GtkInstanceButton* GtkInstanceDialog::has_click_handler(int nResponse)
GtkInstanceButton* pButton = nullptr;
// e.g. map GTK_RESPONSE_DELETE_EVENT to GTK_RESPONSE_CANCEL
nResponse = VclToGtk(GtkToVcl(nResponse));
- if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, nResponse))
+ if (GtkButton* pWidget = get_widget_for_response(nResponse))
{
void* pData = g_object_get_data(G_OBJECT(pWidget), "g-lo-GtkInstanceButton");
pButton = static_cast<GtkInstanceButton*>(pData);
@@ -10368,7 +10664,7 @@ private:
set_help_id(pWidget, sHelpId);
//hook up for extended help
const ImplSVData* pSVData = ImplGetSVData();
- if (pSVData->maHelpData.mbBalloonHelp && !GTK_IS_DIALOG(pWidget))
+ if (pSVData->maHelpData.mbBalloonHelp && !GTK_IS_DIALOG(pWidget) && !GTK_IS_ASSISTANT(pWidget))
{
gtk_widget_set_has_tooltip(pWidget, true);
g_signal_connect(pWidget, "query-tooltip", G_CALLBACK(signalTooltipQuery), nullptr);
@@ -10602,6 +10898,15 @@ public:
return std::make_unique<GtkInstanceAboutDialog>(pAboutDialog, this, bTakeOwnership);
}
+ virtual std::unique_ptr<weld::Assistant> weld_assistant(const OString &id, bool bTakeOwnership) override
+ {
+ GtkAssistant* pAssistant = GTK_ASSISTANT(gtk_builder_get_object(m_pBuilder, id.getStr()));
+ if (!pAssistant)
+ return nullptr;
+ gtk_window_set_transient_for(GTK_WINDOW(pAssistant), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget)));
+ return std::make_unique<GtkInstanceAssistant>(pAssistant, this, bTakeOwnership);
+ }
+
virtual std::unique_ptr<weld::Dialog> weld_dialog(const OString &id, bool bTakeOwnership) override
{
GtkDialog* pDialog = GTK_DIALOG(gtk_builder_get_object(m_pBuilder, id.getStr()));
@@ -10609,7 +10914,7 @@ public:
return nullptr;
if (m_pParentWidget)
gtk_window_set_transient_for(GTK_WINDOW(pDialog), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget)));
- return std::make_unique<GtkInstanceDialog>(pDialog, this, bTakeOwnership);
+ return std::make_unique<GtkInstanceDialog>(GTK_WINDOW(pDialog), this, bTakeOwnership);
}
virtual std::unique_ptr<weld::Window> weld_window(const OString &id, bool bTakeOwnership) override