1 /* This file is part of the KDE project
2 * Copyright 2014 Dan Leinir Turthra Jensen <admin@leinir.dk>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "CloudAccountsModel.h"
23 #include "PropertyContainer.h"
24
25 #include <QDebug>
26 #include <QByteArray>
27 #include <QDataStream>
28 #include <QVariant>
29 #include <QMetaObject>
30 #include <QMetaProperty>
31 #include <QFile>
32 #include <QStandardPaths>
33
34 struct AccountEntry {
35 public:
AccountEntryAccountEntry36 AccountEntry(QObject* parent)
37 : selected(false)
38 , accountDetails(new PropertyContainer("", parent))
39 {}
40 QString text;
41 bool selected;
42 QString accountType;
43 QString stackComponent;
44 QObject* accountDetails;
45
serializeAccountEntry46 QByteArray serialize() {
47 // selected is not serialised, because that would be silly
48 QByteArray ba;
49 QDataStream stream(&ba, QIODevice::WriteOnly);
50 stream << text;
51 stream << accountType;
52 stream << stackComponent;
53 Q_FOREACH(const QByteArray &name, accountDetails->dynamicPropertyNames()) {
54 stream << name << accountDetails->property(name);
55 }
56 for(int i = accountDetails->metaObject()->propertyOffset(); i < accountDetails->metaObject()->propertyCount(); ++i) {
57 stream << accountDetails->metaObject()->property(i).name() << accountDetails->metaObject()->property(i).read(accountDetails);
58 }
59 return ba;
60 }
61
fromByteArrayAccountEntry62 static AccountEntry* fromByteArray(QByteArray& ba, QObject* parent) {
63 AccountEntry* entry = new AccountEntry(parent);
64 QDataStream stream(&ba, QIODevice::ReadOnly);
65 stream >> entry->text;
66 stream >> entry->accountType;
67 stream >> entry->stackComponent;
68 entry->accountDetails->setProperty("text", entry->text);
69 while(!stream.atEnd()) {
70 QByteArray propertyName;
71 QVariant data;
72 stream >> propertyName;
73 stream >> data;
74 entry->accountDetails->setProperty(propertyName, data);
75 }
76 return entry;
77 }
78 };
79
80 class CloudAccountsModel::Private {
81 public:
Private(CloudAccountsModel * qq)82 Private(CloudAccountsModel* qq)
83 : q(qq)
84 {
85 dataFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/calligrageminicloudaccounts");
86 loadList();
87 }
~Private()88 ~Private()
89 {
90 qDeleteAll(entries);
91 }
92 CloudAccountsModel* q;
93 QList<AccountEntry*> entries;
94 QString dataFile;
95
saveList()96 void saveList()
97 {
98 QByteArray ba;
99 QDataStream stream(&ba, QIODevice::WriteOnly);
100 Q_FOREACH(AccountEntry* entry, entries) {
101 stream << entry->serialize();
102 }
103 QFile data(dataFile);
104 data.open(QIODevice::WriteOnly);
105 data.write(ba);
106 data.close();
107 }
108
loadList()109 void loadList()
110 {
111 QByteArray ba;
112 QFile data(dataFile);
113 data.open(QIODevice::ReadOnly);
114 ba = data.readAll();
115 data.close();
116
117 q->beginResetModel();
118 qDeleteAll(entries);
119 entries.clear();
120
121 QDataStream stream(&ba, QIODevice::ReadOnly);
122 QByteArray entryBA;
123 while(!stream.atEnd()) {
124 stream >> entryBA;
125 entries.append(AccountEntry::fromByteArray(entryBA, q));
126 }
127
128 q->endResetModel();
129 }
130 };
131
CloudAccountsModel(QObject * parent)132 CloudAccountsModel::CloudAccountsModel(QObject* parent)
133 : QAbstractListModel(parent)
134 , d(new Private(this))
135 {
136 QHash<int, QByteArray> roles;
137 roles[TextRole] = "text";
138 roles[SelectedRole] = "selected";
139 roles[AccountTypeRole] = "accountType";
140 roles[StackComponentRole] = "stackComponent";
141 roles[AccountDetailsRole] = "accountDetails";
142 setRoleNames(roles);
143 }
144
~CloudAccountsModel()145 CloudAccountsModel::~CloudAccountsModel()
146 {
147 delete d;
148 }
149
data(const QModelIndex & index,int role) const150 QVariant CloudAccountsModel::data(const QModelIndex& index, int role) const
151 {
152 QVariant result;
153 if(index.isValid() && index.row() > -1 && index.row() < d->entries.count())
154 {
155 AccountEntry* entry = d->entries.at(index.row());
156 switch(role)
157 {
158 case TextRole:
159 result = entry->text;
160 break;
161 case SelectedRole:
162 result = entry->selected;
163 break;
164 case AccountTypeRole:
165 result = entry->accountType;
166 break;
167 case StackComponentRole:
168 result = entry->stackComponent;
169 break;
170 case AccountDetailsRole:
171 result = QVariant::fromValue<QObject*>(entry->accountDetails);
172 break;
173 default:
174 break;
175 }
176 }
177 return result;
178 }
179
rowCount(const QModelIndex & parent) const180 int CloudAccountsModel::rowCount(const QModelIndex& parent) const
181 {
182 if(parent.isValid())
183 return 0;
184 return d->entries.count();
185 }
186
selectIndex(int newSelection)187 void CloudAccountsModel::selectIndex(int newSelection)
188 {
189 Q_FOREACH(AccountEntry* entry, d->entries) {
190 entry->selected = false;
191 }
192 if(newSelection >= 0 && newSelection < d->entries.count()) {
193 d->entries.at(newSelection)->selected = true;
194 }
195 dataChanged(index(0), index(d->entries.count() - 1));
196 }
197
addAccount(QString text,QString accountType,QString stackComponent,QObject * accountDetails,bool removeExisting)198 void CloudAccountsModel::addAccount(QString text, QString accountType, QString stackComponent, QObject* accountDetails, bool removeExisting)
199 {
200 if(removeExisting) {
201 removeAccountByName(text);
202 }
203 AccountEntry* entry = new AccountEntry(this);
204 entry->text = text;
205 entry->accountType = accountType;
206 entry->stackComponent = stackComponent;
207 if(accountDetails) {
208 Q_FOREACH(const QByteArray &name, accountDetails->dynamicPropertyNames()) {
209 entry->accountDetails->setProperty(name, accountDetails->property(name));
210 }
211 for(int i = accountDetails->metaObject()->propertyOffset(); i < accountDetails->metaObject()->propertyCount(); ++i) {
212 entry->accountDetails->setProperty(accountDetails->metaObject()->property(i).name(), accountDetails->metaObject()->property(i).read(accountDetails));
213 }
214 }
215 int count = d->entries.count();
216 beginInsertRows(QModelIndex(), count, count);
217 d->entries.append(entry);
218 endInsertRows();
219 d->saveList();
220 }
221
renameAccount(int index,QString newText)222 void CloudAccountsModel::renameAccount(int index, QString newText)
223 {
224 if(index > -1 && index < d->entries.count() - 1)
225 {
226 d->entries.at(index)->text = newText;
227 dataChanged(this->index(index), this->index(index));
228 d->saveList();
229 }
230 }
231
removeAccountByName(QString text)232 void CloudAccountsModel::removeAccountByName(QString text)
233 {
234 beginResetModel();
235 for(int i = d->entries.count() - 1; i > -1; --i) {
236 if(d->entries.at(i)->text == text) {
237 d->entries.removeAt(i);
238 }
239 }
240 endResetModel();
241 }
242
removeAccount(int index)243 void CloudAccountsModel::removeAccount(int index)
244 {
245 if(index > -1 && index < d->entries.count())
246 {
247 beginRemoveRows(QModelIndex(), index, index);
248 delete(d->entries.takeAt(index));
249 endRemoveRows();
250 d->saveList();
251 }
252 }
253
accountDetails(int index)254 QObject* CloudAccountsModel::accountDetails(int index)
255 {
256 if(index > -1 && index < d->entries.count() - 1)
257 {
258 return d->entries.at(index)->accountDetails;
259 }
260 return 0;
261 }
262
setAccountDetails(int index,QObject * newDetails)263 void CloudAccountsModel::setAccountDetails(int index, QObject* newDetails)
264 {
265 if(index > -1 && index < d->entries.count() - 1)
266 {
267 AccountEntry* entry = d->entries.at(index);
268 if(newDetails) {
269 Q_FOREACH(const QByteArray &name, newDetails->dynamicPropertyNames()) {
270 entry->accountDetails->setProperty(name, newDetails->property(name));
271 }
272 for(int i = newDetails->metaObject()->propertyOffset(); i < newDetails->metaObject()->propertyCount(); ++i) {
273 entry->accountDetails->setProperty(newDetails->metaObject()->property(i).name(), newDetails->metaObject()->property(i).read(newDetails));
274 }
275 }
276 d->saveList();
277 }
278 }
279