File indexing completed on 2024-04-28 04:37:04
0001 /* 0002 SPDX-FileCopyrightText: 2008 Aleix Pol <aleixpol@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "projectitemlineedit.h" 0008 0009 #include <QAction> 0010 #include <QCompleter> 0011 #include <QDialog> 0012 #include <QDialogButtonBox> 0013 #include <QHeaderView> 0014 #include <QLabel> 0015 #include <QMenu> 0016 #include <QPushButton> 0017 #include <QTreeView> 0018 #include <QValidator> 0019 #include <QVBoxLayout> 0020 0021 #include <KLocalizedString> 0022 0023 #include <interfaces/icore.h> 0024 #include <interfaces/iprojectcontroller.h> 0025 #include <project/projectmodel.h> 0026 #include <util/kdevstringhandler.h> 0027 #include <interfaces/iproject.h> 0028 #include "projectproxymodel.h" 0029 0030 constexpr QChar sep = QLatin1Char('/'); 0031 constexpr QChar escape = QLatin1Char('\\'); 0032 0033 0034 class ProjectItemCompleter : public QCompleter 0035 { 0036 Q_OBJECT 0037 public: 0038 explicit ProjectItemCompleter(QObject* parent=nullptr); 0039 0040 QString separator() const { return sep; } 0041 QStringList splitPath(const QString &path) const override; 0042 QString pathFromIndex(const QModelIndex& index) const override; 0043 0044 void setBaseItem( KDevelop::ProjectBaseItem* item ) { mBase = item; } 0045 0046 private: 0047 KDevelop::ProjectModel* mModel; 0048 KDevelop::ProjectBaseItem* mBase = nullptr; 0049 }; 0050 0051 class ProjectItemValidator : public QValidator 0052 { 0053 Q_OBJECT 0054 public: 0055 explicit ProjectItemValidator(QObject* parent = nullptr ); 0056 QValidator::State validate( QString& input, int& pos ) const override; 0057 0058 void setBaseItem( KDevelop::ProjectBaseItem* item ) { mBase = item; } 0059 0060 private: 0061 KDevelop::ProjectBaseItem* mBase = nullptr; 0062 }; 0063 0064 ProjectItemCompleter::ProjectItemCompleter(QObject* parent) 0065 : QCompleter(parent) 0066 , mModel(KDevelop::ICore::self()->projectController()->projectModel()) 0067 0068 { 0069 setModel(mModel); 0070 setCaseSensitivity( Qt::CaseInsensitive ); 0071 } 0072 0073 0074 QStringList ProjectItemCompleter::splitPath(const QString& path) const 0075 { 0076 return joinProjectBasePath( KDevelop::splitWithEscaping( path, sep, escape ), mBase ); 0077 } 0078 0079 QString ProjectItemCompleter::pathFromIndex(const QModelIndex& index) const 0080 { 0081 QString postfix; 0082 if(mModel->itemFromIndex(index)->folder()) 0083 postfix=sep; 0084 return KDevelop::joinWithEscaping(removeProjectBasePath( mModel->pathFromIndex(index), mBase ), sep, escape)+postfix; 0085 } 0086 0087 0088 ProjectItemValidator::ProjectItemValidator(QObject* parent): QValidator(parent) 0089 { 0090 } 0091 0092 0093 QValidator::State ProjectItemValidator::validate(QString& input, int& pos) const 0094 { 0095 Q_UNUSED( pos ); 0096 KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); 0097 QStringList path = joinProjectBasePath( KDevelop::splitWithEscaping( input, sep, escape ), mBase ); 0098 QModelIndex idx = model->pathToIndex( path ); 0099 QValidator::State state = input.isEmpty() ? QValidator::Intermediate : QValidator::Invalid; 0100 if( idx.isValid() ) 0101 { 0102 state = QValidator::Acceptable; 0103 } else if( path.count() > 1 ) 0104 { 0105 // Check beginning of path and if that is ok, then try to find a child 0106 QString end = path.takeLast(); 0107 idx = model->pathToIndex( path ); 0108 if( idx.isValid() ) 0109 { 0110 for( int i = 0; i < model->rowCount( idx ); i++ ) 0111 { 0112 if( model->data( model->index( i, 0, idx ) ).toString().startsWith( end, Qt::CaseInsensitive ) ) 0113 { 0114 state = QValidator::Intermediate; 0115 break; 0116 } 0117 } 0118 } 0119 } else if( path.count() == 1 ) 0120 { 0121 // Check for a project whose name beings with the input 0122 QString first = path.first(); 0123 const auto projects = KDevelop::ICore::self()->projectController()->projects(); 0124 bool matchesAnyName = std::any_of(projects.begin(), projects.end(), [&](KDevelop::IProject* project) { 0125 return (project->name().startsWith(first, Qt::CaseInsensitive)); 0126 }); 0127 if (matchesAnyName) { 0128 state = QValidator::Intermediate; 0129 } 0130 } 0131 return state; 0132 } 0133 0134 class ProjectItemLineEditPrivate 0135 { 0136 public: 0137 explicit ProjectItemLineEditPrivate(ProjectItemLineEdit* q) 0138 : completer(new ProjectItemCompleter(q)) 0139 , validator(new ProjectItemValidator(q)) 0140 { 0141 } 0142 KDevelop::ProjectBaseItem* base = nullptr; 0143 ProjectItemCompleter* completer; 0144 ProjectItemValidator* validator; 0145 KDevelop::IProject* suggestion = nullptr; 0146 }; 0147 0148 ProjectItemLineEdit::ProjectItemLineEdit(QWidget* parent) 0149 : QLineEdit(parent), 0150 d_ptr(new ProjectItemLineEditPrivate(this)) 0151 { 0152 Q_D(ProjectItemLineEdit); 0153 0154 setCompleter(d->completer); 0155 setValidator(d->validator); 0156 setPlaceholderText( i18nc("@info:placeholder", "Enter the path to an item from the projects tree..." ) ); 0157 0158 auto* selectItemAction = new QAction(QIcon::fromTheme(QStringLiteral("folder-document")), i18nc("@action", "Select..."), this); 0159 connect(selectItemAction, &QAction::triggered, this, &ProjectItemLineEdit::selectItemDialog); 0160 addAction(selectItemAction); 0161 0162 setContextMenuPolicy(Qt::CustomContextMenu); 0163 connect(this, &ProjectItemLineEdit::customContextMenuRequested, this, &ProjectItemLineEdit::showCtxMenu); 0164 } 0165 0166 ProjectItemLineEdit::~ProjectItemLineEdit() = default; 0167 0168 void ProjectItemLineEdit::showCtxMenu(const QPoint& p) 0169 { 0170 QScopedPointer<QMenu> menu(createStandardContextMenu()); 0171 menu->addActions(actions()); 0172 menu->exec(mapToGlobal(p)); 0173 } 0174 0175 bool ProjectItemLineEdit::selectItemDialog() 0176 { 0177 Q_D(ProjectItemLineEdit); 0178 0179 KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel(); 0180 0181 QDialog dialog; 0182 dialog.setWindowTitle(i18nc("@title:window", "Select an Item")); 0183 0184 auto mainLayout = new QVBoxLayout(&dialog); 0185 0186 auto* view = new QTreeView(&dialog); 0187 auto* proxymodel = new ProjectProxyModel(view); 0188 proxymodel->setSourceModel(model); 0189 view->header()->hide(); 0190 view->setModel(proxymodel); 0191 view->setSelectionMode(QAbstractItemView::SingleSelection); 0192 mainLayout->addWidget(new QLabel(i18n("Select the item you want to get the path from."))); 0193 mainLayout->addWidget(view); 0194 0195 auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0196 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); 0197 okButton->setDefault(true); 0198 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0199 connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); 0200 connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); 0201 mainLayout->addWidget(buttonBox); 0202 0203 if (d->suggestion) { 0204 const QModelIndex idx = proxymodel->proxyIndexFromItem(d->suggestion->projectItem()); 0205 view->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); 0206 } 0207 0208 int res = dialog.exec(); 0209 0210 if(res==QDialog::Accepted && view->selectionModel()->hasSelection()) { 0211 QModelIndex idx=proxymodel->mapToSource(view->selectionModel()->selectedIndexes().first()); 0212 0213 setText(KDevelop::joinWithEscaping(model->pathFromIndex(idx), sep, escape)); 0214 selectAll(); 0215 return true; 0216 } 0217 return false; 0218 } 0219 0220 void ProjectItemLineEdit::setItemPath(const QStringList& list) 0221 { 0222 Q_D(ProjectItemLineEdit); 0223 0224 setText(KDevelop::joinWithEscaping(removeProjectBasePath(list, d->base), sep, escape)); 0225 } 0226 0227 QStringList ProjectItemLineEdit::itemPath() const 0228 { 0229 Q_D(const ProjectItemLineEdit); 0230 0231 return joinProjectBasePath(KDevelop::splitWithEscaping(text(), sep, escape), d->base); 0232 } 0233 0234 void ProjectItemLineEdit::setBaseItem(KDevelop::ProjectBaseItem* item) 0235 { 0236 Q_D(ProjectItemLineEdit); 0237 0238 d->base = item; 0239 d->validator->setBaseItem(d->base); 0240 d->completer->setBaseItem(d->base); 0241 } 0242 0243 KDevelop::ProjectBaseItem* ProjectItemLineEdit::baseItem() const 0244 { 0245 Q_D(const ProjectItemLineEdit); 0246 0247 return d->base; 0248 } 0249 0250 KDevelop::ProjectBaseItem* ProjectItemLineEdit::currentItem() const 0251 { 0252 KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); 0253 return model->itemFromIndex(model->pathToIndex(KDevelop::splitWithEscaping(text(), QLatin1Char('/'), QLatin1Char('\\')))); 0254 } 0255 0256 void ProjectItemLineEdit::setSuggestion(KDevelop::IProject* project) 0257 { 0258 Q_D(ProjectItemLineEdit); 0259 0260 d->suggestion = project; 0261 } 0262 0263 #include "projectitemlineedit.moc" 0264 #include "moc_projectitemlineedit.cpp"