akregator_part.cpp 25 KB
Newer Older
Frank Osterfeld's avatar
Frank Osterfeld committed
1
2
3
/*
    This file is part of Akregator.

4
5
    SPDX-FileCopyrightText: 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
    SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org>
Frank Osterfeld's avatar
Frank Osterfeld committed
6

7
    SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
Frank Osterfeld's avatar
Frank Osterfeld committed
8
*/
Frank Osterfeld's avatar
Frank Osterfeld committed
9

10
#include "akregator_part.h"
Laurent Montel's avatar
Laurent Montel committed
11
#include "akregator_debug.h"
12

Laurent Montel's avatar
Laurent Montel committed
13
#include <MessageViewer/MessageViewerSettings>
14

15
#include "akregatorconfig.h"
16
17
#include "aboutdata.h"
#include "actionmanagerimpl.h"
Frank Osterfeld's avatar
Frank Osterfeld committed
18
19
#include "article.h"
#include "fetchqueue.h"
20
#include "feedlist.h"
21
#include "framemanager.h"
Frank Osterfeld's avatar
Frank Osterfeld committed
22
#include "kernel.h"
23
#include "loadfeedlistcommand.h"
24
#include "mainwidget.h"
Frank Osterfeld's avatar
Frank Osterfeld committed
25
26
27
28
29
30
31
#include "notificationmanager.h"
#include "plugin.h"
#include "pluginmanager.h"
#include "storage.h"
#include "storagefactory.h"
#include "storagefactoryregistry.h"
#include "trayicon.h"
32
#include "widgets/akregatorcentralwidget.h"
33
#include "dummystorage/storagefactorydummyimpl.h"
Laurent Montel's avatar
Laurent Montel committed
34
#include "utils.h"
35
#include "akregator_options.h"
36
#include <PimCommon/BroadcastStatus>
Laurent Montel's avatar
Laurent Montel committed
37
#include "akregator-version.h"
38
#include "unityservicemanager.h"
Laurent Montel's avatar
Laurent Montel committed
39
#include <kio/filecopyjob.h>
40
41
#include <KNotifyConfigWidget>
#include <KConfig>
Laurent Montel's avatar
Laurent Montel committed
42
#include <QFileDialog>
43
#include <KMessageBox>
Laurent Montel's avatar
Laurent Montel committed
44

45
#include <QTemporaryFile>
46
#include <QWebEngineSettings>
Laurent Montel's avatar
Laurent Montel committed
47
#include <QSaveFile>
48
#include <KService>
49
#include <kxmlguifactory.h>
Volker Krause's avatar
Volker Krause committed
50
51
#include <KIO/StoredTransferJob>
#include <KJobWidgets>
Kevin Ottens's avatar
Kevin Ottens committed
52
#include <KPluginFactory>
53
#include <KParts/Plugin>
54
#include <KCMultiDialog>
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
55

56
#include <QApplication>
Frank Osterfeld's avatar
Frank Osterfeld committed
57
58
59
#include <QFile>
#include <QObject>
#include <QStringList>
Laurent Montel's avatar
Port it    
Laurent Montel committed
60
#include <QTextStream>
Frank Osterfeld's avatar
Frank Osterfeld committed
61
62
#include <QTimer>
#include <QWidget>
Laurent Montel's avatar
   
Laurent Montel committed
63
#include <QDomDocument>
Laurent Montel's avatar
Laurent Montel committed
64
#include "akregratormigrateapplication.h"
Volker Krause's avatar
Volker Krause committed
65
#include "partadaptor.h"
66

67
#include <memory>
Laurent Montel's avatar
Laurent Montel committed
68
#include <QFontDatabase>
Laurent Montel's avatar
Laurent Montel committed
69
#include <QStandardPaths>
70

Laurent Montel's avatar
Laurent Montel committed
71
namespace {
Laurent Montel's avatar
Laurent Montel committed
72
73
74
static QDomDocument createDefaultFeedList()
{
    QDomDocument doc;
75
    QDomProcessingInstruction z = doc.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""));
Laurent Montel's avatar
Laurent Montel committed
76
77
    doc.appendChild(z);

78
79
    QDomElement root = doc.createElement(QStringLiteral("opml"));
    root.setAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
Laurent Montel's avatar
Laurent Montel committed
80
81
    doc.appendChild(root);

82
    QDomElement head = doc.createElement(QStringLiteral("head"));
Laurent Montel's avatar
Laurent Montel committed
83
84
    root.appendChild(head);

85
    QDomElement text = doc.createElement(QStringLiteral("text"));
Laurent Montel's avatar
Laurent Montel committed
86
87
88
    text.appendChild(doc.createTextNode(i18n("Feeds")));
    head.appendChild(text);

89
    QDomElement body = doc.createElement(QStringLiteral("body"));
Laurent Montel's avatar
Laurent Montel committed
90
91
    root.appendChild(body);

92
93
    QDomElement mainFolder = doc.createElement(QStringLiteral("outline"));
    mainFolder.setAttribute(QStringLiteral("text"), QStringLiteral("KDE"));
Laurent Montel's avatar
Laurent Montel committed
94
95
    body.appendChild(mainFolder);

96
97
    QDomElement dot = doc.createElement(QStringLiteral("outline"));
    dot.setAttribute(QStringLiteral("text"), i18n("KDE Dot News"));
Christophe Giboudeaux's avatar
Christophe Giboudeaux committed
98
    dot.setAttribute(QStringLiteral("xmlUrl"), QStringLiteral("https://dot.kde.org/rss.xml"));
Laurent Montel's avatar
Laurent Montel committed
99
100
    mainFolder.appendChild(dot);

101
102
    QDomElement linuxFeeds = doc.createElement(QStringLiteral("outline"));
    linuxFeeds.setAttribute(QStringLiteral("text"), i18n("Linux.com"));
103
    linuxFeeds.setAttribute(QStringLiteral("xmlUrl"), QStringLiteral("https://www.linux.com/feed/"));
Laurent Montel's avatar
Laurent Montel committed
104
105
    mainFolder.appendChild(linuxFeeds);

106
107
    QDomElement planetkde = doc.createElement(QStringLiteral("outline"));
    planetkde.setAttribute(QStringLiteral("text"), i18n("Planet KDE"));
108
    planetkde.setAttribute(QStringLiteral("xmlUrl"), QStringLiteral("https://planet.kde.org/global/atom.xml"));
Laurent Montel's avatar
Laurent Montel committed
109
110
    mainFolder.appendChild(planetkde);

111
112
    QDomElement apps = doc.createElement(QStringLiteral("outline"));
    apps.setAttribute(QStringLiteral("text"), i18n("KDE Apps"));
113
    apps.setAttribute(QStringLiteral("xmlUrl"), QStringLiteral("https://store.kde.org/content.rdf"));
Laurent Montel's avatar
Laurent Montel committed
114
115
116
    mainFolder.appendChild(apps);

    // spanish feed(s)
117
118
    QDomElement spanishFolder = doc.createElement(QStringLiteral("outline"));
    spanishFolder.setAttribute(QStringLiteral("text"), i18n("Spanish feeds"));
Laurent Montel's avatar
Laurent Montel committed
119
120
    mainFolder.appendChild(spanishFolder);

121
122
    QDomElement spanishKde = doc.createElement(QStringLiteral("outline"));
    spanishKde.setAttribute(QStringLiteral("text"), i18n("Planet KDE España"));
123
    spanishKde.setAttribute(QStringLiteral("xmlUrl"), QStringLiteral("https://planet.kde-espana.org/atom.xml"));
Laurent Montel's avatar
Laurent Montel committed
124
125
126
127
128
129
    spanishFolder.appendChild(spanishKde);

    return doc;
}
}

Laurent Montel's avatar
Laurent Montel committed
130
namespace Akregator {
Laurent Montel's avatar
Laurent Montel committed
131
132
K_PLUGIN_FACTORY(AkregatorFactory, registerPlugin<Part>();
                 )
Stanislav Karchebny's avatar
   
Stanislav Karchebny committed
133

Laurent Montel's avatar
Laurent Montel committed
134
static Part *mySelf = nullptr;
Sashmit Bhaduri's avatar
Sashmit Bhaduri committed
135

Laurent Montel's avatar
Laurent Montel committed
136
Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList &)
137
    : KParts::Part(parent)
138
{
139
    mySelf = this;
140
141
142
143

    AkregratorMigrateApplication migrate;
    migrate.migrate();

144
145
    //Make sure to initialize settings
    Part::config();
146
147
    initFonts();

Laurent Montel's avatar
Laurent Montel committed
148
149
    setPluginLoadingMode(LoadPluginsIfEnabled);
    setPluginInterfaceVersion(AKREGATOR_PLUGIN_INTERFACE_VERSION);
150

Laurent Montel's avatar
Laurent Montel committed
151
    setComponentName(QStringLiteral("akregator"), i18n("Akregator"));
152
    setXMLFile(QStringLiteral("akregator_part.rc"), true);
Stephan Kulow's avatar
Stephan Kulow committed
153

Laurent Montel's avatar
Laurent Montel committed
154
    new PartAdaptor(this);
155
    QDBusConnection::sessionBus().registerObject(QStringLiteral("/Akregator"), this);
156

157
158
159
    const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/akregator/data/");
    QDir().mkpath(path);
    m_standardFeedList = path + QStringLiteral("/feeds.opml");
160

Laurent Montel's avatar
Laurent Montel committed
161
    auto *dummyFactory = new Backend::StorageFactoryDummyImpl();
162
163
164
165
    if (!Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key())) {
        // There was already a dummy factory registered.
        delete dummyFactory;
    }
Laurent Montel's avatar
Laurent Montel committed
166
    loadPlugins(QStringLiteral("storage"));   // FIXME: also unload them!
167

Laurent Montel's avatar
Laurent Montel committed
168
    m_storage = nullptr;
Laurent Montel's avatar
Laurent Montel committed
169
    Backend::StorageFactory *storageFactory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
Laurent Montel's avatar
Laurent Montel committed
170
    if (storageFactory != nullptr) {
171
        m_storage = storageFactory->createStorage(QStringList());
Laurent Montel's avatar
Laurent Montel committed
172
    }
173

Laurent Montel's avatar
Laurent Montel committed
174
    if (!m_storage) { // Houston, we have a problem
175
        m_storage = Backend::StorageFactoryRegistry::self()->getFactory(QStringLiteral("dummy"))->createStorage(QStringList());
176

Laurent Montel's avatar
Laurent Montel committed
177
        KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.", Settings::archiveBackend()), i18n("Plugin error"));
178
    }
179

180
    m_storage->open(true);
181
    Kernel::self()->setStorage(m_storage);
182

183
    m_actionManager = new ActionManagerImpl(this);
184
    ActionManager::setInstance(m_actionManager);
185

186
187
    mCentralWidget = new Akregator::AkregatorCentralWidget(parentWidget);
    connect(mCentralWidget, &AkregatorCentralWidget::restoreSession, this, &Part::slotRestoreSession);
Laurent Montel's avatar
Laurent Montel committed
188
    m_mainWidget = new Akregator::MainWidget(this, parentWidget, m_actionManager, QStringLiteral("akregator_view"));
189
    mCentralWidget->setMainWidget(m_mainWidget);
190

Laurent Montel's avatar
Laurent Montel committed
191
192
    connect(Kernel::self()->frameManager(), &FrameManager::signalCaptionChanged, this, &Part::setWindowCaption);
    connect(Kernel::self()->frameManager(), &FrameManager::signalStatusText, this, &Part::slotSetStatusText);
193

194
    // notify the part that this is our internal widget
195
    setWidget(mCentralWidget);
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
196

197
198
    //Initialize instance.
    (void)UnityServiceManager::instance();
199
200
201
    if (Settings::showUnreadInTaskbar()) {
        connect(m_mainWidget.data(), &MainWidget::signalUnreadCountChanged, UnityServiceManager::instance(), &UnityServiceManager::slotSetUnread);
    }
202

Laurent Montel's avatar
Laurent Montel committed
203
    if (Settings::showTrayIcon() && !TrayIcon::getInstance()) {
Laurent Montel's avatar
Laurent Montel committed
204
        initializeTrayIcon();
Laurent Montel's avatar
Laurent Montel committed
205
        QWidget *const notificationParent = isTrayIconEnabled() ? m_mainWidget->window() : nullptr;
206
        NotificationManager::self()->setWidget(notificationParent, componentName());
207
    }
208

209
    connect(qApp, &QCoreApplication::aboutToQuit, this, &Part::slotOnShutdown);
210

211
    m_autosaveTimer = new QTimer(this);
Laurent Montel's avatar
Laurent Montel committed
212
    connect(m_autosaveTimer, &QTimer::timeout, this, &Part::slotSaveFeedList);
Laurent Montel's avatar
Laurent Montel committed
213
    m_autosaveTimer->start(5 * 60 * 1000); // 5 minutes
214

Laurent Montel's avatar
Laurent Montel committed
215
    loadPlugins(QStringLiteral("extension"));   // FIXME: also unload them!
216
217
218
    if (mCentralWidget->previousSessionCrashed()) {
        mCentralWidget->needToRestoreCrashedSession();
    } else {
Laurent Montel's avatar
Laurent Montel committed
219
        m_doCrashSave = true;
Laurent Montel's avatar
Laurent Montel committed
220
221
        autoReadProperties();
    }
222
}
223

224
225
226
227
228
229
230
231
232
KSharedConfig::Ptr Part::config()
{
    assert(mySelf);
    if (!mySelf->mConfig) {
        mySelf->mConfig = KSharedConfig::openConfig(QStringLiteral("akregatorrc"));
    }
    return mySelf->mConfig;
}

Laurent Montel's avatar
Laurent Montel committed
233
234
235
236
237
238
239
void Part::updateQuickSearchLineText()
{
    if (m_mainWidget) {
        m_mainWidget->updateQuickSearchLineText();
    }
}

Laurent Montel's avatar
Laurent Montel committed
240
void Part::loadPlugins(const QString &type)
241
{
Laurent Montel's avatar
Laurent Montel committed
242
    const KService::List offers = PluginManager::query(QStringLiteral("[X-KDE-akregator-plugintype] == '%1'").arg(type));
243

Laurent Montel's avatar
Laurent Montel committed
244
    for (const KService::Ptr &i : offers) {
Laurent Montel's avatar
Laurent Montel committed
245
246
        Akregator::Plugin *plugin = PluginManager::createFromService(i, this);
        if (!plugin) {
247
            continue;
Laurent Montel's avatar
Laurent Montel committed
248
        }
249
        plugin->initialize();
Laurent Montel's avatar
Laurent Montel committed
250
        plugin->insertGuiClients(this);
251
252
253
    }
}

254
255
void Part::slotOnShutdown()
{
256
    autoSaveProperties();
257
    m_shuttingDown = true;
258
    m_autosaveTimer->stop();
259
260
261
262
    if (m_mainWidget) {
        saveSettings();
        m_mainWidget->slotOnShutdown();
    }
263
    //delete m_mainWidget;
264
    delete TrayIcon::getInstance();
Laurent Montel's avatar
Laurent Montel committed
265
    TrayIcon::setInstance(nullptr);
266
    delete m_storage;
Laurent Montel's avatar
Laurent Montel committed
267
    m_storage = nullptr;
268
    //delete m_actionManager;
269
}
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
270

Laurent Montel's avatar
Laurent Montel committed
271
void Part::initializeTrayIcon()
272
{
Laurent Montel's avatar
Laurent Montel committed
273
    auto *trayIcon = new TrayIcon(m_mainWidget->window());
Laurent Montel's avatar
Laurent Montel committed
274
275
    TrayIcon::setInstance(trayIcon);
    m_actionManager->setTrayIcon(trayIcon);
276

277
    trayIcon->setEnabled(isTrayIconEnabled());
278

Laurent Montel's avatar
Laurent Montel committed
279
280
281
    connect(m_mainWidget.data(), &MainWidget::signalUnreadCountChanged, trayIcon, &TrayIcon::slotSetUnread);
    connect(m_mainWidget.data(), &MainWidget::signalArticlesSelected,
            this, &Part::signalArticlesSelected);
282

Laurent Montel's avatar
Laurent Montel committed
283
284
    m_mainWidget->slotSetTotalUnread();
}
285

Laurent Montel's avatar
Laurent Montel committed
286
287
void Part::slotSettingsChanged()
{
288
289
290
291
292
293
294
295
    if (Settings::showUnreadInTaskbar()) {
        connect(m_mainWidget.data(), &MainWidget::signalUnreadCountChanged, UnityServiceManager::instance(), &UnityServiceManager::slotSetUnread);
        m_mainWidget->slotSetTotalUnread();
    } else {
        disconnect(m_mainWidget.data(), &MainWidget::signalUnreadCountChanged, UnityServiceManager::instance(), &UnityServiceManager::slotSetUnread);
        UnityServiceManager::instance()->slotSetUnread(0);
    }

296
    NotificationManager::self()->setWidget(isTrayIconEnabled() ? m_mainWidget->window() : nullptr, componentName());
Laurent Montel's avatar
Laurent Montel committed
297
298
299
300
301
302
    if (Settings::showTrayIcon()) {
        if (!TrayIcon::getInstance()) {
            initializeTrayIcon();
            m_mainWidget->slotSetTotalUnread();
        }
    } else {
303
304
        TrayIcon::getInstance()->disconnect();
        delete TrayIcon::getInstance();
Laurent Montel's avatar
Laurent Montel committed
305
306
        TrayIcon::setInstance(nullptr);
        m_actionManager->setTrayIcon(nullptr);
307
308
    }

Laurent Montel's avatar
Laurent Montel committed
309
310
    const QStringList fonts {
        Settings::standardFont(),
Laurent Montel's avatar
Laurent Montel committed
311
312
313
314
315
316
        Settings::fixedFont(),
        Settings::sansSerifFont(),
        Settings::serifFont(),
        Settings::standardFont(),
        Settings::standardFont(),
        QStringLiteral("0")
Laurent Montel's avatar
Laurent Montel committed
317
    };
318
319
    Settings::setFonts(fonts);

Laurent Montel's avatar
Laurent Montel committed
320
    if (Settings::minimumFontSize() > Settings::mediumFontSize()) {
321
        Settings::setMediumFontSize(Settings::minimumFontSize());
Laurent Montel's avatar
Laurent Montel committed
322
    }
323
    saveSettings();
Laurent Montel's avatar
Laurent Montel committed
324
    Q_EMIT signalSettingsChanged();
325
326

    initFonts();
327
}
328

Laurent Montel's avatar
Laurent Montel committed
329
void Part::slotSetStatusText(const QString &statusText)
330
{
331
    PimCommon::BroadcastStatus::instance()->setStatusMsg(statusText);
332
333
}

334
void Part::saveSettings()
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
335
{
336
337
338
    if (m_mainWidget) {
        m_mainWidget->saveSettings();
    }
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
339
340
}

341
Part::~Part()
342
{
343
    disconnect(qApp, &QCoreApplication::aboutToQuit, this, &Part::slotOnShutdown);
Laurent Montel's avatar
Laurent Montel committed
344
    qCDebug(AKREGATOR_LOG) << "Part::~Part() enter";
345
346
347
348
    // If the widget is destroyed for some reason, KParts::Part will set its
    // widget property to 0 and then delete itself (and therefore this object).
    // In this case, it's not safe to do our normal shutdown routine.
    if (widget() && !m_shuttingDown) {
349
        slotOnShutdown();
Laurent Montel's avatar
Laurent Montel committed
350
    }
Laurent Montel's avatar
Laurent Montel committed
351
    qCDebug(AKREGATOR_LOG) << "Part::~Part(): leaving";
352
353
}

Laurent Montel's avatar
Laurent Montel committed
354
void Part::readProperties(const KConfigGroup &config)
355
{
356
    m_backedUpList = false;
357
    openStandardFeedList();
358

Laurent Montel's avatar
Laurent Montel committed
359
    if (m_mainWidget) {
360
        m_mainWidget->readProperties(config);
Laurent Montel's avatar
Laurent Montel committed
361
    }
362
363
}

Laurent Montel's avatar
Laurent Montel committed
364
void Part::saveProperties(KConfigGroup &config)
365
{
Laurent Montel's avatar
Laurent Montel committed
366
    if (m_mainWidget) {
Frank Osterfeld's avatar
Frank Osterfeld committed
367
        slotSaveFeedList();
368
        m_mainWidget->saveProperties(config);
369
    }
370
371
}

Laurent Montel's avatar
Laurent Montel committed
372
373
374
375
376
void Part::exportFile(const QString &str)
{
    exportFile(QUrl(str));
}

377
void Part::openStandardFeedList()
378
{
Laurent Montel's avatar
Laurent Montel committed
379
    if (!m_standardFeedList.isEmpty()) {
380
        openFile(m_standardFeedList);
Laurent Montel's avatar
Laurent Montel committed
381
    }
382
383
}

384
void Part::openFile(const QString &filePath)
Laurent Montel's avatar
Laurent Montel committed
385
386
{
    if (m_loadFeedListCommand || m_standardListLoaded) {
387
        return;
Laurent Montel's avatar
Laurent Montel committed
388
    }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
389
    QScopedPointer<LoadFeedListCommand> cmd(new LoadFeedListCommand(m_mainWidget));
Laurent Montel's avatar
Laurent Montel committed
390
391
    cmd->setParentWidget(m_mainWidget);
    cmd->setStorage(Kernel::self()->storage());
392
    cmd->setFileName(filePath);
Laurent Montel's avatar
Laurent Montel committed
393
    cmd->setDefaultFeedList(createDefaultFeedList());
394
395
    connect(cmd.data(), &LoadFeedListCommand::result,
            this, &Part::feedListLoaded);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
396
    m_loadFeedListCommand = cmd.take();
Frank Osterfeld's avatar
Frank Osterfeld committed
397
    m_loadFeedListCommand->start();
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
398
399
}

Laurent Montel's avatar
Laurent Montel committed
400
401
402
403
bool Part::writeToTextFile(const QString &data, const QString &filename) const
{
    QSaveFile file(filename);
    if (!file.open(QIODevice::WriteOnly)) {
404
        return false;
Laurent Montel's avatar
Laurent Montel committed
405
406
407
    }
    QTextStream stream(&file);
    stream.setCodec("UTF-8");
408
    stream << data
Laurent Montel's avatar
Laurent Montel committed
409
410
        << Qt::endl
    ;
Laurent Montel's avatar
Laurent Montel committed
411
    return file.commit();
412
413
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
414
void Part::feedListLoaded(const QSharedPointer<FeedList> &list)
Laurent Montel's avatar
Laurent Montel committed
415
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
416
    Q_ASSERT(!m_standardListLoaded);
417
418
419
420
    if (!m_mainWidget) {
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
421
    m_mainWidget->setFeedList(list);
Laurent Montel's avatar
Laurent Montel committed
422
    m_standardListLoaded = list != nullptr;
423

Laurent Montel's avatar
Laurent Montel committed
424
    if (Settings::markAllFeedsReadOnStartup()) {
425
        m_mainWidget->slotMarkAllFeedsRead();
Laurent Montel's avatar
Laurent Montel committed
426
    }
427

Laurent Montel's avatar
Laurent Montel committed
428
    if (m_standardListLoaded) {
429
        QTimer::singleShot(0, this, &Part::flushAddFeedRequests);
Laurent Montel's avatar
Laurent Montel committed
430
    }
Frank Osterfeld's avatar
Frank Osterfeld committed
431

Laurent Montel's avatar
Laurent Montel committed
432
    if (Settings::fetchOnStartup()) {
433
        m_mainWidget->slotFetchAllFeeds();
Laurent Montel's avatar
Laurent Montel committed
434
    }
435
}
436

Laurent Montel's avatar
Laurent Montel committed
437
438
void Part::flushAddFeedRequests()
{
439
440
441
442
    if (!m_mainWidget) {
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
443
    for (const AddFeedRequest &i : qAsConst(m_requests)) {
Laurent Montel's avatar
Laurent Montel committed
444
        for (const QString &j : qAsConst(i.urls)) {
Laurent Montel's avatar
Laurent Montel committed
445
446
447
            m_mainWidget->addFeedToGroup(j, i.group);
        }
        NotificationManager::self()->slotNotifyFeeds(i.urls);
Frank Osterfeld's avatar
Frank Osterfeld committed
448
449
450
451
    }
    m_requests.clear();
}

452
void Part::slotSaveFeedList()
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
453
{
454
    // don't save to the standard feed list, when it wasn't completely loaded before
Laurent Montel's avatar
Laurent Montel committed
455
    if (!m_standardListLoaded) {
456
        return;
Laurent Montel's avatar
Laurent Montel committed
457
    }
458

459
    // the first time we overwrite the feed list, we create a backup
Laurent Montel's avatar
Laurent Montel committed
460
    if (!m_backedUpList) {
461
        const QString backup = m_standardFeedList + QLatin1Char('~');
462
463
464
        if (QFile::exists(backup)) {
            QFile::remove(backup);
        }
465
        if (QFile::copy(m_standardFeedList, backup)) {
466
            m_backedUpList = true;
Laurent Montel's avatar
Laurent Montel committed
467
        }
468
    }
469

470
    const QString xml = m_mainWidget->feedListToOPML().toString();
Laurent Montel's avatar
Laurent Montel committed
471
472
473
    if (xml.isEmpty()) {
        return;
    }
Laurent Montel's avatar
Laurent Montel committed
474

Laurent Montel's avatar
Laurent Montel committed
475
    m_storage->storeFeedList(xml);
476
    if (writeToTextFile(xml, m_standardFeedList)) {
477
        return;
Laurent Montel's avatar
Laurent Montel committed
478
    }
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
479

Laurent Montel's avatar
Laurent Montel committed
480
    KMessageBox::error(m_mainWidget,
481
                       i18n("Access denied: Cannot save feed list to <b>%1</b>. Please check your permissions.", m_standardFeedList),
Laurent Montel's avatar
Laurent Montel committed
482
                       i18n("Write Error"));
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
483
484
}

485
bool Part::isTrayIconEnabled() const
486
487
488
489
{
    return Settings::showTrayIcon();
}

Laurent Montel's avatar
Laurent Montel committed
490
void Part::importFile(const QUrl &url)
491
{
492
    QString filename;
Volker Krause's avatar
Volker Krause committed
493
    QTemporaryFile tempFile;
494

Laurent Montel's avatar
Laurent Montel committed
495
    if (url.isLocalFile()) {
496
        filename = url.toLocalFile();
Laurent Montel's avatar
Laurent Montel committed
497
    } else {
Laurent Montel's avatar
Laurent Montel committed
498
        if (!tempFile.open()) {
Volker Krause's avatar
Volker Krause committed
499
            return;
Laurent Montel's avatar
Laurent Montel committed
500
        }
Volker Krause's avatar
Volker Krause committed
501
        filename = tempFile.fileName();
502

Volker Krause's avatar
Volker Krause committed
503
504
505
506
        auto job = KIO::file_copy(url, QUrl::fromLocalFile(filename), -1, KIO::Overwrite | KIO::HideProgressInfo);
        KJobWidgets::setWindow(job, m_mainWidget);
        if (!job->exec()) {
            KMessageBox::error(m_mainWidget, job->errorString());
507
508
509
            return;
        }
    }
510

511
    QFile file(filename);
Laurent Montel's avatar
Laurent Montel committed
512
    if (file.open(QIODevice::ReadOnly)) {
513
514
        // Read OPML feeds list and build QDom tree.
        QDomDocument doc;
Laurent Montel's avatar
Laurent Montel committed
515
516
517
518
519
520
        if (doc.setContent(file.readAll())) {
            m_mainWidget->importFeedList(doc);
        } else {
            KMessageBox::error(m_mainWidget, i18n("Could not import the file %1 (no valid OPML)", filename), i18n("OPML Parsing Error"));
        }
    } else {
521
        KMessageBox::error(m_mainWidget, i18n("The file %1 could not be read, check if it exists or if it is readable for the current user.", filename), i18n("Read Error"));
Laurent Montel's avatar
Laurent Montel committed
522
    }
523
524
}

Laurent Montel's avatar
Laurent Montel committed
525
void Part::exportFile(const QUrl &url)
526
{
Laurent Montel's avatar
Laurent Montel committed
527
    if (url.isLocalFile()) {
528
        const QString fname = url.toLocalFile();
529

Laurent Montel's avatar
Laurent Montel committed
530
531
532
        if (!writeToTextFile(m_mainWidget->feedListToOPML().toString(), fname)) {
            KMessageBox::error(m_mainWidget, i18n("Access denied: cannot write to file %1. Please check your permissions.", fname), i18n("Write Error"));
        }
533

Frank Osterfeld's avatar
Frank Osterfeld committed
534
        return;
Laurent Montel's avatar
Laurent Montel committed
535
    } else {
Volker Krause's avatar
Volker Krause committed
536
537
538
539
        auto job = KIO::storedPut(m_mainWidget->feedListToOPML().toString().toUtf8(), url, -1);
        KJobWidgets::setWindow(job, m_mainWidget);
        if (!job->exec()) {
            KMessageBox::error(m_mainWidget, job->errorString());
Laurent Montel's avatar
Laurent Montel committed
540
        }
541
    }
542
}
Stanislav Karchebny's avatar
   
Stanislav Karchebny committed
543

544
void Part::fileImport()
545
{
546
    const QString filters = i18n("OPML Outlines (%1);;All Files (*)", QStringLiteral("*.opml *.xml"));
Laurent Montel's avatar
Laurent Montel committed
547
    const QUrl url = QFileDialog::getOpenFileUrl(m_mainWidget, QString(), QUrl(), filters);
Laurent Montel's avatar
Laurent Montel committed
548
    if (!url.isEmpty()) {
549
        importFile(url);
Laurent Montel's avatar
Laurent Montel committed
550
    }
551
552
}

Laurent Montel's avatar
Laurent Montel committed
553
void Part::fileExport()
554
{
555
    const QString filters = i18n("OPML Outlines (%1);;All Files (*)", QStringLiteral("*.opml *.xml"));
Laurent Montel's avatar
Laurent Montel committed
556
    const QUrl url = QFileDialog::getSaveFileUrl(m_mainWidget, QString(), QUrl(), filters);
557

Laurent Montel's avatar
Laurent Montel committed
558
    if (!url.isEmpty()) {
559
        exportFile(url);
Laurent Montel's avatar
Laurent Montel committed
560
    }
561
562
}

563
void Part::fetchAllFeeds()
564
{
565
    m_mainWidget->slotFetchAllFeeds();
566
567
}

Laurent Montel's avatar
Laurent Montel committed
568
void Part::fetchFeedUrl(const QString &s)
569
{
Laurent Montel's avatar
Laurent Montel committed
570
    qCDebug(AKREGATOR_LOG) << "fetchFeedURL==" << s;
571
}
Stanislav Karchebny's avatar
   
Stanislav Karchebny committed
572

Laurent Montel's avatar
Laurent Montel committed
573
void Part::addFeedsToGroup(const QStringList &urls, const QString &group)
574
{
Frank Osterfeld's avatar
Frank Osterfeld committed
575
576
577
    AddFeedRequest req;
    req.group = group;
    req.urls = urls;
Laurent Montel's avatar
Laurent Montel committed
578
579
    m_requests.append(req);
    if (m_standardListLoaded) {
Frank Osterfeld's avatar
Frank Osterfeld committed
580
        flushAddFeedRequests();
Laurent Montel's avatar
Laurent Montel committed
581
    }
582
583
}

584
585
void Part::addFeed()
{
586
    m_mainWidget->slotFeedAdd();
587
588
}

589
void Part::showNotificationOptions()
590
{
591
    const Akregator::AboutData about;
592
    KNotifyConfigWidget::configure(m_mainWidget, about.productName());
593
594
}

595
void Part::showOptions()
596
{
597
    saveSettings();
598

Laurent Montel's avatar
Laurent Montel committed
599
600
    if (!m_dialog) {
        m_dialog = new KCMultiDialog(m_mainWidget);
601
        connect(m_dialog, qOverload<>(&KCMultiDialog::configCommitted),
Laurent Montel's avatar
Laurent Montel committed
602
                this, &Part::slotSettingsChanged);
603
        if (TrayIcon::getInstance()) {
604
            connect(m_dialog, qOverload<>(&KCMultiDialog::configCommitted),
605
606
                    TrayIcon::getInstance(), &TrayIcon::settingsChanged);
        }
607

Laurent Montel's avatar
Laurent Montel committed
608
609
610
611
612
        m_dialog->addModule(QStringLiteral("akregator_config_general"));
        m_dialog->addModule(QStringLiteral("akregator_config_appearance"));
        m_dialog->addModule(QStringLiteral("akregator_config_archive"));
        m_dialog->addModule(QStringLiteral("akregator_config_browser"));
        m_dialog->addModule(QStringLiteral("akregator_config_advanced"));
613
        m_dialog->addModule(QStringLiteral("akregator_config_plugins"));
Laurent Montel's avatar
Laurent Montel committed
614
615
616
#ifdef WITH_KUSERFEEDBACK
        m_dialog->addModule(QStringLiteral("akregator_config_userfeedback"));
#endif
617
618
619
620
    }

    m_dialog->show();
    m_dialog->raise();
Stanislav Karchebny's avatar
 
Stanislav Karchebny committed
621
622
}

623
void Part::initFonts()
624
{
625
    QStringList fonts = Settings::fonts();
Laurent Montel's avatar
Laurent Montel committed
626
    if (fonts.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
627
628
629
630
        fonts.append(QFontDatabase::systemFont(QFontDatabase::GeneralFont).family());
        fonts.append(QFontDatabase::systemFont(QFontDatabase::FixedFont).family());
        fonts.append(QFontDatabase::systemFont(QFontDatabase::GeneralFont).family());
        fonts.append(QFontDatabase::systemFont(QFontDatabase::GeneralFont).family());
631
        fonts.append(QStringLiteral("0"));
632
633
    }
    Settings::setFonts(fonts);
Laurent Montel's avatar
Laurent Montel committed
634
    if (Settings::standardFont().isEmpty()) {
635
        Settings::setStandardFont(fonts[0]);
Laurent Montel's avatar
Laurent Montel committed
636
637
    }
    if (Settings::fixedFont().isEmpty()) {
638
        Settings::setFixedFont(fonts[1]);
Laurent Montel's avatar
Laurent Montel committed
639
640
    }
    if (Settings::sansSerifFont().isEmpty()) {
641
        Settings::setSansSerifFont(fonts[2]);
Laurent Montel's avatar
Laurent Montel committed
642
643
    }
    if (Settings::serifFont().isEmpty()) {
644
        Settings::setSerifFont(fonts[3]);
Laurent Montel's avatar
Laurent Montel committed
645
    }
646

Laurent Montel's avatar
Laurent Montel committed
647
648
    //TODO add CursiveFont, FantasyFont

Laurent Montel's avatar
Laurent Montel committed
649
    KConfigGroup conf(Settings::self()->config(), "HTML Settings");
650

651
    KConfig _konq(QStringLiteral("konquerorrc"), KConfig::NoGlobals);
Stephan Kulow's avatar
Stephan Kulow committed
652
    KConfigGroup konq(&_konq, "HTML Settings");
653

Laurent Montel's avatar
Laurent Montel committed
654
    if (!conf.hasKey("MinimumFontSize")) {
655
        int minfs;
Laurent Montel's avatar
Laurent Montel committed
656
        if (konq.hasKey("MinimumFontSize")) {
657
            minfs = konq.readEntry("MinimumFontSize", 8);
Laurent Montel's avatar
Laurent Montel committed
658
659
660
        } else {
            minfs = std::max(QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize() - 2, 4);
        }
661
662
663
        Settings::setMinimumFontSize(minfs);
    }

Laurent Montel's avatar
Laurent Montel committed
664
    if (!conf.hasKey("MediumFontSize")) {
665
        int medfs;
Laurent Montel's avatar
Laurent Montel committed
666
        if (konq.hasKey("MediumFontSize")) {
667
            medfs = konq.readEntry("MediumFontSize", 12);
Laurent Montel's avatar
Laurent Montel committed
668
        } else {
Laurent Montel's avatar
Laurent Montel committed
669
            medfs = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize();
Laurent Montel's avatar
Laurent Montel committed
670
        }
671
672
        Settings::setMediumFontSize(medfs);
    }
Laurent Montel's avatar
Laurent Montel committed
673
674
675
676
677
678
    QWebEngineSettings::defaultSettings()->setFontFamily(QWebEngineSettings::StandardFont, Settings::standardFont());
    QWebEngineSettings::defaultSettings()->setFontFamily(QWebEngineSettings::FixedFont, Settings::fixedFont());
    QWebEngineSettings::defaultSettings()->setFontFamily(QWebEngineSettings::SerifFont, Settings::serifFont());
    QWebEngineSettings::defaultSettings()->setFontFamily(QWebEngineSettings::SansSerifFont, Settings::sansSerifFont());
    QWebEngineSettings::defaultSettings()->setFontSize(QWebEngineSettings::MinimumFontSize, Settings::minimumFontSize());
    QWebEngineSettings::defaultSettings()->setFontSize(QWebEngineSettings::DefaultFontSize, Settings::mediumFontSize());
Frank Osterfeld's avatar
Frank Osterfeld committed
679
680
}

681
bool Part::handleCommandLine(const QStringList &args)
Laurent Montel's avatar
Laurent Montel committed
682
{
683
684
685
    QCommandLineParser parser;
    akregator_options(&parser);
    parser.process(args);
Frank Osterfeld's avatar
Frank Osterfeld committed
686

687
    const QString addFeedGroup = parser.isSet(QStringLiteral("group"))
Laurent Montel's avatar
Laurent Montel committed
688
689
                                 ? parser.value(QStringLiteral("group"))
                                 : i18n("Imported Folder");
690

691
    QStringList feedsToAdd = parser.values(QStringLiteral("addfeed"));
692
693

    if (feedsToAdd.isEmpty() && !parser.positionalArguments().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
694
695
        const auto positionalArguments = parser.positionalArguments();
        for (QString url : positionalArguments) {
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
            const QUrl tempUrl = QUrl::fromUserInput(url);
            if (tempUrl.isLocalFile()) {
                const QString tempLocalFile = tempUrl.toLocalFile();
                if (tempLocalFile.startsWith(QDir::tempPath())) {
                    const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/akregator/data/");
                    QDir().mkpath(path);
                    QFile f(tempLocalFile);
                    const QString newRssFileName = path + QFileInfo(f).fileName();
                    if (!f.copy(newRssFileName)) {
                        qCWarning(AKREGATOR_LOG) << "Impossible to copy in local folder" << newRssFileName;
                    } else {
                        url = newRssFileName;
                    }
                }
            }

Laurent Montel's avatar
Laurent Montel committed
712
            feedsToAdd.append(url);
Laurent Montel's avatar
Laurent Montel committed
713
        }
Frank Osterfeld's avatar
Frank Osterfeld committed
714
715
    }

Laurent Montel's avatar
Laurent Montel committed
716
717
718
    if (!feedsToAdd.isEmpty()) {
        addFeedsToGroup(feedsToAdd, addFeedGroup);
    }
Frank Osterfeld's avatar
Frank Osterfeld committed
719
    return true;
720
721
}

722
723
void Part::clearCrashProperties()
{
Laurent Montel's avatar
Laurent Montel committed
724
    if (!m_doCrashSave) {
725
        return;
Laurent Montel's avatar
Laurent Montel committed
726
    }
727
    KConfig config(QStringLiteral("crashed"), KConfig::SimpleConfig,
728
                   QStandardPaths::AppDataLocation);
729