Git commit a78c6a50dcb33028eb572bc260bdaca8f30a597a by T.C. Hollingsworth, on behalf of Dawit Alemayehu. Committed on 19/09/2012 at 06:12. Pushed by hollingsworth into branch 'KDE/4.9'.
In the session restore dialog, allow the user to select which sessions should be restored. BUG: 260282 FIXED-IN: 4.10 REVIEW: 106503 GUI: new strings DIGEST: Ability to select the session(s) to be restored in Konqueror. (cherry picked from commit 0142a8ca11b00b5b24347b92700b7f6c5bdb3e75) M +336 -56 konqueror/src/konqsessionmanager.cpp M +51 -0 konqueror/src/konqsessionmanager.h http://commits.kde.org/kde-baseapps/a78c6a50dcb33028eb572bc260bdaca8f30a597a diff --git a/konqueror/src/konqsessionmanager.cpp b/konqueror/src/konqsessionmanager.cpp index 68a003f..ac5c691 100644 --- a/konqueror/src/konqsessionmanager.cpp +++ b/konqueror/src/konqsessionmanager.cpp @@ -33,12 +33,14 @@ #include <kvbox.h> #include <khbox.h> #include <klocale.h> -#include <kmessagebox.h> +#include <kdialog.h> #include <kurl.h> #include <ktempdir.h> +#include <ksqueezedtextlabel.h> #include <QPushButton> -#include <QtCore/QFileInfo> +#include <QCheckBox> +#include <QFileInfo> #include <QPoint> #include <QtDBus/QtDBus> #include <QtAlgorithms> @@ -47,6 +49,12 @@ #include <QDBusArgument> #include <QFile> #include <QSize> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QTreeWidget> +#include <QScrollArea> +#include <QScrollBar> + class KonqSessionManagerPrivate { @@ -66,8 +74,291 @@ public: K_GLOBAL_STATIC(KonqSessionManagerPrivate, myKonqSessionManagerPrivate) +static QString viewIdFor(const QString& sessionFile, const QString& viewId) +{ + return (sessionFile + viewId); +} + +static const QList<KConfigGroup> windowConfigGroups(const KConfig& config) +{ + QList<KConfigGroup> groups; + KConfigGroup generalGroup(&config, "General"); + const int size = generalGroup.readEntry("Number of Windows", 0); + for(int i = 0; i < size; i++) { + groups << KConfigGroup(&config, "Window" + QString::number(i)); + } + return groups; +} + +SessionRestoreDialog::SessionRestoreDialog(const QStringList& sessionFilePaths, QWidget* parent) + : KDialog(parent, 0) + ,m_sessionItemsCount(0) + ,m_dontShowChecked(false) +{ + setCaption(i18nc("@title:window", "Restore Session?")); + setButtons(KDialog::Yes | KDialog::No | KDialog::Cancel); + setObjectName(QLatin1String("restoresession")); + setButtonGuiItem(KDialog::Yes, KGuiItem(i18nc("@action:button yes", "Restore Session"), "window-new")); + setButtonGuiItem(KDialog::No, KGuiItem(i18nc("@action:button no","Do Not Restore"), "dialog-close")); + setButtonGuiItem(KDialog::Cancel, KGuiItem(i18nc("@action:button ask later","Ask Me Later"), "chronometer")); + setDefaultButton(KDialog::Yes); + setButtonFocus(KDialog::Yes); + setModal(true); + + QWidget *mainWidget = new QWidget(this); + QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget); + mainLayout->setSpacing(KDialog::spacingHint() * 2); // provide extra spacing + mainLayout->setMargin(0); + + QHBoxLayout *hLayout = new QHBoxLayout(); + hLayout->setMargin(0); + hLayout->setSpacing(-1); // use default spacing + mainLayout->addLayout(hLayout,5); + + KIcon icon (QLatin1String("dialog-warning")); + if (!icon.isNull()) { + QLabel *iconLabel = new QLabel(mainWidget); + QStyleOption option; + option.initFrom(mainWidget); + iconLabel->setPixmap(icon.pixmap(mainWidget->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, mainWidget))); + QVBoxLayout *iconLayout = new QVBoxLayout(); + iconLayout->addStretch(1); + iconLayout->addWidget(iconLabel); + iconLayout->addStretch(5); + hLayout->addLayout(iconLayout,0); + } + + const QString text (i18n("Konqueror did not close correctly. Would you like to restore these previous sessions?")); + QLabel *messageLabel = new QLabel(text, mainWidget); + Qt::TextInteractionFlags flags = (Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + messageLabel->setTextInteractionFlags(flags); + messageLabel->setWordWrap(true); + + hLayout->addSpacing(KDialog::spacingHint()); + hLayout->addWidget(messageLabel,5); + + QTreeWidget* treeWidget = 0; + if (!sessionFilePaths.isEmpty()) { + treeWidget = new QTreeWidget(mainWidget); + treeWidget->setHeader(0); + treeWidget->setHeaderHidden(true); + treeWidget->setToolTip(i18nc("@tooltip:session list", "Uncheck the sessions you do not want to be restored")); + + QStyleOptionViewItem styleOption; + styleOption.initFrom(treeWidget); + QFontMetrics fm(styleOption.font); + int w = treeWidget->width(); + const QRect desktop = KGlobalSettings::desktopGeometry(this); + + // Collect info from the sessions to restore + Q_FOREACH(const QString& sessionFile, sessionFilePaths) { + kDebug() << sessionFile; + QTreeWidgetItem* windowItem = 0; + const KConfig config(sessionFile, KConfig::SimpleConfig); + const QList<KConfigGroup> groups = windowConfigGroups(config); + Q_FOREACH(const KConfigGroup& group, groups) { + // To avoid a recursive search, let's do linear search on Foo_CurrentHistoryItem=1 + Q_FOREACH(const QString& key, group.keyList()) { + if (key.endsWith(QLatin1String("_CurrentHistoryItem"))) { + const QString viewId = key.left(key.length() - qstrlen("_CurrentHistoryItem")); + const QString historyIndex = group.readEntry(key, QString()); + const QString prefix = "HistoryItem" + viewId + '_' + historyIndex; + // Ignore the sidebar views + if (group.readEntry(prefix + "StrServiceName", QString()).startsWith("konq_sidebar")) + continue; + const QString url = group.readEntry(prefix + "Url", QString()); + const QString title = group.readEntry(prefix + "Title", QString()); + kDebug() << viewId << url << title; + const QString displayText = (title.trimmed().isEmpty() ? url : title); + if (!displayText.isEmpty()) { + if (!windowItem) { + windowItem = new QTreeWidgetItem(treeWidget); + const int index = sessionFilePaths.indexOf(sessionFile) + 1; + windowItem->setText(0, i18nc("@item:treewidget", "Window %1", index)); + windowItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + windowItem->setCheckState(0, Qt::Checked); + windowItem->setExpanded(true); + } + QTreeWidgetItem* item = new QTreeWidgetItem (windowItem); + item->setText(0, displayText); + item->setData(0, Qt::UserRole, viewIdFor(sessionFile, viewId)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(0, Qt::Checked); + w = qMax(w, fm.width(displayText)); + m_sessionItemsCount++; + } + } + } + } + + if (windowItem) { + m_checkedSessionItems.insert(windowItem, windowItem->childCount()); + } + } + + const int borderWidth = treeWidget->width() - treeWidget->viewport()->width() + treeWidget->verticalScrollBar()->height(); + w += borderWidth; + if (w > desktop.width() * 0.85) { // limit treeWidget size to 85% of screen width + w = qRound(desktop.width() * 0.85); + } + treeWidget->setMinimumWidth(w); + mainLayout->addWidget(treeWidget, 50); + treeWidget->setSelectionMode(QTreeWidget::NoSelection); + messageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + } + + // Do not connect the itemChanged signal until after the treewidget + // is completely populated to prevent the firing of the itemChanged + // signal while in the process of adding the original session items. + if (treeWidget && treeWidget->topLevelItemCount() > 0) { + connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), + this, SLOT(slotItemChanged(QTreeWidgetItem*,int))); + } + + QCheckBox* checkbox = new QCheckBox(i18n("Do not ask again"), mainWidget); + connect(checkbox, SIGNAL(clicked(bool)), this, SLOT(slotClicked(bool))); + mainLayout->addWidget(checkbox); + + setMainWidget(mainWidget); +} + +SessionRestoreDialog::~SessionRestoreDialog() +{ +} + +QStringList SessionRestoreDialog::discardedSessionList() const +{ + return m_discardedSessionList; +} + +bool SessionRestoreDialog::isDontShowChecked() const +{ + return m_dontShowChecked; +} + +void SessionRestoreDialog::slotClicked(bool checked) +{ + m_dontShowChecked = checked; +} + +static void setCheckState(QTreeWidgetItem* item, int column, Qt::CheckState state) +{ + const bool blocked = item->treeWidget()->blockSignals(true); + item->setCheckState(column, state); + item->treeWidget()->blockSignals(blocked); +} + +void SessionRestoreDialog::slotItemChanged(QTreeWidgetItem* item, int column) +{ + Q_ASSERT(item); + + const int itemChildCount = item->childCount(); + QTreeWidgetItem* parentItem = 0; + + const bool blocked = item->treeWidget()->blockSignals(true); + if (itemChildCount > 0) { + parentItem = item; + for (int i = 0; i < itemChildCount; ++i) { + QTreeWidgetItem* childItem = item->child(i); + if (childItem) { + childItem->setCheckState(column, item->checkState(column)); + switch (childItem->checkState(column)) { + case Qt::Checked: + m_sessionItemsCount++; + m_discardedSessionList.removeAll(childItem->data(column, Qt::UserRole).toString()); + m_checkedSessionItems[item]++; + break; + case Qt::Unchecked: + m_sessionItemsCount--; + m_discardedSessionList.append(childItem->data(column, Qt::UserRole).toString()); + m_checkedSessionItems[item]--; + break; + default: + break; + } + } + } + } else { + parentItem = item->parent(); + switch (item->checkState(column)) { + case Qt::Checked: + m_sessionItemsCount++; + m_discardedSessionList.removeAll(item->data(column, Qt::UserRole).toString()); + m_checkedSessionItems[parentItem]++; + break; + case Qt::Unchecked: + m_sessionItemsCount--; + m_discardedSessionList.append(item->data(column, Qt::UserRole).toString()); + m_checkedSessionItems[parentItem]--; + break; + default: + break; + } + } + + const int numCheckSessions = m_checkedSessionItems.value(parentItem); + switch (parentItem->checkState(column)) { + case Qt::Checked: + if (numCheckSessions == 0) { + parentItem->setCheckState(column, Qt::Unchecked); + } + case Qt::Unchecked: + if (numCheckSessions > 0) { + parentItem->setCheckState(column, Qt::Checked); + } + default: + break; + } + + enableButton(KDialog::Yes, m_sessionItemsCount > 0); + item->treeWidget()->blockSignals(blocked); +} + +void SessionRestoreDialog::saveDontShow(const QString& dontShowAgainName, int result) +{ + if (dontShowAgainName.isEmpty()) { + return; + } + + KConfigGroup::WriteConfigFlags flags = KConfig::Persistent; + if (dontShowAgainName[0] == ':') { + flags |= KConfigGroup::Global; + } + + KConfigGroup cg(KGlobal::config().data(), "Notification Messages"); + cg.writeEntry( dontShowAgainName, result==Yes, flags ); + cg.sync(); +} + +bool SessionRestoreDialog::shouldBeShown(const QString& dontShowAgainName, int* result) +{ + if (dontShowAgainName.isEmpty()) { + return true; + } + + KConfigGroup cg(KGlobal::config().data(), "Notification Messages"); + const QString dontAsk = cg.readEntry(dontShowAgainName, QString()).toLower(); + + if (dontAsk == "yes" || dontAsk == "true") { + if (result) { + *result = Yes; + } + return false; + } + + if (dontAsk == "no" || dontAsk == "false") { + if (result) { + *result = No; + } + return false; + } + + return true; +} + KonqSessionManager::KonqSessionManager() : m_autosaveDir(KStandardDirs::locateLocal("appdata", "autosave")) + , m_autosaveEnabled(false) // so that enableAutosave works { // Initialize dbus interfaces new KonqSessionManagerAdaptor ( this ); @@ -87,7 +378,6 @@ KonqSessionManager::KonqSessionManager() connect( &m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveSession()) ); } - m_autosaveEnabled = false; // so that enableAutosave works enableAutosave(); } @@ -261,17 +551,6 @@ void KonqSessionManager::restoreSessions(const QString &sessionsDir, bool } } -static const QList<KConfigGroup> windowConfigGroups(const KConfig& config) -{ - QList<KConfigGroup> groups; - KConfigGroup generalGroup(&config, "General"); - const int size = generalGroup.readEntry("Number of Windows", 0); - for(int i = 0; i < size; i++) { - groups << KConfigGroup(&config, "Window" + QString::number(i)); - } - return groups; -} - void KonqSessionManager::restoreSession(const QString &sessionFilePath, bool openTabsInsideCurrentWindow, KonqMainWindow *parent) { @@ -288,59 +567,61 @@ void KonqSessionManager::restoreSession(const QString &sessionFilePath, bool } } - -bool KonqSessionManager::askUserToRestoreAutosavedAbandonedSessions() +static void removeDiscardedSessions(const QStringList& sessionFiles, const QStringList& discardedSessions) { - const QStringList sessionFilePaths = takeSessionsOwnership(); - if(sessionFilePaths.isEmpty()) - return false; + if (discardedSessions.isEmpty()) { + return; + } - QStringList detailsList; - // Collect info from the sessions to restore - Q_FOREACH(const QString& sessionFile, sessionFilePaths) { - kDebug() << sessionFile; + Q_FOREACH(const QString& sessionFile, sessionFiles) { const KConfig config(sessionFile, KConfig::SimpleConfig); - const QList<KConfigGroup> groups = windowConfigGroups(config); - Q_FOREACH(const KConfigGroup& group, groups) { - // To avoid a recursive search, let's do linear search on Foo_CurrentHistoryItem=1 - Q_FOREACH(const QString& key, group.keyList()) { - if (key.endsWith("_CurrentHistoryItem")) { - const QString viewId = key.left(key.length() - strlen("_CurrentHistoryItem")); - const QString historyIndex = group.readEntry(key, QString()); - const QString prefix = "HistoryItem" + viewId + '_' + historyIndex; - // Ignore the sidebar views - if (group.readEntry(prefix + "StrServiceName", QString()).startsWith("konq_sidebar")) - continue; - const QString url = group.readEntry(prefix + "Url", QString()); - const QString title = group.readEntry(prefix + "Title", QString()); - kDebug() << viewId << url << title; - if (title.trimmed().isEmpty()) { - detailsList << url; - } else { - detailsList << title; - } + QList<KConfigGroup> groups = windowConfigGroups(config); + for (int i = 0, count = groups.count(); i < count; ++i) { + KConfigGroup& group = groups[i]; + const QString rootItem = group.readEntry("RootItem", "empty"); + const QString viewsKey (rootItem + QLatin1String("_Children")); + QStringList views = group.readEntry(viewsKey, QStringList()); + QMutableStringListIterator it (views); + while (it.hasNext()) { + if (discardedSessions.contains(viewIdFor(sessionFile, it.next()))) { + it.remove(); } } + group.writeEntry(viewsKey, views); } } +} + +bool KonqSessionManager::askUserToRestoreAutosavedAbandonedSessions() +{ + const QStringList sessionFilePaths = takeSessionsOwnership(); + if(sessionFilePaths.isEmpty()) + return false; disableAutosave(); - switch(KMessageBox::warningYesNoCancelList(0, // there is no questionYesNoCancelList - i18n("Konqueror did not close correctly. Would you like to restore the previous session?"), - detailsList, - i18nc("@title:window", "Restore Session?"), - KGuiItem(i18n("Restore Session"), "window-new"), - KGuiItem(i18n("Do Not Restore"), "dialog-close"), - KGuiItem(i18n("Ask Me Later"), "chronometer"), - "Restore session when konqueror didn't close correctly" - )) - { - case KMessageBox::Yes: + int result; + QStringList discardedSessionList; + const QLatin1String dontAskAgainName ("Restore session when konqueror didn't close correctly"); + + if (SessionRestoreDialog::shouldBeShown(dontAskAgainName, &result)) { + SessionRestoreDialog* restoreDlg = new SessionRestoreDialog(sessionFilePaths); + result = restoreDlg->exec(); + discardedSessionList = restoreDlg->discardedSessionList(); + if (restoreDlg->isDontShowChecked()) { + SessionRestoreDialog::saveDontShow(dontAskAgainName, result); + } + delete restoreDlg; + } + + switch (result) { + case KDialog::Yes: + // Remove the discarded session list files. + removeDiscardedSessions(sessionFilePaths, discardedSessionList); restoreSessions(sessionFilePaths); enableAutosave(); return true; - case KMessageBox::No: + case KDialog::No: deleteOwnedSessions(); enableAutosave(); return false; @@ -349,8 +630,7 @@ bool KonqSessionManager::askUserToRestoreAutosavedAbandonedSessions() QDirIterator it(dirForMyOwnedSessionFiles(), QDir::Writable|QDir::Files); - while (it.hasNext()) - { + while (it.hasNext()) { it.next(); // remove ownership of the abandoned file QFile::rename(it.filePath(), m_autosaveDir + '/' + it.fileName()); diff --git a/konqueror/src/konqsessionmanager.h b/konqueror/src/konqsessionmanager.h index ee629e4..d3103ac 100644 --- a/konqueror/src/konqsessionmanager.h +++ b/konqueror/src/konqsessionmanager.h @@ -27,10 +27,61 @@ #include <QString> #include <kconfig.h> +#include <kdialog.h> #include <konqprivate_export.h> class QDBusMessage; class KonqMainWindow; +class QTreeWidgetItem; + +class SessionRestoreDialog : public KDialog +{ + Q_OBJECT +public: + explicit SessionRestoreDialog(const QStringList& sessionFilePaths, QWidget* parent=0); + virtual ~SessionRestoreDialog(); + + /** + * Returns the list of session discarded/unselected by the user. + */ + QStringList discardedSessionList() const; + + /** + * Returns true if the don't show checkbox is checked. + */ + bool isDontShowChecked() const; + + /** + * Returns true if the corresponding session restore dialog should be shown. + * + * @param dontShowAgainName the name that identify the session restore dialog box. + * @param result if not null, it will be set to the result that was chosen the last + * time the dialog box was shown. This is only useful if the restore dialog box should + * be shown. + */ + static bool shouldBeShown(const QString &dontShowAgainName, int* result); + + /** + * Save the fact that the session restore dialog should not be shown again. + * + * @param dontShowAgainName the name that identify the session restore dialog. If + * empty, this method does nothing. + * @param result the value (Yes or No) that should be used as the result + * for the message box. + */ + static void saveDontShow(const QString &dontShowAgainName, int result); + +private Q_SLOTS: + void slotClicked(bool); + void slotItemChanged(QTreeWidgetItem*, int); + +private: + QStringList m_discardedSessionList; + QHash<QTreeWidgetItem*, int> m_checkedSessionItems; + int m_sessionItemsCount; + bool m_dontShowChecked; +}; + /** * This class is a singleton. It does some session related tasks:
