1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 #ifndef INCLUDED_VCL_WIZARDMACHINE_HXX
20 #define INCLUDED_VCL_WIZARDMACHINE_HXX
21 
22 #include <memory>
23 #include <vcl/dllapi.h>
24 #include <vcl/builderpage.hxx>
25 
26 namespace weld {
27     class Builder;
28     class Container;
29 }
30 
31 struct WizPageData;
32 struct ImplWizButtonData;
33 
34 // wizard states
35 #define WZS_INVALID_STATE (::vcl::WizardTypes::WizardState(-1))
36 
37 namespace vcl
38 {
39     //= WizardTypes
40     namespace WizardTypes
41     {
42         typedef sal_Int16  WizardState;
43         enum CommitPageReason
44         {
45             eTravelForward,         // traveling forward (maybe with skipping pages)
46             eTravelBackward,        // traveling backward (maybe with skipping pages)
47             eFinish,                // the wizard is about to be finished
48             eValidate               // the data should be validated only, no traveling will happen
49         };
50     };
51 
52     class SAL_NO_VTABLE IWizardPageController
53     {
54     public:
55 
56         // This methods  behave somewhat different than ActivatePage/DeactivatePage
57         // The latter are handled by the base class itself whenever changing the pages is in the offing,
58         // i.e., when it's already decided which page is the next.
59         // We may have situations where the next page depends on the state of the current, which needs
60         // to be committed for this.
61         // So initializePage and commitPage are designated to initializing/committing data on the page.
62         virtual void        initializePage() = 0;
63         virtual bool        commitPage( WizardTypes::CommitPageReason _eReason ) = 0;
64 
65         /** determines whether or not it is allowed to advance to a next page
66 
67             You should make this dependent on the current state of the page only, not on
68             states on other pages of the whole dialog.
69 
70             The default implementation always returns <TRUE/>.
71         */
72         virtual bool        canAdvance() const = 0;
73 
74     protected:
~IWizardPageController()75         ~IWizardPageController() {}
76     };
77 
78     //= OWizardPage
79     class VCL_DLLPUBLIC OWizardPage : public BuilderPage, public IWizardPageController
80     {
81     public:
82         OWizardPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, const OString& rID);
83         virtual ~OWizardPage() override;
84 
85         // IWizardPageController overridables
86         virtual void        initializePage() override;
87         virtual bool        commitPage( WizardTypes::CommitPageReason _eReason ) override;
88         virtual bool        canAdvance() const override;
89 
90     protected:
91         // BuilderPage overridables
92         virtual void        Activate() override;
93 
94         /** updates the travel-related UI elements of the OWizardMachine we live in (if any)
95 
96             If the parent of the tab page is an OWizardMachine, then updateTravelUI at this instance
97             is called. Otherwise, nothing happens.
98         */
99         void                updateDialogTravelUI();
100     };
101 
102     //= OWizardMachine
103     struct WizardMachineImplData;
104     /** implements some kind of finite automata, where the states of the automata exactly correlate
105         with tab pages.
106 
107         That is, the machine can have up to n states, where at each point in time exactly one state is
108         the current one. A state being current is represented as one of n tab pages being displayed
109         currently.
110 
111         The class handles the UI for traveling between the states (e.g. it administrates the <em>Next</em> and
112         <em>Previous</em> buttons which you usually find in a wizard.
113 
114         Derived classes have to implement the travel logic by overriding <member>determineNextState</member>,
115         which has to determine the state which follows the current state. Since this may depend
116         on the actual data presented in the wizard (e.g. checkboxes checked, or something like this),
117         they can implement non-linear traveling this way.
118     */
119     class VCL_DLLPUBLIC WizardMachine : public weld::AssistantController
120     {
121     protected:
122         BuilderPage* m_pCurTabPage;
123     private:
124         WizardTypes::WizardState m_nCurState;
125         WizPageData* m_pFirstPage;
126 
127     protected:
128         std::unique_ptr<weld::Button> m_xFinish;
129         std::unique_ptr<weld::Button> m_xCancel;
130         std::unique_ptr<weld::Button> m_xNextPage;
131         std::unique_ptr<weld::Button> m_xPrevPage;
132         std::unique_ptr<weld::Button> m_xHelp;
133 
134     private:
135         // hold members in this structure to allow keeping compatible when members are added
136         std::unique_ptr<WizardMachineImplData>  m_pImpl;
137 
138     public:
139         WizardMachine(weld::Window* _pParent, WizardButtonFlags _nButtonFlags );
140         virtual ~WizardMachine() override;
141 
142         bool Finish(short nResult = RET_CANCEL);
143         bool ShowPage(WizardTypes::WizardState nState);
144 
145         bool ShowNextPage();
146         bool ShowPrevPage();
147 
148         void                AddPage( std::unique_ptr<BuilderPage> xPage );
149         void                RemovePage( const BuilderPage* pPage );
150         void                SetPage( WizardTypes::WizardState nLevel, std::unique_ptr<BuilderPage> xPage );
151         BuilderPage*        GetPage( WizardTypes::WizardState eState ) const;
152 
153         /// enable (or disable) buttons
154         void                enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable);
155         /// set the default style for a button
156         void                defaultButton(WizardButtonFlags _nWizardButtonFlags);
157         /// set the default style for a button
158         void                defaultButton(weld::Button* _pNewDefButton);
159 
160         /// set the base of the title to use - the title of the current page is appended
161         void                setTitleBase(const OUString& _rTitleBase);
162 
163         /// determines whether there is a next state to which we can advance
164         virtual bool        canAdvance() const;
165 
166         /** updates the user interface which deals with traveling in the wizard
167 
168             The default implementation simply checks whether both the current page and the wizard
169             itself allow to advance to the next state (<code>canAdvance</code>), and enables the "Next"
170             button if and only if this is the case.
171         */
172         virtual void        updateTravelUI();
173 
174     protected:
175         virtual void        ActivatePage();
176         virtual bool        DeactivatePage();
177 
178         // our own overridables
179 
180         /// to override to create new pages
181         virtual std::unique_ptr<BuilderPage> createPage(WizardTypes::WizardState _nState) = 0;
182 
183         /// will be called when a new page is about to be displayed
184         virtual void        enterState(WizardTypes::WizardState _nState);
185 
186         /** will be called when the current state is about to be left for the given reason
187 
188             The base implementation in this class will simply call <member>OWizardPage::commitPage</member>
189             for the current page, and return whatever this call returns.
190 
191             @param _eReason
192                 The reason why the state is to be left.
193             @return
194                 <TRUE/> if and only if the page is allowed to be left
195         */
196         virtual bool        prepareLeaveCurrentState( WizardTypes::CommitPageReason eReason );
197 
198         /** will be called when the given state is left
199 
200             This is the very last possibility for derived classes to veto the deactivation
201             of a page.
202 
203             @todo Normally, we would not need the return value here - derived classes now have
204             the possibility to veto page deactivations in <member>prepareLeaveCurrentState</member>. However,
205             changing this return type is too incompatible at the moment ...
206 
207             @return
208                 <TRUE/> if and only if the page is allowed to be left
209         */
210         virtual bool        leaveState(WizardTypes::WizardState nState);
211 
212         /** determine the next state to travel from the given one
213 
214             The default behaviour is linear traveling, overwrite this to change it
215 
216             Return WZS_INVALID_STATE to prevent traveling.
217         */
218         virtual WizardTypes::WizardState determineNextState(WizardTypes::WizardState nCurrentState) const;
219 
220         /** called when the finish button is pressed
221             <p>By default, only the base class' Finish method (which is not virtual) is called</p>
222         */
223         virtual bool        onFinish();
224 
225         /// travel to the next state
226         bool                travelNext();
227 
228         /// travel to the previous state
229         bool                travelPrevious();
230 
231         /** enables the automatic enabled/disabled state of the "Next" button
232 
233             If this is <TRUE/>, then upon entering a new state, the "Next" button will automatically be
234             enabled if and only if determineNextState does not return WZS_INVALID_STATE.
235         */
236         void                enableAutomaticNextButtonState();
237         bool                isAutomaticNextButtonStateEnabled() const;
238 
239         /** removes a page from the history. Should be called when the page is being disabled
240         */
241         void                removePageFromHistory(WizardTypes::WizardState nToRemove);
242 
243         /** skip a state
244 
245             The method behaves as if from the current state, <arg>_nSteps</arg> <method>travelNext</method>s were
246             called, but without actually creating or displaying the \EDntermediate pages. Only the
247             (<arg>_nSteps</arg> + 1)th page is created.
248 
249             The skipped states appear in the state history, so <method>travelPrevious</method> will make use of them.
250 
251             A very essential precondition for using this method is that your <method>determineNextState</method>
252             method is able to determine the next state without actually having the page of the current state.
253 
254             @see skipUntil
255             @see skipBackwardUntil
256         */
257         void                    skip();
258 
259         /** skips one or more states, until a given state is reached
260 
261             The method behaves as if from the current state, <method>travelNext</method>s were called
262             successively, until <arg>_nTargetState</arg> is reached, but without actually creating or
263             displaying the \EDntermediate pages.
264 
265             The skipped states appear in the state history, so <method>travelPrevious</method> will make use of them.
266 
267             @return
268                 <TRUE/> if and only if traveling was successful
269 
270             @see skip
271             @see skipBackwardUntil
272         */
273         bool                    skipUntil(WizardTypes::WizardState nTargetState);
274 
275         /** moves back one or more states, until a given state is reached
276 
277             This method allows traveling backwards more than one state without actually showing the intermediate
278             states.
279 
280             For instance, if you want to travel two steps backward at a time, you could used
281             two travelPrevious calls, but this would <em>show</em> both pages, which is not necessary,
282             since you're interested in the target page only. Using <member>skipBackwardUntil</member> relieves
283             you of this.
284 
285             @return
286                 <TRUE/> if and only if traveling was successful
287 
288             @see skipUntil
289             @see skip
290         */
291         bool                    skipBackwardUntil(WizardTypes::WizardState nTargetState);
292 
293         /** returns the current state of the machine
294 
295             Vulgo, this is the identifier of the current tab page :)
296         */
getCurrentState() const297         WizardTypes::WizardState getCurrentState() const { return m_nCurState; }
298 
299         virtual IWizardPageController* getPageController(BuilderPage* pCurrentPage) const;
300 
301         /** retrieves a copy of the state history, i.e. all states we already visited
302         */
303         void                    getStateHistory(std::vector<WizardTypes::WizardState>& out_rHistory);
304 
305     public:
306         class AccessGuard
307         {
308             friend class WizardTravelSuspension;
309         private:
AccessGuard()310             AccessGuard() { }
311         };
312 
313         void                   suspendTraveling( AccessGuard );
314         void                   resumeTraveling( AccessGuard );
315         bool                   isTravelingSuspended() const;
316 
317     protected:
318         BuilderPage* GetOrCreatePage(const WizardTypes::WizardState i_nState);
319 
320     private:
321         DECL_DLLPRIVATE_LINK(OnNextPage, weld::Button&, void);
322         DECL_DLLPRIVATE_LINK(OnPrevPage, weld::Button&, void);
323         DECL_DLLPRIVATE_LINK(OnFinish, weld::Button&, void);
324         DECL_DLLPRIVATE_LINK(OnCancel, weld::Button&, void);
325 
326         VCL_DLLPRIVATE void     implUpdateTitle();
327         VCL_DLLPRIVATE void     implConstruct( const WizardButtonFlags _nButtonFlags );
328     };
329 
330 
331     /// helper class to temporarily suspend any traveling in the wizard
332     class WizardTravelSuspension
333     {
334     public:
WizardTravelSuspension(WizardMachine & rWizard)335         WizardTravelSuspension(WizardMachine& rWizard)
336             : m_pWizard(&rWizard)
337         {
338             m_pWizard->suspendTraveling(WizardMachine::AccessGuard());
339         }
340 
~WizardTravelSuspension()341         ~WizardTravelSuspension()
342         {
343             m_pWizard->resumeTraveling(WizardMachine::AccessGuard());
344         }
345 
346     private:
347         WizardMachine* m_pWizard;
348     };
349 }   // namespace vcl
350 
351 #define WIZARDDIALOG_BUTTON_STDOFFSET_X         6
352 #define WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X    3
353 
354 #endif // INCLUDED_VCL_WIZARDMACHINE_HXX
355 
356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
357