[kde-doc-english] [kde-baseapps] konqueror/src: In the session restore dialog, allow the user to select which sessions should

Dawit Alemayehu adawit at kde.org
Tue Oct 9 23:06:14 UTC 2012


Git commit 0142a8ca11b00b5b24347b92700b7f6c5bdb3e75 by Dawit Alemayehu.
Committed on 19/09/2012 at 06:12.
Pushed by adawit into branch 'master'.

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.

M  +336  -56   konqueror/src/konqsessionmanager.cpp
M  +51   -0    konqueror/src/konqsessionmanager.h

http://commits.kde.org/kde-baseapps/0142a8ca11b00b5b24347b92700b7f6c5bdb3e75

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:


More information about the kde-doc-english mailing list