From 755ec16598656776e94dbaae9de25f514a592b53 Mon Sep 17 00:00:00 2001 From: srazi Date: Fri, 7 Apr 2017 12:32:27 +0430 Subject: [PATCH] Session manager (#2293) --- src/lib/app/browserwindow.cpp | 5 +- src/lib/app/datapaths.cpp | 7 +- src/lib/app/datapaths.h | 3 +- src/lib/app/mainapplication.cpp | 77 ++-- src/lib/app/mainapplication.h | 12 +- src/lib/app/mainmenu.cpp | 17 + src/lib/lib.pro | 2 + .../schemehandlers/qupzillaschemehandler.cpp | 3 +- src/lib/preferences/preferences.ui | 5 + src/lib/session/restoremanager.cpp | 4 +- src/lib/session/restoremanager.h | 2 +- src/lib/session/sessionmanager.cpp | 433 ++++++++++++++++++ src/lib/session/sessionmanager.h | 74 +++ 13 files changed, 585 insertions(+), 59 deletions(-) create mode 100644 src/lib/session/sessionmanager.cpp create mode 100644 src/lib/session/sessionmanager.h diff --git a/src/lib/app/browserwindow.cpp b/src/lib/app/browserwindow.cpp index 53fa877c0..97239f41f 100644 --- a/src/lib/app/browserwindow.cpp +++ b/src/lib/app/browserwindow.cpp @@ -164,6 +164,7 @@ void BrowserWindow::postLaunch() case MainApplication::OpenHomePage: case MainApplication::RestoreSession: + case MainApplication::SelectSession: startUrl = m_homepage; break; @@ -178,7 +179,7 @@ void BrowserWindow::postLaunch() startUrl.clear(); m_tabWidget->addView(QUrl("qupzilla:restore"), Qz::NT_CleanSelectedTabAtTheEnd); } - else if (mApp->afterLaunch() == MainApplication::RestoreSession && mApp->restoreManager()) { + else if ((mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) && mApp->restoreManager()) { addTab = !mApp->restoreSession(this, mApp->restoreManager()->restoreData()); } break; @@ -1316,7 +1317,7 @@ void BrowserWindow::closeEvent(QCloseEvent* event) Settings settings; bool askOnClose = settings.value("Browser-Tabs-Settings/AskOnClosing", true).toBool(); - if (mApp->afterLaunch() == MainApplication::RestoreSession && mApp->windowCount() == 1) { + if ((mApp->afterLaunch() == MainApplication::SelectSession || mApp->afterLaunch() == MainApplication::RestoreSession) && mApp->windowCount() == 1) { askOnClose = false; } diff --git a/src/lib/app/datapaths.cpp b/src/lib/app/datapaths.cpp index 9c2327823..cfee9d6af 100644 --- a/src/lib/app/datapaths.cpp +++ b/src/lib/app/datapaths.cpp @@ -148,5 +148,10 @@ void DataPaths::initCurrentProfile(const QString &profilePath) if (m_paths[Cache].isEmpty()) m_paths[Cache].append(m_paths[CurrentProfile].at(0) + QLatin1String("/cache")); - QDir().mkpath(m_paths[Cache].at(0)); + if (m_paths[Sessions].isEmpty()) + m_paths[Sessions].append(m_paths[CurrentProfile].at(0) + QLatin1String("/sessions")); + + QDir dir; + dir.mkpath(m_paths[Cache].at(0)); + dir.mkpath(m_paths[Sessions].at(0)); } diff --git a/src/lib/app/datapaths.h b/src/lib/app/datapaths.h index d63be0e74..7d052810f 100644 --- a/src/lib/app/datapaths.h +++ b/src/lib/app/datapaths.h @@ -35,7 +35,8 @@ public: CurrentProfile = 6, // $Profiles/current_profile Temp = 7, // $Config/tmp Cache = 8, // $XDG_CACHE_HOME/qupzilla or $CurrentProfile/cache - LastPath = 9 + Sessions = 9, // $CurrentProfile/sessions + LastPath = 10 }; explicit DataPaths(); diff --git a/src/lib/app/mainapplication.cpp b/src/lib/app/mainapplication.cpp index 1aa2506c5..ed130699e 100644 --- a/src/lib/app/mainapplication.cpp +++ b/src/lib/app/mainapplication.cpp @@ -46,6 +46,7 @@ #include "desktopnotificationsfactory.h" #include "html5permissions/html5permissionsmanager.h" #include "scripts.h" +#include "sessionmanager.h" #include #include @@ -91,6 +92,7 @@ MainApplication::MainApplication(int &argc, char** argv) , m_browsingLibrary(0) , m_networkManager(0) , m_restoreManager(0) + , m_sessionManager(0) , m_downloadManager(0) , m_userAgentManager(0) , m_searchEnginesManager(0) @@ -275,8 +277,19 @@ MainApplication::MainApplication(int &argc, char** argv) script.setSourceCode(Scripts::setupWebChannel()); m_webProfile->scripts()->insert(script); - m_autoSaver = new AutoSaver(this); - connect(m_autoSaver, SIGNAL(save()), this, SLOT(saveSession())); + if (!isPrivate()) { + m_sessionManager = new SessionManager(this); + m_autoSaver = new AutoSaver(this); + connect(m_autoSaver, SIGNAL(save()), m_sessionManager, SLOT(autoSaveLastSession())); + + Settings settings; + m_isStartingAfterCrash = settings.value("SessionRestore/isRunning", false).toBool(); + settings.setValue("SessionRestore/isRunning", true); + + // we have to ask about startup session before creating main window + if (!m_isStartingAfterCrash && afterLaunch() == SelectSession) + m_restoreManager = new RestoreManager(sessionManager()->askSessionFromUser()); + } translateApp(); loadSettings(); @@ -294,11 +307,8 @@ MainApplication::MainApplication(int &argc, char** argv) if (!isPrivate()) { - Settings settings; - m_isStartingAfterCrash = settings.value("SessionRestore/isRunning", false).toBool(); - settings.setValue("SessionRestore/isRunning", true); - #ifndef DISABLE_CHECK_UPDATES + Settings settings; bool checkUpdates = settings.value("Web-Browser-Settings/CheckUpdates", true).toBool(); if (checkUpdates) { @@ -306,10 +316,10 @@ MainApplication::MainApplication(int &argc, char** argv) } #endif - backupSavedSessions(); + sessionManager()->backupSavedSessions(); if (m_isStartingAfterCrash || afterLaunch() == RestoreSession) { - m_restoreManager = new RestoreManager(); + m_restoreManager = new RestoreManager(sessionManager()->lastActiveSessionPath()); if (!m_restoreManager->isValid()) { destroyRestoreManager(); } else { @@ -562,6 +572,11 @@ RestoreManager* MainApplication::restoreManager() return m_restoreManager; } +SessionManager* MainApplication::sessionManager() +{ + return m_sessionManager; +} + DownloadManager* MainApplication::downloadManager() { if (!m_downloadManager) { @@ -655,7 +670,8 @@ void MainApplication::restoreOverrideCursor() void MainApplication::changeOccurred() { - m_autoSaver->changeOccurred(); + if (m_autoSaver) + m_autoSaver->changeOccurred(); } void MainApplication::quitApplication() @@ -665,8 +681,8 @@ void MainApplication::quitApplication() return; } - if (m_windows.count() > 0) { - saveSession(); + if (m_sessionManager && m_windows.count() > 0) { + m_sessionManager->autoSaveLastSession(); } m_isClosing = true; @@ -708,7 +724,7 @@ void MainApplication::postLaunch() QTimer::singleShot(5000, this, &MainApplication::runDeferredPostLaunchActions); } -void MainApplication::writeCurrentSession(const QString &filePath) +QByteArray MainApplication::saveState() const { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); @@ -726,19 +742,7 @@ void MainApplication::writeCurrentSession(const QString &filePath) } } - QFile file(filePath); - file.open(QIODevice::WriteOnly); - file.write(data); - file.close(); -} - -void MainApplication::saveSession() -{ - if (m_isPrivate || m_isRestoring || m_windows.count() == 0 || m_restoreManager) { - return; - } - - writeCurrentSession(DataPaths::currentProfilePath() + QLatin1String("/session.dat")); + return data; } void MainApplication::saveSettings() @@ -786,6 +790,8 @@ void MainApplication::saveSettings() qzSettings->saveSettings(); AdBlockManager::instance()->save(); QFile::remove(DataPaths::currentProfilePath() + QLatin1String("/WebpageIcons.db")); + + sessionManager()->saveSettings(); } void MainApplication::messageReceived(const QString &message) @@ -1059,27 +1065,6 @@ void MainApplication::translateApp() installTranslator(sys); } -void MainApplication::backupSavedSessions() -{ - // session.dat - current - // session.dat.old - first backup - // session.dat.old1 - second backup - - const QString sessionFile = DataPaths::currentProfilePath() + QLatin1String("/session.dat"); - - if (!QFile::exists(sessionFile)) { - return; - } - - if (QFile::exists(sessionFile + QLatin1String(".old"))) { - QFile::remove(sessionFile + QLatin1String(".old1")); - QFile::copy(sessionFile + QLatin1String(".old"), sessionFile + QLatin1String(".old1")); - } - - QFile::remove(sessionFile + QLatin1String(".old")); - QFile::copy(sessionFile, sessionFile + QLatin1String(".old")); -} - void MainApplication::checkDefaultWebBrowser() { if (isPortable()) { diff --git a/src/lib/app/mainapplication.h b/src/lib/app/mainapplication.h index 4a73561fd..0a09b8efc 100644 --- a/src/lib/app/mainapplication.h +++ b/src/lib/app/mainapplication.h @@ -49,6 +49,7 @@ class HTML5PermissionsManager; class RegisterQAppAssociation; class DesktopNotificationsFactory; class ProxyStyle; +class SessionManager; class QUPZILLA_EXPORT MainApplication : public QtSingleApplication { @@ -59,7 +60,8 @@ public: OpenBlankPage = 0, OpenHomePage = 1, OpenSpeedDial = 2, - RestoreSession = 3 + RestoreSession = 3, + SelectSession = 4 }; explicit MainApplication(int &argc, char** argv); @@ -101,6 +103,7 @@ public: NetworkManager* networkManager(); RestoreManager* restoreManager(); + SessionManager* sessionManager(); DownloadManager* downloadManager(); UserAgentManager* userAgentManager(); SearchEnginesManager* searchEnginesManager(); @@ -108,6 +111,8 @@ public: DesktopNotificationsFactory* desktopNotifications(); QWebEngineProfile* webProfile() const; + QByteArray saveState() const; + static MainApplication* instance(); public slots: @@ -120,8 +125,6 @@ public slots: void changeOccurred(); void quitApplication(); - void writeCurrentSession(const QString &filePath); - signals: void settingsReloaded(); void activeWindowChanged(BrowserWindow* window); @@ -129,7 +132,6 @@ signals: private slots: void postLaunch(); - void saveSession(); void saveSettings(); void messageReceived(const QString &message); @@ -150,7 +152,6 @@ private: void loadTheme(const QString &name); void translateApp(); - void backupSavedSessions(); void setUserStyleSheet(const QString &filePath); @@ -173,6 +174,7 @@ private: NetworkManager* m_networkManager; RestoreManager* m_restoreManager; + SessionManager* m_sessionManager; DownloadManager* m_downloadManager; UserAgentManager* m_userAgentManager; SearchEnginesManager* m_searchEnginesManager; diff --git a/src/lib/app/mainmenu.cpp b/src/lib/app/mainmenu.cpp index 6ab3a2aa9..ae25339a4 100644 --- a/src/lib/app/mainmenu.cpp +++ b/src/lib/app/mainmenu.cpp @@ -33,6 +33,7 @@ #include "qzsettings.h" #include "pluginproxy.h" #include "webinspector.h" +#include "sessionmanager.h" #include #include @@ -529,6 +530,22 @@ void MainMenu::init() ADD_ACTION("File/OpenFile", m_menuFile, QIcon::fromTheme(QSL("document-open")), tr("Open &File..."), SLOT(openFile()), "Ctrl+O"); ADD_ACTION("File/CloseWindow", m_menuFile, QIcon::fromTheme(QSL("window-close")), tr("Close Window"), SLOT(closeWindow()), "Ctrl+Shift+W"); m_menuFile->addSeparator(); + + if (!mApp->isPrivate()) { + action = new QAction(tr("New Session..."), this); + connect(action, SIGNAL(triggered()), mApp->sessionManager(), SLOT(newSession())); + m_actions[QSL("File/NewSession")] = action; + m_menuFile->addAction(action); + action = new QAction(tr("Save Session..."), this); + connect(action, SIGNAL(triggered()), mApp->sessionManager(), SLOT(saveSession())); + m_actions[QSL("File/SaveSession")] = action; + m_menuFile->addAction(action); + QMenu* sessionsSubmenu = new QMenu(tr("Sessions")); + connect(sessionsSubmenu, SIGNAL(aboutToShow()), mApp->sessionManager(), SLOT(aboutToShowSessionsMenu())); + m_menuFile->addMenu(sessionsSubmenu); + m_menuFile->addSeparator(); + } + ADD_ACTION("File/SavePageAs", m_menuFile, QIcon::fromTheme(QSL("document-save")), tr("&Save Page As..."), SLOT(savePageAs()), "Ctrl+S"); ADD_ACTION("File/SendLink", m_menuFile, QIcon::fromTheme(QSL("mail-message-new")), tr("Send Link..."), SLOT(sendLink()), ""); ADD_ACTION("File/Print", m_menuFile, QIcon::fromTheme(QSL("document-print")), tr("&Print..."), SLOT(printPage()), "Ctrl+P"); diff --git a/src/lib/lib.pro b/src/lib/lib.pro index b0f5044bb..9c7c61586 100644 --- a/src/lib/lib.pro +++ b/src/lib/lib.pro @@ -166,6 +166,7 @@ SOURCES += \ preferences/useragentdialog.cpp \ session/recoveryjsobject.cpp \ session/restoremanager.cpp \ + session/sessionmanager.cpp \ sidebar/bookmarkssidebar.cpp \ sidebar/historysidebar.cpp \ sidebar/sidebar.cpp \ @@ -347,6 +348,7 @@ HEADERS += \ preferences/useragentdialog.h \ session/recoveryjsobject.h \ session/restoremanager.h \ + session/sessionmanager.h \ sidebar/bookmarkssidebar.h \ sidebar/historysidebar.h \ sidebar/sidebar.h \ diff --git a/src/lib/network/schemehandlers/qupzillaschemehandler.cpp b/src/lib/network/schemehandlers/qupzillaschemehandler.cpp index dad3b40ef..45c838baf 100644 --- a/src/lib/network/schemehandlers/qupzillaschemehandler.cpp +++ b/src/lib/network/schemehandlers/qupzillaschemehandler.cpp @@ -27,6 +27,7 @@ #include "datapaths.h" #include "iconprovider.h" #include "useragentmanager.h" +#include "sessionmanager.h" #include #include @@ -375,7 +376,7 @@ QString QupZillaSchemeReply::configPage() cPage.replace(QLatin1String("%PATHS-TEXT%"), QString("
%1
%2
").arg(tr("Profile"), DataPaths::currentProfilePath()) + QString("
%1
%2
").arg(tr("Settings"), DataPaths::currentProfilePath() + "/settings.ini") + - QString("
%1
%2
").arg(tr("Saved session"), DataPaths::currentProfilePath() + "/session.dat") + + QString("
%1
%2
").arg(tr("Saved session"), SessionManager::defaultSessionPath()) + QString("
%1
%2
").arg(tr("Pinned tabs"), DataPaths::currentProfilePath() + "/pinnedtabs.dat") + QString("
%1
%2
").arg(tr("Data"), DataPaths::path(DataPaths::AppData)) + QString("
%1
%2
").arg(tr("Themes"), DataPaths::path(DataPaths::Themes)) + diff --git a/src/lib/preferences/preferences.ui b/src/lib/preferences/preferences.ui index 43584c323..19788e6cb 100644 --- a/src/lib/preferences/preferences.ui +++ b/src/lib/preferences/preferences.ui @@ -318,6 +318,11 @@ Restore session + + + Select session + + diff --git a/src/lib/session/restoremanager.cpp b/src/lib/session/restoremanager.cpp index 81d953f1c..9555fd89e 100644 --- a/src/lib/session/restoremanager.cpp +++ b/src/lib/session/restoremanager.cpp @@ -22,10 +22,10 @@ #include -RestoreManager::RestoreManager() +RestoreManager::RestoreManager(const QString &file) : m_recoveryObject(new RecoveryJsObject(this)) { - createFromFile(DataPaths::currentProfilePath() + QLatin1String("/session.dat")); + createFromFile(file); } RestoreManager::~RestoreManager() diff --git a/src/lib/session/restoremanager.h b/src/lib/session/restoremanager.h index 30069ab0d..69b168c65 100644 --- a/src/lib/session/restoremanager.h +++ b/src/lib/session/restoremanager.h @@ -34,7 +34,7 @@ public: QVector tabsState; }; - explicit RestoreManager(); + explicit RestoreManager(const QString &file); virtual ~RestoreManager(); bool isValid() const; diff --git a/src/lib/session/sessionmanager.cpp b/src/lib/session/sessionmanager.cpp new file mode 100644 index 000000000..1992e1a94 --- /dev/null +++ b/src/lib/session/sessionmanager.cpp @@ -0,0 +1,433 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2017 Razi Alavizadeh +* +* 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 3 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, see . +* ============================================================ */ +#include "browserwindow.h" +#include "datapaths.h" +#include "mainapplication.h" +#include "restoremanager.h" +#include "sessionmanager.h" +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +SessionManager::SessionManager(QObject* parent) + : QObject(parent) + , m_firstBackupSession(DataPaths::currentProfilePath() + QL1S("/session.dat.old")) + , m_secondBackupSession(DataPaths::currentProfilePath() + QL1S("/session.dat.old1")) +{ + QFileSystemWatcher* sessionFilesWatcher = new QFileSystemWatcher({DataPaths::path(DataPaths::Sessions)}, this); + connect(sessionFilesWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(sessionsDirectoryChanged())); + + loadSettings(); +} + +static void addSessionSubmenu(QObject* receiver, QMenu* menu, const QString &title, const QString &filePath, const QFileInfo &lastActiveSessionFileInfo) +{ + QMenu* sessionSubmenu = new QMenu(title, menu); + QObject::connect(sessionSubmenu, SIGNAL(aboutToShow()), receiver, SLOT(aboutToShowSessionSubmenu())); + + QAction* action = menu->addMenu(sessionSubmenu); + action->setData(filePath); + action->setCheckable(true); + action->setChecked(QFileInfo(filePath) == lastActiveSessionFileInfo); +} + +static void addSessionsMetaDataToMenu(QObject* receiver, QMenu* menu, const QFileInfo &lastActiveSessionFileInfo, const QList &sessionsMetaDataList) +{ + for (const SessionManager::SessionMetaData &metaData : sessionsMetaDataList) { + addSessionSubmenu(receiver, menu, metaData.name, metaData.filePath, lastActiveSessionFileInfo); + } +} + +void SessionManager::aboutToShowSessionsMenu() +{ + QMenu* menu = qobject_cast(sender()); + menu->clear(); + + const QFileInfo lastActiveSessionInfo(m_lastActiveSessionPath); + + fillSessionsMetaDataListIfNeeded(); + + addSessionsMetaDataToMenu(this, menu, lastActiveSessionInfo, m_sessionsMetaDataList); + + menu->addSeparator(); + + if (QFile::exists(m_firstBackupSession)) + addSessionSubmenu(this, menu, tr("First Backup"), m_firstBackupSession, lastActiveSessionInfo); + if (QFile::exists(m_secondBackupSession)) + addSessionSubmenu(this, menu, tr("Second Backup"), m_secondBackupSession, lastActiveSessionInfo); +} + +void SessionManager::aboutToShowSessionSubmenu() +{ + QMenu* menu = qobject_cast(sender()); + menu->clear(); + + const QString sessionFilePath = menu->menuAction()->data().toString(); + + const QFileInfo currentSessionFileInfo(m_lastActiveSessionPath); + const QFileInfo sessionFileInfo(sessionFilePath); + + QList actions; + QAction* action; + + if (sessionFileInfo != currentSessionFileInfo || mApp->restoreManager()) { + if (sessionFileInfo != QFileInfo(m_firstBackupSession) && sessionFileInfo != QFileInfo(m_secondBackupSession)) { + action = new QAction(SessionManager::tr("Switch To"), menu); + action->setData(sessionFilePath); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(switchToSession())); + actions << action; + } + + action = new QAction(SessionManager::tr("Open"), menu); + action->setData(sessionFilePath); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(openSession())); + actions << action; + + action = new QAction(menu); + action->setSeparator(true); + actions << action; + } + + action = new QAction(SessionManager::tr("Clone"), menu); + action->setData(sessionFilePath); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(cloneSession())); + actions << action; + + if (sessionFileInfo != currentSessionFileInfo) { + action = new QAction(SessionManager::tr("Rename"), menu); + action->setData(sessionFilePath); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(renameSession())); + actions << action; + } + + if (sessionFileInfo != currentSessionFileInfo && sessionFileInfo != QFileInfo(defaultSessionPath()) && + sessionFileInfo != QFileInfo(m_firstBackupSession) && sessionFileInfo != QFileInfo(m_secondBackupSession)) { + action = new QAction(SessionManager::tr("Remove"), menu); + action->setData(sessionFilePath); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSession())); + actions << action; + } + + menu->addActions(actions); +} + +void SessionManager::sessionsDirectoryChanged() +{ + m_sessionsMetaDataList.clear(); +} + +void SessionManager::switchToSession() +{ + QAction* action = qobject_cast(sender()); + if (!action) + return; + + openSession(action->data().toString(), true); +} + +void SessionManager::openSession(QString sessionFilePath, bool switchSession) +{ + if (sessionFilePath.isEmpty()) { + QAction* action = qobject_cast(sender()); + if (!action) + return; + + sessionFilePath = action->data().toString(); + } + + QVector sessionData; + RestoreManager::createFromFile(sessionFilePath, sessionData); + + if (sessionData.isEmpty()) + return; + + BrowserWindow* window = mApp->getWindow(); + if (switchSession) { + writeCurrentSession(m_lastActiveSessionPath); + + window = mApp->createWindow(Qz::BW_OtherRestoredWindow); + for (BrowserWindow* win : mApp->windows()) { + if (win != window) + win->close(); + } + + m_lastActiveSessionPath = QFileInfo(sessionFilePath).canonicalFilePath(); + } + + mApp->openSession(window, sessionData); +} + +void SessionManager::renameSession(QString sessionFilePath, bool clone) +{ + if (sessionFilePath.isEmpty()) { + QAction* action = qobject_cast(sender()); + if (!action) + return; + + sessionFilePath = action->data().toString(); + } + + bool ok; + const QString suggestedName = QFileInfo(sessionFilePath).baseName() + (clone ? tr("_cloned") : tr("_renamed")); + QString newName = QInputDialog::getText(mApp->activeWindow(), (clone ? tr("Clone Session") : tr("Rename Session")), + tr("Please enter a new name:"), QLineEdit::Normal, + suggestedName, &ok); + + if (!ok) + return; + + const QString newSessionPath = QString("%1/%2.dat").arg(DataPaths::path(DataPaths::Sessions)).arg(newName); + if (QFile::exists(newSessionPath)) { + QMessageBox::information(mApp->activeWindow(), tr("Error!"), tr("The session file \"%1\" exists. Please enter another name.").arg(newName)); + renameSession(sessionFilePath, clone); + return; + } + + if (clone) { + if (!QFile::copy(sessionFilePath, newSessionPath)) { + QMessageBox::information(mApp->activeWindow(), tr("Error!"), tr("An error occurred when cloning session file.")); + return; + } + } + else if (!QFile::rename(sessionFilePath, newSessionPath)) { + QMessageBox::information(mApp->activeWindow(), tr("Error!"), tr("An error occurred when renaming session file.")); + return; + } +} + +void SessionManager::cloneSession() +{ + QAction* action = qobject_cast(sender()); + if (!action) + return; + + renameSession(action->data().toString(), true); +} + +void SessionManager::deleteSession() +{ + QAction* action = qobject_cast(sender()); + if (!action) + return; + + const QString filePath = action->data().toString(); + + QMessageBox::StandardButton result = QMessageBox::information(mApp->activeWindow(), tr("Warning!"), tr("Are you sure to delete following session?\n%1") + .arg(QDir::toNativeSeparators(filePath)), QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) { + QFile::remove(filePath); + } +} + +void SessionManager::saveSession() +{ + bool ok; + QString sessionName = QInputDialog::getText(mApp->activeWindow(), tr("Save Session"), + tr("Please enter a name to save session:"), QLineEdit::Normal, + tr("Saved Session (%1)").arg(QDateTime::currentDateTime().toString("dd MMM yyyy HH-mm-ss")), &ok); + + if (!ok) + return; + + const QString filePath = QString("%1/%2.dat").arg(DataPaths::path(DataPaths::Sessions)).arg(sessionName); + if (QFile::exists(filePath)) { + QMessageBox::information(mApp->activeWindow(), tr("Error!"), tr("The session file \"%1\" exists. Please enter another name.").arg(sessionName)); + saveSession(); + return; + } + + writeCurrentSession(filePath); +} + +void SessionManager::newSession() +{ + bool ok; + QString sessionName = QInputDialog::getText(mApp->activeWindow(), tr("New Session"), + tr("Please enter a name to create new session:"), QLineEdit::Normal, + tr("New Session (%1)").arg(QDateTime::currentDateTime().toString("dd MMM yyyy HH-mm-ss")), &ok); + + if (!ok) + return; + + const QString filePath = QString("%1/%2.dat").arg(DataPaths::path(DataPaths::Sessions)).arg(sessionName); + if (QFile::exists(filePath)) { + QMessageBox::information(mApp->activeWindow(), tr("Error!"), tr("The session file \"%1\" exists. Please enter another name.").arg(sessionName)); + newSession(); + return; + } + + writeCurrentSession(m_lastActiveSessionPath); + + BrowserWindow* window = mApp->createWindow(Qz::BW_NewWindow); + for (BrowserWindow* win : mApp->windows()) { + if (win != window) + win->close(); + } + + m_lastActiveSessionPath = filePath; + autoSaveLastSession(); +} + +void SessionManager::fillSessionsMetaDataListIfNeeded() +{ + if (!m_sessionsMetaDataList.isEmpty()) + return; + + QDir dir(DataPaths::path(DataPaths::Sessions)); + + const QFileInfoList sessionFiles = QFileInfoList() << QFileInfo(defaultSessionPath()) << dir.entryInfoList({QSL("*.*")}, QDir::Files, QDir::Time); + + QStringList fileNames; + + for (int i = 0; i < sessionFiles.size(); ++i) { + const QFileInfo &fileInfo = sessionFiles.at(i); + QVector data; + RestoreManager::createFromFile(fileInfo.absoluteFilePath(), data); + + if (data.isEmpty()) + continue; + + SessionMetaData metaData; + metaData.name = fileInfo.baseName(); + + if (fileInfo == QFileInfo(defaultSessionPath())) + metaData.name = tr("Default Session"); + else if (fileNames.contains(fileInfo.baseName())) + metaData.name = fileInfo.fileName(); + else + metaData.name = fileInfo.baseName(); + + fileNames << metaData.name; + metaData.filePath = fileInfo.canonicalFilePath(); + + m_sessionsMetaDataList << metaData; + } +} + +void SessionManager::loadSettings() +{ + Settings settings; + settings.beginGroup("Web-Browser-Settings"); + m_lastActiveSessionPath = settings.value("lastActiveSessionPath", defaultSessionPath()).toString(); + settings.endGroup(); + + // fallback to default session + if (!QFile::exists(m_lastActiveSessionPath)) + m_lastActiveSessionPath = defaultSessionPath(); +} + +void SessionManager::saveSettings() +{ + Settings settings; + settings.beginGroup("Web-Browser-Settings"); + settings.setValue("lastActiveSessionPath", m_lastActiveSessionPath); + settings.endGroup(); +} + +QString SessionManager::defaultSessionPath() +{ + return DataPaths::currentProfilePath() + QL1S("/session.dat"); +} + +QString SessionManager::lastActiveSessionPath() const +{ + return m_lastActiveSessionPath; +} + +void SessionManager::backupSavedSessions() +{ + if (!QFile::exists(m_lastActiveSessionPath)) { + return; + } + + if (QFile::exists(m_firstBackupSession)) { + QFile::remove(m_secondBackupSession); + QFile::copy(m_firstBackupSession, m_secondBackupSession); + } + + QFile::remove(m_firstBackupSession); + QFile::copy(m_lastActiveSessionPath, m_firstBackupSession); +} + +void SessionManager::writeCurrentSession(const QString &filePath) +{ + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly) || file.write(mApp->saveState()) == -1) { + qWarning() << "Error! can not write the current session file: " << filePath << file.errorString(); + return; + } + file.close(); +} + +void SessionManager::autoSaveLastSession() +{ + if (mApp->isPrivate() || mApp->isRestoring() || mApp->windowCount() == 0 || mApp->restoreManager()) { + return; + } + + saveSettings(); + writeCurrentSession(m_lastActiveSessionPath); +} + +QString SessionManager::askSessionFromUser() +{ + fillSessionsMetaDataListIfNeeded(); + + QDialog dialog(mApp->getWindow(), Qt::WindowStaysOnTopHint); + QLabel label(tr("Please select the startup session:"), &dialog); + QComboBox comboBox(&dialog); + QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); + connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + QVBoxLayout layout; + layout.addWidget(&label); + layout.addWidget(&comboBox); + layout.addWidget(&buttonBox); + dialog.setLayout(&layout); + + const QFileInfo lastActiveSessionFileInfo(m_lastActiveSessionPath); + + for (const SessionMetaData &metaData : m_sessionsMetaDataList) { + if (QFileInfo(metaData.filePath) != lastActiveSessionFileInfo) { + comboBox.addItem(metaData.name, metaData.filePath); + } + else { + comboBox.insertItem(0, tr("%1 (last session)").arg(metaData.name), metaData.filePath); + } + } + + comboBox.setCurrentIndex(0); + + if (dialog.exec() == QDialog::Accepted) { + m_lastActiveSessionPath = comboBox.currentData().toString(); + } + + return m_lastActiveSessionPath; +} diff --git a/src/lib/session/sessionmanager.h b/src/lib/session/sessionmanager.h new file mode 100644 index 000000000..8941c3b1d --- /dev/null +++ b/src/lib/session/sessionmanager.h @@ -0,0 +1,74 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2017 Razi Alavizadeh +* +* 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 3 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, see . +* ============================================================ */ +#ifndef SESSIONMANAGER_H +#define SESSIONMANAGER_H + +#include "qzcommon.h" + +class QAction; +class QMenu; + + +class QUPZILLA_EXPORT SessionManager : public QObject +{ + Q_OBJECT + +public: + explicit SessionManager(QObject* parent = 0); + + struct SessionMetaData { + QString name; + QString filePath; + }; + + void loadSettings(); + void saveSettings(); + + static QString defaultSessionPath(); + QString lastActiveSessionPath() const; + QString askSessionFromUser(); + + void backupSavedSessions(); + void writeCurrentSession(const QString &filePath); + +public slots: + void autoSaveLastSession(); + +private slots: + void aboutToShowSessionsMenu(); + void aboutToShowSessionSubmenu(); + void sessionsDirectoryChanged(); + void switchToSession(); + void openSession(QString sessionFilePath = QString(), bool switchSession = false); + void renameSession(QString sessionFilePath = QString(), bool clone = false); + void cloneSession(); + void deleteSession(); + void saveSession(); + void newSession(); + +private: + void fillSessionsMetaDataListIfNeeded(); + + QList m_sessionsMetaDataList; + + QString m_firstBackupSession; + QString m_secondBackupSession; + QString m_lastActiveSessionPath; +}; + +#endif // SESSIONMANAGER_H