~ubuntu-branches/ubuntu/wily/ubuntu-ui-toolkit/wily-proposed

« back to all changes in this revision

Viewing changes to src/Ubuntu/Components/plugin/statesaverbackend_p.cpp

  • Committer: Package Import Robot
  • Author(s): CI Train Bot, Florian Boucault, Loïc Molinari, Benjamin Zeller, Richard Huddie, Tim Peeters, Zsombor Egri, Timo Jyrinki, Christian Dywan, Albert Astals Cid
  • Date: 2015-07-30 13:04:18 UTC
  • mfrom: (1.1.126)
  • Revision ID: package-import@ubuntu.com-20150730130418-nxjr780u6wqcdqeh
Tags: 1.3.1584+15.10.20150730-0ubuntu1
[ Florian Boucault ]
* New BottomEdgeHint component to represent extra features available from the 
  bottom edge of an application.

[ Loïc Molinari ]
* [UbuntuShape] Added a big radius.
* [UbuntuShape] Added relative radius support. Fixes LP: #1478124.
* Ensured components, styles, examples and tests use the new UbuntuShape 
  properties (not deprecated). Fixes LP: #1437412.

[ Benjamin Zeller ]
* Make use of the official qt build macros to blend into the Qt buildprocess.
* Fix debug builds, optimization is always enabled by the system qt build.

[ Richard Huddie ]
* Fix for autopilot bug lp:1476715. Don't throw an exception if maliit-server
  is not found. Fixes LP: #1476715.

[ Tim Peeters ]
* Clean up the MainView docs.
* Set theme version for Sections component.
* Implement AdaptivePageLayout.

[ Zsombor Egri ]
* Fix width for trailing and leading actions of a ListItem. Fixes LP: #1465582.
* Button and Haptics import wrong toolkit versions, thus they break style 
  versioning. Moving Icon and ProgressBar to 1.0 and 1.1 version source folder.
* SuruDark theme for ListItem style. Fixes LP: #1451225.
* Swiping ListItem when no actions are defined for the gesture breaks 
  selectMode. Fixes LP: #1468100.
* Fixing selected connection with the ListItem's select mode checkbox state. 
  Fixes LP: #1461501, LP: #1469471.

[ Timo Jyrinki ]
* Fix ucstylehints.cpp compilation with Qt 5.5. Fixes LP: #1473873.
* Add PageHeadStyle 1.3 reference to fix install_plugins_qmltypes failure with 
  Qt 5.5. Fixes LP: #1466484.

[ Christian Dywan ]
* Avoid hard-coded skipping of members by name "type" can be a property name 
  regardless of also being a field in the JSON description of a property.
* Add apicheck unit test for QML and Javascript.
* Remove "do cleanup" comments. Fixes LP: #1369874.
* Initialize defaultTypes later to avoid bogus types.
* Implement Action.shortcut property. Fixes LP: #1202464.
* Update text handler to 3gu assert.
* Add a deprecated note to ListItems.ThinDivider. Fixes LP: #1470951.
* Don't include overridden properties in API.
* Clean-up API check wrapper scripts.
* Track version members were introduced.
* Implement ListItemPopover on right-click. Fixes LP: #1452676.
* Move delegate's chevron into the row and size it explicitly. 
  Fixes LP: #1474418.
* Enable (Shift)Tab via activeFocusOnTab. Fixes LP: #1276797.
* Only swipe with left button and block timer otherwise. 
  Fixes LP: #1476300, LP: #1476310.
* Include Javascript libraries in QML documentation. Fixes LP: #1466058.

[ Albert Astals Cid ]
* Fix warning if there's no __propagated
* TypeError: Cannot call method 'hasOwnProperty' of null.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2013 Canonical Ltd.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU Lesser General Public License as published by
 
6
 * the Free Software Foundation; version 3.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Author: Zsombor Egri <zsombor.egri@canonical.com>
 
17
 */
 
18
 
 
19
#include "statesaverbackend_p.h"
 
20
#include "ucapplication.h"
 
21
#include <QtQml/QQmlContext>
 
22
#include <QtQml/QQmlProperty>
 
23
#include <QtQml/qqmlinfo.h>
 
24
#include <QtQml/qqml.h>
 
25
#include <QtCore/QCoreApplication>
 
26
#include <QtCore/QFile>
 
27
#include <QtCore/QStringList>
 
28
#include "i18n.h"
 
29
#include "quickutils.h"
 
30
#include <QtCore/QStandardPaths>
 
31
 
 
32
#include "unixsignalhandler_p.h"
 
33
 
 
34
StateSaverBackend::StateSaverBackend(QObject *parent)
 
35
    : QObject(parent)
 
36
    , m_archive(0)
 
37
    , m_globalEnabled(true)
 
38
{
 
39
    // connect to application quit signal so when that is called, we can clean the states saved
 
40
    QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
 
41
                     this, &StateSaverBackend::cleanup);
 
42
    QObject::connect(&QuickUtils::instance(), &QuickUtils::activated,
 
43
                     this, &StateSaverBackend::reset);
 
44
    QObject::connect(&QuickUtils::instance(), &QuickUtils::deactivated,
 
45
                     this, &StateSaverBackend::initiateStateSaving);
 
46
    // catch eventual app name changes so we can have different path for the states if needed
 
47
    QObject::connect(&UCApplication::instance(), &UCApplication::applicationNameChanged,
 
48
                     this, &StateSaverBackend::initialize);
 
49
    if (!UCApplication::instance().applicationName().isEmpty()) {
 
50
        initialize();
 
51
    }
 
52
 
 
53
    UnixSignalHandler::instance().connectSignal(UnixSignalHandler::Terminate);
 
54
    UnixSignalHandler::instance().connectSignal(UnixSignalHandler::Interrupt);
 
55
    QObject::connect(&UnixSignalHandler::instance(), SIGNAL(signalTriggered(int)),
 
56
                     this, SLOT(signalHandler(int)));
 
57
}
 
58
 
 
59
StateSaverBackend::~StateSaverBackend()
 
60
{
 
61
    if (m_archive) {
 
62
        delete m_archive;
 
63
    }
 
64
}
 
65
 
 
66
void StateSaverBackend::initialize()
 
67
{
 
68
    if (m_archive) {
 
69
        // delete previous archive
 
70
        QFile archiveFile(m_archive.data()->fileName());
 
71
        archiveFile.remove();
 
72
        delete m_archive.data();
 
73
        m_archive.clear();
 
74
    }
 
75
    QString applicationName(UCApplication::instance().applicationName());
 
76
    if (applicationName.isEmpty()) {
 
77
        qCritical() << "[StateSaver] Cannot create appstate file, application name not defined.";
 
78
        return;
 
79
    }
 
80
    // make sure the path is in sync with https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement
 
81
    // the file must be saved under XDG_RUNTIME_DIR/<APP_PKGNAME> path.
 
82
    // NOTE!!: we cannot use QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)
 
83
    // as that is going to perform a chmod +w on the path, see bug #1359831. Therefore we must
 
84
    // fetch the XDG_RUNTIME_DIR either from QStandardPaths::standardLocations() or from env var
 
85
    // see bug https://bugreports.qt-project.org/browse/QTBUG-41735
 
86
    QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
 
87
    if (runtimeDir.isEmpty()) {
 
88
        runtimeDir = qgetenv("XDG_RUNTIME_DIR");
 
89
    }
 
90
    if (runtimeDir.isEmpty()) {
 
91
        qCritical() << "[StateSaver] No XDG_RUNTIME_DIR path set, cannot create appstate file.";
 
92
        return;
 
93
    }
 
94
    m_archive = new QSettings(QString("%1/%2/statesaver.appstate").
 
95
                              arg(runtimeDir).
 
96
                              arg(applicationName), QSettings::NativeFormat);
 
97
    m_archive->setFallbacksEnabled(false);
 
98
}
 
99
 
 
100
void StateSaverBackend::cleanup()
 
101
{
 
102
    reset();
 
103
    m_archive.clear();
 
104
}
 
105
 
 
106
void StateSaverBackend::signalHandler(int type)
 
107
{
 
108
    if (type == UnixSignalHandler::Interrupt) {
 
109
        Q_EMIT initiateStateSaving();
 
110
        // disconnect aboutToQuit() so the state file doesn't get wiped upon quit
 
111
        QObject::disconnect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
 
112
                         this, &StateSaverBackend::cleanup);
 
113
    }
 
114
    QCoreApplication::quit();
 
115
}
 
116
 
 
117
bool StateSaverBackend::enabled() const
 
118
{
 
119
    return m_globalEnabled;
 
120
}
 
121
void StateSaverBackend::setEnabled(bool enabled)
 
122
{
 
123
    if (m_globalEnabled != enabled) {
 
124
        m_globalEnabled = enabled;
 
125
        Q_EMIT enabledChanged(m_globalEnabled);
 
126
        if (!m_globalEnabled) {
 
127
            reset();
 
128
        }
 
129
    }
 
130
}
 
131
 
 
132
bool StateSaverBackend::registerId(const QString &id)
 
133
{
 
134
    if (m_register.contains(id)) {
 
135
        return false;
 
136
    }
 
137
    m_register.insert(id);
 
138
    return true;
 
139
}
 
140
 
 
141
void StateSaverBackend::removeId(const QString &id)
 
142
{
 
143
    m_register.remove(id);
 
144
}
 
145
 
 
146
int StateSaverBackend::load(const QString &id, QObject *item, const QStringList &properties)
 
147
{
 
148
    if (m_archive.isNull()) {
 
149
        return 0;
 
150
    }
 
151
 
 
152
    int result = 0;
 
153
    // save the previous group
 
154
    bool restorePreviousGroup = !m_archive->group().isEmpty();
 
155
    if (restorePreviousGroup) {
 
156
        m_groupStack.push(m_archive->group());
 
157
        // leave the group so we can read the next one
 
158
        m_archive->endGroup();
 
159
    }
 
160
    m_archive.data()->beginGroup(id);
 
161
    QStringList propertyNames = m_archive.data()->childKeys();
 
162
    Q_FOREACH(const QString &propertyName, propertyNames) {
 
163
        QVariant value = m_archive.data()->value(propertyName);
 
164
        if (!properties.contains(propertyName)) {
 
165
            // skip the property
 
166
            continue;
 
167
        }
 
168
        QQmlProperty qmlProperty(item, propertyName.toLocal8Bit().constData(), qmlContext(item));
 
169
        if (qmlProperty.isValid() && qmlProperty.isWritable()) {
 
170
            QVariant type = m_archive.data()->value(propertyName + "_TYPE");
 
171
            value.convert(type.toInt());
 
172
            bool writeSuccess = qmlProperty.write(value);
 
173
            if (writeSuccess) {
 
174
                result++;
 
175
            } else {
 
176
                qmlInfo(item) << UbuntuI18n::instance().tr("property \"%1\" of "
 
177
                    "object %2 has type %3 and cannot be set to value \"%4\" of"
 
178
                    " type %5").arg(propertyName)
 
179
                               .arg(qmlContext(item)->nameForObject(item))
 
180
                               .arg(qmlProperty.propertyTypeName())
 
181
                               .arg(value.toString())
 
182
                               .arg(value.typeName());
 
183
            }
 
184
        } else {
 
185
            qmlInfo(item) << UbuntuI18n::instance().tr("property \"%1\" does not exist or is not writable for object %2")
 
186
                             .arg(propertyName).arg(qmlContext(item)->nameForObject(item));
 
187
        }
 
188
    }
 
189
    // drop cache once properties are successfully restored
 
190
    m_archive.data()->remove("");
 
191
    m_archive.data()->endGroup();
 
192
    // restore leaved group if needed
 
193
    if (restorePreviousGroup) {
 
194
        m_archive->beginGroup(m_groupStack.pop());
 
195
    }
 
196
    return result;
 
197
}
 
198
 
 
199
int StateSaverBackend::save(const QString &id, QObject *item, const QStringList &properties)
 
200
{
 
201
    if (m_archive.isNull()) {
 
202
        return 0;
 
203
    }
 
204
    m_archive.data()->beginGroup(id);
 
205
    int result = 0;
 
206
    Q_FOREACH(const QString &propertyName, properties) {
 
207
        QQmlProperty qmlProperty(item, propertyName.toLocal8Bit().constData());
 
208
        if (qmlProperty.isValid()) {
 
209
            QVariant value = qmlProperty.read();
 
210
            if (static_cast<QMetaType::Type>(value.type()) != QMetaType::QObjectStar) {
 
211
                if (value.userType() == qMetaTypeId<QJSValue>()) {
 
212
                    value = value.value<QJSValue>().toVariant();
 
213
                }
 
214
                m_archive.data()->setValue(propertyName, value);
 
215
                /* Save the type of the property along with its value.
 
216
                 * This is important because QSettings deserializes values as QString.
 
217
                 * Setting these strings to QML properties usually works because the
 
218
                 * implicit type conversion from string to the type of the QML property
 
219
                 * usually works. In some cases cases however (e.g. enum) it fails.
 
220
                 *
 
221
                 * See Qt Bug: https://bugreports.qt-project.org/browse/QTBUG-40474
 
222
                 */
 
223
                m_archive.data()->setValue(propertyName + "_TYPE", QVariant::fromValue((int)value.type()));
 
224
                result++;
 
225
            }
 
226
        }
 
227
    }
 
228
    m_archive.data()->endGroup();
 
229
    m_archive.data()->sync();
 
230
    return result;
 
231
}
 
232
 
 
233
/*
 
234
 * The method resets the register and the state archive for the application.
 
235
 */
 
236
bool StateSaverBackend::reset()
 
237
{
 
238
    m_register.clear();
 
239
    if (m_archive) {
 
240
        QFile archiveFile(m_archive.data()->fileName());
 
241
        return archiveFile.remove();
 
242
    }
 
243
    return true;
 
244
}
 
245
 
 
246