File indexing completed on 2024-05-05 04:40:55
0001 /* 0002 SPDX-FileCopyrightText: 2018 Amish K. Naidu <amhndu@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "scratchpadview.h" 0008 #include "scratchpad.h" 0009 0010 #include <debug.h> 0011 0012 #include <interfaces/icore.h> 0013 #include <interfaces/idocumentcontroller.h> 0014 #include <interfaces/iuicontroller.h> 0015 #include <interfaces/idocument.h> 0016 #include <sublime/message.h> 0017 0018 #include <KLocalizedString> 0019 0020 #include <QAction> 0021 #include <QStandardItemModel> 0022 #include <QSortFilterProxyModel> 0023 #include <QStyledItemDelegate> 0024 #include <QWidgetAction> 0025 #include <QLineEdit> 0026 #include <QInputDialog> 0027 #include <QPainter> 0028 #include <QAbstractItemView> 0029 0030 // Use a delegate because the dataChanged signal doesn't tell us the previous name 0031 class FileRenameDelegate 0032 : public QStyledItemDelegate 0033 { 0034 Q_OBJECT 0035 0036 public: 0037 FileRenameDelegate(QObject* parent, Scratchpad* scratchpad) 0038 : QStyledItemDelegate(parent) 0039 , m_scratchpad(scratchpad) 0040 { 0041 } 0042 0043 void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override 0044 { 0045 const QString previousName = index.data().toString(); 0046 QStyledItemDelegate::setModelData(editor, model, index); 0047 const auto* proxyModel = static_cast<QAbstractProxyModel*>(model); 0048 m_scratchpad->renameScratch(proxyModel->mapToSource(index), previousName); 0049 } 0050 0051 private: 0052 Scratchpad* m_scratchpad; 0053 }; 0054 0055 EmptyMessageListView::EmptyMessageListView(QWidget* parent) 0056 : QListView(parent) 0057 { 0058 } 0059 0060 void EmptyMessageListView::paintEvent(QPaintEvent* event) 0061 { 0062 if (model() && model()->rowCount(rootIndex()) > 0) { 0063 QListView::paintEvent(event); 0064 } else { 0065 QPainter painter(viewport()); 0066 const auto margin = 0067 QMargins(parentWidget()->style()->pixelMetric(QStyle::PM_LayoutLeftMargin), 0, 0068 parentWidget()->style()->pixelMetric(QStyle::PM_LayoutRightMargin), 0); 0069 painter.drawText(rect() - margin, Qt::AlignCenter | Qt::TextWordWrap, m_message); 0070 } 0071 } 0072 0073 void EmptyMessageListView::setEmptyMessage(const QString& message) 0074 { 0075 m_message = message; 0076 } 0077 0078 0079 ScratchpadView::ScratchpadView(QWidget* parent, Scratchpad* scratchpad) 0080 : QWidget(parent) 0081 , m_scratchpad(scratchpad) 0082 { 0083 setupUi(this); 0084 0085 setupActions(); 0086 0087 setWindowTitle(i18nc("@title:window", "Scratchpad")); 0088 setWindowIcon(QIcon::fromTheme(QStringLiteral("note"))); 0089 0090 auto* const modelProxy = new QSortFilterProxyModel(this); 0091 modelProxy->setSourceModel(m_scratchpad->model()); 0092 modelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); 0093 modelProxy->setSortCaseSensitivity(Qt::CaseInsensitive); 0094 modelProxy->setSortRole(Qt::DisplayRole); 0095 connect(m_filter, &QLineEdit::textEdited, 0096 modelProxy, &QSortFilterProxyModel::setFilterWildcard); 0097 0098 scratchView->setModel(modelProxy); 0099 scratchView->setItemDelegate(new FileRenameDelegate(this, m_scratchpad)); 0100 scratchView->setEmptyMessage(i18n("Scratchpad lets you quickly run and experiment with code without a full project, and even store todos. Create a new scratch to start.")); 0101 0102 connect(scratchView, &QListView::activated, this, &ScratchpadView::scratchActivated); 0103 0104 connect(m_scratchpad, &Scratchpad::actionFailed, this, [](const QString& messageText) { 0105 // TODO: could be also messagewidget inside toolview? 0106 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0107 KDevelop::ICore::self()->uiController()->postMessage(message); 0108 }); 0109 0110 connect(commandWidget, &QLineEdit::returnPressed, this, &ScratchpadView::runSelectedScratch); 0111 connect(commandWidget, &QLineEdit::returnPressed, this, [this] { 0112 m_scratchpad->setCommand(proxyModel()->mapToSource(currentIndex()), commandWidget->text()); 0113 }); 0114 commandWidget->setToolTip(i18nc("@info:tooltip", "Command to run this scratch. '$f' will expand to the scratch path.")); 0115 commandWidget->setPlaceholderText(commandWidget->toolTip()); 0116 0117 // change active scratch when changing document 0118 connect(KDevelop::ICore::self()->documentController(), &KDevelop::IDocumentController::documentActivated, this, 0119 [this](const KDevelop::IDocument* document) { 0120 if (document->url().isLocalFile()) { 0121 const auto* model = scratchView->model(); 0122 const auto index = model->match(model->index(0, 0), Scratchpad::FullPathRole, 0123 document->url().toLocalFile()).value({}); 0124 if (index.isValid()) { 0125 scratchView->setCurrentIndex(index); 0126 } 0127 } 0128 }); 0129 0130 connect(scratchView, &QAbstractItemView::pressed, this, &ScratchpadView::validateItemActions); 0131 0132 validateItemActions(); 0133 } 0134 0135 void ScratchpadView::setupActions() 0136 { 0137 auto* action = new QAction(QIcon::fromTheme(QStringLiteral("list-add")), i18nc("@action", "New Scratch"), this); 0138 connect(action, &QAction::triggered, this, &ScratchpadView::createScratch); 0139 addAction(action); 0140 0141 action = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action", "Remove Scratch"), this); 0142 connect(action, &QAction::triggered, this, [this] { 0143 m_scratchpad->removeScratch(proxyModel()->mapToSource(currentIndex())); 0144 validateItemActions(); 0145 }); 0146 addAction(action); 0147 m_itemActions.push_back(action); 0148 0149 action = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action", "Rename Scratch"), this); 0150 connect(action, &QAction::triggered, this, [this] { 0151 scratchView->edit(scratchView->currentIndex()); 0152 }); 0153 addAction(action); 0154 m_itemActions.push_back(action); 0155 0156 action = m_scratchpad->runAction(); 0157 action->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); 0158 action->setText(i18nc("@action", "Run Scratch")); 0159 connect(action, &QAction::triggered, this, &ScratchpadView::runSelectedScratch); 0160 addAction(action); 0161 m_itemActions.push_back(action); 0162 0163 m_filter = new QLineEdit(this); 0164 m_filter->setPlaceholderText(i18nc("@info:placeholder", "Filter...")); 0165 auto filterAction = new QWidgetAction(this); 0166 filterAction->setDefaultWidget(m_filter); 0167 addAction(filterAction); 0168 } 0169 0170 void ScratchpadView::validateItemActions() 0171 { 0172 bool enable = currentIndex().isValid(); 0173 0174 for (auto* action : qAsConst(m_itemActions)) { 0175 action->setEnabled(enable); 0176 } 0177 0178 commandWidget->setReadOnly(!enable); 0179 if (!enable) { 0180 commandWidget->clear(); 0181 } 0182 commandWidget->setText(currentIndex().data(Scratchpad::RunCommandRole).toString()); 0183 } 0184 0185 void ScratchpadView::runSelectedScratch() 0186 { 0187 const auto sourceIndex = proxyModel()->mapToSource(currentIndex()); 0188 if (auto* document = KDevelop::ICore::self()->documentController()->documentForUrl( 0189 QUrl::fromLocalFile(sourceIndex.data(Scratchpad::FullPathRole).toString()))) { 0190 document->save(); 0191 } 0192 m_scratchpad->setCommand(sourceIndex, commandWidget->text()); 0193 m_scratchpad->runScratch(sourceIndex); 0194 } 0195 0196 void ScratchpadView::scratchActivated(const QModelIndex& index) 0197 { 0198 validateItemActions(); 0199 m_scratchpad->openScratch(proxyModel()->mapToSource(index)); 0200 } 0201 0202 void ScratchpadView::createScratch() 0203 { 0204 QString name = QInputDialog::getText(this, i18nc("@title:window", "Create New Scratch"), 0205 i18nc("@label:textbox", "Name for scratch file:"), 0206 QLineEdit::Normal, 0207 QStringLiteral("example.cpp")); 0208 if (!name.isEmpty()) { 0209 m_scratchpad->createScratch(name); 0210 } 0211 } 0212 0213 QAbstractProxyModel* ScratchpadView::proxyModel() const 0214 { 0215 return static_cast<QAbstractProxyModel*>(scratchView->model()); 0216 } 0217 0218 QModelIndex ScratchpadView::currentIndex() const 0219 { 0220 return scratchView->currentIndex(); 0221 } 0222 0223 #include "scratchpadview.moc" 0224 #include "moc_emptymessagelistview.cpp" 0225 #include "moc_scratchpadview.cpp"