source: trunk/src/declarative/util/qdeclarativestategroup.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 13.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativestategroup_p.h"
43
44#include "private/qdeclarativetransition_p.h"
45#include "private/qdeclarativestate_p_p.h"
46
47#include <qdeclarativebinding_p.h>
48#include <qdeclarativeglobal_p.h>
49
50#include <QtCore/qstringbuilder.h>
51#include <QtCore/qdebug.h>
52
53#include <private/qobject_p.h>
54#include <qdeclarativeinfo.h>
55
56QT_BEGIN_NAMESPACE
57
58DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
59
60class QDeclarativeStateGroupPrivate : public QObjectPrivate
61{
62 Q_DECLARE_PUBLIC(QDeclarativeStateGroup)
63public:
64 QDeclarativeStateGroupPrivate()
65 : nullState(0), componentComplete(true),
66 ignoreTrans(false), applyingState(false), unnamedCount(0) {}
67
68 QString currentState;
69 QDeclarativeState *nullState;
70
71 static void append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state);
72 static int count_state(QDeclarativeListProperty<QDeclarativeState> *list);
73 static QDeclarativeState *at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index);
74 static void clear_states(QDeclarativeListProperty<QDeclarativeState> *list);
75
76 QList<QDeclarativeState *> states;
77 QList<QDeclarativeTransition *> transitions;
78
79 bool componentComplete;
80 bool ignoreTrans;
81 bool applyingState;
82 int unnamedCount;
83
84 QDeclarativeTransition *findTransition(const QString &from, const QString &to);
85 void setCurrentStateInternal(const QString &state, bool = false);
86 bool updateAutoState();
87};
88
89/*!
90 \qmlclass StateGroup QDeclarativeStateGroup
91 \ingroup qml-state-elements
92 \since 4.7
93 \brief The StateGroup element provides state support for non-Item elements.
94
95 Item (and all derived elements) provides built in support for states and transitions
96 via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to
97 use this support in other (non-Item-derived) elements.
98
99 \qml
100 MyCustomObject {
101 StateGroup {
102 id: myStateGroup
103 states: State {
104 name: "state1"
105 ...
106 }
107 transitions: Transition {
108 ...
109 }
110 }
111
112 onSomethingHappened: myStateGroup.state = "state1";
113 }
114 \endqml
115
116 \sa {qmlstate}{States} {Transitions}, {QtDeclarative}
117*/
118
119QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent)
120 : QObject(*(new QDeclarativeStateGroupPrivate), parent)
121{
122}
123
124QDeclarativeStateGroup::~QDeclarativeStateGroup()
125{
126 Q_D(const QDeclarativeStateGroup);
127 for (int i = 0; i < d->states.count(); ++i)
128 d->states.at(i)->setStateGroup(0);
129}
130
131QList<QDeclarativeState *> QDeclarativeStateGroup::states() const
132{
133 Q_D(const QDeclarativeStateGroup);
134 return d->states;
135}
136
137/*!
138 \qmlproperty list<State> StateGroup::states
139 This property holds a list of states defined by the state group.
140
141 \qml
142 StateGroup {
143 states: [
144 State { ... },
145 State { ... }
146 ...
147 ]
148 }
149 \endqml
150
151 \sa {qmlstate}{States}
152*/
153QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty()
154{
155 Q_D(QDeclarativeStateGroup);
156 return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state,
157 &QDeclarativeStateGroupPrivate::count_state,
158 &QDeclarativeStateGroupPrivate::at_state,
159 &QDeclarativeStateGroupPrivate::clear_states);
160}
161
162void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state)
163{
164 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
165 if (state) {
166 _this->d_func()->states.append(state);
167 state->setStateGroup(_this);
168 }
169
170}
171
172int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list)
173{
174 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
175 return _this->d_func()->states.count();
176}
177
178QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index)
179{
180 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
181 return _this->d_func()->states.at(index);
182}
183
184void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list)
185{
186 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
187 _this->d_func()->setCurrentStateInternal(QString(), true);
188 for (int i = 0; i < _this->d_func()->states.count(); ++i) {
189 _this->d_func()->states.at(i)->setStateGroup(0);
190 }
191 _this->d_func()->states.clear();
192}
193
194/*!
195 \qmlproperty list<Transition> StateGroup::transitions
196 This property holds a list of transitions defined by the state group.
197
198 \qml
199 StateGroup {
200 transitions: [
201 Transition { ... },
202 Transition { ... }
203 ...
204 ]
205 }
206 \endqml
207
208 \sa {Transitions}
209*/
210QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty()
211{
212 Q_D(QDeclarativeStateGroup);
213 return QDeclarativeListProperty<QDeclarativeTransition>(this, d->transitions);
214}
215
216/*!
217 \qmlproperty string StateGroup::state
218
219 This property holds the name of the current state of the state group.
220
221 This property is often used in scripts to change between states. For
222 example:
223
224 \qml
225 function toggle() {
226 if (button.state == 'On')
227 button.state = 'Off';
228 else
229 button.state = 'On';
230 }
231 \endqml
232
233 If the state group is in its base state (i.e. no explicit state has been
234 set), \c state will be a blank string. Likewise, you can return a
235 state group to its base state by setting its current state to \c ''.
236
237 \sa {qmlstates}{States}
238*/
239QString QDeclarativeStateGroup::state() const
240{
241 Q_D(const QDeclarativeStateGroup);
242 return d->currentState;
243}
244
245void QDeclarativeStateGroup::setState(const QString &state)
246{
247 Q_D(QDeclarativeStateGroup);
248 if (d->currentState == state)
249 return;
250
251 d->setCurrentStateInternal(state);
252}
253
254void QDeclarativeStateGroup::classBegin()
255{
256 Q_D(QDeclarativeStateGroup);
257 d->componentComplete = false;
258}
259
260void QDeclarativeStateGroup::componentComplete()
261{
262 Q_D(QDeclarativeStateGroup);
263 d->componentComplete = true;
264
265 for (int ii = 0; ii < d->states.count(); ++ii) {
266 QDeclarativeState *state = d->states.at(ii);
267 if (!state->isNamed())
268 state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
269 }
270
271 if (d->updateAutoState()) {
272 return;
273 } else if (!d->currentState.isEmpty()) {
274 QString cs = d->currentState;
275 d->currentState.clear();
276 d->setCurrentStateInternal(cs, true);
277 }
278}
279
280/*!
281 Returns true if the state was changed, otherwise false.
282*/
283bool QDeclarativeStateGroup::updateAutoState()
284{
285 Q_D(QDeclarativeStateGroup);
286 return d->updateAutoState();
287}
288
289bool QDeclarativeStateGroupPrivate::updateAutoState()
290{
291 Q_Q(QDeclarativeStateGroup);
292 if (!componentComplete)
293 return false;
294
295 bool revert = false;
296 for (int ii = 0; ii < states.count(); ++ii) {
297 QDeclarativeState *state = states.at(ii);
298 if (state->isWhenKnown()) {
299 if (state->isNamed()) {
300 if (state->when() && state->when()->evaluate().toBool()) {
301 if (stateChangeDebug())
302 qWarning() << "Setting auto state due to:"
303 << state->when()->expression();
304 if (currentState != state->name()) {
305 q->setState(state->name());
306 return true;
307 } else {
308 return false;
309 }
310 } else if (state->name() == currentState) {
311 revert = true;
312 }
313 }
314 }
315 }
316 if (revert) {
317 bool rv = !currentState.isEmpty();
318 q->setState(QString());
319 return rv;
320 } else {
321 return false;
322 }
323}
324
325QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to)
326{
327 QDeclarativeTransition *highest = 0;
328 int score = 0;
329 bool reversed = false;
330 bool done = false;
331
332 for (int ii = 0; !done && ii < transitions.count(); ++ii) {
333 QDeclarativeTransition *t = transitions.at(ii);
334 for (int ii = 0; ii < 2; ++ii)
335 {
336 if (ii && (!t->reversible() ||
337 (t->fromState() == QLatin1String("*") &&
338 t->toState() == QLatin1String("*"))))
339 break;
340 QStringList fromState;
341 QStringList toState;
342
343 fromState = t->fromState().split(QLatin1Char(','));
344 toState = t->toState().split(QLatin1Char(','));
345 if (ii == 1)
346 qSwap(fromState, toState);
347 int tScore = 0;
348 if (fromState.contains(from))
349 tScore += 2;
350 else if (fromState.contains(QLatin1String("*")))
351 tScore += 1;
352 else
353 continue;
354
355 if (toState.contains(to))
356 tScore += 2;
357 else if (toState.contains(QLatin1String("*")))
358 tScore += 1;
359 else
360 continue;
361
362 if (ii == 1)
363 reversed = true;
364 else
365 reversed = false;
366
367 if (tScore == 4) {
368 highest = t;
369 done = true;
370 break;
371 } else if (tScore > score) {
372 score = tScore;
373 highest = t;
374 }
375 }
376 }
377
378 if (highest)
379 highest->setReversed(reversed);
380
381 return highest;
382}
383
384void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state,
385 bool ignoreTrans)
386{
387 Q_Q(QDeclarativeStateGroup);
388 if (!componentComplete) {
389 currentState = state;
390 return;
391 }
392
393 if (applyingState) {
394 qmlInfo(q) << "Can't apply a state change as part of a state definition.";
395 return;
396 }
397
398 applyingState = true;
399
400 QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state);
401 if (stateChangeDebug()) {
402 qWarning() << this << "Changing state. From" << currentState << ". To" << state;
403 if (transition)
404 qWarning() << " using transition" << transition->fromState()
405 << transition->toState();
406 }
407
408 QDeclarativeState *oldState = 0;
409 if (!currentState.isEmpty()) {
410 for (int ii = 0; ii < states.count(); ++ii) {
411 if (states.at(ii)->name() == currentState) {
412 oldState = states.at(ii);
413 break;
414 }
415 }
416 }
417
418 currentState = state;
419 emit q->stateChanged(currentState);
420
421 QDeclarativeState *newState = 0;
422 for (int ii = 0; ii < states.count(); ++ii) {
423 if (states.at(ii)->name() == currentState) {
424 newState = states.at(ii);
425 break;
426 }
427 }
428
429 if (oldState == 0 || newState == 0) {
430 if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); }
431 if (!oldState) oldState = nullState;
432 if (!newState) newState = nullState;
433 }
434
435 newState->apply(q, transition, oldState);
436 applyingState = false;
437 if (!transition)
438 static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(newState))->complete();
439}
440
441QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const
442{
443 Q_D(const QDeclarativeStateGroup);
444 for (int i = 0; i < d->states.count(); ++i) {
445 QDeclarativeState *state = d->states.at(i);
446 if (state->name() == name)
447 return state;
448 }
449
450 return 0;
451}
452
453void QDeclarativeStateGroup::removeState(QDeclarativeState *state)
454{
455 Q_D(QDeclarativeStateGroup);
456 d->states.removeOne(state);
457}
458
459QT_END_NAMESPACE
460
461
Note: See TracBrowser for help on using the repository browser.