File indexing completed on 2024-05-12 04:37:37
0001 /* 0002 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "pathmappings.h" 0008 0009 #include <debug.h> 0010 0011 #include <QAbstractTableModel> 0012 #include <QVBoxLayout> 0013 #include <QTableView> 0014 #include <QHeaderView> 0015 #include <QAction> 0016 #include <QFile> 0017 #include <QIcon> 0018 0019 #include <KLocalizedString> 0020 #include <KConfigGroup> 0021 0022 namespace { 0023 0024 static QUrl rebaseMatchingUrl(const QUrl& toRebase, const KConfigGroup& config, const QString& baseEntry, const QString& rebaseEntry) 0025 { 0026 const QUrl::UrlFormattingOption matchOpts = QUrl::NormalizePathSegments; 0027 const auto configGroups = config.groupList(); 0028 for (const QString& group : configGroups) { 0029 KConfigGroup pathCfg = config.group(group); 0030 const QString baseStr = pathCfg.readEntry(baseEntry, QUrl()).url(matchOpts); 0031 const QString searchStr = toRebase.url(matchOpts); 0032 if (searchStr.contains(baseStr)) { 0033 const QUrl rebase = pathCfg.readEntry(rebaseEntry, QUrl()); 0034 return rebase.resolved(QUrl(searchStr.mid(baseStr.length()))); 0035 } 0036 } 0037 //No mapping found 0038 return toRebase; 0039 } 0040 0041 } 0042 0043 namespace KDevelop { 0044 0045 const QString PathMappings::pathMappingsEntry(QStringLiteral("Path Mappings")); 0046 const QString PathMappings::pathMappingRemoteEntry(QStringLiteral("Remote")); 0047 const QString PathMappings::pathMappingLocalEntry(QStringLiteral("Local")); 0048 0049 QUrl PathMappings::convertToLocalUrl(const KConfigGroup& config, const QUrl& remoteUrl) 0050 { 0051 if (remoteUrl.isLocalFile() && QFile::exists(remoteUrl.toLocalFile())) { 0052 return remoteUrl; 0053 } 0054 0055 KConfigGroup cfg = config.group(pathMappingsEntry); 0056 return rebaseMatchingUrl(remoteUrl, cfg, pathMappingRemoteEntry, pathMappingLocalEntry); 0057 } 0058 0059 QUrl PathMappings::convertToRemoteUrl(const KConfigGroup& config, const QUrl& localUrl) 0060 { 0061 KConfigGroup cfg = config.group(pathMappingsEntry); 0062 return rebaseMatchingUrl(localUrl, cfg, pathMappingLocalEntry, pathMappingRemoteEntry); 0063 } 0064 0065 0066 class PathMappingModel : public QAbstractTableModel 0067 { 0068 Q_OBJECT 0069 public: 0070 int columnCount(const QModelIndex& parent = QModelIndex()) const override 0071 { 0072 if (parent.isValid()) return 0; 0073 return 2; 0074 } 0075 0076 int rowCount(const QModelIndex& parent = QModelIndex()) const override 0077 { 0078 if (parent.isValid()) return 0; 0079 return m_paths.count() + 1; 0080 } 0081 0082 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override 0083 { 0084 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0085 if (section == 0) { 0086 return i18n("Remote Path"); 0087 } else if (section == 1) { 0088 return i18n("Local Path"); 0089 } 0090 } 0091 return QAbstractTableModel::headerData(section, orientation, role); 0092 } 0093 0094 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override 0095 { 0096 if (!index.isValid()) return QVariant(); 0097 if (index.parent().isValid()) return QVariant(); 0098 if (index.column() > 1) return QVariant(); 0099 if (index.row() > m_paths.count()) return QVariant(); 0100 if (role == Qt::DisplayRole || role == Qt::EditRole) { 0101 if (index.row() == m_paths.count()) return QString(); 0102 if (index.column() == 0) { 0103 return m_paths[index.row()].remote.toDisplayString(QUrl::PreferLocalFile); 0104 } else if (index.column() == 1) { 0105 return m_paths[index.row()].local.toDisplayString(QUrl::PreferLocalFile); 0106 } 0107 } 0108 return QVariant(); 0109 } 0110 0111 Qt::ItemFlags flags(const QModelIndex& index) const override 0112 { 0113 if (index.parent().isValid()) return Qt::NoItemFlags; 0114 if (!index.isValid()) return Qt::NoItemFlags; 0115 return ( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ); 0116 0117 } 0118 0119 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override 0120 { 0121 if (!index.isValid()) return false; 0122 if (index.parent().isValid()) return false; 0123 if (index.column() > 1) return false; 0124 if (index.row() > m_paths.count()) return false; 0125 if (role == Qt::EditRole) { 0126 if (index.row() == m_paths.count()) { 0127 beginInsertRows(QModelIndex(), index.row()+1, index.row()+1); 0128 m_paths.append(Path()); 0129 endInsertRows(); 0130 } 0131 if (index.column() == 0) { 0132 m_paths[index.row()].remote = QUrl::fromUserInput(value.toString()); 0133 } else if (index.column() == 1) { 0134 m_paths[index.row()].local = QUrl::fromLocalFile(value.toString()); 0135 } 0136 emit dataChanged(index, index); 0137 return true; 0138 } 0139 return false; 0140 } 0141 0142 bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override 0143 { 0144 if (parent.isValid()) return false; 0145 if (row+count > m_paths.count()) return false; 0146 beginRemoveRows(parent, row, row+count-1); 0147 for (int i=0; i<count; ++i) { 0148 qCDebug(DEBUGGER) << row + i; 0149 m_paths.removeAt(row + i); 0150 } 0151 qCDebug(DEBUGGER) << m_paths.count(); 0152 endRemoveRows(); 0153 0154 return true; 0155 } 0156 0157 void loadFromConfiguration(const KConfigGroup &config) 0158 { 0159 beginResetModel(); 0160 m_paths.clear(); 0161 KConfigGroup cfg = config.group(PathMappings::pathMappingsEntry); 0162 const auto pathCount = cfg.readEntry("Count", 0); 0163 m_paths.reserve(pathCount); 0164 for (int i = 0; i < pathCount; ++i) { 0165 KConfigGroup pCfg = cfg.group(QString::number(i+1)); 0166 Path p; 0167 p.remote = pCfg.readEntry(PathMappings::pathMappingRemoteEntry, QUrl()); 0168 p.local = pCfg.readEntry(PathMappings::pathMappingLocalEntry, QUrl()); 0169 m_paths << p; 0170 } 0171 endResetModel(); 0172 } 0173 0174 void saveToConfiguration(KConfigGroup config) const 0175 { 0176 qCDebug(DEBUGGER) << m_paths.count(); 0177 0178 KConfigGroup cfg = config.group(PathMappings::pathMappingsEntry); 0179 cfg.writeEntry("Count", m_paths.count()); 0180 int i=0; 0181 for (const Path& p : m_paths) { 0182 i++; 0183 KConfigGroup pCfg = cfg.group(QString::number(i)); 0184 pCfg.writeEntry(PathMappings::pathMappingRemoteEntry, p.remote); 0185 pCfg.writeEntry(PathMappings::pathMappingLocalEntry, p.local); 0186 } 0187 cfg.sync(); 0188 } 0189 0190 private: 0191 struct Path { 0192 QUrl remote; 0193 QUrl local; 0194 }; 0195 QVector<Path> m_paths; 0196 }; 0197 0198 0199 PathMappingsWidget::PathMappingsWidget(QWidget* parent): QWidget(parent) 0200 { 0201 auto *verticalLayout = new QVBoxLayout(this); 0202 0203 m_pathMappingTable = new QTableView(this); 0204 m_pathMappingTable->setSelectionBehavior(QAbstractItemView::SelectRows); 0205 m_pathMappingTable->horizontalHeader()->setDefaultSectionSize(150); 0206 m_pathMappingTable->horizontalHeader()->setStretchLastSection(true); 0207 0208 verticalLayout->addWidget(m_pathMappingTable); 0209 0210 m_pathMappingTable->setModel(new PathMappingModel()); 0211 connect(m_pathMappingTable->model(), &QAbstractItemModel::dataChanged, this, &PathMappingsWidget::changed); 0212 connect(m_pathMappingTable->model(), &QAbstractItemModel::rowsRemoved, this, &PathMappingsWidget::changed); 0213 connect(m_pathMappingTable->model(), &QAbstractItemModel::rowsInserted, this, &PathMappingsWidget::changed); 0214 0215 auto* deletePath = new QAction( 0216 QIcon::fromTheme(QStringLiteral("edit-delete")), 0217 i18n( "Delete" ), 0218 this 0219 ); 0220 connect(deletePath, &QAction::triggered, this, &PathMappingsWidget::deletePath); 0221 deletePath->setShortcut(Qt::Key_Delete); 0222 deletePath->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0223 m_pathMappingTable->addAction(deletePath); 0224 0225 } 0226 0227 void PathMappingsWidget::deletePath() 0228 { 0229 const auto selectedRows = m_pathMappingTable->selectionModel()->selectedRows(); 0230 for (const QModelIndex& i : selectedRows) { 0231 m_pathMappingTable->model()->removeRow(i.row(), i.parent()); 0232 } 0233 } 0234 void PathMappingsWidget::loadFromConfiguration(const KConfigGroup& cfg) 0235 { 0236 static_cast<PathMappingModel*>(m_pathMappingTable->model())->loadFromConfiguration(cfg); 0237 } 0238 0239 void PathMappingsWidget::saveToConfiguration(const KConfigGroup& cfg) const 0240 { 0241 static_cast<PathMappingModel*>(m_pathMappingTable->model())->saveToConfiguration(cfg); 0242 } 0243 0244 } 0245 0246 #include "pathmappings.moc" 0247 #include "moc_pathmappings.cpp"