File indexing completed on 2024-04-21 05:41:04
0001 /* 0002 SPDX-FileCopyrightText: 2019-2020 Nikolai Krasheninnikov <nkrasheninnikov@yandex.ru> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "svncommitdialog.h" 0008 0009 #include <QVBoxLayout> 0010 #include <QPlainTextEdit> 0011 #include <QLabel> 0012 #include <QTableWidget> 0013 #include <QHeaderView> 0014 #include <QTemporaryFile> 0015 #include <QDialogButtonBox> 0016 #include <QPushButton> 0017 #include <QShortcut> 0018 #include <QMenu> 0019 #include <QDebug> 0020 0021 #include <KLocalizedString> 0022 #include <KWindowConfig> 0023 #include <KSharedConfig> 0024 0025 namespace { 0026 0027 // Helper function: returns true if str starts with any string in a list. 0028 bool startsWith(const QStringList &list, const QString &str) 0029 { 0030 for (const auto &i : std::as_const(list)) { 0031 if (str.startsWith(i)) { 0032 return true; 0033 } 0034 } 0035 0036 return false; 0037 } 0038 0039 // Helper function: makes a new list from an existing one and a hashTable. It combines all the list 0040 // records which exists in hashTable. Existence means hashTable entry starts with a list entry. 0041 QStringList makeContext(const QStringList &list, const QHash<QString, KVersionControlPlugin::ItemVersion> *hashTable) 0042 { 0043 QStringList ret; 0044 0045 for (const auto &i : std::as_const(list)) { 0046 for ( auto it = hashTable->cbegin(); it != hashTable->cend(); ++it ) { 0047 if (it.key().startsWith(i)) { 0048 ret.append(i); 0049 break; 0050 } 0051 } 0052 } 0053 0054 return ret; 0055 } 0056 0057 } 0058 0059 struct svnCommitEntryInfo_t { 0060 svnCommitEntryInfo_t() : 0061 localPath(QString()), 0062 fileVersion( KVersionControlPlugin::NormalVersion ) 0063 {} 0064 0065 QString localPath; ///< Affected local path. 0066 KVersionControlPlugin::ItemVersion fileVersion; ///< File status in terms of KVersionControlPlugin 0067 }; 0068 Q_DECLARE_METATYPE(svnCommitEntryInfo_t); 0069 0070 enum columns_t { 0071 columnPath, 0072 columnStatus 0073 }; 0074 0075 SvnCommitDialog::SvnCommitDialog(const QHash<QString, KVersionControlPlugin::ItemVersion> *versionInfo, const QStringList& context, QWidget *parent) : 0076 QDialog(parent), 0077 m_versionInfoHash(versionInfo), 0078 m_context(context) 0079 { 0080 Q_ASSERT(versionInfo); 0081 Q_ASSERT(!context.empty()); 0082 0083 /* 0084 * Setup UI. 0085 */ 0086 QVBoxLayout* boxLayout = new QVBoxLayout(this); 0087 0088 boxLayout->addWidget(new QLabel(i18nc("@label", "Description:"), this)); 0089 m_editor = new QPlainTextEdit(this); 0090 boxLayout->addWidget(m_editor, 1); 0091 0092 QFrame* line = new QFrame(this); 0093 line->setFrameShape(QFrame::HLine); 0094 line->setFrameShadow(QFrame::Sunken); 0095 boxLayout->addWidget(line); 0096 0097 m_changes = new QTableWidget(this); 0098 boxLayout->addWidget(m_changes, 3); 0099 0100 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); 0101 // The more appropriate role is QDialogButtonBox::ActionRole but we use QDialogButtonBox::ResetRole 0102 // because of QDialogButtonBox automatic button layout (button is separated from others). 0103 auto refreshButton = buttonBox->addButton(i18nc("@action:button", "Refresh"), QDialogButtonBox::ResetRole); 0104 refreshButton->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 0105 0106 auto okButton = buttonBox->button(QDialogButtonBox::Ok); 0107 okButton->setDefault(true); 0108 okButton->setText(i18nc("@action:button", "Commit")); 0109 boxLayout->addWidget(buttonBox); 0110 0111 /* 0112 * Add actions, establish connections. 0113 */ 0114 m_actRevertFile = new QAction(i18nc("@item:inmenu", "Revert"), this); 0115 m_actRevertFile->setIcon(QIcon::fromTheme(QStringLiteral("document-revert"))); 0116 connect(m_actRevertFile, &QAction::triggered, this, [this] () { 0117 const QString filePath = m_actRevertFile->data().value<svnCommitEntryInfo_t>().localPath; 0118 Q_EMIT revertFiles(QStringList() << filePath); 0119 } ); 0120 0121 m_actDiffFile = new QAction(i18nc("@item:inmenu", "Show changes"), this); 0122 m_actDiffFile->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); 0123 connect(m_actDiffFile, &QAction::triggered, this, [this] () { 0124 const QString filePath = m_actDiffFile->data().value<svnCommitEntryInfo_t>().localPath; 0125 Q_EMIT diffFile(filePath); 0126 } ); 0127 0128 m_actAddFile = new QAction(i18nc("@item:inmenu", "Add file"), this); 0129 m_actAddFile->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0130 connect(m_actAddFile, &QAction::triggered, this, [this] () { 0131 const QString filePath = m_actAddFile->data().value<svnCommitEntryInfo_t>().localPath; 0132 Q_EMIT addFiles(QStringList() << filePath); 0133 } ); 0134 0135 connect(buttonBox, &QDialogButtonBox::accepted, this, [this] () { 0136 // Form a new context list from an existing one and a possibly modified m_versionInfoHash (some 0137 // files from original context might no longer be in m_versionInfoHash). 0138 QStringList context = makeContext(m_context, m_versionInfoHash); 0139 Q_EMIT commit(context, m_editor->toPlainText()); 0140 QDialog::accept(); 0141 } ); 0142 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0143 connect(refreshButton, &QPushButton::clicked, this, &SvnCommitDialog::refreshChangesList); 0144 0145 connect(m_changes, &QWidget::customContextMenuRequested, this, &SvnCommitDialog::contextMenu); 0146 0147 QShortcut *refreshShortcut = new QShortcut(QKeySequence::Refresh, this); 0148 connect(refreshShortcut, &QShortcut::activated, this, &SvnCommitDialog::refreshChangesList); 0149 refreshShortcut->setAutoRepeat(false); 0150 0151 /* 0152 * Additional setup. 0153 */ 0154 setWindowTitle(i18nc("@title:window", "SVN Commit")); 0155 0156 const QStringList tableHeader = { i18nc("@title:column", "Path"), 0157 i18nc("@title:column", "Status") }; 0158 m_changes->setColumnCount(tableHeader.size()); 0159 m_changes->setHorizontalHeaderLabels(tableHeader); 0160 m_changes->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); 0161 m_changes->horizontalHeader()->setSectionResizeMode(columnStatus, QHeaderView::ResizeToContents); 0162 m_changes->verticalHeader()->setVisible(false); 0163 m_changes->setSortingEnabled(false); 0164 m_changes->setSelectionMode(QAbstractItemView::SingleSelection); 0165 m_changes->setSelectionBehavior(QAbstractItemView::SelectRows); 0166 m_changes->setContextMenuPolicy(Qt::CustomContextMenu); 0167 0168 refreshChangesList(); 0169 } 0170 0171 SvnCommitDialog::~SvnCommitDialog() 0172 { 0173 KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")), QStringLiteral("SvnCommitDialog")); 0174 KWindowConfig::saveWindowSize(windowHandle(), dialogConfig, KConfigBase::Persistent); 0175 } 0176 0177 void SvnCommitDialog::refreshChangesList() 0178 { 0179 // Remove all the contents. 0180 m_changes->clearContents(); 0181 m_changes->setRowCount(0); 0182 0183 auto it = m_versionInfoHash->cbegin(); 0184 for ( int row = 0 ; it != m_versionInfoHash->cend(); ++it ) { 0185 // If current item is not in a context list we skip it. Each file must be in a context dir 0186 // or be in a context itself. 0187 if (!startsWith(m_context, it.key())) { 0188 continue; 0189 } 0190 0191 QTableWidgetItem *path = new QTableWidgetItem( it.key() ); 0192 QTableWidgetItem *status = new QTableWidgetItem; 0193 0194 path->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); 0195 status->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); 0196 0197 m_changes->insertRow(row); 0198 m_changes->setItem(row, columnPath, path); 0199 m_changes->setItem(row, columnStatus, status); 0200 row++; 0201 0202 svnCommitEntryInfo_t info; 0203 info.localPath = it.key(); 0204 info.fileVersion = it.value(); 0205 path->setData(Qt::UserRole, QVariant::fromValue(info)); 0206 status->setData(Qt::UserRole, QVariant::fromValue(info)); 0207 0208 switch(it.value()) { 0209 case KVersionControlPlugin::UnversionedVersion: 0210 status->setText( i18nc("@item:intable", "Unversioned") ); 0211 break; 0212 case KVersionControlPlugin::LocallyModifiedVersion: 0213 status->setText( i18nc("@item:intable", "Modified") ); 0214 break; 0215 case KVersionControlPlugin::AddedVersion: 0216 status->setText( i18nc("@item:intable", "Added") ); 0217 break; 0218 case KVersionControlPlugin::RemovedVersion: 0219 status->setText( i18nc("@item:intable", "Deleted") ); 0220 break; 0221 case KVersionControlPlugin::ConflictingVersion: 0222 status->setText( i18nc("@item:intable", "Conflict") ); 0223 break; 0224 case KVersionControlPlugin::MissingVersion: 0225 status->setText( i18nc("@item:intable", "Missing") ); 0226 break; 0227 case KVersionControlPlugin::UpdateRequiredVersion: 0228 status->setText( i18nc("@item:intable", "Update required") ); 0229 break; 0230 default: 0231 // For SVN normaly we shouldn't be here with: 0232 // NormalVersion, LocallyModifiedUnstagedVersion, IgnoredVersion. 0233 // 'default' is for any future changes in ItemVersion enum. 0234 qWarning() << QStringLiteral("Unknown SVN status for item %1, ItemVersion = %2").arg(it.key()).arg(it.value()); 0235 status->setText(QString()); 0236 } 0237 } 0238 0239 // Sort by status: unversioned is at the bottom. 0240 m_changes->sortByColumn(columnStatus, Qt::AscendingOrder); 0241 } 0242 0243 void SvnCommitDialog::show() 0244 { 0245 QWidget::show(); 0246 0247 // Restore window size after show() for workaround for QTBUG-40584. See KWindowConfig::restoreWindowSize() docs. 0248 KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")), QStringLiteral("SvnCommitDialog")); 0249 KWindowConfig::restoreWindowSize(windowHandle(), dialogConfig); 0250 } 0251 0252 void SvnCommitDialog::contextMenu(const QPoint& pos) 0253 { 0254 QTableWidgetItem *item = m_changes->item( m_changes->currentRow(), 0 ); 0255 if (item == nullptr) { 0256 return; 0257 } 0258 0259 const QVariant data = item->data(Qt::UserRole); 0260 m_actRevertFile->setData( data ); 0261 m_actDiffFile->setData( data ); 0262 m_actAddFile->setData( data ); 0263 0264 m_actRevertFile->setEnabled(false); 0265 m_actDiffFile->setEnabled(false); 0266 m_actAddFile->setEnabled(false); 0267 0268 const svnCommitEntryInfo_t info = data.value<svnCommitEntryInfo_t>(); 0269 switch(info.fileVersion) { 0270 case KVersionControlPlugin::UnversionedVersion: 0271 m_actAddFile->setEnabled(true); 0272 break; 0273 case KVersionControlPlugin::LocallyModifiedVersion: 0274 m_actRevertFile->setEnabled(true); 0275 m_actDiffFile->setEnabled(true); 0276 break; 0277 case KVersionControlPlugin::AddedVersion: 0278 case KVersionControlPlugin::RemovedVersion: 0279 m_actRevertFile->setEnabled(true); 0280 break; 0281 case KVersionControlPlugin::MissingVersion: 0282 m_actRevertFile->setEnabled(true); 0283 break; 0284 default: 0285 // For this items we don't show any menu: return now. 0286 return; 0287 } 0288 0289 QMenu *menu = new QMenu(this); 0290 menu->addAction(m_actAddFile); 0291 menu->addAction(m_actRevertFile); 0292 menu->addAction(m_actDiffFile); 0293 0294 // Adjust popup menu position for QTableWidget header height. 0295 const QPoint popupPoint = QPoint(pos.x(), pos.y() + m_changes->horizontalHeader()->height()); 0296 menu->exec( m_changes->mapToGlobal(popupPoint) ); 0297 } 0298 0299 #include "moc_svncommitdialog.cpp"