File indexing completed on 2024-05-12 17:16:18
0001 /*************************************************************************** 0002 * Copyright (C) 2005-2009 by Rajko Albrecht * 0003 * ral@alwins-world.de * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 #include "svnlogdlgimp.h" 0021 #include "settings/kdesvnsettings.h" 0022 #include "svnactions.h" 0023 #include "svnfrontend/fronthelpers/revisionbuttonimpl.h" 0024 #include "svnfrontend/models/logitemmodel.h" 0025 #include "svnfrontend/models/logmodelhelper.h" 0026 #include "helpers/windowgeometryhelper.h" 0027 0028 #include <kconfig.h> 0029 0030 #include <KHelpClient> 0031 #include <KStandardGuiItem> 0032 #include <QDialogButtonBox> 0033 #include <QKeyEvent> 0034 #include <QMenu> 0035 #include <QSortFilterProxyModel> 0036 #include <QTextDocumentFragment> 0037 0038 const QLatin1String groupName("log_dialog_size"); 0039 0040 SvnLogDlgImp::SvnLogDlgImp(SvnActions *ac, bool modal, QWidget *parent) 0041 : QDialog(parent) 0042 { 0043 setupUi(this); 0044 setModal(modal); 0045 m_pbClose->setDefault(true); 0046 m_pbClose->setShortcut(Qt::CTRL | Qt::Key_Return); 0047 KStandardGuiItem::assign(m_pbClose, KStandardGuiItem::Close); 0048 KStandardGuiItem::assign(m_pbHelp, KStandardGuiItem::Help); 0049 m_DispPrevButton->setIcon(QIcon::fromTheme(QStringLiteral("kdesvndiff"))); 0050 m_DispSpecDiff->setIcon(QIcon::fromTheme(QStringLiteral("kdesvndiff"))); 0051 buttonBlame->setIcon(QIcon::fromTheme(QStringLiteral("kdesvnblame"))); 0052 m_SortModel = nullptr; 0053 m_CurrentModel = nullptr; 0054 m_ControlKeyDown = false; 0055 0056 if (Kdesvnsettings::self()->log_always_list_changed_files()) { 0057 buttonListFiles->hide(); 0058 } else { 0059 m_ChangedList->hide(); 0060 } 0061 m_Actions = ac; 0062 KConfigGroup cs(Kdesvnsettings::self()->config(), groupName); 0063 QByteArray t1 = cs.readEntry("logsplitter", QByteArray()); 0064 if (!t1.isEmpty()) { 0065 m_centralSplitter->restoreState(t1); 0066 } 0067 t1 = cs.readEntry("right_logsplitter", QByteArray()); 0068 if (!t1.isEmpty()) { 0069 if (cs.readEntry("laststate", false) == m_ChangedList->isHidden()) { 0070 m_rightSplitter->restoreState(t1); 0071 } 0072 } 0073 } 0074 0075 SvnLogDlgImp::~SvnLogDlgImp() 0076 { 0077 KConfigGroup cs(Kdesvnsettings::self()->config(), groupName); 0078 cs.writeEntry("right_logsplitter", m_rightSplitter->saveState()); 0079 cs.writeEntry("logsplitter", m_centralSplitter->saveState()); 0080 cs.writeEntry("laststate", m_ChangedList->isHidden()); 0081 delete m_SortModel; 0082 } 0083 0084 void SvnLogDlgImp::dispLog(const svn::LogEntriesMapPtr &log, const QString &what, const QString &root, const svn::Revision &peg, const QString &pegUrl) 0085 { 0086 m_peg = peg; 0087 m_PegUrl = pegUrl; 0088 m_startRevButton->setNoWorking(m_PegUrl.isUrl()); 0089 m_endRevButton->setNoWorking(m_PegUrl.isUrl()); 0090 if (!m_PegUrl.isUrl() || Kdesvnsettings::remote_special_properties()) { 0091 QString s = m_Actions->searchProperty(_bugurl, QStringLiteral("bugtraq:url"), pegUrl, peg, true); 0092 if (!s.isEmpty()) { 0093 QString reg; 0094 s = m_Actions->searchProperty(reg, QStringLiteral("bugtraq:logregex"), pegUrl, peg, true); 0095 if (!s.isNull() && !reg.isEmpty()) { 0096 const QVector<QStringRef> s1 = reg.splitRef(QLatin1Char('\n')); 0097 if (!s1.isEmpty()) { 0098 _r1.setPattern(s1.at(0).toString()); 0099 if (s1.size() > 1) { 0100 _r2.setPattern(s1.at(1).toString()); 0101 } 0102 } 0103 } 0104 } 0105 } 0106 _base = root; 0107 m_Entries = log; 0108 if (!what.isEmpty()) { 0109 setWindowTitle(i18nc("@title:window", "SVN Log of %1", what)); 0110 } else { 0111 setWindowTitle(i18nc("@title:window", "SVN Log")); 0112 } 0113 _name = what; 0114 if (!_name.startsWith(QLatin1Char('/'))) { 0115 _name = QLatin1Char('/') + _name; 0116 } 0117 dispLog(log); 0118 } 0119 0120 void SvnLogDlgImp::dispLog(const svn::LogEntriesMapPtr &_log) 0121 { 0122 if (!_log) { 0123 return; 0124 } 0125 bool must_init = false; 0126 if (!m_SortModel) { 0127 m_SortModel = new SvnLogSortModel(m_LogTreeView); 0128 m_CurrentModel = new SvnLogModel(_log, _name, m_SortModel); 0129 m_SortModel->setSourceModel(m_CurrentModel); 0130 must_init = true; 0131 } else { 0132 m_CurrentModel->setLogData(_log, _name); 0133 } 0134 0135 if (must_init) { 0136 m_LogTreeView->setModel(m_SortModel); 0137 m_LogTreeView->sortByColumn(SvnLogModel::Revision, Qt::DescendingOrder); 0138 connect(m_LogTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, 0139 this, &SvnLogDlgImp::slotSelectionChanged); 0140 m_LogTreeView->resizeColumnToContents(SvnLogModel::Revision); 0141 m_LogTreeView->resizeColumnToContents(SvnLogModel::Author); 0142 m_LogTreeView->resizeColumnToContents(SvnLogModel::Date); 0143 } 0144 m_startRevButton->setRevision(m_CurrentModel->max()); 0145 m_endRevButton->setRevision(m_CurrentModel->min()); 0146 QModelIndex ind = m_CurrentModel->index(m_CurrentModel->rowCount(QModelIndex()) - 1); 0147 if (ind.isValid()) { 0148 m_LogTreeView->selectionModel()->select(m_SortModel->mapFromSource(ind), 0149 QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0150 } 0151 m_LogTreeView->setFocus(); 0152 } 0153 0154 QString SvnLogDlgImp::genReplace(const QString &r1match) 0155 { 0156 static QString anf(QStringLiteral("<a href=\"")); 0157 static QString mid(QStringLiteral("\">")); 0158 static QString end(QStringLiteral("</a>")); 0159 QString res; 0160 if (_r2.pattern().length() < 1) { 0161 res = _bugurl; 0162 res.replace(QStringLiteral("%BUGID%"), _r1.cap(1)); 0163 res = anf + res + mid + r1match + end; 0164 return res; 0165 } 0166 int pos = 0; 0167 int count = 0; 0168 int oldpos; 0169 0170 while (pos > -1) { 0171 oldpos = pos + count; 0172 pos = r1match.indexOf(_r2, pos + count); 0173 if (pos == -1) { 0174 break; 0175 } 0176 count = _r2.matchedLength(); 0177 res += r1match.midRef(oldpos, pos - oldpos); 0178 QString sub = r1match.mid(pos, count); 0179 QString _url = _bugurl; 0180 _url.replace(QStringLiteral("%BUGID%"), sub); 0181 res += anf + _url + mid + sub + end; 0182 } 0183 res += r1match.midRef(oldpos); 0184 return res; 0185 } 0186 0187 void SvnLogDlgImp::replaceBugids(QString &msg) 0188 { 0189 if (!_r1.isValid() || _r1.pattern().length() < 1 || _bugurl.isEmpty()) { 0190 return; 0191 } 0192 int pos = 0; 0193 int count = 0; 0194 0195 pos = _r1.indexIn(msg, pos + count); 0196 count = _r1.matchedLength(); 0197 0198 while (pos > -1) { 0199 QString s1 = msg.mid(pos, count); 0200 QString rep = genReplace(s1); 0201 msg = msg.replace(pos, count, rep); 0202 pos = _r1.indexIn(msg, pos + rep.length()); 0203 count = _r1.matchedLength(); 0204 } 0205 } 0206 0207 void SvnLogDlgImp::slotSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous) 0208 { 0209 Q_UNUSED(previous); 0210 m_ChangedList->clear(); 0211 QModelIndexList _l = current.indexes(); 0212 if (_l.count() < 1) { 0213 m_DispPrevButton->setEnabled(false); 0214 buttonListFiles->setEnabled(false); 0215 buttonBlame->setEnabled(false); 0216 m_ChangedList->clear(); 0217 return; 0218 } 0219 QModelIndex _index = m_SortModel->mapToSource(_l[0]); 0220 m_CurrentModel->fillChangedPaths(_index, m_ChangedList); 0221 QTextDocumentFragment _m = QTextDocumentFragment::fromPlainText(m_CurrentModel->fullMessage(_index)); 0222 QString msg = _m.toHtml(); 0223 replaceBugids(msg); 0224 m_LogDisplay->setHtml(msg); 0225 m_DispPrevButton->setEnabled(_index.row() > 0); 0226 buttonBlame->setEnabled(true); 0227 } 0228 0229 0230 /*! 0231 \fn SvnLogDlgImp::slotDispPrevious() 0232 */ 0233 void SvnLogDlgImp::slotDispPrevious() 0234 { 0235 QModelIndex _index = selectedRow(); 0236 if (!_index.isValid() || _index.row() == 0) { 0237 m_DispPrevButton->setEnabled(false); 0238 return; 0239 } 0240 QModelIndex _it = m_CurrentModel->index(_index.row() - 1); 0241 if (!_it.isValid()) { 0242 m_DispPrevButton->setEnabled(false); 0243 return; 0244 } 0245 const SvnLogModelNodePtr k = m_CurrentModel->indexNode(_index); 0246 const SvnLogModelNodePtr p = m_CurrentModel->indexNode(_it); 0247 if (!k || !p) { 0248 m_DispPrevButton->setEnabled(false); 0249 return; 0250 } 0251 0252 const QString s(_base + k->realName()); 0253 const QString e(_base + p->realName()); 0254 emit makeDiff(e, p->revision(), s, k->revision(), this); 0255 } 0256 0257 0258 /*! 0259 \fn SvnLogDlgImp::saveSize() 0260 */ 0261 void SvnLogDlgImp::saveSize() 0262 { 0263 WindowGeometryHelper::save(this, groupName); 0264 } 0265 0266 void SvnLogDlgImp::slotRevisionSelected() 0267 { 0268 m_goButton->setFocus(); 0269 //m_DispSpecDiff->setEnabled( m_first && m_second && m_first != m_second); 0270 } 0271 0272 void SvnLogDlgImp::slotDispSelected() 0273 { 0274 SvnLogModelNodePtr m_first = m_CurrentModel->indexNode(m_CurrentModel->index(m_CurrentModel->leftRow())); 0275 SvnLogModelNodePtr m_second = m_CurrentModel->indexNode(m_CurrentModel->index(m_CurrentModel->rightRow())); 0276 if (m_first && m_second) { 0277 emit makeDiff(_base + m_first->realName(), m_first->revision(), _base + m_second->realName(), m_second->revision(), this); 0278 } 0279 } 0280 0281 bool SvnLogDlgImp::getSingleLog(svn::LogEntry &t, const svn::Revision &r, const QString &what, const svn::Revision &peg, QString &root) 0282 { 0283 root = _base; 0284 const svn::LogEntriesMap::const_iterator it = m_Entries->constFind(r.revnum()); 0285 if (it == m_Entries->constEnd()) { 0286 return m_Actions->getSingleLog(t, r, what, peg, root); 0287 } 0288 t = it.value(); 0289 return true; 0290 } 0291 0292 void SvnLogDlgImp::slotGetLogs() 0293 { 0294 svn::LogEntriesMapPtr lm = m_Actions->getLog(m_startRevButton->revision(), 0295 m_endRevButton->revision(), m_peg, 0296 _base + _name, Kdesvnsettings::self()->log_always_list_changed_files(), 0, Kdesvnsettings::last_node_follow(), this); 0297 if (lm) { 0298 dispLog(lm); 0299 } 0300 } 0301 0302 void SvnLogDlgImp::slotPrevFifty() 0303 { 0304 svn::Revision now = m_CurrentModel->min(); 0305 if (now == 1) { 0306 return; 0307 } 0308 svn::Revision begin = now.revnum() - 1; 0309 if (begin.revnum() < 1) { 0310 begin = 1; 0311 } 0312 svn::LogEntriesMapPtr lm = m_Actions->getLog(begin, 0313 (begin.revnum() > 50 ? svn::Revision::START : svn::Revision::HEAD), m_peg, 0314 _base + _name, Kdesvnsettings::self()->log_always_list_changed_files(), 50, Kdesvnsettings::last_node_follow(), this); 0315 if (lm) { 0316 dispLog(lm); 0317 } 0318 } 0319 0320 void SvnLogDlgImp::slotBeginHead() 0321 { 0322 svn::LogEntriesMapPtr lm = m_Actions->getLog(svn::Revision::HEAD, 0323 1, m_peg, 0324 _base + _name, Kdesvnsettings::self()->log_always_list_changed_files(), 50, Kdesvnsettings::last_node_follow(), this); 0325 if (lm) { 0326 dispLog(lm); 0327 } 0328 } 0329 0330 void SvnLogDlgImp::slotHelpRequested() 0331 { 0332 KHelpClient::invokeHelp(QLatin1String("logdisplay-dlg"), QLatin1String("kdesvn")); 0333 } 0334 0335 0336 void SvnLogDlgImp::slotListEntries() 0337 { 0338 QModelIndex _index = selectedRow(); 0339 SvnLogModelNodePtr ptr = m_CurrentModel->indexNode(_index); 0340 if (!ptr) { 0341 buttonListFiles->setEnabled(false); 0342 return; 0343 } 0344 if (ptr->changedPaths().isEmpty()) { 0345 svn::LogEntriesMapPtr _log = m_Actions->getLog(ptr->revision(), ptr->revision(), ptr->revision(), 0346 _name, true, 0, Kdesvnsettings::last_node_follow()); 0347 if (!_log) { 0348 return; 0349 } 0350 if (!_log->isEmpty()) { 0351 ptr->setChangedPaths(_log->value(ptr->revision())); 0352 } 0353 } 0354 if (ptr->changedPaths().isEmpty()) { 0355 m_CurrentModel->fillChangedPaths(_index, m_ChangedList); 0356 } 0357 buttonListFiles->setEnabled(false); 0358 } 0359 0360 void SvnLogDlgImp::keyPressEvent(QKeyEvent *e) 0361 { 0362 if (!e) { 0363 return; 0364 } 0365 if (e->text().isEmpty() && e->key() == Qt::Key_Control) { 0366 m_ControlKeyDown = true; 0367 } 0368 QDialog::keyPressEvent(e); 0369 } 0370 0371 void SvnLogDlgImp::keyReleaseEvent(QKeyEvent *e) 0372 { 0373 if (!e) { 0374 return; 0375 } 0376 if (e->text().isEmpty() && e->key() == Qt::Key_Control) { 0377 m_ControlKeyDown = false; 0378 } 0379 QDialog::keyReleaseEvent(e); 0380 } 0381 0382 void SvnLogDlgImp::showEvent(QShowEvent *e) 0383 { 0384 QDialog::showEvent(e); 0385 WindowGeometryHelper::restore(this, groupName); 0386 } 0387 0388 0389 void SvnLogDlgImp::slotBlameItem() 0390 { 0391 QModelIndex ind = selectedRow(); 0392 if (!ind.isValid()) { 0393 buttonBlame->setEnabled(false); 0394 return; 0395 } 0396 qlonglong rev = m_CurrentModel->toRevision(ind); 0397 svn::Revision start(svn::Revision::START); 0398 m_Actions->makeBlame(start, rev, _base + m_CurrentModel->realName(ind), QApplication::activeModalWidget(), rev, this); 0399 } 0400 0401 /* it works 'cause we use single selection only */ 0402 QModelIndex SvnLogDlgImp::selectedRow(int column) 0403 { 0404 QModelIndexList _mi = m_LogTreeView->selectionModel()->selectedRows(column); 0405 if (_mi.count() < 1) { 0406 return QModelIndex(); 0407 } 0408 return m_SortModel->mapToSource(_mi[0]); 0409 } 0410 0411 void SvnLogDlgImp::slotCustomContextMenu(const QPoint &e) 0412 { 0413 QModelIndex ind = m_LogTreeView->indexAt(e); 0414 QModelIndex bel; 0415 if (ind.isValid()) { 0416 bel = m_LogTreeView->indexBelow(ind); 0417 ind = m_SortModel->mapToSource(ind); 0418 } 0419 int row = -1; 0420 if (ind.isValid()) { 0421 row = ind.row(); 0422 } else { 0423 return; 0424 } 0425 0426 qlonglong rev = -1; 0427 if (bel.isValid()) { 0428 bel = m_SortModel->mapToSource(bel); 0429 rev = m_CurrentModel->toRevision(bel); 0430 } 0431 QMenu popup; 0432 QAction *ac; 0433 bool unset = false; 0434 if (row != m_CurrentModel->rightRow()) { 0435 ac = popup.addAction(QIcon::fromTheme(QStringLiteral("kdesvnright")), i18n("Set version as right side of diff")); 0436 ac->setData(101); 0437 } else { 0438 unset = true; 0439 } 0440 if (row != m_CurrentModel->leftRow()) { 0441 ac = popup.addAction(QIcon::fromTheme(QStringLiteral("kdesvnleft")), i18n("Set version as left side of diff")); 0442 ac->setData(102); 0443 } else { 0444 unset = true; 0445 } 0446 if (unset) { 0447 ac = popup.addAction(i18n("Unset version for diff")); 0448 ac->setData(103); 0449 } 0450 if (rev > -1 && !m_PegUrl.isUrl()) { 0451 ac = popup.addAction(i18n("Revert this commit")); 0452 ac->setData(104); 0453 } 0454 ac = popup.exec(m_LogTreeView->viewport()->mapToGlobal(e)); 0455 if (!ac) { 0456 return; 0457 } 0458 int r = ac->data().toInt(); 0459 switch (r) { 0460 case 101: 0461 m_CurrentModel->setRightRow(row); 0462 break; 0463 case 102: 0464 m_CurrentModel->setLeftRow(row); 0465 break; 0466 case 103: 0467 if (row != m_CurrentModel->leftRow()) { 0468 m_CurrentModel->setLeftRow(-1); 0469 } 0470 if (row != m_CurrentModel->rightRow()) { 0471 m_CurrentModel->setRightRow(-1); 0472 } 0473 break; 0474 case 104: { 0475 svn::Revision previous(rev); 0476 svn::Revision current(m_CurrentModel->toRevision(ind)); 0477 QString _path = m_PegUrl.path(); 0478 m_Actions->slotMergeWcRevisions(_path, current, previous, true, true, false, false, false); 0479 } 0480 break; 0481 } 0482 m_DispSpecDiff->setEnabled(m_CurrentModel->leftRow() != -1 && m_CurrentModel->rightRow() != -1 && m_CurrentModel->leftRow() != m_CurrentModel->rightRow()); 0483 } 0484 0485 void SvnLogDlgImp::slotChangedPathContextMenu(const QPoint &e) 0486 { 0487 QTreeWidgetItem *_item = m_ChangedList->currentItem(); 0488 if (!_item) { 0489 return; 0490 } 0491 0492 LogChangePathItem *item = static_cast<LogChangePathItem *>(_item); 0493 if (item->action() == 'D') { 0494 return; 0495 } 0496 QModelIndex ind = selectedRow(); 0497 if (!ind.isValid()) { 0498 return; 0499 } 0500 const qlonglong rev = m_CurrentModel->toRevision(ind); 0501 QMenu popup; 0502 const QString name = item->path(); 0503 const QString source = item->revision() > -1 ? item->source() : item->path(); 0504 QAction *ac; 0505 ac = popup.addAction(i18n("Annotate")); 0506 if (ac) { 0507 ac->setData(101); 0508 } 0509 if (item->action() != 'A' || item->revision() > -1) { 0510 ac = popup.addAction(i18n("Diff previous")); 0511 if (ac) { 0512 ac->setData(102); 0513 } 0514 } 0515 ac = popup.addAction(i18n("Cat this version")); 0516 if (ac) { 0517 ac->setData(103); 0518 } 0519 ac = popup.exec(m_ChangedList->viewport()->mapToGlobal(e)); 0520 if (!ac) { 0521 return; 0522 } 0523 int r = ac->data().toInt(); 0524 svn::Revision start(svn::Revision::START); 0525 switch (r) { 0526 case 101: { 0527 m_Actions->makeBlame(start, rev, _base + name, QApplication::activeModalWidget(), rev, this); 0528 break; 0529 } 0530 case 102: { 0531 const svn_revnum_t prev = item->revision() > 0 ? item->revision() : rev - 1; 0532 emit makeDiff(_base + source, prev, _base + name, rev, this); 0533 break; 0534 } 0535 case 103: { 0536 emit makeCat(rev, _base + source, source, rev, QApplication::activeModalWidget()); 0537 } 0538 default: 0539 break; 0540 } 0541 } 0542 0543 void SvnLogDlgImp::slotSingleDoubleClicked(QTreeWidgetItem *_item, int) 0544 { 0545 if (!_item) { 0546 return; 0547 } 0548 0549 const LogChangePathItem *item = static_cast<LogChangePathItem *>(_item); 0550 const QModelIndex ind = selectedRow(); 0551 if (!ind.isValid()) { 0552 return; 0553 } 0554 svn::Revision start(svn::Revision::START); 0555 if (item->action() != 'D') { 0556 const QString name = item->path(); 0557 const qlonglong rev = m_CurrentModel->toRevision(ind); 0558 m_Actions->makeBlame(start, rev, _base + name, QApplication::activeModalWidget(), rev, this); 0559 } 0560 }