1
/***************************************************************************
2
* Copyright Ā© 2010-2012 Jonathan Thomas <echidnaman@kubuntu.org> *
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. *
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. *
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
***************************************************************************/
21
#include "MainWindow.h"
24
#include <QStandardItemModel>
25
#include <QtCore/QStringBuilder>
26
#include <QtCore/QTimer>
27
#include <QAbstractItemView>
29
#include <QtWidgets/QStackedWidget>
32
#include <KActionCollection>
33
#include <KMessageBox>
34
#include <KMessageWidget>
35
#include <KPixmapSequence>
36
#include <KPixmapSequenceOverlayPainter>
38
#include <KToolInvocation>
41
#include <QApt/Backend>
44
#include <HistoryView/HistoryView.h>
45
#include "../libmuonapt/QAptActions.h"
48
#include <Transaction/TransactionModel.h>
49
#include <resources/ResourcesModel.h>
52
#include "ApplicationLauncher.h"
53
#include "ResourceView/ResourceListView.h"
54
#include "AvailableView.h"
55
#include "ProgressView.h"
56
#include "ViewSwitcher.h"
57
#include "LaunchListModel.h"
58
#include "MuonInstallerSettings.h"
61
/// A role for storing ViewType
62
ViewTypeRole = Qt::UserRole + 1,
63
/// A role for storing origin filter data
65
/// A role for storing state filter data
72
/// A simple ApplicationView that is filterable by status or origin
74
/// An ApplicationView that has a Categorical homepage
76
/// A CategoryView showing subcategories
78
/// A view for showing history
80
/// A view for showing in-progress transactions
84
MainWindow::MainWindow()
86
, m_appBackend(nullptr)
88
, m_launcherMessage(nullptr)
89
, m_appLauncher(nullptr)
90
, m_progressItem(nullptr)
94
TransactionModel *transModel = TransactionModel::global();
95
connect(transModel, SIGNAL(startingFirstTransaction()), SLOT(addProgressItem()));
96
connect(transModel, SIGNAL(lastTransactionFinished()), SLOT(removeProgressItem()));
97
m_launches = new LaunchListModel(this);
99
ResourcesModel* resourcesModel = ResourcesModel::global();
100
resourcesModel->integrateMainWindow(this);
101
connect(resourcesModel, SIGNAL(backendsChanged()), SLOT(initObject()));
104
MainWindow::~MainWindow()
106
MuonInstallerSettings::self()->writeConfig();
109
void MainWindow::initGUI()
111
m_mainWidget = new QSplitter(this);
112
m_mainWidget->setOrientation(Qt::Horizontal);
113
connect(m_mainWidget, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSplitterSizes()));
114
setCentralWidget(m_mainWidget);
116
// Set up the navigational sidebar on the right
117
m_viewSwitcher = new ViewSwitcher(this);
118
connect(m_viewSwitcher, SIGNAL(activated(QModelIndex)),
119
this, SLOT(changeView(QModelIndex)));
120
m_mainWidget->addWidget(m_viewSwitcher);
122
// Set up the main pane
123
KVBox *leftWidget = new KVBox(this);
124
m_launcherMessage = new KMessageWidget(leftWidget);
125
m_launcherMessage->hide();
126
m_launcherMessage->setMessageType(KMessageWidget::Positive);
128
m_viewStack = new QStackedWidget(leftWidget);
129
m_viewStack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
132
m_busyWidget = new QWidget(m_viewStack);
133
KPixmapSequenceOverlayPainter *busyWidget = new KPixmapSequenceOverlayPainter(m_busyWidget);
134
busyWidget->setSequence(KPixmapSequence("process-working", KIconLoader::SizeSmallMedium));
135
busyWidget->setWidget(m_busyWidget);
138
m_viewStack->addWidget(m_busyWidget);
139
m_viewStack->setCurrentWidget(m_busyWidget);
141
m_mainWidget->addWidget(leftWidget);
144
m_viewModel = new QStandardItemModel(this);
145
m_viewSwitcher->setModel(m_viewModel);
148
setupGUI(StandardWindowOption(KXmlGuiWindow::Default & ~KXmlGuiWindow::StatusBar));
151
void MainWindow::initObject()
153
ResourcesModel *resourcesModel = ResourcesModel::global();
155
QVector<AbstractResourcesBackend*> backends = resourcesModel->backends();
156
for (AbstractResourcesBackend *backend : backends) {
158
backend->metaObject()->className()==QLatin1String("ApplicationBackend"))
160
m_appBackend = backend;
161
connect(m_appBackend, SIGNAL(fetchingChanged()), SLOT(aptFetchingChanged()));
162
connect(m_appBackend, SIGNAL(sourcesEditorFinished()), SLOT(sourcesEditorFinished()));
166
connect(resourcesModel, SIGNAL(allInitialized()), SLOT(populateViews()));
169
void MainWindow::aptFetchingChanged()
171
if(m_appBackend && !m_appBackend->isFetching())
172
showLauncherMessage();
175
void MainWindow::loadSplitterSizes()
177
QList<int> sizes = MuonInstallerSettings::self()->splitterSizes();
179
if (sizes.isEmpty()) {
180
sizes << 115 << (this->width() - 115);
182
m_mainWidget->setSizes(sizes);
185
void MainWindow::saveSplitterSizes()
187
MuonInstallerSettings::self()->setSplitterSizes(m_mainWidget->sizes());
188
MuonInstallerSettings::self()->writeConfig();
191
void MainWindow::clearViews()
193
setCanExit(false); // APT is reloading at this point
194
foreach (QWidget *widget, m_viewHash) {
198
m_viewModel->clear();
201
QStandardItem* MainWindow::createOriginItem(const QString& originName)
203
// TODO: when origins are abstracted out this should go too
204
QApt::Backend* backend = qobject_cast<QApt::Backend*>(m_appBackend->property("backend").value<QObject*>());
205
QString originLabel = backend->originLabel(originName);
206
if(originLabel.isEmpty())
207
originLabel = originName;
209
// We must spread the word of Origin. Hallowed are the Ori! ;P
210
QStandardItem *viewItem = new QStandardItem;
211
viewItem->setEditable(false);
212
viewItem->setText(originLabel);
213
viewItem->setData(originName, OriginFilterRole);
214
viewItem->setData(AppView, ViewTypeRole);
216
if (originName == "Ubuntu") {
217
viewItem->setText(i18nc("@item:inlistbox", "Provided by Kubuntu"));
218
viewItem->setIcon(QIcon::fromTheme("ubuntu-logo"));
221
if (originName == "Debian") {
222
viewItem->setText(i18nc("@item:inlistbox", "Provided by Debian"));
223
viewItem->setIcon(QIcon::fromTheme("emblem-debian"));
226
if (originName == "Canonical") {
227
viewItem->setText(i18nc("@item:inlistbox The name of the repository provided by Canonical, Ltd. ",
228
"Canonical Partners"));
229
viewItem->setIcon(QIcon::fromTheme("partner"));
232
if (originName.startsWith(QLatin1String("LP-PPA"))) {
233
viewItem->setIcon(QIcon::fromTheme("user-identity"));
235
if (originName == QLatin1String("LP-PPA-app-review-board")) {
236
viewItem->setText(i18nc("@item:inlistbox An independent software source",
238
viewItem->setIcon(QIcon::fromTheme("system-users"));
240
viewItem->setIcon(QIcon::fromTheme("user-identity"));
245
bool repositoryNameLessThan(const QString& a, const QString& b)
247
static QStringList prioritary(QStringList() << "Debian" << "Ubuntu" << "Canonical" << "LP-PPA-app-review-board");
248
int idxA = prioritary.indexOf(a), idxB = prioritary.indexOf(b);
251
else if((idxB == -1) ^ (idxA == -1))
257
QPair<QStringList, QStringList> fetchOrigins()
259
QSet<QString> originSet, instOriginSet;
261
ResourcesModel *resourcesModel = ResourcesModel::global();
262
for(int i=0; i<resourcesModel->rowCount(); i++) {
263
AbstractResource* app = resourcesModel->resourceAt(i);
264
if (app->backend()->isFetching())
266
if (app->isInstalled())
267
instOriginSet << app->origin();
269
originSet << app->origin();
272
originSet.remove(QString());
273
instOriginSet.remove(QString());
274
originSet += instOriginSet;
276
QStringList originList=originSet.toList(), instOriginList=instOriginSet.toList();
277
qSort(originList.begin(), originList.end(), repositoryNameLessThan);
278
qSort(instOriginList.begin(), instOriginList.end(), repositoryNameLessThan);
279
return qMakePair(originList, instOriginList);
282
void MainWindow::populateViews()
284
m_viewModel->clear();
285
QStandardItem *availableItem = new QStandardItem;
286
availableItem->setEditable(false);
287
availableItem->setIcon(QIcon::fromTheme("applications-other").pixmap(32,32));
288
availableItem->setText(i18nc("@item:inlistbox Parent item for available software", "Get Software"));
289
availableItem->setData(CatView, ViewTypeRole);
291
QStandardItem *installedItem = new QStandardItem;
292
installedItem->setEditable(false);
293
installedItem->setIcon(QIcon::fromTheme("computer"));
294
installedItem->setText(i18nc("@item:inlistbox Parent item for installed software", "Installed Software"));
295
installedItem->setData(AppView, ViewTypeRole);
296
installedItem->setData(AbstractResource::State::Installed, StateFilterRole);
298
QPair< QStringList, QStringList > origins = fetchOrigins();
299
QStringList originNames = origins.first;
300
foreach(const QString &originName, originNames) {
301
availableItem->appendRow(createOriginItem(originName));
304
QStringList instOriginNames = origins.second;
305
foreach(const QString & originName, instOriginNames) {
306
QStandardItem* viewItem = createOriginItem(originName);
308
viewItem->setData(AbstractResource::State::Installed, StateFilterRole);
309
installedItem->appendRow(viewItem);
312
QStandardItem *historyItem = new QStandardItem;
313
historyItem->setEditable(false);
314
historyItem->setIcon(QIcon::fromTheme("view-history").pixmap(32,32));
315
historyItem->setText(i18nc("@item:inlistbox Item for showing the history view", "History"));
316
historyItem->setData(History, ViewTypeRole);
318
m_viewModel->appendRow(availableItem);
319
m_viewModel->appendRow(installedItem);
320
m_viewModel->appendRow(historyItem);
321
selectFirstRow(m_viewSwitcher);
323
emit viewsPopulated();
326
void MainWindow::changeView(const QModelIndex &index)
328
QWidget *view = m_viewHash.value(index);
330
// Create new widget if not already created
332
switch (index.data(ViewTypeRole).toInt()) {
334
QString originFilter = index.data(OriginFilterRole).toString();
335
AbstractResource::State stateFilter = (AbstractResource::State)index.data(StateFilterRole).toInt();
337
view = new ResourceListView(this, index);
338
ResourceListView *appView = static_cast<ResourceListView *>(view);
339
appView->setStateFilter(stateFilter);
340
appView->setOriginFilter(originFilter);
341
appView->setCanShowTechnical(true);
343
if (originFilter != QLatin1String("Ubuntu") && originFilter != QLatin1String("Debian"))
344
appView->setShouldShowTechnical(true);
348
view = new AvailableView(this);
351
view = new HistoryView(this);
354
view = new ProgressView(this);
360
m_viewStack->addWidget(view);
363
m_viewStack->addWidget(view);
364
m_viewStack->setCurrentWidget(view);
365
m_viewStack->removeWidget(m_busyWidget);
368
m_busyWidget = nullptr;
370
m_viewHash[index] = view;
373
void MainWindow::selectFirstRow(const QAbstractItemView *itemView)
375
QModelIndex firstRow = itemView->model()->index(0, 0);
376
itemView->selectionModel()->select(firstRow, QItemSelectionModel::Select);
377
changeView(firstRow);
380
void MainWindow::sourcesEditorFinished()
384
find(effectiveWinId())->setEnabled(true);
387
void MainWindow::showLauncherMessage()
389
clearMessageActions();
391
if (m_launches->rowCount()==1) {
392
QModelIndex index = m_launches->index(0, 0);
393
QString name = index.data().toString();
394
m_launcherMessage->setText(i18nc("@info", "%1 was successfully installed.", name));
396
QIcon launchIcon = index.data(Qt::DecorationRole).value<QIcon>();
397
QAction *launchAction = new QAction(launchIcon, i18nc("@action", "Start"), this);
398
connect(launchAction, SIGNAL(activated()), this, SLOT(launchSingleApp()));
400
m_launcherMessage->addAction(launchAction);
401
m_launcherMessage->animatedShow();
402
} else if (m_launches->rowCount() > 1) {
403
m_launcherMessage->setText(i18nc("@info", "Applications successfully installed."));
404
QAction *launchAction = new QAction(i18nc("@action", "Run New Applications..."), this);
405
connect(launchAction, SIGNAL(activated()), this, SLOT(showAppLauncher()));
406
m_launcherMessage->addAction(launchAction);
407
m_launcherMessage->animatedShow();
411
void MainWindow::launchSingleApp()
413
m_launches->invokeApplication(0);
414
m_launcherMessage->animatedHide();
415
m_launcherMessage->removeAction(m_launcherMessage->actions().first());
418
void MainWindow::showAppLauncher()
420
if (!m_appLauncher && !m_launches->rowCount()==0) {
421
m_appLauncher = new ApplicationLauncher(m_launches);
422
connect(m_appLauncher, SIGNAL(destroyed(QObject*)),
423
this, SLOT(onAppLauncherClosed()));
424
connect(m_appLauncher, SIGNAL(finished(int)),
425
this, SLOT(onAppLauncherClosed()));
426
m_appLauncher->setWindowTitle(i18nc("@title:window", "Installation Complete"));
427
m_appLauncher->show();
429
m_launcherMessage->animatedHide();
432
void MainWindow::onAppLauncherClosed()
437
void MainWindow::clearMessageActions()
439
foreach (QAction *action, m_launcherMessage->actions()) {
440
m_launcherMessage->removeAction(action);
444
void MainWindow::addProgressItem()
446
QStandardItem *parentItem = m_viewModel->invisibleRootItem();
451
m_progressItem = new QStandardItem;
452
m_progressItem->setEditable(false);
453
m_progressItem->setIcon(QIcon::fromTheme("download").pixmap(32,32));
454
m_progressItem->setText(i18nc("@item:inlistbox Item for showing the progress view", "In Progress"));
455
m_progressItem->setData(Progress, ViewTypeRole);
456
parentItem->appendRow(m_progressItem);
458
m_viewHash[m_progressItem->index()] = 0;
461
void MainWindow::removeProgressItem()
466
QObject *progressView = m_viewHash.take(m_progressItem->index());
468
progressView->deleteLater();
470
m_viewModel->removeRow(m_progressItem->row());
471
m_progressItem = nullptr;
474
void MainWindow::openApplication(const QString &app)
477
m_appToBeOpened = app;
478
triggerOpenApplication();
479
if(!m_appToBeOpened.isEmpty())
480
connect(this, SIGNAL(viewsPopulated()), SLOT(triggerOpenApplication()));
483
void MainWindow::triggerOpenApplication()
485
if (!ResourcesModel::global())
488
AbstractResource* app = ResourcesModel::global()->resourceByPackageName(m_appToBeOpened);
490
AvailableView *view = qobject_cast<AvailableView *>(m_viewStack->currentWidget());
491
view->setResource(app);
493
m_appToBeOpened.clear();
494
disconnect(this, SIGNAL(viewsPopulated()), this, SLOT(triggerOpenApplication()));