rootmodel.cpp 13.4 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2014-2015 by Eike Hein <hein@kde.org>                   *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

#include "rootmodel.h"
21
#include "actionlist.h"
22
#include "kastatsfavoritesmodel.h"
23
#include "recentcontactsmodel.h"
24
#include "recentusagemodel.h"
25
26
27
28
#include "systemmodel.h"

#include <KLocalizedString>

29
30
31
#include <QCollator>

GroupEntry::GroupEntry(AppsModel *parentModel, const QString &name,
32
33
34
35
36
    const QString &iconName, AbstractModel *childModel)
: AbstractGroupEntry(parentModel)
, m_name(name)
, m_iconName(iconName)
, m_childModel(childModel)
37
{
38
    QObject::connect(parentModel, &RootModel::cleared, childModel, &AbstractModel::deleteLater);
39
40
41
42
43
44
45
46
47
48
49
50
51

    QObject::connect(childModel, &AbstractModel::countChanged,
        [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }
    );
}

QString GroupEntry::name() const
{
    return m_name;
}

QIcon GroupEntry::icon() const
{
52
    return QIcon::fromTheme(m_iconName, QIcon::fromTheme(QStringLiteral("unknown")));
53
54
55
56
57
58
59
60
61
62
}

bool GroupEntry::hasChildren() const
{
    return m_childModel && m_childModel->count() > 0;
}

AbstractModel *GroupEntry::childModel() const
{
    return m_childModel;
63
64
65
}

RootModel::RootModel(QObject *parent) : AppsModel(QString(), parent)
66
, m_favorites(new KAStatsFavoritesModel(this))
67
, m_systemModel(nullptr)
68
, m_showAllApps(false)
69
, m_showAllAppsCategorized(false)
70
71
, m_showRecentApps(true)
, m_showRecentDocs(true)
Eike Hein's avatar
Eike Hein committed
72
, m_showRecentContacts(false)
73
, m_recentOrdering(RecentUsageModel::Recent)
74
, m_showPowerSession(true)
75
76
77
, m_recentAppsModel(nullptr)
, m_recentDocsModel(nullptr)
, m_recentContactsModel(nullptr)
78
79
80
81
82
83
84
{
}

RootModel::~RootModel()
{
}

85
86
87
88
89
90
91
92
93
94
95
QVariant RootModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || index.row() >= m_entryList.count()) {
        return QVariant();
    }

    if (role == Kicker::HasActionListRole || role == Kicker::ActionListRole) {
        const AbstractEntry *entry = m_entryList.at(index.row());

        if (entry->type() == AbstractEntry::GroupType) {
            const GroupEntry *group = static_cast<const GroupEntry *>(entry);
96
            AbstractModel *model = group->childModel();
97
98
99
100
101
102
103

            if (model == m_recentAppsModel
                || model == m_recentDocsModel
                || model == m_recentContactsModel) {
                if (role ==  Kicker::HasActionListRole) {
                    return true;
                } else if (role == Kicker::ActionListRole) {
104
105
106
107
                    QVariantList actionList;
                    actionList << model->actions();
                    actionList << Kicker::createSeparatorActionItem();
                    actionList << Kicker::createActionItem(i18n("Hide %1",
108
                        group->name()), QStringLiteral("hideCategory"));
109
                    return actionList;
110
111
112
113
114
115
116
117
118
119
                }
            }
        }
    }

    return AppsModel::data(index, role);
}

bool RootModel::trigger(int row, const QString& actionId, const QVariant& argument)
{
120
    const AbstractEntry *entry = m_entryList.at(row);
121

122
    if (entry->type() == AbstractEntry::GroupType) {
123
        if (actionId == QLatin1String("hideCategory")) {
124
            AbstractModel *model = entry->childModel();
125
126
127
128
129
130
131
132
133
134
135
136
137
138

            if (model == m_recentAppsModel) {
                setShowRecentApps(false);

                return true;
            } else if (model == m_recentDocsModel) {
                setShowRecentDocs(false);

                return true;
            } else if (model == m_recentContactsModel) {
                setShowRecentContacts(false);

                return true;
            }
139
140
        } else if (entry->childModel()->hasActions()) {
            return entry->childModel()->trigger(-1, actionId, QVariant());
141
142
143
144
145
146
        }
    }

    return AppsModel::trigger(row, actionId, argument);
}

147
bool RootModel::showAllApps() const
148
{
149
    return m_showAllApps;
150
151
}

152
void RootModel::setShowAllApps(bool show)
153
{
154
155
    if (m_showAllApps != show) {
        m_showAllApps = show;
156
157
158

        refresh();

159
        emit showAllAppsChanged();
160
161
162
    }
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
bool RootModel::showAllAppsCategorized() const
{
    return m_showAllAppsCategorized;
}

void RootModel::setShowAllAppsCategorized(bool showCategorized)
{
    if (m_showAllAppsCategorized != showCategorized) {
        m_showAllAppsCategorized = showCategorized;

        refresh();

        emit showAllAppsCategorizedChanged();
    }
}

179
bool RootModel::showRecentApps() const
180
{
181
    return m_showRecentApps;
182
183
}

184
void RootModel::setShowRecentApps(bool show)
185
{
186
187
    if (show != m_showRecentApps) {
        m_showRecentApps = show;
188
189
190

        refresh();

191
        emit showRecentAppsChanged();
192
193
194
    }
}

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
bool RootModel::showRecentDocs() const
{
    return m_showRecentDocs;
}

void RootModel::setShowRecentDocs(bool show)
{
    if (show != m_showRecentDocs) {
        m_showRecentDocs = show;

        refresh();

        emit showRecentDocsChanged();
    }
}

bool RootModel::showRecentContacts() const
{
    return m_showRecentContacts;
}

void RootModel::setShowRecentContacts(bool show)
{
    if (show != m_showRecentContacts) {
        m_showRecentContacts = show;

        refresh();

        emit showRecentContactsChanged();
    }
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
int RootModel::recentOrdering() const
{
    return m_recentOrdering;
}

void RootModel::setRecentOrdering(int ordering)
{
    if (ordering != m_recentOrdering) {
        m_recentOrdering = ordering;

        refresh();

        emit recentOrderingChanged();
    }
}

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
bool RootModel::showPowerSession() const
{
    return m_showPowerSession;
}

void RootModel::setShowPowerSession(bool show)
{
    if (show != m_showPowerSession) {
        m_showPowerSession = show;

        refresh();

        emit showPowerSessionChanged();
    }
}

259
260
261
262
263
264
265
266
267
268
269
270
AbstractModel* RootModel::favoritesModel()
{
    return m_favorites;
}

AbstractModel* RootModel::systemFavoritesModel()
{
    if (m_systemModel) {
        return m_systemModel->favoritesModel();
    }

    return nullptr;
271
272
273
274
}

void RootModel::refresh()
{
275
    if (!m_complete) {
276
277
278
        return;
    }

279
    beginResetModel();
280

281
    AppsModel::refreshInternal();
282

283
284
285
286
    AppsModel *allModel = nullptr;
    m_recentAppsModel = nullptr;
    m_recentDocsModel = nullptr;
    m_recentContactsModel = nullptr;
287

288
    if (m_showAllApps) {
289
290
291
292
293
294
295
296
        QHash<QString, AbstractEntry *> appsHash;

        std::function<void(AbstractEntry *)> processEntry = [&](AbstractEntry *entry) {
            if (entry->type() == AbstractEntry::RunnableType) {
                AppEntry *appEntry = static_cast<AppEntry*>(entry);
                appsHash.insert(appEntry->service()->menuId(), appEntry);
            } else if (entry->type() == AbstractEntry::GroupType) {
                GroupEntry *groupEntry = static_cast<GroupEntry*>(entry);
297
298
                AbstractModel *model = groupEntry->childModel();

299
300
301
                if (!model) {
                    return;
                }
302

303
                for (int i = 0; i < model->count(); ++i) {
304
                    processEntry(static_cast<AbstractEntry*>(model->index(i, 0).internalPointer()));
Eike Hein's avatar
Eike Hein committed
305
                }
306
            }
307
        };
308

309
310
311
        for (AbstractEntry *entry : m_entryList) {
            processEntry(entry);
        }
312

313
314
        QList<AbstractEntry *> apps(appsHash.values());
        QCollator c;
315

316
317
318
319
320
321
322
323
        std::sort(apps.begin(), apps.end(),
            [&c](AbstractEntry* a, AbstractEntry* b) {
                if (a->type() != b->type()) {
                    return a->type() > b->type();
                } else {
                    return c.compare(a->name(), b->name()) < 0;
                }
            });
Eike Hein's avatar
Eike Hein committed
324

325
326
327
328
329
330
331
        if (!m_showAllAppsCategorized && !m_paginate) { // The app list built above goes into a model.
            allModel = new AppsModel(apps, false, this);
        } else if (m_paginate) { // We turn the apps list into a subtree of pages.
            m_favorites = new KAStatsFavoritesModel(this);
            emit favoritesModelChanged();

            QList<AbstractEntry *> groups;
332
333
334

            int at = 0;
            QList<AbstractEntry *> page;
Eike Hein's avatar
Eike Hein committed
335
            page.reserve(m_pageSize);
336

337
            foreach(AbstractEntry *app, apps) {
338
339
340
341
342
343
344
345
346
347
                page.append(app);

                if (at == (m_pageSize - 1)) {
                    at = 0;
                    AppsModel *model = new AppsModel(page, false, this);
                    groups.append(new GroupEntry(this, QString(), QString(), model));
                    page.clear();
                } else {
                    ++at;
                }
348
            }
349

350
351
352
353
            if (!page.isEmpty()) {
                AppsModel *model = new AppsModel(page, false, this);
                groups.append(new GroupEntry(this, QString(), QString(), model));
            }
354

355
            groups.prepend(new GroupEntry(this, QString(), QString(), m_favorites));
356
357
358
359

            allModel = new AppsModel(groups, true, this);
        } else { // We turn the apps list into a subtree of apps by starting letter.
            QList<AbstractEntry *> groups;
360
361
362
363
364
            QHash<QString, QList<AbstractEntry *>> m_categoryHash;

            foreach (const AbstractEntry *groupEntry, m_entryList) {
                AbstractModel *model = groupEntry->childModel();

365
366
                if (!model) continue;

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
                for (int i = 0; i < model->count(); ++i) {
                    AbstractEntry *appEntry = static_cast<AbstractEntry *>(model->index(i, 0).internalPointer());

                    if (appEntry->name().isEmpty()) {
                        continue;
                    }

                    const QChar &first = appEntry->name().at(0).toUpper();
                    m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry);
                }
            }

            QHashIterator<QString, QList<AbstractEntry *>> i(m_categoryHash);

            while (i.hasNext()) {
                i.next();
                AppsModel *model = new AppsModel(i.value(), false, this);
                model->setDescription(i.key());
                groups.append(new GroupEntry(this, i.key(), QString(), model));
            }
387
388

            allModel = new AppsModel(groups, true, this);
389
        }
390

391
392
        allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n.
    }
393

394
    int separatorPosition = 0;
395

396
    if (allModel) {
397
        m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QString("applications-all"), allModel));
398
        ++separatorPosition;
399
    }
400

401
402
    if (m_showRecentContacts) {
        m_recentContactsModel = new RecentContactsModel(this);
403
        m_entryList.prepend(new GroupEntry(this, i18n("Recent Contacts"), QString("view-history"), m_recentContactsModel));
404
        ++separatorPosition;
405
    }
406

407
    if (m_showRecentDocs) {
408
409
410
411
412
        m_recentDocsModel = new RecentUsageModel(this, RecentUsageModel::OnlyDocs, m_recentOrdering);
        m_entryList.prepend(new GroupEntry(this,
                    m_recentOrdering == RecentUsageModel::Recent
                        ? i18n("Recent Documents")
                        : i18n("Often Used Documents"),
413
414
415
                    m_recentOrdering == RecentUsageModel::Recent
                        ? QString("view-history")
                        : QString("office-chart-pie"),
416
                    m_recentDocsModel));
417
        ++separatorPosition;
418
    }
419

420
    if (m_showRecentApps) {
421
422
423
424
425
        m_recentAppsModel = new RecentUsageModel(this, RecentUsageModel::OnlyApps, m_recentOrdering);
        m_entryList.prepend(new GroupEntry(this,
                    m_recentOrdering == RecentUsageModel::Recent
                        ? i18n("Recent Applications")
                        : i18n("Often Used Applications"),
426
427
428
                    m_recentOrdering == RecentUsageModel::Recent
                        ? QString("view-history")
                        : QString("office-chart-pie"),
429
                    m_recentAppsModel));
430
431
432
433
434
435
        ++separatorPosition;
    }

    if (m_showSeparators && separatorPosition > 0) {
        m_entryList.insert(separatorPosition, new SeparatorEntry(this));
        ++m_separatorCount;
436
    }
437

438
    m_systemModel = new SystemModel(this);
439
440

    if (m_showPowerSession) {
441
        m_entryList << new GroupEntry(this, i18n("Power / Session"), QString("system-log-out"), m_systemModel);
442
    }
443
444
445
446

    endResetModel();

    m_favorites->refresh();
447

448
    emit systemFavoritesModelChanged();
449
    emit countChanged();
450
    emit separatorCountChanged();
451
452
453

    emit refreshed();
}