Commit e078ee32 authored by Christoph Cullmann's avatar Christoph Cullmann

Review user interaction in session management

Summary:
* SessionManageDialog
    - Build user interface by ui file
    - Avoid obsolete Qt members
    - Choose more meaningful member names
    - Add filter field and sort button
    - Open session by double click
    - Add buttons for "Copy" and "Open as Template"
    - Reorder a couple of code to be a little bit more logic ordered
    - Delete a session with delay which offer a restore and avoid annoying
      confirmation dialog
    - Rename a session inside of the list view to avoid extra popup
      window to enter the new name.

* Remove SessionOpenDialog, use SessionManageDialog instead
* Remove SessionChooser, use SessionManageDialog instead

* SessionManager
    - Add signal sessionListChanged()
      To avoid unneded signals is updateSessionList() slightly modified
      with a clearer look and an added check for changes in an easy way.
    - Add copySession()
    - Let rename/copySession() ask for a new name when needed
    - Move session creation parts from newSessionName() to sessionSaveAs()
    - Rename newSessionName() to askForNewSessionName()
    - Add suggestNewSessionName()
    - Don't create anonymous session in ctor
    - Don't save anonymous session as last session

* MainWindow
    - Remove from sessions menu "Open Session" because it's now the
      same as "Manage Sessions"

Test Plan:
{F6443763}
{F6443766}

Reviewers: #kate, dhaumann, ngraham, #vdg, cullmann

Reviewed By: #kate, dhaumann, cullmann

Subscribers: fabianr, anthonyfieroni, cullmann, kwrite-devel, #kate

Tags: #kate

Differential Revision: https://phabricator.kde.org/D16926
parent b1b09ed9
......@@ -33,11 +33,9 @@ set (KATE_LIBRARY_SRCS
katetabbar.cpp
# session
session/katesessionchooser.cpp
session/katesessionsaction.cpp
session/katesessionmanager.cpp
session/katesessionmanagedialog.cpp
session/katesessionopendialog.cpp
session/katesession.cpp
katemdi.cpp
......@@ -48,6 +46,7 @@ set (KATE_LIBRARY_SRCS
ki18n_wrap_ui(KATE_LIBRARY_SRCS
ui/sessionconfigwidget.ui
session/katesessionmanagedialog.ui
)
qt5_add_resources( KATE_LIBRARY_SRCS data/kate.qrc )
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="kate" version="84" translationDomain="kate">
<kpartgui name="kate" version="85" translationDomain="kate">
<MenuBar>
<Menu name="file" noMerge="1">
<text>&amp;File</text>
......@@ -96,13 +96,11 @@
<Menu name="sessions">
<text>Sess&amp;ions</text>
<Action name="sessions_new"/>
<Action name="sessions_open"/>
<Action name="sessions_list"/>
<Action name="sessions_manage"/>
<Separator/>
<Action name="sessions_save"/>
<Action name="sessions_save_as"/>
<Separator/>
<Action name="sessions_manage"/>
</Menu>
<Menu name="settings">
<text>&amp;Settings</text>
......
......@@ -443,11 +443,6 @@ void KateMainWindow::setupActions()
a->setText(i18nc("Menu entry Session->New", "&New"));
// Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionNew()), Qt::QueuedConnection);
a = actionCollection()->addAction(QStringLiteral("sessions_open"));
a->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
a->setText(i18n("&Open Session"));
// Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
connect(a, SIGNAL(triggered()), KateApp::self()->sessionManager(), SLOT(sessionOpen()), Qt::QueuedConnection);
a = actionCollection()->addAction(QStringLiteral("sessions_save"));
a->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
a->setText(i18n("&Save Session"));
......
/* This file is part of the KDE project
*
* Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "katesessionchooser.h"
#include "kateapp.h"
#include "katesessionmanager.h"
#include "katesessionchooseritem.h"
#include "katedebug.h"
#include <KLocalizedString>
#include <KStandardGuiItem>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QMenu>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHeaderView>
//BEGIN CHOOSER DIALOG
KateSessionChooser::KateSessionChooser(QWidget *parent, const QString &lastSession)
: QDialog(parent)
{
setWindowTitle(i18n("Session Chooser"));
QVBoxLayout *mainLayout = new QVBoxLayout(this);
m_sessions = new QTreeWidget(this);
m_sessions->setMinimumSize(400, 200);
mainLayout->addWidget(m_sessions);
QStringList header;
header << i18n("Session Name");
header << i18nc("The number of open documents", "Open Documents");
header << QString();
m_sessions->setHeaderLabels(header);
m_sessions->header()->setStretchLastSection(false);
m_sessions->header()->setSectionResizeMode(0, QHeaderView::Stretch);
m_sessions->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_sessions->header()->setSectionResizeMode(2, QHeaderView::Fixed);
m_sessions->header()->resizeSection(2, 32);
m_sessions->setRootIsDecorated(false);
m_sessions->setItemsExpandable(false);
m_sessions->setAllColumnsShowFocus(true);
m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows);
m_sessions->setSelectionMode(QAbstractItemView::SingleSelection);
qCDebug(LOG_KATE) << "Last session is:" << lastSession;
KateSessionList slist = KateApp::self()->sessionManager()->sessionList();
qSort(slist.begin(), slist.end(), KateSession::compareByName);
foreach(const KateSession::Ptr & session, slist) {
KateSessionChooserItem *item = new KateSessionChooserItem(m_sessions, session);
QPushButton *tmp = new QPushButton(QIcon::fromTheme(QStringLiteral("document")), QString(), m_sessions);
QMenu *popup = new QMenu(tmp);
QAction *a = popup->addAction(i18n("Clone session settings"));
a->setData(QVariant::fromValue((void *)item));
connect(a, SIGNAL(triggered()), this, SLOT(slotCopySession()));
a = popup->addAction(i18n("Delete this session"));
a->setData(QVariant::fromValue((void *)item));
connect(a, SIGNAL(triggered()), this, SLOT(slotDeleteSession()));
tmp->setMenu(popup);
m_sessions->setItemWidget(item, 2, tmp);
if (session->name() == lastSession) {
m_sessions->setCurrentItem(item);
}
}
connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
connect(m_sessions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotOpen()));
// bottom box
QHBoxLayout *hb = new QHBoxLayout();
hb->setMargin(0);
mainLayout->addLayout(hb);
m_useLast = new QCheckBox(i18n("&Always use this choice"), this);
hb->addWidget(m_useLast);
// buttons
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
hb->addWidget(buttonBox);
QPushButton *cancelButton = new QPushButton();
KGuiItem::assign(cancelButton, KStandardGuiItem::quit());
connect(cancelButton, SIGNAL(clicked()), this, SLOT(slotCancel()));
buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole);
m_openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Session"));
m_openButton->setEnabled(m_sessions->currentIndex().isValid());
m_openButton->setDefault(true);
m_openButton->setFocus();
buttonBox->addButton(m_openButton, QDialogButtonBox::ActionRole);
connect(m_openButton, SIGNAL(clicked()), this, SLOT(slotOpen()));
QPushButton *newButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Session"));
buttonBox->addButton(newButton, QDialogButtonBox::ActionRole);
connect(newButton, SIGNAL(clicked()), this, SLOT(slotNew()));
setResult(resultNone);
selectionChanged(nullptr, nullptr);
}
KateSessionChooser::~KateSessionChooser()
{}
void KateSessionChooser::slotCopySession()
{
m_sessions->setCurrentItem((KateSessionChooserItem *)((QAction *)sender())->data().value<void *>());
Q_ASSERT(static_cast<KateSessionChooserItem *>(m_sessions->currentItem()));
done(resultCopy);
}
void KateSessionChooser::slotDeleteSession()
{
KateSessionChooserItem *item = (KateSessionChooserItem *)((QAction *)sender())->data().value<void *>();
if (!item) {
return;
}
KateApp::self()->sessionManager()->deleteSession(item->session);
m_sessions->removeItemWidget(item, 2);
delete item;
}
KateSession::Ptr KateSessionChooser::selectedSession()
{
KateSessionChooserItem *item = static_cast<KateSessionChooserItem *>(m_sessions->currentItem());
Q_ASSERT(item || ((result() != resultOpen) && (result() != resultCopy)));
if (!item) {
return KateSession::Ptr();
}
return item->session;
}
bool KateSessionChooser::reopenLastSession()
{
return m_useLast->isChecked();
}
void KateSessionChooser::slotOpen()
{
Q_ASSERT(static_cast<KateSessionChooserItem *>(m_sessions->currentItem()));
done(resultOpen);
}
void KateSessionChooser::slotNew()
{
done(resultNew);
}
void KateSessionChooser::slotCancel()
{
done(resultQuit);
}
void KateSessionChooser::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
{
Q_UNUSED(current);
m_openButton->setEnabled(true);
}
//END CHOOSER DIALOG
/* This file is part of the KDE project
*
* Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __KATE_SESSION_CHOOSER_H__
#define __KATE_SESSION_CHOOSER_H__
#include <QDialog>
class QPushButton;
class QCheckBox;
class QTreeWidget;
class QTreeWidgetItem;
#include "katesession.h"
class KateSessionChooser : public QDialog
{
Q_OBJECT
public:
KateSessionChooser(QWidget *parent, const QString &lastSession);
~KateSessionChooser() override;
KateSession::Ptr selectedSession();
bool reopenLastSession();
enum {
resultQuit = QDialog::Rejected,
resultOpen,
resultNew,
resultNone,
resultCopy
};
protected Q_SLOTS:
void slotCancel();
void slotOpen();
void slotNew();
void slotCopySession();
void slotDeleteSession();
/**
* selection has changed
*/
void selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
private:
QTreeWidget *m_sessions;
QCheckBox *m_useLast;
QPushButton *m_openButton;
};
#endif
......@@ -21,142 +21,269 @@
#include "katesessionmanagedialog.h"
#include "kateapp.h"
#include "katesessionmanager.h"
#include "katesessionchooseritem.h"
#include "katesessionmanager.h"
#include <KColorScheme>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <KStandardGuiItem>
#include <QApplication>
#include <QDialogButtonBox>
#include <QFile>
#include <QInputDialog>
#include <QPushButton>
#include <QTreeWidget>
#include <QVBoxLayout>
KateSessionManageDialog::KateSessionManageDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
setWindowTitle(i18n("Manage Sessions"));
m_dontAskCheckBox->hide();
QVBoxLayout *mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
m_sessionList->installEventFilter(this);
connect(m_sessionList, &QTreeWidget::currentItemChanged, this, &KateSessionManageDialog::selectionChanged);
connect(m_sessionList, &QTreeWidget::itemDoubleClicked, this, &KateSessionManageDialog::openSession);
m_sessionList->header()->moveSection(0, 1); // Re-order columns to "Files, Sessions"
QHBoxLayout *hb = new QHBoxLayout();
mainLayout->addLayout(hb);
m_filterBox->installEventFilter(this);
connect(m_filterBox, &QLineEdit::textChanged, this, &KateSessionManageDialog::filterChanged);
connect(m_sortButton, &QPushButton::clicked, this, &KateSessionManageDialog::changeSortOrder);
m_sessions = new QTreeWidget(this);
m_sessions->setMinimumSize(400, 200);
hb->addWidget(m_sessions);
m_sessions->setColumnCount(2);
QStringList header;
header << i18n("Session Name");
header << i18nc("The number of open documents", "Open Documents");
m_sessions->setHeaderLabels(header);
m_sessions->setRootIsDecorated(false);
m_sessions->setItemsExpandable(false);
m_sessions->setAllColumnsShowFocus(true);
m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows);
m_sessions->setSelectionMode(QAbstractItemView::SingleSelection);
connect(m_newButton, &QPushButton::clicked, this, &KateSessionManageDialog::openNewSession);
connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
KGuiItem::assign(m_openButton, KStandardGuiItem::open());
m_openButton->setDefault(true);
connect(m_openButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSession);
updateSessionList();
m_sessions->resizeColumnToContents(0);
connect(m_templateButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSessionAsTemplate);
// right column buttons
QDialogButtonBox *rightButtons = new QDialogButtonBox(this);
rightButtons->setOrientation(Qt::Vertical);
hb->addWidget(rightButtons);
connect(m_copyButton, &QPushButton::clicked, this, &KateSessionManageDialog::copySession);
m_rename = new QPushButton(i18n("&Rename..."));
connect(m_rename, SIGNAL(clicked()), this, SLOT(rename()));
rightButtons->addButton(m_rename, QDialogButtonBox::ApplyRole);
connect(m_renameButton, &QPushButton::clicked, this, &KateSessionManageDialog::editBegin);
m_del = new QPushButton();
KGuiItem::assign(m_del, KStandardGuiItem::del());
connect(m_del, SIGNAL(clicked()), this, SLOT(del()));
rightButtons->addButton(m_del, QDialogButtonBox::ApplyRole);
connect(m_deleteButton, &QPushButton::clicked, this, &KateSessionManageDialog::updateDeleteList);
// dialog buttons
QDialogButtonBox *bottomButtons = new QDialogButtonBox(this);
mainLayout->addWidget(bottomButtons);
KGuiItem::assign(m_closeButton, KStandardGuiItem::close());
connect(m_closeButton, &QPushButton::clicked, this, &KateSessionManageDialog::closeDialog);
QPushButton *closeButton = new QPushButton;
KGuiItem::assign(closeButton, KStandardGuiItem::close());
closeButton->setDefault(true);
bottomButtons->addButton(closeButton, QDialogButtonBox::RejectRole);
connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose()));
connect(KateApp::self()->sessionManager(), &KateSessionManager::sessionListChanged, this, &KateSessionManageDialog::updateSessionList);
changeSortOrder(); // Set order to SortAlphabetical, set button text and fill session list
}
m_openButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), i18n("&Open"));
bottomButtons->addButton(m_openButton, QDialogButtonBox::AcceptRole);
connect(m_openButton, SIGNAL(clicked()), this, SLOT(open()));
// trigger action update
selectionChanged(nullptr, nullptr);
KateSessionManageDialog::KateSessionManageDialog(QWidget *parent, const QString &lastSession)
: KateSessionManageDialog(parent)
{
setWindowTitle(i18n("Session Chooser"));
m_dontAskCheckBox->show();
m_chooserMode = true;
connect(m_dontAskCheckBox, &QCheckBox::toggled, this, &KateSessionManageDialog::dontAskToggled);
m_prefferedSession = lastSession;
changeSortOrder(); // Set order to SortChronological
}
KateSessionManageDialog::~KateSessionManageDialog()
{}
void KateSessionManageDialog::slotClose()
void KateSessionManageDialog::dontAskToggled()
{
done(0);
m_templateButton->setEnabled(!m_dontAskCheckBox->isChecked());
}
void KateSessionManageDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
void KateSessionManageDialog::changeSortOrder()
{
const bool validItem = (current != nullptr);
switch (m_sortOrder) {
case SortAlphabetical:
m_sortOrder = SortChronological;
m_sortButton->setText(i18n("Sort Alpabetical"));
//m_sortButton->setIcon(QIcon::fromTheme(QStringLiteral("FIXME")));
break;
case SortChronological:
m_sortOrder = SortAlphabetical;
m_sortButton->setText(i18n("Sort Last Used"));
//m_sortButton->setIcon(QIcon::fromTheme(QStringLiteral("FIXME")));
break;
}
m_rename->setEnabled(validItem);
m_del->setEnabled(validItem && (static_cast<KateSessionChooserItem *>(current))->session != KateApp::self()->sessionManager()->activeSession());
m_openButton->setEnabled(true);
updateSessionList();
}
void KateSessionManageDialog::rename()
void KateSessionManageDialog::filterChanged()
{
KateSessionChooserItem *item = static_cast<KateSessionChooserItem *>(m_sessions->currentItem());
static QPointer<QTimer> delay;
if (!item) {
if (!delay) {
delay = new QTimer(this); // Should be auto cleard by Qt when we die
delay->setSingleShot(true);
delay->setInterval(400);
connect(delay, &QTimer::timeout, this, &KateSessionManageDialog::updateSessionList);
}
delay->start();
}
void KateSessionManageDialog::done(int result)
{
for (auto session : qAsConst(m_deleteList)) {
KateApp::self()->sessionManager()->deleteSession(session);
}
m_deleteList.clear(); // May not needed, but anyway
if (ResultQuit == result) {
QDialog::done(0);
return;
}
bool ok = false;
QString name = QInputDialog::getText(QApplication::activeWindow(), // nasty trick:)
i18n("Specify New Name for Session"), i18n("Session name:"),
QLineEdit::Normal, item->session->name(), &ok);
if (m_chooserMode && m_dontAskCheckBox->isChecked()) {
// write back our nice boolean :)
KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General"));
switch (result) {
case ResultOpen:
generalConfig.writeEntry("Startup Session", "last");
break;
case ResultNew:
generalConfig.writeEntry("Startup Session", "new");
break;
default:
break;
}
generalConfig.sync();
}
if (!ok) {
QDialog::done(1);
}
void KateSessionManageDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
Q_UNUSED(previous);
if (m_editByUser) {
editDone(); // Field was left unchanged, no need to apply
return;
}
if (name.isEmpty()) {
KMessageBox::sorry(this, i18n("To save a session, you must specify a name."), i18n("Missing Session Name"));
if (!current) {
m_openButton->setEnabled(false);
m_templateButton->setEnabled(false);
m_copyButton->setEnabled(false);
m_renameButton->setEnabled(false);
m_deleteButton->setEnabled(false);
return;
}
if (KateApp::self()->sessionManager()->renameSession(item->session, name)) {
updateSessionList();
const KateSession::Ptr activeSession = KateApp::self()->sessionManager()->activeSession();
const bool notActiveSession = !KateApp::self()->sessionManager()->sessionIsActive(currentSelectedSession()->name());
m_deleteButton->setEnabled(notActiveSession);
if (m_deleteList.contains(currentSelectedSession())) {
m_deleteButton->setText(i18n("Restore"));
m_openButton->setEnabled(false);
m_templateButton->setEnabled(false);
m_copyButton->setEnabled(true); // Looks a little strange but is OK
m_renameButton->setEnabled(false);
} else {
KGuiItem::assign(m_deleteButton, KStandardGuiItem::del());
m_openButton->setEnabled(currentSelectedSession() != activeSession);
m_templateButton->setEnabled(true);
m_copyButton->setEnabled(true);
m_renameButton->setEnabled(true);
}
}
void KateSessionManageDialog::del()
void KateSessionManageDialog::disableButtons()
{
m_openButton->setEnabled(false);
m_newButton->setEnabled(false);
m_templateButton->setEnabled(false);
m_dontAskCheckBox->setEnabled(false);
m_copyButton->setEnabled(false);
m_renameButton->setEnabled(false);
m_deleteButton->setEnabled(false);
m_closeButton->setEnabled(false);
m_sortButton->setEnabled(false);
m_filterBox->setEnabled(false);
}
void KateSessionManageDialog::editBegin()
{
KateSessionChooserItem *item = static_cast<KateSessionChooserItem *>(m_sessions->currentItem());
if (m_editByUser) {
return;
}
KateSessionChooserItem *item = currentSessionItem();
if (!item) {
return;
}
KateApp::self()->sessionManager()->deleteSession(item->session);
disableButtons();
item->setFlags(item->flags() | Qt::ItemIsEditable);
m_sessionList->clearSelection();
m_sessionList->editItem(item, 0);
// Always apply changes user did, like Dolphin
connect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply);
connect(m_sessionList->itemWidget(item, 0), &QObject::destroyed, this, &KateSessionManageDialog::editApply);
m_editByUser = item; // Do it last to block eventFilter() actions until we are ready
}
void KateSessionManageDialog::editDone()
{
m_editByUser = nullptr;
disconnect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply);
updateSessionList();
m_newButton->setEnabled(true);
m_dontAskCheckBox->setEnabled(true);
m_closeButton->setEnabled(true);
m_sortButton->setEnabled(true);
m_filterBox->setEnabled(true);