File indexing completed on 2024-04-21 04:58:13
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2007 Eduardo Robles Elvira <edulix@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "konqclosedwindowsmanager.h" 0009 #include "konqsettingsxt.h" 0010 #include "konqmisc.h" 0011 #include "konqcloseditem.h" 0012 #include "konqclosedwindowsmanageradaptor.h" 0013 #include "konqclosedwindowsmanager_interface.h" 0014 #include <kio/fileundomanager.h> 0015 #include <QDirIterator> 0016 #include <QMetaType> 0017 #include <QDBusConnection> 0018 #include <QDBusMessage> 0019 #include <QDBusReply> 0020 #include <KLocalizedString> 0021 0022 #include <kconfig.h> 0023 #include <QStandardPaths> 0024 #include <KSharedConfig> 0025 0026 Q_DECLARE_METATYPE(QList<QVariant>) 0027 0028 class KonqClosedWindowsManagerPrivate 0029 { 0030 public: 0031 KonqClosedWindowsManager instance; 0032 int m_maxNumClosedItems; 0033 }; 0034 0035 static KonqClosedWindowsManagerPrivate *myKonqClosedWindowsManagerPrivate = nullptr; 0036 0037 KonqClosedWindowsManager::KonqClosedWindowsManager() 0038 { 0039 new KonqClosedWindowsManagerAdaptor(this); 0040 0041 const QString dbusPath = QStringLiteral("/KonqUndoManager"); 0042 const QString dbusInterface = QStringLiteral("org.kde.Konqueror.UndoManager"); 0043 0044 QDBusConnection dbus = QDBusConnection::sessionBus(); 0045 dbus.registerObject(dbusPath, this); 0046 dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("notifyClosedWindowItem"), this, SLOT(slotNotifyClosedWindowItem(QString,int,QString,QString,QDBusMessage))); 0047 dbus.connect(QString(), dbusPath, dbusInterface, QStringLiteral("notifyRemove"), this, SLOT(slotNotifyRemove(QString,QString,QDBusMessage))); 0048 0049 QString filename = "closeditems/" + KonqMisc::encodeFilename(dbus.baseService()); 0050 QString file = QDir::tempPath() + QLatin1Char('/') + filename; 0051 QFile::remove(file); 0052 0053 KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo"); 0054 m_numUndoClosedItems = configGroup.readEntry("Number of Closed Windows", 0); 0055 0056 m_konqClosedItemsConfig = nullptr; 0057 m_blockClosedItems = false; 0058 m_konqClosedItemsStore = new KConfig(file, KConfig::SimpleConfig); 0059 } 0060 0061 KonqClosedWindowsManager::~KonqClosedWindowsManager() 0062 { 0063 // Do some file cleaning 0064 removeClosedItemsConfigFiles(); 0065 0066 qDeleteAll(m_closedWindowItemList); // must be done before deleting the kconfigs 0067 delete m_konqClosedItemsConfig; 0068 delete m_konqClosedItemsStore; 0069 } 0070 0071 KConfig *KonqClosedWindowsManager::memoryStore() 0072 { 0073 return m_konqClosedItemsStore; 0074 } 0075 0076 KonqClosedWindowsManager *KonqClosedWindowsManager::self() 0077 { 0078 if (!myKonqClosedWindowsManagerPrivate) { 0079 myKonqClosedWindowsManagerPrivate = new KonqClosedWindowsManagerPrivate; 0080 } 0081 return &myKonqClosedWindowsManagerPrivate->instance; 0082 } 0083 0084 void KonqClosedWindowsManager::destroy() 0085 { 0086 delete myKonqClosedWindowsManagerPrivate; 0087 myKonqClosedWindowsManagerPrivate = nullptr; 0088 } 0089 0090 void KonqClosedWindowsManager::addClosedWindowItem(KonqUndoManager 0091 *real_sender, KonqClosedWindowItem *closedWindowItem, bool propagate) 0092 { 0093 readConfig(); 0094 // If we are off the limit, remove the last closed window item 0095 if (m_closedWindowItemList.size() >= 0096 KonqSettings::maxNumClosedItems()) { 0097 KonqClosedWindowItem *last = m_closedWindowItemList.last(); 0098 0099 emit removeWindowInOtherInstances(nullptr, last); 0100 emitNotifyRemove(last); 0101 0102 m_closedWindowItemList.removeLast(); 0103 delete last; 0104 } 0105 0106 if (!m_blockClosedItems) { 0107 m_numUndoClosedItems++; 0108 emit addWindowInOtherInstances(real_sender, closedWindowItem); 0109 } 0110 0111 // The prepend goes after emit addWindowInOtherInstances() because otherwise 0112 // the first time addWindowInOtherInstances() is emitted, KonqUndoManager 0113 // will catch it and it will call to its private populate() function which 0114 // will add to the undo list closedWindowItem, and then it will add it again 0115 // but we want it to be added only once. 0116 m_closedWindowItemList.prepend(closedWindowItem); 0117 0118 if (propagate) { 0119 // if it needs to be propagated means that it's a local window and thus 0120 // we need to call to saveConfig() to keep updated the kconfig file, so 0121 // that new konqueror instances can read it correctly updated. 0122 saveConfig(); 0123 0124 // Once saved, tell to other konqi processes 0125 emitNotifyClosedWindowItem(closedWindowItem); 0126 } 0127 } 0128 0129 void KonqClosedWindowsManager::removeClosedWindowItem(KonqUndoManager 0130 *real_sender, const KonqClosedWindowItem *closedWindowItem, bool propagate) 0131 { 0132 readConfig(); 0133 auto it = std::find(m_closedWindowItemList.begin(), m_closedWindowItemList.end(), closedWindowItem); 0134 0135 // If the item was found, remove it from the list 0136 if (it != m_closedWindowItemList.end()) { 0137 m_closedWindowItemList.erase(it); 0138 m_numUndoClosedItems--; 0139 } 0140 emit removeWindowInOtherInstances(real_sender, closedWindowItem); 0141 0142 if (propagate) { 0143 emitNotifyRemove(closedWindowItem); 0144 } 0145 } 0146 0147 const QList<KonqClosedWindowItem *> &KonqClosedWindowsManager::closedWindowItemList() 0148 { 0149 readConfig(); 0150 return m_closedWindowItemList; 0151 } 0152 0153 void KonqClosedWindowsManager::readSettings() 0154 { 0155 readConfig(); 0156 } 0157 0158 static QString dbusService() 0159 { 0160 return QDBusConnection::sessionBus().baseService(); 0161 } 0162 0163 /** 0164 * Returns whether the DBUS call we are handling was a call from us self 0165 */ 0166 bool isSenderOfSignal(const QDBusMessage &msg) 0167 { 0168 return dbusService() == msg.service(); 0169 } 0170 0171 bool isSenderOfSignal(const QString &service) 0172 { 0173 return dbusService() == service; 0174 } 0175 0176 void KonqClosedWindowsManager::emitNotifyClosedWindowItem( 0177 const KonqClosedWindowItem *closedWindowItem) 0178 { 0179 emit notifyClosedWindowItem(closedWindowItem->title(), 0180 closedWindowItem->numTabs(), 0181 m_konqClosedItemsStore->name(), 0182 closedWindowItem->configGroup().name()); 0183 } 0184 0185 void KonqClosedWindowsManager::emitNotifyRemove( 0186 const KonqClosedWindowItem *closedWindowItem) 0187 { 0188 const KonqClosedRemoteWindowItem *closedRemoteWindowItem = 0189 dynamic_cast<const KonqClosedRemoteWindowItem *>(closedWindowItem); 0190 0191 // Here we do this because there's no need to call to configGroup() if it's 0192 // a remote window item, and it would be error prone to be so, because 0193 // it could give us a null pointer and konqueror would crash 0194 if (closedRemoteWindowItem) 0195 emit notifyRemove(closedRemoteWindowItem->remoteConfigFileName(), 0196 closedRemoteWindowItem->remoteGroupName()); 0197 else 0198 emit notifyRemove(closedWindowItem->configGroup().config()->name(), 0199 closedWindowItem->configGroup().name()); 0200 } 0201 0202 void KonqClosedWindowsManager::slotNotifyClosedWindowItem( 0203 const QString &title, int numTabs, const QString &configFileName, 0204 const QString &configGroup, const QString &service) 0205 { 0206 if (isSenderOfSignal(service)) { 0207 return; 0208 } 0209 0210 // Create a new ClosedWindowItem and add it to the list 0211 KonqClosedWindowItem *closedWindowItem = new KonqClosedRemoteWindowItem( 0212 title, memoryStore(), configGroup, configFileName, 0213 KIO::FileUndoManager::self()->newCommandSerialNumber(), numTabs, 0214 service); 0215 0216 // Add it to all the windows but don't propagate over dbus, 0217 // as it already comes from dbus) 0218 addClosedWindowItem(nullptr, closedWindowItem, false); 0219 } 0220 0221 void KonqClosedWindowsManager::slotNotifyClosedWindowItem( 0222 const QString &title, int numTabs, const QString &configFileName, 0223 const QString &configGroup, const QDBusMessage &msg) 0224 { 0225 slotNotifyClosedWindowItem(title, numTabs, configFileName, configGroup, 0226 msg.service()); 0227 } 0228 0229 void KonqClosedWindowsManager::slotNotifyRemove( 0230 const QString &configFileName, const QString &configGroup, 0231 const QDBusMessage &msg) 0232 { 0233 if (isSenderOfSignal(msg)) { 0234 return; 0235 } 0236 0237 // Find the window item. It can be either remote or local 0238 KonqClosedWindowItem *closedWindowItem = 0239 findClosedRemoteWindowItem(configFileName, configGroup); 0240 if (!closedWindowItem) { 0241 closedWindowItem = findClosedLocalWindowItem(configFileName, configGroup); 0242 if (!closedWindowItem) { 0243 return; 0244 } 0245 } 0246 0247 // Remove it in all the windows but don't propagate over dbus, 0248 // as it already comes from dbus) 0249 removeClosedWindowItem(nullptr, closedWindowItem, false); 0250 } 0251 0252 KonqClosedRemoteWindowItem *KonqClosedWindowsManager::findClosedRemoteWindowItem( 0253 const QString &configFileName, 0254 const QString &configGroup) 0255 { 0256 readConfig(); 0257 0258 KonqClosedRemoteWindowItem *closedRemoteWindowItem = nullptr; 0259 for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin(); 0260 it != m_closedWindowItemList.constEnd(); ++it) { 0261 closedRemoteWindowItem = dynamic_cast<KonqClosedRemoteWindowItem *>(*it); 0262 0263 if (closedRemoteWindowItem && 0264 closedRemoteWindowItem->equalsTo(configFileName, configGroup)) { 0265 return closedRemoteWindowItem; 0266 } 0267 } 0268 0269 return closedRemoteWindowItem; 0270 } 0271 0272 KonqClosedWindowItem *KonqClosedWindowsManager::findClosedLocalWindowItem( 0273 const QString &configFileName, 0274 const QString &configGroup) 0275 { 0276 readConfig(); 0277 KonqClosedWindowItem *closedWindowItem = nullptr; 0278 for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin(); 0279 it != m_closedWindowItemList.constEnd(); ++it) { 0280 closedWindowItem = *it; 0281 KonqClosedRemoteWindowItem *closedRemoteWindowItem = 0282 dynamic_cast<KonqClosedRemoteWindowItem *>(closedWindowItem); 0283 0284 if (!closedRemoteWindowItem && closedWindowItem && 0285 closedWindowItem->configGroup().config()->name() == configFileName && 0286 closedWindowItem->configGroup().name() == configGroup) { 0287 return closedWindowItem; 0288 } 0289 } 0290 0291 return closedWindowItem; 0292 } 0293 0294 /** 0295 * @returns the number of konqueror processes by counting the number of 0296 * org.kde.konqueror services in dbus. 0297 * 0298 * If dbus fails it returns -1. 0299 */ 0300 static int numberOfKonquerorProcesses() 0301 { 0302 QDBusConnection dbus = QDBusConnection::sessionBus(); 0303 QDBusReply<QStringList> reply = dbus.interface()->registeredServiceNames(); 0304 if (!reply.isValid()) { 0305 return -1; 0306 } 0307 0308 const QStringList allServices = reply; 0309 int count = 0; // count the number of running konqueror processes. Should be at least one, us. 0310 for (QStringList::const_iterator it = allServices.begin(), end = allServices.end(); it != end; ++it) { 0311 const QString service = *it; 0312 if (service.startsWith(QLatin1String("org.kde.konqueror"))) { 0313 count++; 0314 } 0315 } 0316 return count; 0317 } 0318 0319 void KonqClosedWindowsManager::removeClosedItemsConfigFiles() 0320 { 0321 // We'll only remove closed items config files if we are the only process 0322 // left or if dbus fails (just in case there is any other konqi process 0323 // but we couldn't see it). 0324 int count = numberOfKonquerorProcesses(); 0325 if (count > 1 || count == -1) { 0326 return; 0327 } 0328 0329 // We are the only instance of konqueror left and thus we can safely remove 0330 // all those temporary files. 0331 QString dir = QDir::tempPath() + QLatin1Char('/') + "closeditems/"; 0332 QDBusConnectionInterface *idbus = QDBusConnection::sessionBus().interface(); 0333 QDirIterator it(dir, QDir::Writable | QDir::Files); 0334 while (it.hasNext()) { 0335 // Only remove the files for those konqueror instances not running anymore 0336 QString filename = it.next(); 0337 if (!idbus->isServiceRegistered(KonqMisc::decodeFilename(it.fileName()))) { 0338 QFile::remove(filename); 0339 } 0340 } 0341 } 0342 0343 void KonqClosedWindowsManager::saveConfig() 0344 { 0345 readConfig(); 0346 0347 // Create / overwrite the saved closed windows list 0348 QString filename = QStringLiteral("closeditems_saved"); 0349 QString file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + filename; 0350 QFile::remove(file); 0351 0352 KConfig *config = new KConfig(file, KConfig::SimpleConfig); 0353 0354 // Populate the config file 0355 KonqClosedWindowItem *closedWindowItem = nullptr; 0356 uint counter = m_closedWindowItemList.size() - 1; 0357 for (QList<KonqClosedWindowItem *>::const_iterator it = m_closedWindowItemList.constBegin(); 0358 it != m_closedWindowItemList.constEnd(); ++it, --counter) { 0359 closedWindowItem = *it; 0360 KConfigGroup configGroup(config, "Closed_Window" + QString::number(counter)); 0361 configGroup.writeEntry("title", closedWindowItem->title()); 0362 configGroup.writeEntry("numTabs", closedWindowItem->numTabs()); 0363 closedWindowItem->configGroup().copyTo(&configGroup); 0364 } 0365 0366 KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo"); 0367 configGroup.writeEntry("Number of Closed Windows", m_closedWindowItemList.size()); 0368 configGroup.sync(); 0369 0370 // Finally the most important thing, which is to save the store config 0371 // so that other konqi processes can reopen windows closed in this process. 0372 m_konqClosedItemsStore->sync(); 0373 0374 delete config; 0375 } 0376 0377 void KonqClosedWindowsManager ::readConfig() 0378 { 0379 if (m_konqClosedItemsConfig) { 0380 return; 0381 } 0382 0383 QString filename = QStringLiteral("closeditems_saved"); 0384 QString file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + filename; 0385 0386 m_konqClosedItemsConfig = new KConfig(file, KConfig::SimpleConfig); 0387 0388 // If the config file doesn't exists, there's nothing to read 0389 if (!QFile::exists(file)) { 0390 return; 0391 } 0392 0393 m_blockClosedItems = true; 0394 for (int i = 0; i < m_numUndoClosedItems; i++) { 0395 // For each item, create a new ClosedWindowItem 0396 KConfigGroup configGroup(m_konqClosedItemsConfig, "Closed_Window" + 0397 QString::number(i)); 0398 0399 // The number of closed items was not correctly set, fix it and save the 0400 // correct number. 0401 if (!configGroup.exists()) { 0402 m_numUndoClosedItems = i; 0403 KConfigGroup configGroup(KSharedConfig::openConfig(), "Undo"); 0404 configGroup.writeEntry("Number of Closed Windows", 0405 m_closedWindowItemList.size()); 0406 configGroup.sync(); 0407 break; 0408 } 0409 0410 QString title = configGroup.readEntry("title", i18n("no name")); 0411 int numTabs = configGroup.readEntry("numTabs", 0); 0412 0413 KonqClosedWindowItem *closedWindowItem = new KonqClosedWindowItem( 0414 title, memoryStore(), i, numTabs); 0415 configGroup.copyTo(&closedWindowItem->configGroup()); 0416 configGroup.writeEntry("foo", 0); 0417 0418 // Add the item only to this window 0419 addClosedWindowItem(nullptr, closedWindowItem, false); 0420 } 0421 0422 m_blockClosedItems = false; 0423 } 0424 0425 bool KonqClosedWindowsManager::undoAvailable() const 0426 { 0427 return m_numUndoClosedItems > 0; 0428 }