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