File indexing completed on 2024-04-28 04:37:24

0001 /*
0002     SPDX-FileCopyrightText: 2008 Andreas Pakulat <apaku@gmx.de>
0003     SPDX-FileCopyrightText: 2010 David Nolden <david.nolden.kdevelop@art-master.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "sessionchooserdialog.h"
0009 #include "sessioncontroller.h"
0010 #include "core.h"
0011 
0012 #include <QDialogButtonBox>
0013 #include <QKeyEvent>
0014 #include <QLineEdit>
0015 #include <QListView>
0016 #include <QPushButton>
0017 #include <QVBoxLayout>
0018 
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 #include <KMessageBox_KDevCompat>
0022 
0023 using namespace KDevelop;
0024 
0025 SessionChooserDialog::SessionChooserDialog(QListView* view, QAbstractItemModel* model, QLineEdit* filter)
0026     : m_view(view), m_model(model), m_filter(filter), m_deleteCandidateRow(-1)
0027 {
0028     m_updateStateTimer.setInterval(5000);
0029     m_updateStateTimer.setSingleShot(false);
0030     m_updateStateTimer.start();
0031     connect(&m_updateStateTimer, &QTimer::timeout, this, &SessionChooserDialog::updateState);
0032     connect(view, &QListView::doubleClicked, this, &SessionChooserDialog::doubleClicked);
0033     connect(view, &QListView::entered, this, &SessionChooserDialog::itemEntered);
0034 
0035     m_deleteButton = new QPushButton(view->viewport());
0036     m_deleteButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0037     m_deleteButton->setToolTip(i18nc("@info:tooltip", "Delete session"));
0038     m_deleteButton->hide();
0039     connect(m_deleteButton, &QPushButton::clicked, this, &SessionChooserDialog::deleteButtonPressed);
0040 
0041     m_deleteButtonTimer.setInterval(500);
0042     m_deleteButtonTimer.setSingleShot(true);
0043     connect(&m_deleteButtonTimer, &QTimer::timeout, this, &SessionChooserDialog::showDeleteButton);
0044 
0045     view->setMouseTracking(true);
0046     view->installEventFilter(this);
0047     filter->installEventFilter(this);
0048     connect(filter, &QLineEdit::textChanged, this, &SessionChooserDialog::filterTextChanged);
0049 
0050     setWindowTitle(i18nc("@title:window", "Pick a Session"));
0051 
0052     m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close);
0053     auto mainLayout = new QVBoxLayout(this);
0054     m_mainWidget = new QWidget(this);
0055     mainLayout->addWidget(m_mainWidget);
0056 
0057     QPushButton *okButton = m_buttonBox->button(QDialogButtonBox::Ok);
0058     okButton->setDefault(true);
0059     okButton->setShortcut(Qt::Key_Return);
0060     connect(m_buttonBox, &QDialogButtonBox::accepted, this, &SessionChooserDialog::accept);
0061     connect(m_buttonBox, &QDialogButtonBox::rejected, this, &SessionChooserDialog::reject);
0062     mainLayout->addWidget(m_buttonBox);
0063     okButton->setText(i18nc("@action:button", "Run"));
0064     okButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0065 }
0066 
0067 void SessionChooserDialog::filterTextChanged()
0068 {
0069     m_view->selectionModel()->setCurrentIndex(m_model->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
0070     bool enabled = m_view->model()->rowCount(QModelIndex())>0;
0071     m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
0072     m_deleteButton->setVisible(false);
0073 }
0074 
0075 void SessionChooserDialog::doubleClicked(const QModelIndex& index)
0076 {
0077     if(m_model->flags(index) & Qt::ItemIsEnabled)
0078         accept();
0079 }
0080 
0081 void SessionChooserDialog::updateState() {
0082     // Sometimes locking may take some time, so we stop the timer, to prevent an 'avalanche' of events
0083     m_updateStateTimer.stop();
0084     for(int row = 0; row < m_model->rowCount(); ++row)
0085     {
0086         QString session = m_model->index(row, 0).data().toString();
0087 
0088         if(session.isEmpty()) //create new session
0089             continue;
0090 
0091         QString state, tooltip;
0092         SessionRunInfo info = SessionController::sessionRunInfo(session);
0093         if(info.isRunning)
0094         {
0095             tooltip = i18nc("@info:tooltip", "Active session.\npid %1, app %2, host %3",
0096                            info.holderPid, info.holderApp, info.holderHostname);
0097             state = i18n("Running");
0098         }
0099 
0100         m_model->setData(m_model->index(row, 1),
0101                          !info.isRunning ? QIcon() : QIcon::fromTheme(QStringLiteral("media-playback-start")),
0102                          Qt::DecorationRole);
0103         m_model->setData(m_model->index(row, 1), tooltip, Qt::ToolTipRole);
0104         m_model->setData(m_model->index(row, 2), state, Qt::DisplayRole);
0105     }
0106 
0107     m_updateStateTimer.start();
0108 }
0109 
0110 void SessionChooserDialog::itemEntered(const QModelIndex& index)
0111 {
0112     // The last row says "Create new session", we don't want to delete that
0113     if(index.row() == m_model->rowCount()-1) {
0114         m_deleteButton->hide();
0115         m_deleteButtonTimer.stop();
0116         return;
0117     }
0118 
0119     // align the delete-button to stay on the right border of the item
0120     // we need the right most column's index
0121     QModelIndex in = m_model->index( index.row(), 1 );
0122     const QRect rect = m_view->visualRect(in);
0123     m_deleteButton->resize(rect.height(), rect.height());
0124 
0125     QPoint p(rect.right() - m_deleteButton->size().width(), rect.top()+rect.height()/2-m_deleteButton->height()/2);
0126     m_deleteButton->move(p);
0127 
0128     m_deleteCandidateRow = index.row();
0129     m_deleteButtonTimer.start();
0130 }
0131 
0132 void SessionChooserDialog::showDeleteButton()
0133 {
0134     m_deleteButton->show();
0135 }
0136 
0137 bool SessionChooserDialog::eventFilter(QObject* object, QEvent* event)
0138 {
0139     if(object == m_view && event->type() == QEvent::Leave ) {
0140         m_deleteButtonTimer.stop();
0141         m_deleteButton->hide();
0142         // don't eat the event, pass on
0143    } else if (object == m_filter && event->type() == QEvent::KeyPress) {
0144         auto *keyEvent = static_cast<QKeyEvent*>(event);
0145         if (keyEvent->key() == Qt::Key_Return) {
0146             accept();
0147             // don't eat the event, pass on
0148         } else if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) {
0149             QModelIndex currentIndex = m_view->selectionModel()->currentIndex();
0150             int selectRow = -1;
0151             if (keyEvent->key() == Qt::Key_Up) {
0152                 if(!currentIndex.isValid()) {
0153                     selectRow = m_model->rowCount()-1;
0154                 } else if(currentIndex.row()-1 >= 0) {
0155                     selectRow = currentIndex.row()-1;
0156                 }
0157             } else {
0158                 if(!currentIndex.isValid()) {
0159                     selectRow = 0;
0160                 } else if(currentIndex.row()+1 < m_model->rowCount()) {
0161                     selectRow = currentIndex.row()+1;
0162                 }
0163             }
0164 
0165             if (selectRow != -1) {
0166                     m_view->selectionModel()->setCurrentIndex(m_model->index(selectRow, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
0167             }
0168             return true; // eat event
0169         }
0170     }
0171 
0172     return QDialog::eventFilter(object, event);
0173 }
0174 
0175 QWidget* SessionChooserDialog::mainWidget() const
0176 {
0177     return m_mainWidget;
0178 }
0179 
0180 void SessionChooserDialog::deleteButtonPressed()
0181 {
0182     if(m_deleteCandidateRow == -1)
0183         return;
0184 
0185     QModelIndex uuidIndex = m_model->index(m_deleteCandidateRow, 0);
0186     QModelIndex sessionNameIndex = m_model->index(m_deleteCandidateRow, 3);
0187     const QString uuid = m_model->data(uuidIndex, Qt::DisplayRole).toString();
0188     const QString sessionName = m_model->data(sessionNameIndex, Qt::DisplayRole).toString();
0189 
0190     TryLockSessionResult result = SessionController::tryLockSession( uuid );
0191     if( !result.lock ) {
0192         const QString errCaption = i18nc("@title:window", "Cannot Delete Session");
0193         QString errText = i18nc("@info", "<p>Cannot delete a locked session.");
0194 
0195         if( result.runInfo.holderPid != -1 ) {
0196             errText += i18nc("@info", "<p>The session <b>%1</b> is locked by %2 on %3 (PID %4).",
0197                                 sessionName, result.runInfo.holderApp, result.runInfo.holderHostname, result.runInfo.holderPid);
0198         }
0199 
0200         KMessageBox::error( this, errText, errCaption );
0201         return;
0202     }
0203 
0204     const QString text = i18nc("@info", "The session <b>%1</b> and all contained settings will be deleted. The projects will stay unaffected. Do you really want to continue?", sessionName);
0205     const QString caption = i18nc("@title:window", "Delete Session");
0206     const KGuiItem deleteItem = KStandardGuiItem::del();
0207     const KGuiItem cancelItem = KStandardGuiItem::cancel();
0208 
0209     if (KMessageBox::warningTwoActions(this, text, caption, deleteItem, cancelItem) == KMessageBox::PrimaryAction) {
0210         SessionController::deleteSessionFromDisk(result.lock);
0211 
0212         m_model->removeRows( m_deleteCandidateRow, 1 );
0213         m_deleteCandidateRow = -1;
0214     }
0215 }
0216 
0217 #include "moc_sessionchooserdialog.cpp"