konqclosedwindowsmanager.cpp 15.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* This file is part of the KDE project
   Copyright 2007 David Faure <faure@kde.org>
   Copyright 2007 Eduardo Robles Elvira <edulix@gmail.com>

   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; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "konqclosedwindowsmanager.h"
#include "konqsettingsxt.h"
#include "konqmisc.h"
#include "konqcloseditem.h"
#include "konqclosedwindowsmanageradaptor.h"
#include "konqclosedwindowsmanager_interface.h"
#include <kio/fileundomanager.h>
#include <QDirIterator>
#include <QMetaType>
30
31
32
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusReply>
Laurent Montel's avatar
Laurent Montel committed
33
#include <KLocalizedString>
34

35
#include <kconfig.h>
36
37
#include <QStandardPaths>
#include <KSharedConfig>
38
39
40
41
42
43
44
45
46
47

Q_DECLARE_METATYPE(QList<QVariant>)

class KonqClosedWindowsManagerPrivate
{
public:
    KonqClosedWindowsManager instance;
    int m_maxNumClosedItems;
};

48
static KonqClosedWindowsManagerPrivate *myKonqClosedWindowsManagerPrivate = nullptr;
49
50
51

KonqClosedWindowsManager::KonqClosedWindowsManager()
{
52
    new KonqClosedWindowsManagerAdaptor(this);
53

54
55
    const QString dbusPath = QStringLiteral("/KonqUndoManager");
    const QString dbusInterface = QStringLiteral("org.kde.Konqueror.UndoManager");
56
57

    QDBusConnection dbus = QDBusConnection::sessionBus();
58
    dbus.registerObject(dbusPath, this);
59
60
    dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("notifyClosedWindowItem"), this, SLOT(slotNotifyClosedWindowItem(QString,int,QString,QString,QDBusMessage)));
    dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("notifyRemove"), this, SLOT(slotNotifyRemove(QString,QString,QDBusMessage)));
61
62

    QString filename = "closeditems/" + KonqMisc::encodeFilename(dbus.baseService());
63
    QString file = QDir::tempPath() + QLatin1Char('/') +  filename;
64
65
    QFile::remove(file);

Laurent Montel's avatar
Laurent Montel committed
66
    KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo");
67
68
    m_numUndoClosedItems = configGroup.readEntry("Number of Closed Windows", 0);

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
69
    m_konqClosedItemsConfig = nullptr;
70
    m_blockClosedItems = false;
71
    m_konqClosedItemsStore = new KConfig(file, KConfig::SimpleConfig);
72
73
74
75
76
77
78
}

KonqClosedWindowsManager::~KonqClosedWindowsManager()
{
    // Do some file cleaning
    removeClosedItemsConfigFiles();

79
    qDeleteAll(m_closedWindowItemList); // must be done before deleting the kconfigs
80
    delete m_konqClosedItemsConfig;
81
    delete m_konqClosedItemsStore;
82
83
}

84
KConfig *KonqClosedWindowsManager::memoryStore()
85
{
86
    return m_konqClosedItemsStore;
87
88
89
90
}

KonqClosedWindowsManager *KonqClosedWindowsManager::self()
{
91
92
93
    if (!myKonqClosedWindowsManagerPrivate) {
        myKonqClosedWindowsManagerPrivate = new KonqClosedWindowsManagerPrivate;
    }
94
95
96
    return &myKonqClosedWindowsManagerPrivate->instance;
}

97
98
99
100
101
102
void KonqClosedWindowsManager::destroy()
{
    delete myKonqClosedWindowsManagerPrivate;
    myKonqClosedWindowsManagerPrivate = nullptr;
}

103
void KonqClosedWindowsManager::addClosedWindowItem(KonqUndoManager
104
        *real_sender, KonqClosedWindowItem *closedWindowItem, bool propagate)
105
106
107
{
    readConfig();
    // If we are off the limit, remove the last closed window item
108
109
110
    if (m_closedWindowItemList.size() >=
            KonqSettings::maxNumClosedItems()) {
        KonqClosedWindowItem *last = m_closedWindowItemList.last();
111

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
112
        emit removeWindowInOtherInstances(nullptr, last);
113
114
115
116
117
118
        emitNotifyRemove(last);

        m_closedWindowItemList.removeLast();
        delete last;
    }

119
    if (!m_blockClosedItems) {
120
121
122
        m_numUndoClosedItems++;
        emit addWindowInOtherInstances(real_sender, closedWindowItem);
    }
123

124
125
126
127
128
129
    // The prepend goes after emit addWindowInOtherInstances() because otherwise
    // the first time addWindowInOtherInstances() is emitted, KonqUndoManager
    // will catch it and it will call to its private populate() function which
    // will add to the undo list closedWindowItem, and then it will add it again
    // but we want it to be added only once.
    m_closedWindowItemList.prepend(closedWindowItem);
130

131
    if (propagate) {
132
133
134
135
        // if it needs to be propagated means that it's a local window and thus
        // we need to call to saveConfig() to keep updated the kconfig file, so
        // that new konqueror instances can read it correctly updated.
        saveConfig();
136

137
138
        // Once saved, tell to other konqi processes
        emitNotifyClosedWindowItem(closedWindowItem);
139
140
141
142
    }
}

void KonqClosedWindowsManager::removeClosedWindowItem(KonqUndoManager
143
        *real_sender, const KonqClosedWindowItem *closedWindowItem, bool propagate)
144
145
{
    readConfig();
146
    auto it = std::find(m_closedWindowItemList.begin(), m_closedWindowItemList.end(), closedWindowItem);
147
148

    // If the item was found, remove it from the list
149
    if (it != m_closedWindowItemList.end()) {
150
151
152
153
154
        m_closedWindowItemList.erase(it);
        m_numUndoClosedItems--;
    }
    emit removeWindowInOtherInstances(real_sender, closedWindowItem);

155
    if (propagate) {
156
        emitNotifyRemove(closedWindowItem);
157
    }
158
159
}

160
const QList<KonqClosedWindowItem *> &KonqClosedWindowsManager::closedWindowItemList()
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
{
    readConfig();
    return m_closedWindowItemList;
}

void KonqClosedWindowsManager::readSettings()
{
    readConfig();
}

static QString dbusService()
{
    return QDBusConnection::sessionBus().baseService();
}

/**
 * Returns whether the DBUS call we are handling was a call from us self
 */
179
bool isSenderOfSignal(const QDBusMessage &msg)
180
181
182
183
{
    return dbusService() == msg.service();
}

184
bool isSenderOfSignal(const QString &service)
185
186
187
188
189
{
    return dbusService() == service;
}

void KonqClosedWindowsManager::emitNotifyClosedWindowItem(
190
    const KonqClosedWindowItem *closedWindowItem)
191
{
192
193
194
195
    emit notifyClosedWindowItem(closedWindowItem->title(),
                                closedWindowItem->numTabs(),
                                m_konqClosedItemsStore->name(),
                                closedWindowItem->configGroup().name());
196
197
198
199
200
}

void KonqClosedWindowsManager::emitNotifyRemove(
    const KonqClosedWindowItem *closedWindowItem)
{
201
    const KonqClosedRemoteWindowItem *closedRemoteWindowItem =
202
203
204
205
206
        dynamic_cast<const KonqClosedRemoteWindowItem *>(closedWindowItem);

    // Here we do this because there's no need to call to configGroup() if it's
    // a remote window item, and it would be error prone to be so, because
    // it could give us a null pointer and konqueror would crash
207
208
209
    if (closedRemoteWindowItem)
        emit notifyRemove(closedRemoteWindowItem->remoteConfigFileName(),
                          closedRemoteWindowItem->remoteGroupName());
210
    else
211
212
        emit notifyRemove(closedWindowItem->configGroup().config()->name(),
                          closedWindowItem->configGroup().name());
213
214
215
}

void KonqClosedWindowsManager::slotNotifyClosedWindowItem(
216
    const QString &title, int numTabs, const QString &configFileName,
217
    const QString &configGroup, const QString &service)
218
{
219
    if (isSenderOfSignal(service)) {
220
        return;
221
    }
222
223

    // Create a new ClosedWindowItem and add it to the list
224
    KonqClosedWindowItem *closedWindowItem = new KonqClosedRemoteWindowItem(
225
        title, memoryStore(), configGroup, configFileName,
226
227
228
        KIO::FileUndoManager::self()->newCommandSerialNumber(), numTabs,
        service);

229
    // Add it to all the windows but don't propagate over dbus,
230
    // as it already comes from dbus)
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
231
    addClosedWindowItem(nullptr, closedWindowItem, false);
232
233
234
}

void KonqClosedWindowsManager::slotNotifyClosedWindowItem(
235
    const QString &title, int numTabs, const QString &configFileName,
236
    const QString &configGroup, const QDBusMessage &msg)
237
{
238
239
    slotNotifyClosedWindowItem(title, numTabs, configFileName, configGroup,
                               msg.service());
240
241
242
}

void KonqClosedWindowsManager::slotNotifyRemove(
243
244
    const QString &configFileName, const QString &configGroup,
    const QDBusMessage &msg)
245
{
246
    if (isSenderOfSignal(msg)) {
247
        return;
248
    }
249
250

    // Find the window item. It can be either remote or local
251
    KonqClosedWindowItem *closedWindowItem =
252
        findClosedRemoteWindowItem(configFileName, configGroup);
253
    if (!closedWindowItem) {
254
        closedWindowItem = findClosedLocalWindowItem(configFileName, configGroup);
255
        if (!closedWindowItem) {
256
            return;
257
        }
258
259
    }

260
    // Remove it in all the windows but don't propagate over dbus,
261
    // as it already comes from dbus)
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
262
    removeClosedWindowItem(nullptr, closedWindowItem, false);
263
264
}

265
266
267
KonqClosedRemoteWindowItem *KonqClosedWindowsManager::findClosedRemoteWindowItem(
    const QString &configFileName,
    const QString &configGroup)
268
269
270
{
    readConfig();

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
271
    KonqClosedRemoteWindowItem *closedRemoteWindowItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
272
    for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin();
273
            it != m_closedWindowItemList.constEnd(); ++it) {
274
275
        closedRemoteWindowItem = dynamic_cast<KonqClosedRemoteWindowItem *>(*it);

276
277
        if (closedRemoteWindowItem &&
                closedRemoteWindowItem->equalsTo(configFileName, configGroup)) {
278
            return closedRemoteWindowItem;
279
        }
280
281
282
283
284
    }

    return closedRemoteWindowItem;
}

285
286
287
KonqClosedWindowItem *KonqClosedWindowsManager::findClosedLocalWindowItem(
    const QString &configFileName,
    const QString &configGroup)
288
289
{
    readConfig();
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
290
    KonqClosedWindowItem *closedWindowItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
291
    for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin();
292
            it != m_closedWindowItemList.constEnd(); ++it) {
293
        closedWindowItem = *it;
294
        KonqClosedRemoteWindowItem *closedRemoteWindowItem =
295
296
            dynamic_cast<KonqClosedRemoteWindowItem *>(closedWindowItem);

297
298
299
        if (!closedRemoteWindowItem && closedWindowItem &&
                closedWindowItem->configGroup().config()->name() == configFileName &&
                closedWindowItem->configGroup().name() == configGroup) {
300
            return closedWindowItem;
301
        }
302
303
304
305
306
    }

    return closedWindowItem;
}

307
/**
308
 * @returns the number of konqueror processes by counting the number of
309
 * org.kde.konqueror services in dbus.
310
 *
311
312
313
314
315
316
 * If dbus fails it returns -1.
 */
static int numberOfKonquerorProcesses()
{
    QDBusConnection dbus = QDBusConnection::sessionBus();
    QDBusReply<QStringList> reply = dbus.interface()->registeredServiceNames();
317
    if (!reply.isValid()) {
318
        return -1;
319
    }
320
321
322

    const QStringList allServices = reply;
    int count = 0; // count the number of running konqueror processes. Should be at least one, us.
323
    for (QStringList::const_iterator it = allServices.begin(), end = allServices.end(); it != end; ++it) {
324
        const QString service = *it;
325
        if (service.startsWith(QLatin1String("org.kde.konqueror"))) {
326
            count++;
327
328
329
330
331
        }
    }
    return count;
}

332
333
void KonqClosedWindowsManager::removeClosedItemsConfigFiles()
{
334
335
336
337
    // We'll only remove closed items config files if we are the only process
    // left or if dbus fails (just in case there is any other konqi process
    // but we couldn't see it).
    int count = numberOfKonquerorProcesses();
338
    if (count > 1 || count == -1) {
339
        return;
340
    }
341

342
343
    // We are the only instance of konqueror left and thus we can safely remove
    // all those temporary files.
344
    QString dir = QDir::tempPath() + QLatin1Char('/') +  "closeditems/";
345
    QDBusConnectionInterface *idbus = QDBusConnection::sessionBus().interface();
346
347
    QDirIterator it(dir, QDir::Writable | QDir::Files);
    while (it.hasNext()) {
348
349
        // Only remove the files for those konqueror instances not running anymore
        QString filename = it.next();
350
        if (!idbus->isServiceRegistered(KonqMisc::decodeFilename(it.fileName()))) {
351
            QFile::remove(filename);
352
        }
353
354
355
356
357
358
359
360
    }
}

void KonqClosedWindowsManager::saveConfig()
{
    readConfig();

    // Create / overwrite the saved closed windows list
361
    QString filename = QStringLiteral("closeditems_saved");
362
    QString file = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + filename;
363
364
    QFile::remove(file);

365
    KConfig *config = new KConfig(file, KConfig::SimpleConfig);
366
367

    // Populate the config file
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
368
    KonqClosedWindowItem *closedWindowItem = nullptr;
369
    uint counter = m_closedWindowItemList.size() - 1;
Laurent Montel's avatar
Laurent Montel committed
370
    for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin();
371
            it != m_closedWindowItemList.constEnd(); ++it, --counter) {
372
373
374
375
376
377
378
        closedWindowItem = *it;
        KConfigGroup configGroup(config, "Closed_Window" + QString::number(counter));
        configGroup.writeEntry("title", closedWindowItem->title());
        configGroup.writeEntry("numTabs", closedWindowItem->numTabs());
        closedWindowItem->configGroup().copyTo(&configGroup);
    }

Laurent Montel's avatar
Laurent Montel committed
379
    KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo");
380
381
    configGroup.writeEntry("Number of Closed Windows", m_closedWindowItemList.size());
    configGroup.sync();
382

383
384
385
    // Finally the most important thing, which is to save the store config
    // so that other konqi processes can reopen windows closed in this process.
    m_konqClosedItemsStore->sync();
386
387
388
389
390
391

    delete config;
}

void KonqClosedWindowsManager ::readConfig()
{
392
    if (m_konqClosedItemsConfig) {
393
        return;
394
    }
395

396
    QString filename = QStringLiteral("closeditems_saved");
397
    QString file = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + filename;
398
399
400
401

    m_konqClosedItemsConfig = new KConfig(file, KConfig::SimpleConfig);

    // If the config file doesn't exists, there's nothing to read
402
    if (!QFile::exists(file)) {
403
        return;
404
    }
405
406

    m_blockClosedItems = true;
407
    for (int i = 0; i < m_numUndoClosedItems; i++) {
408
409
        // For each item, create a new ClosedWindowItem
        KConfigGroup configGroup(m_konqClosedItemsConfig, "Closed_Window" +
410
                                 QString::number(i));
411
412
413

        // The number of closed items was not correctly set, fix it and save the
        // correct number.
414
        if (!configGroup.exists()) {
415
            m_numUndoClosedItems = i;
Laurent Montel's avatar
Laurent Montel committed
416
            KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo");
417
            configGroup.writeEntry("Number of Closed Windows",
418
                                   m_closedWindowItemList.size());
419
420
421
422
423
424
425
            configGroup.sync();
            break;
        }

        QString title = configGroup.readEntry("title", i18n("no name"));
        int numTabs = configGroup.readEntry("numTabs", 0);

426
        KonqClosedWindowItem *closedWindowItem = new KonqClosedWindowItem(
427
            title, memoryStore(), i, numTabs);
428
429
430
431
        configGroup.copyTo(&closedWindowItem->configGroup());
        configGroup.writeEntry("foo", 0);

        // Add the item only to this window
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
432
        addClosedWindowItem(nullptr, closedWindowItem, false);
433
434
435
436
437
438
439
440
441
    }

    m_blockClosedItems = false;
}

bool KonqClosedWindowsManager::undoAvailable() const
{
    return m_numUndoClosedItems > 0;
}