From fe89c7a4ff760564f3cdbcd8e27c04eadd9f79d8 Mon Sep 17 00:00:00 2001 From: link2xt Date: Thu, 5 Nov 2020 22:03:37 +0300 Subject: Move accounts model from C++ to QML C++ models are not well documented and it is easier to manage the model in QML. Now all QObjects are thin wrappers around Delta Chat core structures. --- CMakeLists.txt | 2 +- accounts.cpp | 97 +++++++++++++++++++++++++++++++++++ accounts.h | 31 +++++++++++ accounts_model.cpp | 142 --------------------------------------------------- accounts_model.h | 51 ------------------ main.cpp | 4 +- qml/AccountsPage.qml | 33 ++++++++++-- qml/main.qml | 14 ++--- 8 files changed, 166 insertions(+), 208 deletions(-) create mode 100644 accounts.cpp create mode 100644 accounts.h delete mode 100644 accounts_model.cpp delete mode 100644 accounts_model.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48ae24e..e380217 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ add_executable(kdeltachat main.cpp message.cpp context.cpp - accounts_model.cpp + accounts.cpp chatlist.cpp contact.cpp chat.cpp diff --git a/accounts.cpp b/accounts.cpp new file mode 100644 index 0000000..f1486b5 --- /dev/null +++ b/accounts.cpp @@ -0,0 +1,97 @@ +#include + +#include "accounts.h" + +DcAccounts::DcAccounts(QObject *parent) + : QObject(parent) +{ + m_accounts = dc_accounts_new("Qt", "./deltachat-data"); +} + +DcAccounts::~DcAccounts() +{ + dc_accounts_unref(m_accounts); +} + +uint32_t +DcAccounts::addAccount() +{ + return dc_accounts_add_account(m_accounts); +} + +uint32_t +DcAccounts::importAccount(QString tarfile) +{ + QByteArray utf8Text = tarfile.toUtf8(); + return dc_accounts_import_account(m_accounts, utf8Text.constData()); +} + +uint32_t +DcAccounts::migrateAccount(QString dbfile) +{ + QByteArray utf8Text = dbfile.toUtf8(); + return dc_accounts_migrate_account(m_accounts, utf8Text.constData()); +} + +bool +DcAccounts::removeAccount(uint32_t accountId) +{ + return dc_accounts_remove_account(m_accounts, accountId); +} + +QVariantList +DcAccounts::getAll() +{ + QVariantList result; + dc_array_t *accountIdArray = dc_accounts_get_all(m_accounts); + for (size_t i = 0; i < dc_array_get_cnt(accountIdArray); i++) { + result << dc_array_get_id(accountIdArray, i); + } + dc_array_unref(accountIdArray); + return result; +} + +Context * +DcAccounts::getAccount(uint32_t accountId) +{ + dc_context_t *context = dc_accounts_get_account(m_accounts, accountId); + + return new Context(this, context); +} + +Context * +DcAccounts::getSelectedAccount() +{ + dc_context_t *context = dc_accounts_get_selected_account(m_accounts); + + return new Context(this, context); +} + +bool +DcAccounts::selectAccount(uint32_t accountId) { + return dc_accounts_select_account(m_accounts, accountId); +} + +void +DcAccounts::startIo() +{ + dc_accounts_start_io(m_accounts); +} + +void +DcAccounts::stopIo() +{ + dc_accounts_stop_io(m_accounts); +} + +void +DcAccounts::maybeNetwork() +{ + dc_accounts_maybe_network(m_accounts); +} + +DcAccountsEventEmitter * +DcAccounts::getEventEmitter() +{ + return new DcAccountsEventEmitter{dc_accounts_get_event_emitter(m_accounts)}; +} diff --git a/accounts.h b/accounts.h new file mode 100644 index 0000000..20ede38 --- /dev/null +++ b/accounts.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "eventemitter.h" +#include "context.h" + +class DcAccounts : public QObject { + Q_OBJECT + +public: + explicit DcAccounts(QObject *parent = nullptr); + ~DcAccounts(); + + Q_INVOKABLE uint32_t addAccount(); + Q_INVOKABLE uint32_t importAccount(QString tarfile); + Q_INVOKABLE uint32_t migrateAccount(QString dbfile); + Q_INVOKABLE bool removeAccount(uint32_t accountId); + Q_INVOKABLE QVariantList getAll(); + Q_INVOKABLE Context *getAccount(uint32_t accountId); + Q_INVOKABLE Context *getSelectedAccount(); + Q_INVOKABLE bool selectAccount(uint32_t accountId); + Q_INVOKABLE void startIo(); + Q_INVOKABLE void stopIo(); + Q_INVOKABLE void maybeNetwork(); + Q_INVOKABLE DcAccountsEventEmitter *getEventEmitter(); + +private: + dc_accounts_t *m_accounts{nullptr}; +}; diff --git a/accounts_model.cpp b/accounts_model.cpp deleted file mode 100644 index f701245..0000000 --- a/accounts_model.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include - -#include "accounts_model.h" - -AccountsModel::AccountsModel(QObject *parent) - : QAbstractListModel(parent) -{ - m_accounts = dc_accounts_new("Qt", "./deltachat-data"); -} - -AccountsModel::~AccountsModel() -{ - dc_accounts_unref(m_accounts); -} - -int -AccountsModel::rowCount(const QModelIndex & parent) const -{ - Q_UNUSED(parent); - return accountCount(); -} - -QVariant AccountsModel::data(const QModelIndex & index, int role) const { - QVariant result{}; - - dc_array_t *accounts_arr = dc_accounts_get_all(m_accounts); - if (index.row() >= 0 && index.row() < dc_array_get_cnt(accounts_arr)) { - result = dc_array_get_id(accounts_arr, index.row()); - } - dc_array_unref(accounts_arr); - - return result; -} - -QHash -AccountsModel::roleNames() const { - QHash roles; - roles[NumberRole] = "number"; - return roles; -} - -size_t -AccountsModel::accountCount() const { - dc_array_t *accounts_arr = dc_accounts_get_all(m_accounts); - size_t result = dc_array_get_cnt(accounts_arr); - dc_array_unref(accounts_arr); - return result; -} - -uint32_t -AccountsModel::selectedAccount() { - return m_selectedAccount; -} - -void -AccountsModel::setSelectedAccount(uint32_t selectedAccount) { - if (m_selectedAccount != selectedAccount) { - if (dc_accounts_select_account (m_accounts, selectedAccount)) { - m_selectedAccount = selectedAccount; - emit selectedAccountChanged(); - } - } -} - -uint32_t -AccountsModel::addAccount() -{ - int row = accountCount(); - emit beginInsertRows(QModelIndex(), row, row); - uint32_t res = dc_accounts_add_account(m_accounts); - if (res != 0) { - emit accountCountChanged(); - } - emit endInsertRows(); - return res; -} - -void -AccountsModel::removeAccount(uint32_t accountId) -{ - size_t index = -1; - - dc_array_t *accounts_arr = dc_accounts_get_all(m_accounts); - int res = dc_array_search_id(accounts_arr, accountId, &index); - dc_array_unref(accounts_arr); - - if (res) { - emit beginRemoveRows(QModelIndex(), index, index); - dc_accounts_remove_account(m_accounts, accountId); - emit endRemoveRows(); - emit accountCountChanged(); - } -} - -uint32_t -AccountsModel::importAccount(const QString &filename) { - int row = accountCount(); - QByteArray ba = QFile::encodeName(filename); - uint32_t res = dc_accounts_import_account(m_accounts, ba.data()); - if (res) { - // XXX: Looks like there is no way - // to abort inserting rows, - // so we begin and end at the same time to notify the UI about the added - // row. https://forum.qt.io/topic/19194/how-to-abort-begininsertrows - emit beginInsertRows(QModelIndex(), row, row); - emit endInsertRows(); - } - return res; -} - -Context * -AccountsModel::getSelectedAccount() -{ - dc_context_t *context = dc_accounts_get_selected_account(m_accounts); - - return new Context(this, context); -} - -void -AccountsModel::startIo() -{ - dc_accounts_start_io(m_accounts); -} - -void -AccountsModel::stopIo() -{ - dc_accounts_stop_io(m_accounts); -} - -void -AccountsModel::maybeNetwork() -{ - dc_accounts_maybe_network(m_accounts); -} - -DcAccountsEventEmitter * -AccountsModel::getEventEmitter() -{ - std::cerr << "GETTING EVENT EMITTER" << std::endl; - return new DcAccountsEventEmitter{dc_accounts_get_event_emitter(m_accounts)}; -} diff --git a/accounts_model.h b/accounts_model.h deleted file mode 100644 index 5541f7e..0000000 --- a/accounts_model.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -#include "eventemitter.h" -#include "context.h" - -class AccountsModel : public QAbstractListModel { - Q_OBJECT - Q_PROPERTY(size_t accountCount READ accountCount NOTIFY accountCountChanged) - Q_PROPERTY(uint32_t selectedAccount READ selectedAccount WRITE setSelectedAccount NOTIFY selectedAccountChanged) - -public: - enum AccountRoles { - NumberRole = Qt::UserRole + 1 - }; - explicit AccountsModel(QObject *parent = nullptr); - ~AccountsModel(); - - int rowCount(const QModelIndex & parent = QModelIndex()) const; - - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - - size_t accountCount() const; - - uint32_t selectedAccount(); - void setSelectedAccount(uint32_t selectedAccount); - - Q_INVOKABLE uint32_t addAccount(); - Q_INVOKABLE void removeAccount(uint32_t accountId); - Q_INVOKABLE uint32_t importAccount(const QString &filename); - Q_INVOKABLE Context *getSelectedAccount(); - - Q_INVOKABLE void startIo(); - Q_INVOKABLE void stopIo(); - Q_INVOKABLE void maybeNetwork(); - - Q_INVOKABLE DcAccountsEventEmitter *getEventEmitter(); - -signals: - void accountCountChanged(); - void selectedAccountChanged(); - -protected: - QHash roleNames() const; - -private: - dc_accounts_t *m_accounts{nullptr}; - uint32_t m_selectedAccount{0}; -}; diff --git a/main.cpp b/main.cpp index 6c9af57..f2687e0 100644 --- a/main.cpp +++ b/main.cpp @@ -2,7 +2,7 @@ #include #include -#include "accounts_model.h" +#include "accounts.h" #include "message.h" #include "chat.h" #include "chatlist.h" @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) app.setOrganizationDomain("delta.chat"); // TODO: switch to using Qt 5.15 QML_ELEMENT macro - if (qmlRegisterType("DeltaChat", 1, 0, "AccountsModel") == -1) + if (qmlRegisterType("DeltaChat", 1, 0, "DcAccounts") == -1) { QCoreApplication::exit(-1); } diff --git a/qml/AccountsPage.qml b/qml/AccountsPage.qml index c9368d2..3278031 100644 --- a/qml/AccountsPage.qml +++ b/qml/AccountsPage.qml @@ -12,7 +12,10 @@ Kirigami.Page { mainAction: Kirigami.Action { iconName: "list-add-user" text: "Add account" - onTriggered: accountsModel.addAccount() + onTriggered: { + let accountId = dcAccounts.addAccount() + accountsModel.insert(accountsModel.count, { number: accountId }) + } } contextualActions: [ @@ -31,7 +34,7 @@ Kirigami.Page { if (url.startsWith("file://")) { var filename = url.substring(7) console.log("Importing " + filename) - var accountId = accountsModel.importAccount (filename) + var accountId = dcAccounts.importAccount(filename) if (accountId == 0) { console.log("Import failed") } else { @@ -41,6 +44,23 @@ Kirigami.Page { } } + ListModel { + id: accountsModel + } + + function updateAccounts() { + let accountsList = dcAccounts.getAll() + + accountsModel.clear() + for (let i = 0; i < accountsList.length; i++) { + accountsModel.insert(i, { number: accountsList[i] }) + } + } + + Component.onCompleted: { + updateAccounts() + } + ListView { id: accountsListView anchors.fill: parent @@ -63,8 +83,8 @@ Kirigami.Page { while (pageStack.depth > 1) { pageStack.pop() } - accountsModel.selectedAccount = model.number - let context = accountsModel.getSelectedAccount() + dcAccounts.selectAccount(model.number) + let context = dcAccounts.getSelectedAccount() if (context.isConfigured()) { pageStack.push("qrc:/qml/ChatlistPage.qml", {context: context}) } else { @@ -76,7 +96,10 @@ Kirigami.Page { Button { width: 100 text: "Delete" - onClicked: accountsModel.removeAccount(model.number) + onClicked: { + dcAccounts.removeAccount(model.number) + accountsModel.remove(model.index) + } } } } diff --git a/qml/main.qml b/qml/main.qml index cb116b3..546d553 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -19,9 +19,9 @@ Kirigami.ApplicationWindow { text: "Start IO" onCheckedChanged: { if (checked) { - accountsModel.startIo() + dcAccounts.startIo() } else { - accountsModel.stopIo() + dcAccouts.stopIo() } } } @@ -30,7 +30,7 @@ Kirigami.ApplicationWindow { Kirigami.Action { text: "Maybe network" iconName: "view-refresh" - onTriggered: accountsModel.maybeNetwork() + onTriggered: dcAccounts.maybeNetwork() } ] } @@ -39,18 +39,18 @@ Kirigami.ApplicationWindow { id: contextDrawer } - AccountsModel { - id: accountsModel + DcAccounts { + id: dcAccounts } Component.onCompleted: { console.log('starting') - eventEmitter = accountsModel.getEventEmitter() + eventEmitter = dcAccounts.getEventEmitter() eventEmitter.start(); } onClosing: { // Cancel all tasks that may block the termination of event loop. - accountsModel.stopIo() + dcAccounts.stopIo() } } -- cgit v1.2.3-54-g00ecf