File indexing completed on 2024-04-28 05:42:05
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 "helpers/windowgeometryhelper.h" 0022 #include "settings/kdesvnsettings.h" 0023 #include "svnactions.h" 0024 #include "svnfrontend/fronthelpers/revisionbuttonimpl.h" 0025 #include "svnfrontend/models/logitemmodel.h" 0026 #include "svnfrontend/models/logmodelhelper.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, this, &SvnLogDlgImp::slotSelectionChanged); 0139 m_LogTreeView->resizeColumnToContents(SvnLogModel::Revision); 0140 m_LogTreeView->resizeColumnToContents(SvnLogModel::Author); 0141 m_LogTreeView->resizeColumnToContents(SvnLogModel::Date); 0142 } 0143 m_startRevButton->setRevision(m_CurrentModel->max()); 0144 m_endRevButton->setRevision(m_CurrentModel->min()); 0145 QModelIndex ind = m_CurrentModel->index(m_CurrentModel->rowCount(QModelIndex()) - 1); 0146 if (ind.isValid()) { 0147 m_LogTreeView->selectionModel()->select(m_SortModel->mapFromSource(ind), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0148 } 0149 m_LogTreeView->setFocus(); 0150 } 0151 0152 QString SvnLogDlgImp::genReplace(const QString &r1match) 0153 { 0154 static QString anf(QStringLiteral("<a href=\"")); 0155 static QString mid(QStringLiteral("\">")); 0156 static QString end(QStringLiteral("</a>")); 0157 QString res; 0158 if (_r2.pattern().length() < 1) { 0159 res = _bugurl; 0160 res.replace(QStringLiteral("%BUGID%"), _r1.cap(1)); 0161 res = anf + res + mid + r1match + end; 0162 return res; 0163 } 0164 int pos = 0; 0165 int count = 0; 0166 int oldpos; 0167 0168 while (pos > -1) { 0169 oldpos = pos + count; 0170 pos = r1match.indexOf(_r2, pos + count); 0171 if (pos == -1) { 0172 break; 0173 } 0174 count = _r2.matchedLength(); 0175 res += r1match.midRef(oldpos, pos - oldpos); 0176 QString sub = r1match.mid(pos, count); 0177 QString _url = _bugurl; 0178 _url.replace(QStringLiteral("%BUGID%"), sub); 0179 res += anf + _url + mid + sub + end; 0180 } 0181 res += r1match.midRef(oldpos); 0182 return res; 0183 } 0184 0185 void SvnLogDlgImp::replaceBugids(QString &msg) 0186 { 0187 if (!_r1.isValid() || _r1.pattern().length() < 1 || _bugurl.isEmpty()) { 0188 return; 0189 } 0190 int pos = 0; 0191 int count = 0; 0192 0193 pos = _r1.indexIn(msg, pos + count); 0194 count = _r1.matchedLength(); 0195 0196 while (pos > -1) { 0197 QString s1 = msg.mid(pos, count); 0198 QString rep = genReplace(s1); 0199 msg = msg.replace(pos, count, rep); 0200 pos = _r1.indexIn(msg, pos + rep.length()); 0201 count = _r1.matchedLength(); 0202 } 0203 } 0204 0205 void SvnLogDlgImp::slotSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous) 0206 { 0207 Q_UNUSED(previous); 0208 m_ChangedList->clear(); 0209 QModelIndexList _l = current.indexes(); 0210 if (_l.count() < 1) { 0211 m_DispPrevButton->setEnabled(false); 0212 buttonListFiles->setEnabled(false); 0213 buttonBlame->setEnabled(false); 0214 m_ChangedList->clear(); 0215 return; 0216 } 0217 QModelIndex _index = m_SortModel->mapToSource(_l[0]); 0218 m_CurrentModel->fillChangedPaths(_index, m_ChangedList); 0219 QTextDocumentFragment _m = QTextDocumentFragment::fromPlainText(m_CurrentModel->fullMessage(_index)); 0220 QString msg = _m.toHtml(); 0221 replaceBugids(msg); 0222 m_LogDisplay->setHtml(msg); 0223 m_DispPrevButton->setEnabled(_index.row() > 0); 0224 buttonBlame->setEnabled(true); 0225 } 0226 0227 /*! 0228 \fn SvnLogDlgImp::slotDispPrevious() 0229 */ 0230 void SvnLogDlgImp::slotDispPrevious() 0231 { 0232 QModelIndex _index = selectedRow(); 0233 if (!_index.isValid() || _index.row() == 0) { 0234 m_DispPrevButton->setEnabled(false); 0235 return; 0236 } 0237 QModelIndex _it = m_CurrentModel->index(_index.row() - 1); 0238 if (!_it.isValid()) { 0239 m_DispPrevButton->setEnabled(false); 0240 return; 0241 } 0242 const SvnLogModelNodePtr k = m_CurrentModel->indexNode(_index); 0243 const SvnLogModelNodePtr p = m_CurrentModel->indexNode(_it); 0244 if (!k || !p) { 0245 m_DispPrevButton->setEnabled(false); 0246 return; 0247 } 0248 0249 const QString s(_base + k->realName()); 0250 const QString e(_base + p->realName()); 0251 emit makeDiff(e, p->revision(), s, k->revision(), this); 0252 } 0253 0254 /*! 0255 \fn SvnLogDlgImp::saveSize() 0256 */ 0257 void SvnLogDlgImp::saveSize() 0258 { 0259 WindowGeometryHelper::save(this, groupName); 0260 } 0261 0262 void SvnLogDlgImp::slotRevisionSelected() 0263 { 0264 m_goButton->setFocus(); 0265 // m_DispSpecDiff->setEnabled( m_first && m_second && m_first != m_second); 0266 } 0267 0268 void SvnLogDlgImp::slotDispSelected() 0269 { 0270 SvnLogModelNodePtr m_first = m_CurrentModel->indexNode(m_CurrentModel->index(m_CurrentModel->leftRow())); 0271 SvnLogModelNodePtr m_second = m_CurrentModel->indexNode(m_CurrentModel->index(m_CurrentModel->rightRow())); 0272 if (m_first && m_second) { 0273 emit makeDiff(_base + m_first->realName(), m_first->revision(), _base + m_second->realName(), m_second->revision(), this); 0274 } 0275 } 0276 0277 bool SvnLogDlgImp::getSingleLog(svn::LogEntry &t, const svn::Revision &r, const QString &what, const svn::Revision &peg, QString &root) 0278 { 0279 root = _base; 0280 const svn::LogEntriesMap::const_iterator it = m_Entries->constFind(r.revnum()); 0281 if (it == m_Entries->constEnd()) { 0282 return m_Actions->getSingleLog(t, r, what, peg, root); 0283 } 0284 t = it.value(); 0285 return true; 0286 } 0287 0288 void SvnLogDlgImp::slotGetLogs() 0289 { 0290 svn::LogEntriesMapPtr lm = m_Actions->getLog(m_startRevButton->revision(), 0291 m_endRevButton->revision(), 0292 m_peg, 0293 _base + _name, 0294 Kdesvnsettings::self()->log_always_list_changed_files(), 0295 0, 0296 Kdesvnsettings::last_node_follow(), 0297 this); 0298 if (lm) { 0299 dispLog(lm); 0300 } 0301 } 0302 0303 void SvnLogDlgImp::slotPrevFifty() 0304 { 0305 svn::Revision now = m_CurrentModel->min(); 0306 if (now == 1) { 0307 return; 0308 } 0309 svn::Revision begin = now.revnum() - 1; 0310 if (begin.revnum() < 1) { 0311 begin = 1; 0312 } 0313 svn::LogEntriesMapPtr lm = m_Actions->getLog(begin, 0314 (begin.revnum() > 50 ? svn::Revision::START : svn::Revision::HEAD), 0315 m_peg, 0316 _base + _name, 0317 Kdesvnsettings::self()->log_always_list_changed_files(), 0318 50, 0319 Kdesvnsettings::last_node_follow(), 0320 this); 0321 if (lm) { 0322 dispLog(lm); 0323 } 0324 } 0325 0326 void SvnLogDlgImp::slotBeginHead() 0327 { 0328 svn::LogEntriesMapPtr lm = m_Actions->getLog(svn::Revision::HEAD, 0329 1, 0330 m_peg, 0331 _base + _name, 0332 Kdesvnsettings::self()->log_always_list_changed_files(), 0333 50, 0334 Kdesvnsettings::last_node_follow(), 0335 this); 0336 if (lm) { 0337 dispLog(lm); 0338 } 0339 } 0340 0341 void SvnLogDlgImp::slotHelpRequested() 0342 { 0343 KHelpClient::invokeHelp(QLatin1String("logdisplay-dlg"), QLatin1String("kdesvn")); 0344 } 0345 0346 void SvnLogDlgImp::slotListEntries() 0347 { 0348 QModelIndex _index = selectedRow(); 0349 SvnLogModelNodePtr ptr = m_CurrentModel->indexNode(_index); 0350 if (!ptr) { 0351 buttonListFiles->setEnabled(false); 0352 return; 0353 } 0354 if (ptr->changedPaths().isEmpty()) { 0355 svn::LogEntriesMapPtr _log = m_Actions->getLog(ptr->revision(), ptr->revision(), ptr->revision(), _name, true, 0, Kdesvnsettings::last_node_follow()); 0356 if (!_log) { 0357 return; 0358 } 0359 if (!_log->isEmpty()) { 0360 ptr->setChangedPaths(_log->value(ptr->revision())); 0361 } 0362 } 0363 if (ptr->changedPaths().isEmpty()) { 0364 m_CurrentModel->fillChangedPaths(_index, m_ChangedList); 0365 } 0366 buttonListFiles->setEnabled(false); 0367 } 0368 0369 void SvnLogDlgImp::keyPressEvent(QKeyEvent *e) 0370 { 0371 if (!e) { 0372 return; 0373 } 0374 if (e->text().isEmpty() && e->key() == Qt::Key_Control) { 0375 m_ControlKeyDown = true; 0376 } 0377 QDialog::keyPressEvent(e); 0378 } 0379 0380 void SvnLogDlgImp::keyReleaseEvent(QKeyEvent *e) 0381 { 0382 if (!e) { 0383 return; 0384 } 0385 if (e->text().isEmpty() && e->key() == Qt::Key_Control) { 0386 m_ControlKeyDown = false; 0387 } 0388 QDialog::keyReleaseEvent(e); 0389 } 0390 0391 void SvnLogDlgImp::showEvent(QShowEvent *e) 0392 { 0393 QDialog::showEvent(e); 0394 WindowGeometryHelper::restore(this, groupName); 0395 } 0396 0397 void SvnLogDlgImp::slotBlameItem() 0398 { 0399 QModelIndex ind = selectedRow(); 0400 if (!ind.isValid()) { 0401 buttonBlame->setEnabled(false); 0402 return; 0403 } 0404 qlonglong rev = m_CurrentModel->toRevision(ind); 0405 svn::Revision start(svn::Revision::START); 0406 m_Actions->makeBlame(start, rev, _base + m_CurrentModel->realName(ind), QApplication::activeModalWidget(), rev, this); 0407 } 0408 0409 /* it works 'cause we use single selection only */ 0410 QModelIndex SvnLogDlgImp::selectedRow(int column) 0411 { 0412 QModelIndexList _mi = m_LogTreeView->selectionModel()->selectedRows(column); 0413 if (_mi.count() < 1) { 0414 return QModelIndex(); 0415 } 0416 return m_SortModel->mapToSource(_mi[0]); 0417 } 0418 0419 void SvnLogDlgImp::slotCustomContextMenu(const QPoint &e) 0420 { 0421 QModelIndex ind = m_LogTreeView->indexAt(e); 0422 QModelIndex bel; 0423 if (ind.isValid()) { 0424 bel = m_LogTreeView->indexBelow(ind); 0425 ind = m_SortModel->mapToSource(ind); 0426 } 0427 int row = -1; 0428 if (ind.isValid()) { 0429 row = ind.row(); 0430 } else { 0431 return; 0432 } 0433 0434 qlonglong rev = -1; 0435 if (bel.isValid()) { 0436 bel = m_SortModel->mapToSource(bel); 0437 rev = m_CurrentModel->toRevision(bel); 0438 } 0439 QMenu popup; 0440 QAction *ac; 0441 bool unset = false; 0442 if (row != m_CurrentModel->rightRow()) { 0443 ac = popup.addAction(QIcon::fromTheme(QStringLiteral("kdesvnright")), i18n("Set version as right side of diff")); 0444 ac->setData(101); 0445 } else { 0446 unset = true; 0447 } 0448 if (row != m_CurrentModel->leftRow()) { 0449 ac = popup.addAction(QIcon::fromTheme(QStringLiteral("kdesvnleft")), i18n("Set version as left side of diff")); 0450 ac->setData(102); 0451 } else { 0452 unset = true; 0453 } 0454 if (unset) { 0455 ac = popup.addAction(i18n("Unset version for diff")); 0456 ac->setData(103); 0457 } 0458 if (rev > -1 && !m_PegUrl.isUrl()) { 0459 ac = popup.addAction(i18n("Revert this commit")); 0460 ac->setData(104); 0461 } 0462 ac = popup.exec(m_LogTreeView->viewport()->mapToGlobal(e)); 0463 if (!ac) { 0464 return; 0465 } 0466 int r = ac->data().toInt(); 0467 switch (r) { 0468 case 101: 0469 m_CurrentModel->setRightRow(row); 0470 break; 0471 case 102: 0472 m_CurrentModel->setLeftRow(row); 0473 break; 0474 case 103: 0475 if (row != m_CurrentModel->leftRow()) { 0476 m_CurrentModel->setLeftRow(-1); 0477 } 0478 if (row != m_CurrentModel->rightRow()) { 0479 m_CurrentModel->setRightRow(-1); 0480 } 0481 break; 0482 case 104: { 0483 svn::Revision previous(rev); 0484 svn::Revision current(m_CurrentModel->toRevision(ind)); 0485 QString _path = m_PegUrl.path(); 0486 m_Actions->slotMergeWcRevisions(_path, current, previous, true, true, false, false, false); 0487 } break; 0488 } 0489 m_DispSpecDiff->setEnabled(m_CurrentModel->leftRow() != -1 && m_CurrentModel->rightRow() != -1 && m_CurrentModel->leftRow() != m_CurrentModel->rightRow()); 0490 } 0491 0492 void SvnLogDlgImp::slotChangedPathContextMenu(const QPoint &e) 0493 { 0494 QTreeWidgetItem *_item = m_ChangedList->currentItem(); 0495 if (!_item) { 0496 return; 0497 } 0498 0499 LogChangePathItem *item = static_cast<LogChangePathItem *>(_item); 0500 if (item->action() == 'D') { 0501 return; 0502 } 0503 QModelIndex ind = selectedRow(); 0504 if (!ind.isValid()) { 0505 return; 0506 } 0507 const qlonglong rev = m_CurrentModel->toRevision(ind); 0508 QMenu popup; 0509 const QString name = item->path(); 0510 const QString source = item->revision() > -1 ? item->source() : item->path(); 0511 QAction *ac; 0512 ac = popup.addAction(i18n("Annotate")); 0513 if (ac) { 0514 ac->setData(101); 0515 } 0516 if (item->action() != 'A' || item->revision() > -1) { 0517 ac = popup.addAction(i18n("Diff previous")); 0518 if (ac) { 0519 ac->setData(102); 0520 } 0521 } 0522 ac = popup.addAction(i18n("Cat this version")); 0523 if (ac) { 0524 ac->setData(103); 0525 } 0526 ac = popup.exec(m_ChangedList->viewport()->mapToGlobal(e)); 0527 if (!ac) { 0528 return; 0529 } 0530 int r = ac->data().toInt(); 0531 svn::Revision start(svn::Revision::START); 0532 switch (r) { 0533 case 101: { 0534 m_Actions->makeBlame(start, rev, _base + name, QApplication::activeModalWidget(), rev, this); 0535 break; 0536 } 0537 case 102: { 0538 const svn_revnum_t prev = item->revision() > 0 ? item->revision() : rev - 1; 0539 emit makeDiff(_base + source, prev, _base + name, rev, this); 0540 break; 0541 } 0542 case 103: { 0543 emit makeCat(rev, _base + source, source, rev, QApplication::activeModalWidget()); 0544 } 0545 default: 0546 break; 0547 } 0548 } 0549 0550 void SvnLogDlgImp::slotSingleDoubleClicked(QTreeWidgetItem *_item, int) 0551 { 0552 if (!_item) { 0553 return; 0554 } 0555 0556 const LogChangePathItem *item = static_cast<LogChangePathItem *>(_item); 0557 const QModelIndex ind = selectedRow(); 0558 if (!ind.isValid()) { 0559 return; 0560 } 0561 svn::Revision start(svn::Revision::START); 0562 if (item->action() != 'D') { 0563 const QString name = item->path(); 0564 const qlonglong rev = m_CurrentModel->toRevision(ind); 0565 m_Actions->makeBlame(start, rev, _base + name, QApplication::activeModalWidget(), rev, this); 0566 } 0567 } 0568 0569 #include "moc_svnlogdlgimp.cpp"