Browse Source

added first basic datasync viewmodel (no gui yet)

pull/2/head
Skycoder42 7 years ago
parent
commit
de7843b484
  1. 244
      examples/mvvmcore/SampleCore/samplecore_de.ts
  2. 36
      src/mvvmcore/coreapp.cpp
  3. 36
      src/mvvmcore/message.cpp
  4. 26
      src/mvvmcore/message.h
  5. 10
      src/mvvmcore/settingsviewmodel.cpp
  6. 131
      src/mvvmdatasynccore/accountmodel.cpp
  7. 51
      src/mvvmdatasynccore/accountmodel.h
  8. 20
      src/mvvmdatasynccore/accountmodel_p.h
  9. 11
      src/mvvmdatasynccore/application-x-datasync-account-data.xml
  10. 422
      src/mvvmdatasynccore/datasyncviewmodel.cpp
  11. 86
      src/mvvmdatasynccore/datasyncviewmodel.h
  12. 32
      src/mvvmdatasynccore/datasyncviewmodel_p.h
  13. 82
      src/mvvmdatasynccore/exportsetupviewmodel.cpp
  14. 53
      src/mvvmdatasynccore/exportsetupviewmodel_p.h
  15. 43
      src/mvvmdatasynccore/mvvmdatasynccore.pro
  16. 14
      src/mvvmdatasynccore/qpmx.json
  17. 12
      src/mvvmdatasynccore/qtmvvmdatasynccore_global.h
  18. 4
      src/mvvmdatasynccore/translations/qtmvvmdatasynccore_de.ts
  19. 4
      src/mvvmdatasynccore/translations/qtmvvmdatasynccore_template.ts
  20. 4
      src/mvvmquick/quickpresenter.cpp
  21. 2
      src/mvvmwidgets/inputwidgetfactory.cpp
  22. 6
      src/mvvmwidgets/widgetspresenter.cpp
  23. 4
      src/src.pro
  24. 3
      sync.profile

244
examples/mvvmcore/SampleCore/samplecore_de.ts

@ -0,0 +1,244 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>DrawerViewModel</name>
<message>
<location filename="drawerviewmodel.cpp" line="+11"/>
<source>Main Sample</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Tab Sample</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SampleViewModel</name>
<message>
<location filename="sampleviewmodel.cpp" line="+81"/>
<source>Random input</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a number:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+12"/>
<source>Open Files:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+12"/>
<source>Clear Eventlist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Do you really want to clear the eventlist?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+9"/>
<source>QtMvvm sample application</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
<source>BSD 3 Clause</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TabItemViewModel</name>
<message>
<location filename="tabviewmodel.cpp" line="+28"/>
<source>No Title</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TabViewModel</name>
<message>
<location line="-13"/>
<source>New Tab</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>Enter a tab title:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>qtmvvm_settings_xml</name>
<message>
<location filename=".ts-dummy/settings.tsdummy.cpp" line="+4"/>
<source>property</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Text 1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value C</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Another Section</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value B</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a nice name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value B+C</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Variant A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>This is a tooltip</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Variant C</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value A+B</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Sub-Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>&amp;Check me</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a &amp;value</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>The value must be between 0 and 1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Open &amp;system settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Another main category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value A+C</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Variant B</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Choose a &amp;font</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>This is another section</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>bool</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Select a &amp;mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>I am a checkbox!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Value A+B+C</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a &amp;number</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>You can use this to trigger whatever kind of action you need</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Text 2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a &amp;website</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Enter a &amp;name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>https://example.org/test</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

36
src/mvvmcore/coreapp.cpp

@ -104,20 +104,20 @@ bool CoreApp::autoParse(QCommandLineParser &parser, const QStringList &arguments
if(parser.parse(arguments)) {
if(parser.isSet(QStringLiteral("help"))) {
MessageConfig helpConfig {MessageConfig::TypeMessageBox, MessageConfig::SubTypeQuestion};
helpConfig.setTitle(tr("Help"));
helpConfig.setText(parser.helpText());
helpConfig.setButtons(MessageConfig::Ok);
helpConfig.setTitle(tr("Help"))
.setText(parser.helpText())
.setButtons(MessageConfig::Ok);
auto res = showDialog(helpConfig);
connect(res, &MessageResult::dialogDone,
qApp, &QCoreApplication::quit);
return false;
} else if(parser.isSet(QStringLiteral("version"))) {
MessageConfig versionConfig {MessageConfig::TypeMessageBox, MessageConfig::SubTypeInformation};
versionConfig.setTitle(tr("Application Version"));
versionConfig.setText(QGuiApplication::applicationDisplayName() +
QLatin1Char(' ') +
QCoreApplication::applicationVersion());
versionConfig.setButtons(MessageConfig::Ok);
versionConfig.setTitle(tr("Application Version"))
.setText(QGuiApplication::applicationDisplayName() +
QLatin1Char(' ') +
QCoreApplication::applicationVersion())
.setButtons(MessageConfig::Ok);
auto res = showDialog(versionConfig);
connect(res, &MessageResult::dialogDone,
qApp, &QCoreApplication::quit);
@ -126,9 +126,9 @@ bool CoreApp::autoParse(QCommandLineParser &parser, const QStringList &arguments
return true;
} else {
MessageConfig errorConfig {MessageConfig::TypeMessageBox, MessageConfig::SubTypeCritical};
errorConfig.setTitle(tr("Invalid Arguments"));
errorConfig.setText(parser.errorText());
errorConfig.setButtons(MessageConfig::Ok);
errorConfig.setTitle(tr("Invalid Arguments"))
.setText(parser.errorText())
.setButtons(MessageConfig::Ok);
auto res = showDialog(errorConfig);
connect(res, &MessageResult::dialogDone,
qApp, [](){
@ -197,11 +197,21 @@ void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariant
void CoreAppPrivate::showDialog(const MessageConfig &config, MessageResult *result)
{
if(presenter)
presenter->showDialog(config, result);
if(presenter) {
try {
presenter->showDialog(config, result);
} catch(QException &e) {
logCritical() << "Failed to show dialog ff type"
<< config.type() << ":" << config.subType()
<< "with error:"
<< e.what();
result->complete(MessageConfig::NoButton);
}
}
else {
logCritical() << "Failed to show dialog ff type"
<< config.type() << ":" << config.subType()
<< "- no presenter was set";
result->complete(MessageConfig::NoButton);
}
}

36
src/mvvmcore/message.cpp

@ -82,57 +82,67 @@ QVariantMap MessageConfig::viewProperties() const
return d->editProperties;
}
void MessageConfig::setType(const QByteArray &type)
MessageConfig &MessageConfig::setType(const QByteArray &type)
{
d->type = type;
return (*this);
}
void MessageConfig::setSubType(const QByteArray &subType)
MessageConfig &MessageConfig::setSubType(const QByteArray &subType)
{
d->subType = subType;
return (*this);
}
void MessageConfig::setTitle(const QString &title)
MessageConfig &MessageConfig::setTitle(const QString &title)
{
d->title = title;
return (*this);
}
void MessageConfig::setText(const QString &text)
MessageConfig &MessageConfig::setText(const QString &text)
{
d->text = text;
return (*this);
}
void MessageConfig::setButtons(StandardButtons buttons)
MessageConfig &MessageConfig::setButtons(StandardButtons buttons)
{
d->buttons = buttons;
return (*this);
}
void MessageConfig::setButtonTexts(const QHash<StandardButton, QString> &buttonTexts)
MessageConfig &MessageConfig::setButtonTexts(const QHash<StandardButton, QString> &buttonTexts)
{
d->buttonTexts = buttonTexts;
return (*this);
}
void MessageConfig::setButtonText(MessageConfig::StandardButton button, const QString &text)
MessageConfig &MessageConfig::setButtonText(MessageConfig::StandardButton button, const QString &text)
{
d->buttonTexts.insert(button, text);
return (*this);
}
void MessageConfig::setDefaultValue(const QVariant &defaultValue)
MessageConfig &MessageConfig::setDefaultValue(const QVariant &defaultValue)
{
d->defaultValue = defaultValue;
return (*this);
}
void MessageConfig::setViewProperties(const QVariantMap &editProperties)
MessageConfig &MessageConfig::setViewProperties(const QVariantMap &editProperties)
{
d->editProperties = editProperties;
return (*this);
}
void MessageConfig::setViewProperty(const QString &key, const QVariant &value)
MessageConfig &MessageConfig::setViewProperty(const QString &key, const QVariant &value)
{
d->editProperties.insert(key, value);
return (*this);
}
void MessageConfig::resetSubType()
MessageConfig &MessageConfig::resetSubType()
{
if(d->type == TypeMessageBox)
d->subType = SubTypeInformation;
@ -140,9 +150,10 @@ void MessageConfig::resetSubType()
d->subType = QMetaType::typeName(QMetaType::QString);
else
d->subType.clear();
return (*this);
}
void MessageConfig::resetButtons()
MessageConfig &MessageConfig::resetButtons()
{
if(d->type == TypeMessageBox) {
if(d->subType == SubTypeQuestion)
@ -157,6 +168,7 @@ void MessageConfig::resetButtons()
d->buttons = Ok;
d->buttonTexts.clear();
return (*this);
}
QVariantMap MessageConfig::buttonTextsMap() const

26
src/mvvmcore/message.h

@ -87,19 +87,19 @@ public:
QVariant defaultValue() const;
QVariantMap viewProperties() const;
void setType(const QByteArray &type);
void setSubType(const QByteArray &subType);
void setTitle(const QString &title);
void setText(const QString &text);
void setButtons(StandardButtons buttons);
void setButtonTexts(const QHash<StandardButton, QString> &buttonTexts);
void setButtonText(StandardButton button, const QString &text);
void setDefaultValue(const QVariant &defaultValue);
void setViewProperties(const QVariantMap &viewProperties);
void setViewProperty(const QString &key, const QVariant &value);
void resetSubType();
void resetButtons();
MessageConfig &setType(const QByteArray &type);
MessageConfig &setSubType(const QByteArray &subType);
MessageConfig &setTitle(const QString &title);
MessageConfig &setText(const QString &text);
MessageConfig &setButtons(StandardButtons buttons);
MessageConfig &setButtonTexts(const QHash<StandardButton, QString> &buttonTexts);
MessageConfig &setButtonText(StandardButton button, const QString &text);
MessageConfig &setDefaultValue(const QVariant &defaultValue);
MessageConfig &setViewProperties(const QVariantMap &viewProperties);
MessageConfig &setViewProperty(const QString &key, const QVariant &value);
MessageConfig &resetSubType();
MessageConfig &resetButtons();
private:
QSharedDataPointer<MessageConfigPrivate> d;

10
src/mvvmcore/settingsviewmodel.cpp

@ -31,12 +31,10 @@ bool SettingsViewModel::canRestoreDefaults() const
MessageConfig SettingsViewModel::restoreConfig() const
{
MessageConfig config;
config.setType(MessageConfig::TypeMessageBox);
config.setSubType(MessageConfig::SubTypeWarning);
config.setTitle(tr("Restore Defaults?"));
config.setText(tr("All custom changes will be deleted and the defaults restored. <i>This cannot be undone!</i>"));
config.setButtons(MessageConfig::Yes | MessageConfig::No);
MessageConfig config {MessageConfig::TypeMessageBox, MessageConfig::SubTypeWarning};
config.setTitle(tr("Restore Defaults?"))
.setText(tr("All custom changes will be deleted and the defaults restored. <i>This cannot be undone!</i>"))
.setButtons(MessageConfig::Yes | MessageConfig::No);
return config;
}

131
src/mvvmdatasynccore/accountmodel.cpp

@ -0,0 +1,131 @@
#include "accountmodel.h"
#include "accountmodel_p.h"
#include <QRemoteObjectReplica>
using namespace QtMvvm;
using namespace QtDataSync;
AccountModel::AccountModel(QObject *parent) :
QAbstractListModel(parent),
d(new AccountModelPrivate())
{}
AccountModel::~AccountModel() {}
void AccountModel::setup(AccountManager *manager)
{
beginResetModel();
d->devices.clear();
if(d->manager)
d->manager->disconnect(this);
d->manager = manager;
endResetModel();
connect(d->manager, &AccountManager::accountDevices,
this, &AccountModel::accountDevices);
connect(d->manager, &AccountManager::importAccepted,
this, &AccountModel::reload);
connect(d->manager, &AccountManager::accountAccessGranted,
this, &AccountModel::reload);
if(d->manager->replica()->isInitialized())
d->manager->listDevices();
else {
connect(d->manager->replica(), &QRemoteObjectReplica::initialized,
d->manager, &AccountManager::listDevices);
}
}
QVariant AccountModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation != Qt::Horizontal)
return {};
switch (section) {
case 0:
switch(role) {
case NameRole:
return tr("Name");
case FingerPrintRole:
return tr("Fingerprint");
default:
break;
}
break;
case 1:
if(role == Qt::DisplayRole)
return tr("Fingerprint");
break;
default:
break;
}
return {};
}
int AccountModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
else
return d->devices.size();
}
QVariant AccountModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
switch (index.column()) {
case 0:
switch(role) {
case NameRole:
return d->devices.value(index.row()).name();
case FingerPrintRole:
return d->devices.value(index.row()).fingerprint();
default:
break;
}
break;
case 1:
if(role == Qt::DisplayRole)
return d->devices.value(index.row()).fingerprint();
break;
default:
break;
}
return {};
}
bool AccountModel::removeDevice(const QModelIndex &index)
{
if (!index.isValid())
return false;
else {
d->manager->removeDevice(d->devices.value(index.row()));
return true;
}
}
void AccountModel::reload()
{
if(d->manager)
d->manager->listDevices();
}
void AccountModel::accountDevices(const QList<DeviceInfo> &devices)
{
beginResetModel();
d->devices = devices;
endResetModel();
}
// ------------- Private Implementation -------------
AccountModelPrivate::AccountModelPrivate() :
manager(nullptr),
devices()
{}

51
src/mvvmdatasynccore/accountmodel.h

@ -0,0 +1,51 @@
#ifndef QTMVVM_ACCOUNTMODEL_H
#define QTMVVM_ACCOUNTMODEL_H
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qscopedpointer.h>
#include <QtDataSync/accountmanager.h>
#include "QtMvvmDataSyncCore/qtmvvmdatasynccore_global.h"
namespace QtMvvm {
class AccountModelPrivate;
class Q_MVVMDATASYNCCORE_EXPORT AccountModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
NameRole = Qt::DisplayRole,
FingerPrintRole = Qt::UserRole + 1
};
Q_ENUM(Roles)
explicit AccountModel(QObject *parent = nullptr);
~AccountModel();
Q_INVOKABLE void setup(QtDataSync::AccountManager *manager);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_INVOKABLE bool removeDevice(const QModelIndex &index);
Q_INVOKABLE inline bool removeDevice(int row) {
return removeDevice(index(row));
}
public Q_SLOTS:
void reload();
private Q_SLOTS:
void accountDevices(const QList<QtDataSync::DeviceInfo> &devices);
private:
QScopedPointer<AccountModelPrivate> d;
};
}
#endif // QTMVVM_ACCOUNTMODEL_H

20
src/mvvmdatasynccore/accountmodel_p.h

@ -0,0 +1,20 @@
#ifndef QTMVVM_ACCOUNTMODEL_P_H
#define QTMVVM_ACCOUNTMODEL_P_H
#include "qtmvvmdatasynccore_global.h"
#include "accountmodel.h"
namespace QtMvvm {
class AccountModelPrivate
{
public:
AccountModelPrivate();
QtDataSync::AccountManager *manager;
QList<QtDataSync::DeviceInfo> devices;
};
}
#endif // QTMVVM_ACCOUNTMODEL_P_H

11
src/mvvmdatasynccore/application-x-datasync-account-data.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-datasync-account-data">
<comment>Account data of a datasync account as file export</comment>
<comment xml:lang="en">Account data of a datasync account as file export</comment>
<comment xml:lang="de">Account-Daten eines Datasync-Accounts als Dateiexport</comment>
<icon name="x-datasync-account-data"/>
<glob-deleteall/>
<glob pattern="*.dsad"/>
</mime-type>
</mime-info>

422
src/mvvmdatasynccore/datasyncviewmodel.cpp

@ -0,0 +1,422 @@
#include "datasyncviewmodel.h"
#include "datasyncviewmodel_p.h"
#include "exportsetupviewmodel_p.h"
#include <QtCore/QStandardPaths>
#include <QtCore/QFile>
#include <QtRemoteObjects/QRemoteObjectNode>
#include <QtMvvmCore/CoreApp>
#include <QtMvvmCore/Messages>
#include <QtDataSync/SetupDoesNotExistException>
#undef logDebug
#undef logInfo
#undef logWarning
#undef logCritical
#include <QtMvvmCore/private/qtmvvm_logging_p.h>
using namespace QtMvvm;
using namespace QtDataSync;
const QString DataSyncViewModel::paramSetup(QStringLiteral("setup"));
const QString DataSyncViewModel::paramReplicaNode(QStringLiteral("node"));
QVariantHash DataSyncViewModel::showParams(const QString &setup)
{
return {
{paramSetup, setup}
};
}
QVariantHash DataSyncViewModel::showParams(QRemoteObjectNode *node)
{
return {
{paramReplicaNode, QVariant::fromValue(node)}
};
}
DataSyncViewModel::DataSyncViewModel(QObject *parent) :
ViewModel(parent),
d(new DataSyncViewModelPrivate(this))
{
resetColorMap();
}
DataSyncViewModel::~DataSyncViewModel() {}
SyncManager *DataSyncViewModel::syncManager() const
{
return d->syncManager;
}
AccountManager *DataSyncViewModel::accountManager() const
{
return d->accountManager;
}
DataSyncViewModel::ColorMap DataSyncViewModel::colorMap() const
{
return d->colorMap;
}
QString DataSyncViewModel::statusString() const
{
if(!d->syncManager)
return {};
auto state = d->syncManager->syncState();
QString baseText = QStringLiteral("<font color=\"%1\">%2</font>")
.arg(d->colorMap.value(state).name());
switch (state) {
case SyncManager::Initializing:
return baseText.arg(tr("Connecting…"));
case SyncManager::Downloading:
return baseText.arg(tr("Downloading…"));
case SyncManager::Uploading:
return baseText.arg(tr("Uploading…"));
case SyncManager::Synchronized:
return baseText.arg(tr("Synchronized"));
case SyncManager::Error:
return baseText.arg(tr("Error!"));
case SyncManager::Disconnected:
return baseText.arg(tr("Disconnected"));
default:
Q_UNREACHABLE();
return QString();
}
}
AccountModel *DataSyncViewModel::accountModel() const
{
return d->accountModel;
}
QString DataSyncViewModel::formatFingerPrint(const QByteArray &fingerPrint)
{
QByteArrayList res;
for(char c : fingerPrint)
res.append(QByteArray(1, c).toHex().toUpper());
return QString::fromUtf8(res.join(':'));
}
void DataSyncViewModel::showDeviceInfo()
{
Q_UNIMPLEMENTED();
}
void DataSyncViewModel::startExport()
{
showForResult<ExportSetupViewModel>(DataSyncViewModelPrivate::ExportRequestCode);
}
void DataSyncViewModel::startImport()
{
auto home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
getOpenFile(this, [this](QUrl url) {
if(url.isValid()) {
QSharedPointer<QIODevice> device;
//TODO add support for android content device
if(url.isLocalFile())
device.reset(new QFile(url.toLocalFile()));
else {
critical(tr("Import failed"), tr("Unsupported URL Scheme: %1").arg(url.scheme()));
return;
}
if(!device->open(QIODevice::ReadOnly | QIODevice::Text)) {
critical(tr("Import failed"),
tr("Failed to open URL \"%1\" with error: %1")
.arg(url.toString())
.arg(device->errorString()));
return;
}
auto data = device->readAll();
device->close();
if(AccountManager::isTrustedImport(data)) {
MessageConfig config{MessageConfig::TypeInputDialog, QMetaType::typeName(QMetaType::QString)};
config.setTitle(tr("Import account data"))
.setText(tr("Enter the password to decrypt the account data. "
"Then choose whether you want to keep you local data or not:"))
.setButtons(MessageConfig::YesToAll | MessageConfig::Yes | MessageConfig::Cancel) //TODO adjust to get ideal placement
.setButtonText(MessageConfig::YesToAll, tr("Reset data"))
.setButtonText(MessageConfig::Yes, tr("Keep data"))
.setViewProperties({
{QStringLiteral("echoMode"), 2} //QLineEdit::Password
});
auto res = CoreApp::showDialog(config);
connect(res, &MessageResult::dialogDone, this, [this, res, data](MessageConfig::StandardButton btn) {
switch (btn) {
case MessageConfig::YesToAll:
d->performImport(true, res->result().toString(), data, false);
break;
case MessageConfig::Yes:
d->performImport(true, res->result().toString(), data, true);
break;
default:
break;
}
});
} else {
MessageConfig config{MessageConfig::TypeMessageBox, MessageConfig::SubTypeQuestion};
config.setTitle(tr("Import account data"))
.setText(tr("Keep the local data after changing the account?"))
.setButtons(MessageConfig::YesToAll | MessageConfig::Yes | MessageConfig::Cancel) //TODO adjust to get ideal placement
.setButtonText(MessageConfig::YesToAll, tr("Reset data"))
.setButtonText(MessageConfig::Yes, tr("Keep data"));
auto res = CoreApp::showDialog(config);
connect(res, &MessageResult::dialogDone, this, [this, res, data](MessageConfig::StandardButton btn) {
switch (btn) {
case MessageConfig::YesToAll:
d->performImport(false, {}, data, false);
break;
case MessageConfig::Yes:
d->performImport(false, {}, data, true);
break;
default:
break;
}
});
}
}
}, tr("Import account data"),
{QStringLiteral("application/x-datasync-account-data"), QStringLiteral("application/octet-stream")},
QUrl::fromLocalFile(home));
}
void DataSyncViewModel::performReset()
{
MessageConfig config {MessageConfig::TypeMessageBox, MessageConfig::SubTypeQuestion};
config.setTitle(tr("Reset Account?"))
.setText(tr("Do you want to reset your account? "
"You will loose the connection to all other devices and get a new identity."
"You can either keep your data or reset it as well."
"This cannot be undone!"))
.setButtons(MessageConfig::YesToAll | MessageConfig::Yes | MessageConfig::Cancel) //TODO adjust to get ideal placement
.setButtonText(MessageConfig::YesToAll, tr("Reset data"))
.setButtonText(MessageConfig::Yes, tr("Keep data"));
auto res = CoreApp::showDialog(config);
connect(res, &MessageResult::dialogDone, this, [this](MessageConfig::StandardButton btn) {
switch (btn) {
case MessageConfig::YesToAll:
d->accountManager->resetAccount(false);
break;
case MessageConfig::Yes:
d->accountManager->resetAccount(true);
break;
default:
break;
}
});
}
void DataSyncViewModel::changeRemote()
{
Q_UNIMPLEMENTED();
}
void DataSyncViewModel::startNetworkExchange()
{
Q_UNIMPLEMENTED();
}
void DataSyncViewModel::setColorMap(DataSyncViewModel::ColorMap colorMap)
{
if (d->colorMap == colorMap)
return;
d->colorMap = colorMap;
emit colorMapChanged(d->colorMap);
}
void DataSyncViewModel::resetColorMap()
{
d->colorMap.clear();
d->colorMap.insert(SyncManager::Initializing, Qt::darkCyan);
d->colorMap.insert(SyncManager::Downloading, Qt::darkCyan);
d->colorMap.insert(SyncManager::Uploading, Qt::darkCyan);
d->colorMap.insert(SyncManager::Synchronized, Qt::darkGreen);
d->colorMap.insert(SyncManager::Error, Qt::darkRed);
d->colorMap.insert(SyncManager::Disconnected, Qt::darkYellow);
emit colorMapChanged(d->colorMap);
}
void DataSyncViewModel::showImportDialog(LoginRequest request)
{
if(request.handled())
return;
question(tr("Login requested!"),
tr("<p>A device wants to log into your account:</p>"
"<p>Name: %1<br/>"
"Fingerprint: %2</p>"
"<p>Do you want accept the request?</p>")
.arg(request.device().name())
.arg(formatFingerPrint(request.device().fingerprint())),
this, [this, request](bool ok) {
auto req = request;
if(!req.handled()) {
if(ok)
req.accept();
else
req.reject();
}
});
}
void DataSyncViewModel::showImportAccepted()
{
information(tr("Import accepted"),
tr("The partner has accepted the import. You are now logged in."));
}
void DataSyncViewModel::showAccessGranted(const QUuid &id)
{
d->pendingGrants.insert(id);
//no need to call list devices, is done by the model
}
void DataSyncViewModel::triggerGranted(const QList<DeviceInfo> &devices)
{
for(auto device : devices) {
if(d->pendingGrants.remove(device.deviceId())) {
information(tr("Account access granted"),
tr("<p>Account access has been granted to device:</p>"
"<p>Name: %1<br/>"
"Fingerprint: %2</p>")
.arg(device.name())
.arg(formatFingerPrint(device.fingerprint())));
}
}
}
void DataSyncViewModel::onInit(const QVariantHash &params)
{
try {
if(params.contains(paramReplicaNode)) {
auto node = params.value(paramReplicaNode).value<QRemoteObjectNode*>();
d->syncManager = new SyncManager(node, this);
d->accountManager = new AccountManager(node, this);
} else {
auto setup = params.value(paramSetup, QtDataSync::DefaultSetup).toString();
d->syncManager = new SyncManager(setup, this);
d->accountManager = new AccountManager(setup, this);
}
//sync manager
connect(d->syncManager, &SyncManager::syncStateChanged,
this, &DataSyncViewModel::statusStringChanged);
connect(d->syncManager, &SyncManager::lastErrorChanged,
this, &DataSyncViewModel::statusStringChanged);
//account manager
connect(d->accountManager, &AccountManager::loginRequested,
this, &DataSyncViewModel::showImportDialog);
connect(d->accountManager, &AccountManager::importAccepted,
this, &DataSyncViewModel::showImportAccepted);
connect(d->accountManager, &AccountManager::accountAccessGranted,
this, &DataSyncViewModel::showAccessGranted);
connect(d->accountManager, &AccountManager::accountDevices,
this, &DataSyncViewModel::triggerGranted);
d->accountModel->setup(d->accountManager);
emit syncManagerChanged(d->syncManager);
emit accountManagerChanged(d->accountManager);
} catch(SetupDoesNotExistException &e) {
logCritical() << "Failed to init DataSyncViewModel with error:"
<< e.what();
}
}
void DataSyncViewModel::onResult(quint32 requestCode, const QVariant &result)
{
switch (requestCode) {
case DataSyncViewModelPrivate::ExportRequestCode:
if(result.isValid()) {
auto res = ExportSetupViewModel::result(result);
d->performExport(std::get<0>(res), std::get<1>(res), std::get<2>(res));
}
break;
default:
break;
}
}
// ------------- Private Implementation -------------
QtMvvm::DataSyncViewModelPrivate::DataSyncViewModelPrivate(DataSyncViewModel *q_ptr) :
q(q_ptr),
syncManager(nullptr),
accountManager(nullptr),
colorMap(),
accountModel(new AccountModel(q_ptr))
{}
void DataSyncViewModelPrivate::performExport(bool trusted, bool includeServer, const QString &password)
{
auto home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
getSaveFile(q, [this, trusted, includeServer, password](QUrl url) {
if(url.isValid()) {
QSharedPointer<QIODevice> device;
//TODO add support for android content device
if(url.isLocalFile())
device.reset(new QFile(url.toLocalFile()));
else {
critical(DataSyncViewModel::tr("Export failed"),
DataSyncViewModel::tr("Unsupported URL Scheme: %1").arg(url.scheme()));
return;
}
if(!device->open(QIODevice::WriteOnly | QIODevice::Text)) {
critical(DataSyncViewModel::tr("Export failed"),
DataSyncViewModel::tr("Failed to open URL \"%1\" with error: %1")
.arg(url.toString())
.arg(device->errorString()));
return;
}
QPointer<DataSyncViewModel> qPtr(q);
auto resFn = [this, qPtr, device](QByteArray data) {
if(!qPtr)
return;
device->write(data);
device->close();
information(DataSyncViewModel::tr("Export completed"),
DataSyncViewModel::tr("Data was successfully exported."));
};
auto errFn = [this, qPtr, device](QString error){
if(!qPtr)
return;
critical(DataSyncViewModel::tr("Export failed"), error);
};
if(trusted)
accountManager->exportAccountTrusted(includeServer, password, resFn, errFn);
else
accountManager->exportAccount(includeServer, resFn, errFn);
}
}, DataSyncViewModel::tr("Export account data"),
{QStringLiteral("application/x-datasync-account-data"), QStringLiteral("application/octet-stream")},
QUrl::fromLocalFile(home));
}
void DataSyncViewModelPrivate::performImport(bool trusted, const QString &password, const QByteArray &data, bool keepData)
{
QPointer<DataSyncViewModel> qPtr(q);
auto resFn = [this, qPtr](bool ok, QString error) {
if(!qPtr)
return;
if(ok) {
information(DataSyncViewModel::tr("Import completed"),
DataSyncViewModel::tr("Data was successfully imported."));
} else
critical(DataSyncViewModel::tr("Import failed"), error);
};
if(trusted)
accountManager->importAccountTrusted(data, password, resFn, keepData);
else
accountManager->importAccount(data, resFn, keepData);
}

86
src/mvvmdatasynccore/datasyncviewmodel.h

@ -0,0 +1,86 @@
#ifndef QTMVVM_DATASYNCVIEWMODEL_H
#define QTMVVM_DATASYNCVIEWMODEL_H
#include <QtCore/qmap.h>
#include <QtCore/qscopedpointer.h>
#include <QtGui/qcolor.h>
#include <QtMvvmCore/viewmodel.h>
#include <QtDataSync/syncmanager.h>
#include <QtDataSync/accountmanager.h>
#include "QtMvvmDataSyncCore/qtmvvmdatasynccore_global.h"
#include "QtMvvmDataSyncCore/accountmodel.h"
namespace QtMvvm {
class DataSyncViewModelPrivate;
class Q_MVVMDATASYNCCORE_EXPORT DataSyncViewModel : public ViewModel
{
Q_OBJECT
Q_PROPERTY(QtDataSync::SyncManager* syncManager READ syncManager NOTIFY syncManagerChanged)
Q_PROPERTY(QtDataSync::AccountManager* accountManager READ accountManager NOTIFY accountManagerChanged)
Q_PROPERTY(ColorMap colorMap READ colorMap WRITE setColorMap RESET resetColorMap NOTIFY colorMapChanged)
Q_PROPERTY(QString statusString READ statusString NOTIFY statusStringChanged)
Q_PROPERTY(QtMvvm::AccountModel* accountModel READ accountModel CONSTANT)
public:
typedef QMap<QtDataSync::SyncManager::SyncState, QColor> ColorMap;
static const QString paramSetup;
static const QString paramReplicaNode;
static QVariantHash showParams(const QString &setup);
static QVariantHash showParams(QRemoteObjectNode *node);
Q_INVOKABLE explicit DataSyncViewModel(QObject *parent = nullptr);
~DataSyncViewModel();
QtDataSync::SyncManager* syncManager() const;
QtDataSync::AccountManager* accountManager() const;
ColorMap colorMap() const;
QString statusString() const;
AccountModel *accountModel() const;
Q_INVOKABLE static QString formatFingerPrint(const QByteArray &fingerPrint);
public Q_SLOTS:
void showDeviceInfo();
void startExport();
void startImport();
void performReset();
void changeRemote();
void startNetworkExchange();
void setColorMap(ColorMap colorMap);
void resetColorMap();
private Q_SLOTS:
void showImportDialog(QtDataSync::LoginRequest request);
void showImportAccepted();
void showAccessGranted(const QUuid &id);
void triggerGranted(const QList<QtDataSync::DeviceInfo> &devices);
Q_SIGNALS:
void syncManagerChanged(QtDataSync::SyncManager* syncManager);
void accountManagerChanged(QtDataSync::AccountManager* accountManager);
void colorMapChanged(ColorMap colorMap);
void statusStringChanged();
protected:
void onInit(const QVariantHash &params) override;
void onResult(quint32 requestCode, const QVariant &result) override;
private:
QScopedPointer<DataSyncViewModelPrivate> d;
};
}
#endif // QTMVVM_DATASYNCVIEWMODEL_H

32
src/mvvmdatasynccore/datasyncviewmodel_p.h

@ -0,0 +1,32 @@
#ifndef QTMVVM_DATASYNCVIEWMODEL_P_H
#define QTMVVM_DATASYNCVIEWMODEL_P_H
#include <QtCore/QSet>
#include "qtmvvmdatasynccore_global.h"
#include "datasyncviewmodel.h"
namespace QtMvvm {
class DataSyncViewModelPrivate
{
public:
static const quint32 ExportRequestCode = 0xb201;
DataSyncViewModelPrivate(DataSyncViewModel *q_ptr);
DataSyncViewModel *q;
QtDataSync::SyncManager *syncManager;
QtDataSync::AccountManager *accountManager;
DataSyncViewModel::ColorMap colorMap;
AccountModel *accountModel;
QSet<QUuid> pendingGrants;
void performExport(bool trusted, bool includeServer, const QString &password);
void performImport(bool trusted, const QString &password, const QByteArray &data, bool keepData);
};
}
#endif // QTMVVM_DATASYNCVIEWMODEL_P_H

82
src/mvvmdatasynccore/exportsetupviewmodel.cpp

@ -0,0 +1,82 @@
#include "exportsetupviewmodel_p.h"
using namespace QtMvvm;
std::tuple<bool, bool, QString> ExportSetupViewModel::result(const QVariant &data)
{
auto map = data.toHash();
return std::make_tuple(map.value(QStringLiteral("trusted")).toBool(),
map.value(QStringLiteral("includeServer")).toBool(),
map.value(QStringLiteral("password")).toString());
}
ExportSetupViewModel::ExportSetupViewModel(QObject *parent) :
ViewModel(parent),
_trusted(false),
_includeServer(false),
_password()
{
connect(this, &ExportSetupViewModel::trustedChanged,
this, &ExportSetupViewModel::validChanged);
connect(this, &ExportSetupViewModel::passwordChanged,
this, &ExportSetupViewModel::validChanged);
}
bool ExportSetupViewModel::trusted() const
{
return _trusted;
}
bool ExportSetupViewModel::includeServer() const
{
return _includeServer;
}
QString ExportSetupViewModel::password() const
{
return _password;
}
bool ExportSetupViewModel::isValid() const
{
return !_trusted || !_password.isEmpty();
}
bool ExportSetupViewModel::completeSetup()
{
if(!isValid())
return false;
QVariantHash hash;
hash[QStringLiteral("trusted")] = _trusted;
hash[QStringLiteral("includeServer")] = _includeServer;
hash[QStringLiteral("password")] = _trusted ? _password : QString();
emit resultReady(hash);
return true;
}
void ExportSetupViewModel::setTrusted(bool trusted)
{
if (_trusted == trusted)
return;
_trusted = trusted;
emit trustedChanged(_trusted);
}
void ExportSetupViewModel::setIncludeServer(bool includeServer)
{
if (_includeServer == includeServer)
return;
_includeServer = includeServer;
emit includeServerChanged(_includeServer);
}
void ExportSetupViewModel::setPassword(QString password)
{
if (_password == password)
return;
_password = password;
emit passwordChanged(_password);
}

53
src/mvvmdatasynccore/exportsetupviewmodel_p.h

@ -0,0 +1,53 @@
#ifndef QTMVVM_EXPORTSETUPVIEWMODEL_P_H
#define QTMVVM_EXPORTSETUPVIEWMODEL_P_H
#include <tuple>
#include <QtMvvmCore/ViewModel>
#include "qtmvvmdatasynccore_global.h"
namespace QtMvvm {
class Q_MVVMDATASYNCCORE_EXPORT ExportSetupViewModel : public ViewModel
{
Q_OBJECT
Q_PROPERTY(bool trusted READ trusted WRITE setTrusted NOTIFY trustedChanged)
Q_PROPERTY(bool includeServer READ includeServer WRITE setIncludeServer NOTIFY includeServerChanged)
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
public:
static std::tuple<bool, bool, QString> result(const QVariant &data);
Q_INVOKABLE explicit ExportSetupViewModel(QObject *parent = nullptr);
bool trusted() const;
bool includeServer() const;
QString password() const;
bool isValid() const;
public Q_SLOTS:
bool completeSetup();
void setTrusted(bool trusted);
void setIncludeServer(bool includeServer);
void setPassword(QString password);
Q_SIGNALS:
void trustedChanged(bool trusted);
void includeServerChanged(bool includeServer);
void passwordChanged(QString password);
void validChanged();
private:
bool _trusted;
QString _password;
bool _includeServer;
};
}
#endif // QTMVVM_EXPORTSETUPVIEWMODEL_P_H

43
src/mvvmdatasynccore/mvvmdatasynccore.pro

@ -0,0 +1,43 @@
TARGET = QtMvvmDataSyncCore
QT = core gui mvvmcore datasync mvvmcore-private
HEADERS += \
qtmvvmdatasynccore_global.h \
datasyncviewmodel.h \
datasyncviewmodel_p.h \
accountmodel.h \
accountmodel_p.h \
exportsetupviewmodel_p.h
SOURCES += \
datasyncviewmodel.cpp \
accountmodel.cpp \
exportsetupviewmodel.cpp
TRANSLATIONS += \
translations/qtmvvmdatasynccore_de.ts \
translations/qtmvvmdatasynccore_template.ts
DISTFILES += $$TRANSLATIONS \
application-x-datasync-account-data.xml
qpmx_ts_target.path = $$[QT_INSTALL_TRANSLATIONS]
qpmx_ts_target.depends += lrelease
load(qt_module)
win32 {
QMAKE_TARGET_PRODUCT = "$$TARGET"
QMAKE_TARGET_COMPANY = "Skycoder42"
QMAKE_TARGET_COPYRIGHT = "Felix Barz"
} else:mac {
QMAKE_TARGET_BUNDLE_PREFIX = "com.skycoder42."
}
!ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.)
else: include($$OUT_PWD/qpmx_generated.pri)
qpmx_ts_target.files -= $$OUT_PWD/$$QPMX_WORKINGDIR/qtmvvmdatasynccore_template.qm
qpmx_ts_target.files += translations/qtmvvmdatasynccore_template.ts

14
src/mvvmdatasynccore/qpmx.json

@ -0,0 +1,14 @@
{
"dependencies": [],
"license": {
"file": "",
"name": ""
},
"prcFile": "",
"priFile": "",
"priIncludes": [
],
"publishers": {
},
"source": false
}

12
src/mvvmdatasynccore/qtmvvmdatasynccore_global.h

@ -0,0 +1,12 @@
#ifndef QTMVVMDATASYNCCORE_GLOBAL_H
#define QTMVVMDATASYNCCORE_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(QT_BUILD_MVVMDATASYNCCORE_LIB)
# define Q_MVVMDATASYNCCORE_EXPORT Q_DECL_EXPORT
#else
# define Q_MVVMDATASYNCCORE_EXPORT Q_DECL_IMPORT
#endif
#endif // QTMVVMDATASYNCCORE_GLOBAL_H

4
src/mvvmdatasynccore/translations/qtmvvmdatasynccore_de.ts

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
</TS>

4
src/mvvmdatasynccore/translations/qtmvvmdatasynccore_template.ts

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
</TS>

4
src/mvvmquick/quickpresenter.cpp

@ -64,7 +64,7 @@ void QuickPresenter::present(QtMvvm::ViewModel *viewModel, const QVariantHash &p
Q_ARG(QtMvvm::ViewModel*, viewModel),
Q_ARG(QVariantHash, params),
Q_ARG(QUrl, url),
Q_ARG(QPointer<QtMvvm::ViewModel>, parent));
Q_ARG(QPointer<QtMvvm::ViewModel>, parent)); //TODO invoke with result?
} else
throw PresenterException("QML presenter not ready - cannot present yet");
}
@ -74,7 +74,7 @@ void QuickPresenter::showDialog(const QtMvvm::MessageConfig &config, QtMvvm::Mes
if(d->qmlPresenter) {
QMetaObject::invokeMethod(d->qmlPresenter, "showDialog",
Q_ARG(QtMvvm::MessageConfig, config),
Q_ARG(QtMvvm::MessageResult*, result));
Q_ARG(QtMvvm::MessageResult*, result)); //TODO invoke with result?
} else
throw PresenterException("QML presenter not ready - cannot present yet");
}

2
src/mvvmwidgets/inputwidgetfactory.cpp

@ -66,7 +66,7 @@ QWidget *InputWidgetFactory::createInput(const QByteArray &type, QWidget *parent
widget = new SelectComboBox(parent);
else {
logCritical() << "Failed to find any input view for input type:" << type;
return nullptr;
return nullptr; //TODO throw?
}
for(auto it = viewProperties.constBegin(); it != viewProperties.constEnd(); it++)

6
src/mvvmwidgets/widgetspresenter.cpp

@ -288,8 +288,10 @@ void WidgetsPresenter::presentMessageBox(const MessageConfig &config, QPointer<M
void WidgetsPresenter::presentInputDialog(const MessageConfig &config, QPointer<MessageResult> result)
{
auto input = d->inputViewFactory->createInput(config.subType(), nullptr, config.viewProperties());
if(!input)
return;
if(!input) {
throw PresenterException(QByteArrayLiteral("Unable to find an input for type") +
config.subType());
}
QWidget *parent = nullptr;
if(!config.viewProperties().value(QStringLiteral("modal"), false).toBool())

4
src/src.pro

@ -6,5 +6,9 @@ SUBDIRS += mvvmcore \
mvvmquick \
imports
qtHaveModule(datasync) {
SUBDIRS += mvvmdatasynccore
}
prepareRecursiveTarget(lrelease)
QMAKE_EXTRA_TARGETS += lrelease

3
sync.profile

@ -1,7 +1,8 @@
%modules = (
"QtMvvmCore" => "$basedir/src/mvvmcore",
"QtMvvmWidgets" => "$basedir/src/mvvmwidgets",
"QtMvvmQuick" => "$basedir/src/mvvmquick"
"QtMvvmQuick" => "$basedir/src/mvvmquick",
"QtMvvmDataSyncCore" => "$basedir/src/mvvmdatasynccore"
);
# Force generation of camel case headers for classes inside QtDataSync namespaces

Loading…
Cancel
Save