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