1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qhistorystate.h" |
41 | #include "qhistorystate_p.h" |
42 | |
43 | QT_BEGIN_NAMESPACE |
44 | |
45 | /*! |
46 | \class QHistoryState |
47 | \inmodule QtCore |
48 | |
49 | \brief The QHistoryState class provides a means of returning to a previously active substate. |
50 | |
51 | \since 4.6 |
52 | \ingroup statemachine |
53 | |
54 | A history state is a pseudo-state that represents the child state that the |
55 | parent state was in the last time the parent state was exited. A transition |
56 | with a history state as its target is in fact a transition to one or more |
57 | other child states of the parent state. QHistoryState is part of \l{The |
58 | State Machine Framework}. |
59 | |
60 | Use the setDefaultState() function to set the state that should be entered |
61 | if the parent state has never been entered. Example: |
62 | |
63 | \code |
64 | QStateMachine machine; |
65 | |
66 | QState *s1 = new QState(); |
67 | QState *s11 = new QState(s1); |
68 | QState *s12 = new QState(s1); |
69 | |
70 | QHistoryState *s1h = new QHistoryState(s1); |
71 | s1h->setDefaultState(s11); |
72 | |
73 | machine.addState(s1); |
74 | |
75 | QState *s2 = new QState(); |
76 | machine.addState(s2); |
77 | |
78 | QPushButton *button = new QPushButton(); |
79 | // Clicking the button will cause the state machine to enter the child state |
80 | // that s1 was in the last time s1 was exited, or the history state's default |
81 | // state if s1 has never been entered. |
82 | s1->addTransition(button, SIGNAL(clicked()), s1h); |
83 | \endcode |
84 | |
85 | If more than one default state has to be entered, or if the transition to the default state(s) |
86 | has to be acted upon, the defaultTransition should be set instead. Note that the eventTest() |
87 | method of that transition will never be called: the selection and execution of the transition is |
88 | done automatically when entering the history state. |
89 | |
90 | By default a history state is shallow, meaning that it won't remember nested |
91 | states. This can be configured through the historyType property. |
92 | */ |
93 | |
94 | /*! |
95 | \property QHistoryState::defaultTransition |
96 | |
97 | \brief the default transition of this history state |
98 | */ |
99 | |
100 | /*! |
101 | \property QHistoryState::defaultState |
102 | |
103 | \brief the default state of this history state |
104 | */ |
105 | |
106 | /*! |
107 | \property QHistoryState::historyType |
108 | |
109 | \brief the type of history that this history state records |
110 | |
111 | The default value of this property is QHistoryState::ShallowHistory. |
112 | */ |
113 | |
114 | /*! |
115 | \enum QHistoryState::HistoryType |
116 | |
117 | This enum specifies the type of history that a QHistoryState records. |
118 | |
119 | \value ShallowHistory Only the immediate child states of the parent state |
120 | are recorded. In this case a transition with the history state as its |
121 | target will end up in the immediate child state that the parent was in the |
122 | last time it was exited. This is the default. |
123 | |
124 | \value DeepHistory Nested states are recorded. In this case a transition |
125 | with the history state as its target will end up in the most deeply nested |
126 | descendant state the parent was in the last time it was exited. |
127 | */ |
128 | |
129 | namespace { |
130 | class DefaultStateTransition: public QAbstractTransition |
131 | { |
132 | Q_OBJECT |
133 | |
134 | public: |
135 | DefaultStateTransition(QHistoryState *source, QAbstractState *target); |
136 | |
137 | protected: |
138 | // It doesn't matter whether this transition matches any event or not. It is always associated |
139 | // with a QHistoryState, and as soon as the state-machine detects that it enters a history |
140 | // state, it will handle this transition as a special case. The history state itself is never |
141 | // entered either: either the stored configuration will be used, or the target(s) of this |
142 | // transition are used. |
143 | bool eventTest(QEvent *event) override { Q_UNUSED(event); return false; } |
144 | void onTransition(QEvent *event) override { Q_UNUSED(event); } |
145 | }; |
146 | } |
147 | |
148 | QHistoryStatePrivate::QHistoryStatePrivate() |
149 | : QAbstractStatePrivate(HistoryState) |
150 | , defaultTransition(nullptr) |
151 | , historyType(QHistoryState::ShallowHistory) |
152 | { |
153 | } |
154 | |
155 | DefaultStateTransition::DefaultStateTransition(QHistoryState *source, QAbstractState *target) |
156 | : QAbstractTransition() |
157 | { |
158 | setParent(source); |
159 | setTargetState(target); |
160 | } |
161 | |
162 | /*! |
163 | Constructs a new shallow history state with the given \a parent state. |
164 | */ |
165 | QHistoryState::QHistoryState(QState *parent) |
166 | : QAbstractState(*new QHistoryStatePrivate, parent) |
167 | { |
168 | } |
169 | /*! |
170 | Constructs a new history state of the given \a type, with the given \a |
171 | parent state. |
172 | */ |
173 | QHistoryState::QHistoryState(HistoryType type, QState *parent) |
174 | : QAbstractState(*new QHistoryStatePrivate, parent) |
175 | { |
176 | Q_D(QHistoryState); |
177 | d->historyType = type; |
178 | } |
179 | |
180 | /*! |
181 | Destroys this history state. |
182 | */ |
183 | QHistoryState::~QHistoryState() |
184 | { |
185 | } |
186 | |
187 | /*! |
188 | Returns this history state's default transition. The default transition is |
189 | taken when the history state has never been entered before. The target states |
190 | of the default transition therefore make up the default state. |
191 | |
192 | \since 5.6 |
193 | */ |
194 | QAbstractTransition *QHistoryState::defaultTransition() const |
195 | { |
196 | Q_D(const QHistoryState); |
197 | return d->defaultTransition; |
198 | } |
199 | |
200 | /*! |
201 | Sets this history state's default transition to be the given \a transition. |
202 | This will set the source state of the \a transition to the history state. |
203 | |
204 | Note that the eventTest method of the \a transition will never be called. |
205 | |
206 | \since 5.6 |
207 | */ |
208 | void QHistoryState::setDefaultTransition(QAbstractTransition *transition) |
209 | { |
210 | Q_D(QHistoryState); |
211 | if (d->defaultTransition != transition) { |
212 | d->defaultTransition = transition; |
213 | transition->setParent(this); |
214 | emit defaultTransitionChanged(QHistoryState::QPrivateSignal()); |
215 | } |
216 | } |
217 | |
218 | /*! |
219 | Returns this history state's default state. The default state indicates the |
220 | state to transition to if the parent state has never been entered before. |
221 | */ |
222 | QAbstractState *QHistoryState::defaultState() const |
223 | { |
224 | Q_D(const QHistoryState); |
225 | return d->defaultTransition ? d->defaultTransition->targetState() : nullptr; |
226 | } |
227 | |
228 | static inline bool isSoleEntry(const QList<QAbstractState*> &states, const QAbstractState * state) |
229 | { |
230 | return states.size() == 1 && states.first() == state; |
231 | } |
232 | |
233 | /*! |
234 | Sets this history state's default state to be the given \a state. |
235 | \a state must be a sibling of this history state. |
236 | |
237 | Note that this function does not set \a state as the initial state |
238 | of its parent. |
239 | */ |
240 | void QHistoryState::setDefaultState(QAbstractState *state) |
241 | { |
242 | Q_D(QHistoryState); |
243 | if (state && state->parentState() != parentState()) { |
244 | qWarning(msg: "QHistoryState::setDefaultState: state %p does not belong " |
245 | "to this history state's group (%p)" , state, parentState()); |
246 | return; |
247 | } |
248 | if (!d->defaultTransition || !isSoleEntry(states: d->defaultTransition->targetStates(), state)) { |
249 | if (!d->defaultTransition || !qobject_cast<DefaultStateTransition*>(object: d->defaultTransition)) { |
250 | d->defaultTransition = new DefaultStateTransition(this, state); |
251 | emit defaultTransitionChanged(QHistoryState::QPrivateSignal()); |
252 | } else { |
253 | d->defaultTransition->setTargetState(state); |
254 | } |
255 | emit defaultStateChanged(QHistoryState::QPrivateSignal()); |
256 | } |
257 | } |
258 | |
259 | /*! |
260 | Returns the type of history that this history state records. |
261 | */ |
262 | QHistoryState::HistoryType QHistoryState::historyType() const |
263 | { |
264 | Q_D(const QHistoryState); |
265 | return d->historyType; |
266 | } |
267 | |
268 | /*! |
269 | Sets the \a type of history that this history state records. |
270 | */ |
271 | void QHistoryState::setHistoryType(HistoryType type) |
272 | { |
273 | Q_D(QHistoryState); |
274 | if (d->historyType != type) { |
275 | d->historyType = type; |
276 | emit historyTypeChanged(QHistoryState::QPrivateSignal()); |
277 | } |
278 | } |
279 | |
280 | /*! |
281 | \reimp |
282 | */ |
283 | void QHistoryState::onEntry(QEvent *event) |
284 | { |
285 | Q_UNUSED(event); |
286 | } |
287 | |
288 | /*! |
289 | \reimp |
290 | */ |
291 | void QHistoryState::onExit(QEvent *event) |
292 | { |
293 | Q_UNUSED(event); |
294 | } |
295 | |
296 | /*! |
297 | \reimp |
298 | */ |
299 | bool QHistoryState::event(QEvent *e) |
300 | { |
301 | return QAbstractState::event(e); |
302 | } |
303 | |
304 | /*! |
305 | \fn QHistoryState::defaultStateChanged() |
306 | \since 5.4 |
307 | |
308 | This signal is emitted when the defaultState property is changed. |
309 | |
310 | \sa QHistoryState::defaultState |
311 | */ |
312 | |
313 | /*! |
314 | \fn QHistoryState::historyTypeChanged() |
315 | \since 5.4 |
316 | |
317 | This signal is emitted when the historyType property is changed. |
318 | |
319 | \sa QHistoryState::historyType |
320 | */ |
321 | |
322 | /*! |
323 | \fn QHistoryState::defaultTransitionChanged() |
324 | \since 5.6 |
325 | |
326 | This signal is emitted when the defaultTransition property is changed. |
327 | |
328 | \sa QHistoryState::defaultTransition |
329 | */ |
330 | |
331 | QT_END_NAMESPACE |
332 | |
333 | #include "moc_qhistorystate.cpp" |
334 | #include "qhistorystate.moc" |
335 | |