File indexing completed on 2024-04-28 05:49:07

0001 /*  This file is part of the Kate project.
0002  *
0003  *  SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "kateprojectinfoviewindex.h"
0009 #include "kateproject.h"
0010 #include "kateprojectindex.h"
0011 #include "kateprojectplugin.h"
0012 #include "kateprojectpluginview.h"
0013 #include "ktexteditor_utils.h"
0014 
0015 #include <KLocalizedString>
0016 #include <KMessageWidget>
0017 #include <QAction>
0018 #include <QVBoxLayout>
0019 
0020 KateProjectInfoViewIndex::KateProjectInfoViewIndex(KateProjectPluginView *pluginView, KateProject *project, QWidget *parent)
0021     : QWidget(parent)
0022     , m_pluginView(pluginView)
0023     , m_project(project)
0024     , m_messageWidget(nullptr)
0025     , m_lineEdit(new QLineEdit())
0026     , m_treeView(new QTreeView())
0027     , m_model(new QStandardItemModel(m_treeView))
0028 {
0029     /**
0030      * default style
0031      */
0032     m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
0033     m_treeView->setUniformRowHeights(true);
0034     m_treeView->setRootIsDecorated(false);
0035     m_model->setHorizontalHeaderLabels(QStringList() << i18n("Name") << i18n("Kind") << i18n("File") << i18n("Line"));
0036     m_lineEdit->setPlaceholderText(i18n("Search"));
0037     m_lineEdit->setClearButtonEnabled(true);
0038 
0039     /**
0040      * attach model
0041      * kill selection model
0042      */
0043     QItemSelectionModel *m = m_treeView->selectionModel();
0044     m_treeView->setModel(m_model);
0045     delete m;
0046 
0047     /**
0048      * layout widget
0049      */
0050     QVBoxLayout *layout = new QVBoxLayout;
0051     layout->setSpacing(0);
0052     layout->setContentsMargins(0, 0, 0, 0);
0053     layout->addWidget(m_lineEdit);
0054     layout->addWidget(m_treeView);
0055     setLayout(layout);
0056     setFocusProxy(m_lineEdit);
0057 
0058     /**
0059      * connect needed signals
0060      */
0061     connect(m_pluginView, &KateProjectPluginView::projectLookupWord, m_lineEdit, &QLineEdit::setText);
0062     connect(m_lineEdit, &QLineEdit::textChanged, this, &KateProjectInfoViewIndex::slotTextChanged);
0063     connect(m_treeView, &QTreeView::clicked, this, &KateProjectInfoViewIndex::slotClicked);
0064     if (m_project) {
0065         connect(m_project, &KateProject::indexChanged, this, &KateProjectInfoViewIndex::indexAvailable);
0066     } else {
0067         connect(m_pluginView, &KateProjectPluginView::gotoSymbol, this, &KateProjectInfoViewIndex::slotGotoSymbol);
0068         enableWidgets(true);
0069     }
0070 
0071     /**
0072      * trigger once search with nothing
0073      */
0074     slotTextChanged(QString());
0075 }
0076 
0077 KateProjectInfoViewIndex::~KateProjectInfoViewIndex()
0078 {
0079 }
0080 
0081 void KateProjectInfoViewIndex::slotGotoSymbol(const QString &text, int &results)
0082 {
0083     // trigger fill model
0084     m_lineEdit->setText(text);
0085     results = m_model->rowCount();
0086     // immediately goto if only a single option
0087     if (results == 1) {
0088         slotClicked(m_model->index(0, 0));
0089     }
0090 }
0091 
0092 void KateProjectInfoViewIndex::slotTextChanged(const QString &text)
0093 {
0094     /**
0095      * init
0096      */
0097     m_treeView->setSortingEnabled(false);
0098     m_model->setRowCount(0);
0099 
0100     /**
0101      * get results
0102      */
0103     if (m_project && m_project->projectIndex() && !text.isEmpty()) {
0104         m_project->projectIndex()->findMatches(*m_model, text, KateProjectIndex::FindMatches);
0105     } else if (!text.isEmpty()) {
0106         const auto projects = m_pluginView->plugin()->projects();
0107         for (const auto project : projects) {
0108             if (project->projectIndex()) {
0109                 project->projectIndex()->findMatches(*m_model, text, KateProjectIndex::FindMatches, TAG_FULLMATCH | TAG_OBSERVECASE);
0110             }
0111         }
0112     }
0113 
0114     /**
0115      * tree view polish ;)
0116      */
0117     m_treeView->setSortingEnabled(true);
0118     m_treeView->resizeColumnToContents(2);
0119     m_treeView->resizeColumnToContents(1);
0120     m_treeView->resizeColumnToContents(0);
0121 }
0122 
0123 void KateProjectInfoViewIndex::slotClicked(const QModelIndex &index)
0124 {
0125     /**
0126      * get path
0127      */
0128     QString filePath = m_model->item(index.row(), 2)->text();
0129     if (filePath.isEmpty()) {
0130         return;
0131     }
0132 
0133     /** Save current position before jumping **/
0134     auto currentView = m_pluginView->mainWindow()->activeView();
0135     QUrl url;
0136     KTextEditor::Cursor pos;
0137     if (currentView) {
0138         url = currentView->document()->url();
0139         pos = currentView->cursorPosition();
0140     }
0141 
0142     /**
0143      * create view
0144      */
0145     KTextEditor::View *view = m_pluginView->mainWindow()->openUrl(QUrl::fromLocalFile(filePath));
0146     if (!view) {
0147         return;
0148     }
0149 
0150     /** save current position in location history */
0151     Utils::addPositionToHistory(url, pos, m_pluginView->mainWindow());
0152 
0153     /**
0154      * set cursor, if possible
0155      */
0156     int line = m_model->item(index.row(), 3)->text().toInt();
0157     if (line >= 1) {
0158         view->setCursorPosition(KTextEditor::Cursor(line - 1, 0));
0159 
0160         /** save the jump position in location history */
0161         Utils::addPositionToHistory(view->document()->url(), {line - 1, 0}, m_pluginView->mainWindow());
0162     }
0163 }
0164 
0165 void KateProjectInfoViewIndex::indexAvailable()
0166 {
0167     const bool valid = m_project->projectIndex() && m_project->projectIndex()->isValid();
0168     enableWidgets(valid);
0169 }
0170 
0171 void KateProjectInfoViewIndex::enableWidgets(bool valid)
0172 {
0173     /**
0174      * update enabled state of widgets
0175      */
0176     m_lineEdit->setEnabled(valid);
0177     m_treeView->setEnabled(valid);
0178 
0179     /**
0180      * if index exists, hide possible message widget, else create it
0181      */
0182     if (valid) {
0183         if (m_messageWidget && m_messageWidget->isVisible()) {
0184             m_messageWidget->animatedHide();
0185         }
0186     } else if (!m_messageWidget) {
0187         m_messageWidget = new KMessageWidget();
0188         m_messageWidget->setPosition(KMessageWidget::Header);
0189         m_messageWidget->setCloseButtonVisible(true);
0190         m_messageWidget->setMessageType(KMessageWidget::Warning);
0191         m_messageWidget->setWordWrap(false);
0192         static_cast<QVBoxLayout *>(layout())->insertWidget(0, m_messageWidget);
0193         m_messageWidget->animatedShow();
0194     }
0195 
0196     // disabled or failed to create?
0197     if (!valid && m_project->projectIndex()) {
0198         m_messageWidget->setText(i18n("The index could not be created. Please install 'ctags'."));
0199         // Make sure we remove the action
0200         const QList<QAction *> actions = m_messageWidget->actions();
0201         if (actions.size() == 1) {
0202             m_messageWidget->removeAction(actions.first());
0203         }
0204     } else if (!valid) {
0205         if (!m_messageWidget->text().isEmpty()) {
0206             return;
0207         }
0208         m_messageWidget->setText(i18n("Indexing is not enabled"));
0209         auto enableIndexing = new QAction(i18n("Enable indexing"), m_messageWidget);
0210         connect(enableIndexing, &QAction::triggered, m_messageWidget, [this]() {
0211             m_project->plugin()->setIndex(true, QUrl());
0212             m_project->reload(true);
0213         });
0214         m_messageWidget->addAction(enableIndexing);
0215     }
0216 }
0217 
0218 #include "moc_kateprojectinfoviewindex.cpp"