summaryrefslogtreecommitdiff
path: root/include/svtools/wizardmachine.hxx
blob: 68ad14a134bf7cde001f4fceef113118f08c63ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
/* -*- 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 .
 */
#ifndef INCLUDED_SVTOOLS_WIZARDMACHINE_HXX
#define INCLUDED_SVTOOLS_WIZARDMACHINE_HXX

#include <memory>
#include <svtools/svtdllapi.h>
#include <svtools/wizdlg.hxx>
#include <vcl/button.hxx>
#include <vcl/tabpage.hxx>
#include <o3tl/typed_flags_set.hxx>

class Bitmap;

enum class WizardButtonFlags
{
    NONE                = 0x0000,
    NEXT                = 0x0001,
    PREVIOUS            = 0x0002,
    FINISH              = 0x0004,
    CANCEL              = 0x0008,
    HELP                = 0x0010,
};
namespace o3tl
{
    template<> struct typed_flags<WizardButtonFlags> : is_typed_flags<WizardButtonFlags, 0x001f> {};
}

namespace svt
{


// wizard states
#define WZS_INVALID_STATE       (WizardState(-1))


    //= WizardTypes

    struct WizardTypes
    {
        typedef sal_Int16  WizardState;
        enum CommitPageReason
        {
            eTravelForward,         // traveling forward (maybe with skipping pages)
            eTravelBackward,        // traveling backward (maybe with skipping pages)
            eFinish,                // the wizard is about to be finished
            eValidate               // the data should be validated only, no traveling will happen
        };
    };

    class SAL_NO_VTABLE IWizardPageController
    {
    public:

        // This methods  behave somewhat different than ActivatePage/DeactivatePage
        // The latter are handled by the base class itself whenever changing the pages is in the offing,
        // i.e., when it's already decided which page is the next.
        // We may have situations where the next page depends on the state of the current, which needs
        // to be committed for this.
        // So initializePage and commitPage are designated to initializing/committing data on the page.
        virtual void        initializePage() = 0;
        virtual bool        commitPage( WizardTypes::CommitPageReason _eReason ) = 0;

        /** determines whether or not it is allowed to advance to a next page

            You should make this dependent on the current state of the page only, not on
            states on other pages of the whole dialog.

            The default implementation always returns <TRUE/>.
        */
        virtual bool        canAdvance() const = 0;

    protected:
        ~IWizardPageController() {}
    };


    //= OWizardPage

    class OWizardMachine;

    class SVT_DLLPUBLIC OWizardPage : public TabPage, public IWizardPageController
    {

    public:
        /** @param _pParent
                if the OWizardPage is used in an OWizardMachine, this parameter
                must be the OWizardMachine (which is derived from Window)
         */
        OWizardPage(vcl::Window *pParent, const OString& rID, const OUString& rUIXMLDescription);
        virtual ~OWizardPage() override;

        // IWizardPageController overridables
        virtual void        initializePage() override;
        virtual bool        commitPage( WizardTypes::CommitPageReason _eReason ) override;
        virtual bool        canAdvance() const override;

    protected:
        // TabPage overridables
        virtual void        ActivatePage() override;

        /** updates the travel-related UI elements of the OWizardMachine we live in (if any)

            If the parent of the tab page is a OWizardMachine, then updateTravelUI at this instance
            is called. Otherwise, nothing happens.
        */
        void                updateDialogTravelUI();
    };


    //= OWizardMachine

    struct WizardMachineImplData;
    /** implements some kind of finite automata, where the states of the automata exactly correlate
        with tab pages.

        That is, the machine can have up to n states, where at each point in time exactly one state is
        the current one. A state being current is represented as one of n tab pages being displayed
        currently.

        The class handles the UI for traveling between the states (e.g. it administrates the <em>Next</em> and
        <em>Previous</em> buttons which you usually find in a wizard.

        Derived classes have to implement the travel logic by overriding <member>determineNextState</member>,
        which has to determine the state which follows the current state. Since this may depend
        on the actual data presented in the wizard (e.g. checkboxes checked, or something like this),
        they can implement non-linear traveling this way.
    */

    class SVT_DLLPUBLIC OWizardMachine : public WizardDialog, public WizardTypes
    {
    private:
        // restrict access to some aspects of our base class
        using WizardDialog::AddPage;
        using WizardDialog::SetPage;
        //  TabPage*            GetPage( sal_uInt16 nLevel ) const { return WizardDialog::GetPage(nLevel); }
        // TODO: probably the complete page handling (next, previous etc.) should be prohibited ...

        // IMPORTANT:
        // traveling pages should not be done by calling these base class member, some mechanisms of this class
        // here (e.g. committing page data) depend on having full control over page traveling.
        // So use the travelXXX methods if you need to travel

    protected:
        VclPtr<OKButton>       m_pFinish;
        VclPtr<CancelButton>   m_pCancel;
        VclPtr<PushButton>     m_pNextPage;
        VclPtr<PushButton>     m_pPrevPage;
        VclPtr<HelpButton>     m_pHelp;

    private:
        // hold members in this structure to allow keeping compatible when members are added
        std::unique_ptr<WizardMachineImplData>  m_pImpl;

    public:
        OWizardMachine(vcl::Window* _pParent, WizardButtonFlags _nButtonFlags );
        virtual ~OWizardMachine() override;
        virtual void dispose() override;

        /// enable (or disable) buttons
        void                enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable);
        /// set the default style for a button
        void                defaultButton(WizardButtonFlags _nWizardButtonFlags);
        /// set the default style for a button
        void                defaultButton(PushButton* _pNewDefButton);

        /// set the base of the title to use - the title of the current page is appended
        void                setTitleBase(const OUString& _rTitleBase);

        /// determines whether there is a next state to which we can advance
        virtual bool        canAdvance() const;

        /** updates the user interface which deals with traveling in the wizard

            The default implementation simply checks whether both the current page and the wizard
            itself allow to advance to the next state (<code>canAdvance</code>), and enables the "Next"
            button if and only if this is the case.
        */
        virtual void        updateTravelUI();

    protected:
        // WizardDialog overridables
        virtual void        ActivatePage() override;
        virtual bool        DeactivatePage() override;

        // our own overridables

        /// to override to create new pages
        virtual VclPtr<TabPage> createPage(WizardState _nState) = 0;

        /// will be called when a new page is about to be displayed
        virtual void        enterState(WizardState _nState);

        /** will be called when the current state is about to be left for the given reason

            The base implementation in this class will simply call <member>OWizardPage::commitPage</member>
            for the current page, and return whatever this call returns.

            @param _eReason
                The reason why the state is to be left.
            @return
                <TRUE/> if and only if the page is allowed to be left
        */
        virtual bool        prepareLeaveCurrentState( CommitPageReason _eReason );

        /** will be called when the given state is left

            This is the very last possibility for derived classes to veto the deactivation
            of a page.

            @todo Normally, we would not need the return value here - derived classes now have
            the possibility to veto page deactivations in <member>prepareLeaveCurrentState</member>. However,
            changing this return type is too incompatible at the moment ...

            @return
                <TRUE/> if and only if the page is allowed to be left
        */
        virtual bool        leaveState( WizardState _nState );

        /** determine the next state to travel from the given one

            The default behaviour is linear traveling, overwrite this to change it

            Return WZS_INVALID_STATE to prevent traveling.
        */
        virtual WizardState determineNextState( WizardState _nCurrentState ) const;

        /** called when the finish button is pressed
            <p>By default, only the base class' Finish method (which is not virtual) is called</p>
        */
        virtual bool        onFinish();

        /// travel to the next state
        bool                travelNext();

        /// travel to the previous state
        bool                travelPrevious();

        /** enables the automatic enabled/disabled state of the "Next" button

            If this is <TRUE/>, then upon entering a new state, the "Next" button will automatically be
            enabled if and only if determineNextState does not return WZS_INVALID_STATE.
        */
        void                enableAutomaticNextButtonState();
        bool                isAutomaticNextButtonStateEnabled() const;

        /** removes a page from the history. Should be called when the page is being disabled
        */
        void                removePageFromHistory( WizardState nToRemove );

        /** skip a state

            The method behaves as if from the current state, <arg>_nSteps</arg> <method>travelNext</method>s were
            called, but without actually creating or displaying the \EDntermediate pages. Only the
            (<arg>_nSteps</arg> + 1)th page is created.

            The skipped states appear in the state history, so <method>travelPrevious</method> will make use of them.

            A very essential precondition for using this method is that your <method>determineNextState</method>
            method is able to determine the next state without actually having the page of the current state.

            @see skipUntil
            @see skipBackwardUntil
        */
        void                    skip();

        /** skips one or more states, until a given state is reached

            The method behaves as if from the current state, <method>travelNext</method>s were called
            successively, until <arg>_nTargetState</arg> is reached, but without actually creating or
            displaying the \EDntermediate pages.

            The skipped states appear in the state history, so <method>travelPrevious</method> will make use of them.

            @return
                <TRUE/> if and only if traveling was successful

            @see skip
            @see skipBackwardUntil
        */
        bool                    skipUntil( WizardState _nTargetState );

        /** moves back one or more states, until a given state is reached

            This method allows traveling backwards more than one state without actually showing the intermediate
            states.

            For instance, if you want to travel two steps backward at a time, you could used
            two travelPrevious calls, but this would <em>show</em> both pages, which is not necessary,
            since you're interested in the target page only. Using <member>skipBackwardUntil</member> relieves
            you of this.

            @return
                <TRUE/> if and only if traveling was successful

            @see skipUntil
            @see skip
        */
        bool                    skipBackwardUntil( WizardState _nTargetState );

        /** returns the current state of the machine

            Vulgo, this is the identifier of the current tab page :)
        */
        WizardState             getCurrentState() const { return WizardDialog::GetCurLevel(); }

        virtual IWizardPageController*
                                getPageController( TabPage* _pCurrentPage ) const;

        /** retrieves a copy of the state history, i.e. all states we already visited
        */
        void                    getStateHistory( ::std::vector< WizardState >& _out_rHistory );

    public:
        class AccessGuard
        {
            friend class WizardTravelSuspension;
        private:
            AccessGuard() { }
        };

        void                   suspendTraveling( AccessGuard );
        void                   resumeTraveling( AccessGuard );
        bool                   isTravelingSuspended() const;

    protected:
        TabPage* GetOrCreatePage( const WizardState i_nState );

    private:
        DECL_DLLPRIVATE_LINK(OnNextPage, Button*, void);
        DECL_DLLPRIVATE_LINK(OnPrevPage, Button*, void);
        DECL_DLLPRIVATE_LINK(OnFinish, Button*, void);

        SVT_DLLPRIVATE void     implResetDefault(vcl::Window const * _pWindow);
        SVT_DLLPRIVATE void     implUpdateTitle();
        SVT_DLLPRIVATE void     implConstruct( const WizardButtonFlags _nButtonFlags );
    };

    /// helper class to temporarily suspend any traveling in the wizard
    class WizardTravelSuspension
    {
    public:
        WizardTravelSuspension( OWizardMachine& _rWizard )
            :m_rWizard( _rWizard )
        {
            m_rWizard.suspendTraveling( OWizardMachine::AccessGuard() );
        }

        ~WizardTravelSuspension()
        {
            m_rWizard.resumeTraveling( OWizardMachine::AccessGuard() );
        }

    private:
        OWizardMachine& m_rWizard;
    };


}   // namespace svt


#endif // INCLUDED_SVTOOLS_WIZARDMACHINE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */