File indexing completed on 2024-05-12 05:52:08

0001 /*
0002 
0003     SPDX-FileCopyrightText: 2005 Christoph Cullmann <cullmann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "katesessionmanagedialog.h"
0009 
0010 #include "kateapp.h"
0011 #include "katesessionchooseritem.h"
0012 #include "katesessionmanager.h"
0013 
0014 #include <KColorScheme>
0015 #include <KConfigGroup>
0016 #include <KLocalizedString>
0017 #include <KSharedConfig>
0018 #include <KStandardGuiItem>
0019 
0020 #include <QKeyEvent>
0021 #include <QTimer>
0022 
0023 KateSessionManageDialog::KateSessionManageDialog(QWidget *parent)
0024     : QDialog(parent)
0025 {
0026     setupUi(this);
0027     setWindowTitle(i18n("Manage Sessions"));
0028     m_dontAskCheckBox->hide();
0029 
0030     m_sessionList->installEventFilter(this);
0031     connect(m_sessionList, &QTreeWidget::currentItemChanged, this, &KateSessionManageDialog::selectionChanged);
0032     connect(m_sessionList, &QTreeWidget::itemDoubleClicked, this, &KateSessionManageDialog::openSession);
0033     m_sessionList->header()->moveSection(0, 1); // Re-order columns to "Files, Sessions"
0034 
0035     m_filterBox->installEventFilter(this);
0036     connect(m_filterBox, &QLineEdit::textChanged, this, &KateSessionManageDialog::filterChanged);
0037 
0038     connect(m_newButton, &QPushButton::clicked, this, &KateSessionManageDialog::openNewSession);
0039 
0040     KGuiItem::assign(m_openButton, KStandardGuiItem::open());
0041     m_openButton->setDefault(true);
0042     connect(m_openButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSession);
0043 
0044     connect(m_templateButton, &QPushButton::clicked, this, &KateSessionManageDialog::openSessionAsTemplate);
0045 
0046     connect(m_copyButton, &QPushButton::clicked, this, &KateSessionManageDialog::copySession);
0047 
0048     connect(m_renameButton, &QPushButton::clicked, this, &KateSessionManageDialog::editBegin);
0049 
0050     connect(m_deleteButton, &QPushButton::clicked, this, &KateSessionManageDialog::updateDeleteList);
0051 
0052     KGuiItem::assign(m_closeButton, KStandardGuiItem::close());
0053     connect(m_closeButton, &QPushButton::clicked, this, &KateSessionManageDialog::closeDialog);
0054 
0055     connect(KateApp::self()->sessionManager(), &KateSessionManager::sessionListChanged, this, &KateSessionManageDialog::updateSessionList);
0056 
0057     updateSessionList();
0058 }
0059 
0060 KateSessionManageDialog::KateSessionManageDialog(QWidget *parent, const QString &lastSession)
0061     : KateSessionManageDialog(parent)
0062 {
0063     setWindowTitle(i18n("Session Chooser"));
0064     m_dontAskCheckBox->show();
0065     m_chooserMode = true;
0066     connect(m_dontAskCheckBox, &QCheckBox::toggled, this, &KateSessionManageDialog::dontAskToggled);
0067 
0068     m_preferredSession = lastSession;
0069     updateSessionList();
0070 }
0071 
0072 void KateSessionManageDialog::dontAskToggled()
0073 {
0074     m_templateButton->setEnabled(!m_dontAskCheckBox->isChecked());
0075 }
0076 
0077 void KateSessionManageDialog::filterChanged()
0078 {
0079     static QPointer<QTimer> delay;
0080 
0081     if (!delay) {
0082         delay = new QTimer(this); // Should be auto cleard by Qt when we die
0083         delay->setSingleShot(true);
0084         delay->setInterval(400);
0085         connect(delay, &QTimer::timeout, this, &KateSessionManageDialog::updateSessionList);
0086     }
0087 
0088     delay->start();
0089 }
0090 
0091 void KateSessionManageDialog::done(int result)
0092 {
0093     for (const auto &session : qAsConst(m_deleteList)) {
0094         KateApp::self()->sessionManager()->deleteSession(session);
0095         KateApp::self()->stashManager()->clearStashForSession(session);
0096     }
0097     m_deleteList.clear(); // May not needed, but anyway
0098 
0099     if (ResultQuit == result) {
0100         QDialog::done(0);
0101         return;
0102     }
0103 
0104     if (m_chooserMode && m_dontAskCheckBox->isChecked()) {
0105         // write back our nice boolean :)
0106         KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General"));
0107         switch (result) {
0108         case ResultOpen:
0109             generalConfig.writeEntry("Startup Session", "last");
0110             break;
0111         case ResultNew:
0112             generalConfig.writeEntry("Startup Session", "new");
0113             break;
0114         default:
0115             break;
0116         }
0117         generalConfig.sync();
0118     }
0119 
0120     QDialog::done(1);
0121 }
0122 
0123 void KateSessionManageDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
0124 {
0125     Q_UNUSED(previous);
0126 
0127     if (m_editByUser) {
0128         editDone(); // Field was left unchanged, no need to apply
0129         return;
0130     }
0131 
0132     if (!current) {
0133         m_openButton->setEnabled(false);
0134         m_templateButton->setEnabled(false);
0135         m_copyButton->setEnabled(false);
0136         m_renameButton->setEnabled(false);
0137         m_deleteButton->setEnabled(false);
0138         return;
0139     }
0140 
0141     const KateSession::Ptr activeSession = KateApp::self()->sessionManager()->activeSession();
0142     const bool notActiveSession = !KateApp::self()->sessionManager()->sessionIsActive(currentSelectedSession()->name());
0143 
0144     m_deleteButton->setEnabled(notActiveSession);
0145 
0146     if (m_deleteList.contains(currentSelectedSession())) {
0147         m_deleteButton->setText(i18n("Restore"));
0148         m_openButton->setEnabled(false);
0149         m_templateButton->setEnabled(false);
0150         m_copyButton->setEnabled(true); // Looks a little strange but is OK
0151         m_renameButton->setEnabled(false);
0152     } else {
0153         KGuiItem::assign(m_deleteButton, KStandardGuiItem::del());
0154         m_openButton->setEnabled(currentSelectedSession() != activeSession);
0155         m_templateButton->setEnabled(true);
0156         m_copyButton->setEnabled(true);
0157         m_renameButton->setEnabled(true);
0158     }
0159 }
0160 
0161 void KateSessionManageDialog::disableButtons()
0162 {
0163     m_openButton->setEnabled(false);
0164     m_newButton->setEnabled(false);
0165     m_templateButton->setEnabled(false);
0166     m_dontAskCheckBox->setEnabled(false);
0167     m_copyButton->setEnabled(false);
0168     m_renameButton->setEnabled(false);
0169     m_deleteButton->setEnabled(false);
0170     m_closeButton->setEnabled(false);
0171     m_filterBox->setEnabled(false);
0172 }
0173 
0174 void KateSessionManageDialog::editBegin()
0175 {
0176     if (m_editByUser) {
0177         return;
0178     }
0179 
0180     KateSessionChooserItem *item = currentSessionItem();
0181 
0182     if (!item) {
0183         return;
0184     }
0185 
0186     disableButtons();
0187 
0188     item->setFlags(item->flags() | Qt::ItemIsEditable);
0189     m_sessionList->clearSelection();
0190     m_sessionList->editItem(item, 0);
0191 
0192     // Always apply changes user did, like Dolphin
0193     connect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply);
0194     connect(m_sessionList->itemWidget(item, 0), &QObject::destroyed, this, &KateSessionManageDialog::editApply);
0195 
0196     m_editByUser = item; // Do it last to block eventFilter() actions until we are ready
0197 }
0198 
0199 void KateSessionManageDialog::editDone()
0200 {
0201     m_editByUser = nullptr;
0202     disconnect(m_sessionList, &QTreeWidget::itemChanged, this, &KateSessionManageDialog::editApply);
0203 
0204     // avoid crash, see bug 142127
0205     QTimer::singleShot(0, this, &KateSessionManageDialog::updateSessionList);
0206     m_sessionList->setFocus();
0207 
0208     m_newButton->setEnabled(true);
0209     m_dontAskCheckBox->setEnabled(true);
0210     m_closeButton->setEnabled(true);
0211     m_filterBox->setEnabled(true);
0212 }
0213 
0214 void KateSessionManageDialog::editApply()
0215 {
0216     if (!m_editByUser) {
0217         return;
0218     }
0219 
0220     KateApp::self()->sessionManager()->renameSession(m_editByUser->session, m_editByUser->text(0));
0221     editDone();
0222 }
0223 
0224 void KateSessionManageDialog::copySession()
0225 {
0226     KateSessionChooserItem *item = currentSessionItem();
0227 
0228     if (!item) {
0229         return;
0230     }
0231 
0232     m_preferredSession = KateApp::self()->sessionManager()->copySession(item->session);
0233     m_sessionList->setFocus(); // Only needed when user abort
0234 }
0235 
0236 void KateSessionManageDialog::openSession()
0237 {
0238     KateSessionChooserItem *item = currentSessionItem();
0239 
0240     if (!item) {
0241         return;
0242     }
0243 
0244     hide();
0245 
0246     // this might fail, e.g. if session is in use, then e.g. end kate, bug 390740
0247     const bool success = KateApp::self()->sessionManager()->activateSession(item->session);
0248     done(success ? ResultOpen : ResultQuit);
0249 }
0250 
0251 void KateSessionManageDialog::openSessionAsTemplate()
0252 {
0253     KateSessionChooserItem *item = currentSessionItem();
0254 
0255     if (!item) {
0256         return;
0257     }
0258 
0259     hide();
0260 
0261     KateSessionManager *sm = KateApp::self()->sessionManager();
0262     KateSession::Ptr ns = KateSession::createAnonymousFrom(item->session, sm->anonymousSessionFile());
0263     sm->activateSession(ns);
0264 
0265     done(ResultOpen);
0266 }
0267 
0268 void KateSessionManageDialog::openNewSession()
0269 {
0270     hide();
0271     KateApp::self()->sessionManager()->sessionNew();
0272     done(ResultNew);
0273 }
0274 
0275 void KateSessionManageDialog::updateDeleteList()
0276 {
0277     KateSessionChooserItem *item = currentSessionItem();
0278 
0279     if (!item) {
0280         return;
0281     }
0282 
0283     const KateSession::Ptr session = item->session;
0284     if (m_deleteList.contains(session)) {
0285         m_deleteList.remove(session);
0286         item->setForeground(0, QBrush(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color()));
0287         item->setIcon(0, QIcon());
0288         item->setToolTip(0, QString());
0289     } else {
0290         m_deleteList.insert(session);
0291         markItemAsToBeDeleted(item);
0292     }
0293 
0294     // To ease multiple deletions, move the selection
0295     QTreeWidgetItem *newItem = m_sessionList->itemBelow(item) ? m_sessionList->itemBelow(item) : m_sessionList->topLevelItem(0);
0296     m_sessionList->setCurrentItem(newItem);
0297     m_sessionList->setFocus();
0298 }
0299 
0300 void KateSessionManageDialog::markItemAsToBeDeleted(QTreeWidgetItem *item)
0301 {
0302     item->setForeground(0, QBrush(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText).color()));
0303     item->setIcon(0, QIcon::fromTheme(QStringLiteral("emblem-warning")));
0304     item->setToolTip(0, i18n("Session will be deleted on dialog close"));
0305 }
0306 
0307 void KateSessionManageDialog::closeDialog()
0308 {
0309     done(ResultQuit);
0310 }
0311 
0312 void KateSessionManageDialog::updateSessionList()
0313 {
0314     if (m_editByUser) {
0315         // Don't crash accidentally an ongoing edit
0316         return;
0317     }
0318 
0319     KateSession::Ptr currSelSession = currentSelectedSession();
0320     KateSession::Ptr activeSession = KateApp::self()->sessionManager()->activeSession();
0321 
0322     m_sessionList->clear();
0323 
0324     KateSessionList slist = KateApp::self()->sessionManager()->sessionList();
0325 
0326     KateSessionChooserItem *preferredItem = nullptr;
0327     KateSessionChooserItem *currSessionItem = nullptr;
0328     KateSessionChooserItem *activeSessionItem = nullptr;
0329 
0330     KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General"));
0331     int col = generalConfig.readEntry("Session Manager Sort Column", 0);
0332     int order = generalConfig.readEntry("Session Manager Sort Order", (int)Qt::AscendingOrder);
0333     if (size_t(col) > 2) {
0334         col = 0;
0335     }
0336     if (order != Qt::AscendingOrder && order != Qt::DescendingOrder) {
0337         order = Qt::AscendingOrder;
0338     }
0339 
0340     for (const KateSession::Ptr &session : qAsConst(slist)) {
0341         if (!m_filterBox->text().isEmpty()) {
0342             if (!session->name().contains(m_filterBox->text(), Qt::CaseInsensitive)) {
0343                 continue;
0344             }
0345         }
0346 
0347         KateSessionChooserItem *item = new KateSessionChooserItem(m_sessionList, session);
0348         if (session == currSelSession) {
0349             currSessionItem = item;
0350         } else if (session == activeSession) {
0351             activeSessionItem = item;
0352         } else if (session->name() == m_preferredSession) {
0353             preferredItem = item;
0354             m_preferredSession.clear();
0355         }
0356 
0357         if (m_deleteList.contains(session)) {
0358             markItemAsToBeDeleted(item);
0359         }
0360     }
0361 
0362     connect(m_sessionList->header(), &QHeaderView::sortIndicatorChanged, this, [](int logIdx, Qt::SortOrder order) {
0363         KConfigGroup generalConfig(KSharedConfig::openConfig(), QStringLiteral("General"));
0364         generalConfig.writeEntry("Session Manager Sort Column", logIdx);
0365         generalConfig.writeEntry("Session Manager Sort Order", (int)order);
0366     });
0367 
0368     m_sessionList->header()->setStretchLastSection(false);
0369     m_sessionList->header()->setSectionResizeMode(0, QHeaderView::Stretch); // stretch "Session Name" column
0370     m_sessionList->resizeColumnToContents(1); // Fit "Files" column
0371     m_sessionList->resizeColumnToContents(2); // Fit "Last update" column
0372     m_sessionList->sortByColumn(col, Qt::SortOrder(order)); // sort by "Session Name" column.. don't worry, it only sorts when the model data changes.
0373 
0374     if (!preferredItem) {
0375         preferredItem = currSessionItem ? currSessionItem : activeSessionItem;
0376     }
0377 
0378     if (preferredItem) {
0379         m_sessionList->setCurrentItem(preferredItem);
0380         m_sessionList->scrollToItem(preferredItem);
0381     } else if (m_sessionList->topLevelItemCount() > 0) {
0382         m_sessionList->setCurrentItem(m_sessionList->topLevelItem(0));
0383     }
0384 
0385     if (m_filterBox->hasFocus()) {
0386         return;
0387     }
0388 
0389     if (m_sessionList->topLevelItemCount() == 0) {
0390         m_openButton->setEnabled(false);
0391         m_templateButton->setEnabled(false);
0392         m_dontAskCheckBox->setEnabled(false);
0393         m_copyButton->setEnabled(false);
0394         m_renameButton->setEnabled(false);
0395         m_deleteButton->setEnabled(false);
0396         m_filterBox->setEnabled(false);
0397 
0398         m_newButton->setFocus();
0399     } else {
0400         m_sessionList->setFocus();
0401     }
0402 }
0403 
0404 KateSessionChooserItem *KateSessionManageDialog::currentSessionItem() const
0405 {
0406     return static_cast<KateSessionChooserItem *>(m_sessionList->currentItem());
0407 }
0408 
0409 KateSession::Ptr KateSessionManageDialog::currentSelectedSession() const
0410 {
0411     KateSessionChooserItem *item = currentSessionItem();
0412 
0413     if (!item) {
0414         return KateSession::Ptr();
0415     }
0416 
0417     return item->session;
0418 }
0419 
0420 bool KateSessionManageDialog::eventFilter(QObject *object, QEvent *event)
0421 {
0422     if (object == m_sessionList) {
0423         if (!m_editByUser) { // No need for further action
0424             return false;
0425         }
0426         if (event->type() == QEvent::KeyPress) {
0427             QKeyEvent *ke = static_cast<QKeyEvent *>(event);
0428             switch (ke->key()) {
0429             // Avoid to apply changes with untypical keys/don't left edit field this way
0430             case Qt::Key_Up:
0431             case Qt::Key_Down:
0432             case Qt::Key_PageUp:
0433             case Qt::Key_PageDown:
0434                 return true;
0435             default:
0436                 break;
0437             }
0438 
0439         } else if (event->type() == QEvent::KeyRelease) {
0440             QKeyEvent *ke = static_cast<QKeyEvent *>(event);
0441             switch (ke->key()) {
0442             case Qt::Key_Escape:
0443                 editDone(); // Abort edit
0444                 break;
0445             case Qt::Key_Return:
0446                 editApply();
0447                 break;
0448             default:
0449                 break;
0450             }
0451         }
0452 
0453     } else if (object == m_filterBox && event->type() == QEvent::KeyPress) {
0454         QKeyEvent *ke = static_cast<QKeyEvent *>(event);
0455         // Catch Return key to avoid to finish the dialog
0456         if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
0457             // avoid crash, see bug 142127
0458             QTimer::singleShot(0, this, &KateSessionManageDialog::updateSessionList);
0459             m_sessionList->setFocus();
0460             return true;
0461         }
0462     }
0463 
0464     return false;
0465 }
0466 
0467 void KateSessionManageDialog::closeEvent(QCloseEvent *event)
0468 {
0469     Q_UNUSED(event);
0470 
0471     if (m_editByUser) {
0472         // We must catch closeEvent here due to connected signal of QLineEdit::destroyed->editApply()->crash!
0473         editDone(); // editApply() don't work, m_editByUser->text(0) will not updated from QLineEdit
0474     }
0475 }
0476 
0477 #include "moc_katesessionmanagedialog.cpp"