File indexing completed on 2024-05-12 04:38:56

0001 /*
0002     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0003     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "vcseventwidget.h"
0009 
0010 #include <QAction>
0011 #include <QClipboard>
0012 #include <QDialog>
0013 #include <QDialogButtonBox>
0014 #include <QHeaderView>
0015 #include <QMenu>
0016 #include <QPushButton>
0017 #include <QVBoxLayout>
0018 #include <QDesktopServices>
0019 
0020 #include <KLocalizedString>
0021 #include <KTextToHTML>
0022 
0023 #include <interfaces/iplugin.h>
0024 
0025 #include "ui_vcseventwidget.h"
0026 #include "vcsdiffwidget.h"
0027 
0028 #include "../interfaces/ibasicversioncontrol.h"
0029 #include "../models/vcseventmodel.h"
0030 #include "../models/vcsitemeventmodel.h"
0031 #include "../vcsevent.h"
0032 #include "../vcsjob.h"
0033 #include "../vcsrevision.h"
0034 #include "debug.h"
0035 
0036 
0037 namespace KDevelop
0038 {
0039 
0040 class VcsEventWidgetPrivate
0041 {
0042 public:
0043     explicit VcsEventWidgetPrivate( VcsEventWidget* w )
0044         : q( w )
0045     {
0046         m_copyAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "Copy Revision Id"), q);
0047         m_copyAction->setShortcut(Qt::CTRL | Qt::Key_C);
0048         QObject::connect(m_copyAction, &QAction::triggered, q, [&] { copyRevision(); });
0049     }
0050 
0051     Ui::VcsEventWidget* m_ui;
0052     VcsItemEventModel* m_detailModel;
0053     VcsEventLogModel *m_logModel;
0054     QUrl m_url;
0055     QModelIndex m_contextIndex;
0056     VcsEventWidget* q;
0057     QAction* m_copyAction;
0058     IBasicVersionControl* m_iface;
0059     void eventViewCustomContextMenuRequested( const QPoint &point );
0060     void eventViewClicked( const QModelIndex &index );
0061     void jobReceivedResults( KDevelop::VcsJob* job );
0062     void copyRevision();
0063     void diffToPrevious();
0064     void diffRevisions();
0065     void currentRowChanged(const QModelIndex& start, const QModelIndex& end);
0066 };
0067 
0068 void VcsEventWidgetPrivate::eventViewCustomContextMenuRequested( const QPoint &point )
0069 {
0070     m_contextIndex = m_ui->eventView->indexAt( point );
0071     if( !m_contextIndex.isValid() ){
0072         qCDebug(VCS) << "contextMenu is not in TreeView";
0073         return;
0074     }
0075 
0076     QMenu menu( m_ui->eventView );
0077     menu.addAction(m_copyAction);
0078     auto diffToPreviousAction = menu.addAction(i18nc("@action:inmenu", "Diff to Previous Revision"));
0079     QObject::connect(diffToPreviousAction, &QAction::triggered, q, [&] { diffToPrevious(); });
0080 
0081     auto diffRevisionsAction = menu.addAction(i18nc("@action:inmenu", "Diff between Revisions"));
0082     QObject::connect(diffRevisionsAction, &QAction::triggered, q, [&] { diffRevisions(); });
0083     diffRevisionsAction->setEnabled(m_ui->eventView->selectionModel()->selectedRows().size()>=2);
0084 
0085     menu.exec( m_ui->eventView->viewport()->mapToGlobal(point) );
0086 }
0087 
0088 void VcsEventWidgetPrivate::currentRowChanged(const QModelIndex& start, const QModelIndex& end)
0089 {
0090     Q_UNUSED(end);
0091     if(start.isValid())
0092         eventViewClicked(start);
0093 }
0094 
0095 void VcsEventWidgetPrivate::eventViewClicked( const QModelIndex &index )
0096 {
0097     KDevelop::VcsEvent ev = m_logModel->eventForIndex( index );
0098     m_detailModel->removeRows(0, m_detailModel->rowCount());
0099 
0100     if( ev.revision().revisionType() != KDevelop::VcsRevision::Invalid )
0101     {
0102         m_ui->itemEventView->setEnabled(true);
0103         m_ui->message->setEnabled(true);
0104         const KTextToHTML::Options markupOptions = KTextToHTML::PreserveSpaces;
0105         const QString markupMessage =
0106             QLatin1String("<tt>") + KTextToHTML::convertToHtml(ev.message(), markupOptions) + QLatin1String("</tt>");
0107         m_ui->message->setHtml(markupMessage);
0108         m_detailModel->addItemEvents( ev.items() );
0109     }else
0110     {
0111         m_ui->itemEventView->setEnabled(false);
0112         m_ui->message->setEnabled(false);
0113         m_ui->message->clear();
0114     }
0115 
0116     QHeaderView* header = m_ui->itemEventView->header();
0117     header->setSectionResizeMode(QHeaderView::ResizeToContents);
0118     header->setStretchLastSection(true);
0119 }
0120 
0121 void VcsEventWidgetPrivate::copyRevision()
0122 {
0123     qApp->clipboard()->setText(m_contextIndex.sibling(m_contextIndex.row(), 0).data().toString());
0124 }
0125 
0126 void VcsEventWidgetPrivate::diffToPrevious()
0127 {
0128     KDevelop::VcsEvent ev = m_logModel->eventForIndex( m_contextIndex );
0129     KDevelop::VcsRevision prev = KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Previous);
0130     KDevelop::VcsJob* job = m_iface->diff( m_url, prev, ev.revision() );
0131 
0132     auto* widget = new VcsDiffWidget( job );
0133     widget->setRevisions( prev, ev.revision() );
0134     auto* dlg = new QDialog( q );
0135 
0136     widget->connect(widget, &VcsDiffWidget::destroyed, dlg, &QDialog::deleteLater);
0137 
0138     dlg->setWindowTitle( i18nc("@title:window", "Difference To Previous") );
0139 
0140     auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
0141     auto mainWidget = new QWidget;
0142     auto *mainLayout = new QVBoxLayout;
0143     dlg->setLayout(mainLayout);
0144     mainLayout->addWidget(mainWidget);
0145     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0146     okButton->setDefault(true);
0147     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0148     dlg->connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept);
0149     dlg->connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject);
0150     mainLayout->addWidget(widget);
0151     mainLayout->addWidget(buttonBox);
0152 
0153     dlg->show();
0154 }
0155 
0156 void VcsEventWidgetPrivate::diffRevisions()
0157 {
0158     QModelIndexList l = m_ui->eventView->selectionModel()->selectedRows();
0159     KDevelop::VcsEvent ev1 = m_logModel->eventForIndex( l.first() );
0160     KDevelop::VcsEvent ev2 = m_logModel->eventForIndex( l.last() );
0161     KDevelop::VcsJob* job = m_iface->diff( m_url, ev1.revision(), ev2.revision() );
0162 
0163     auto* widget = new VcsDiffWidget( job );
0164     widget->setRevisions( ev1.revision(), ev2.revision() );
0165 
0166     auto dlg = new QDialog( q );
0167     dlg->setWindowTitle( i18nc("@title:window", "Difference between Revisions") );
0168 
0169     widget->connect(widget, &VcsDiffWidget::destroyed, dlg, &QDialog::deleteLater);
0170 
0171     auto mainLayout = new QVBoxLayout(dlg);
0172     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
0173     auto okButton = buttonBox->button(QDialogButtonBox::Ok);
0174     okButton->setDefault(true);
0175     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0176     dlg->connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept);
0177     dlg->connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject);
0178     mainLayout->addWidget(buttonBox);
0179     mainLayout->addWidget(widget);
0180     dlg->show();
0181 }
0182 
0183 VcsEventWidget::VcsEventWidget( const QUrl& url, const VcsRevision& rev, KDevelop::IBasicVersionControl* iface, QWidget* parent )
0184     : QWidget(parent)
0185     , d_ptr(new VcsEventWidgetPrivate(this))
0186 {
0187     Q_D(VcsEventWidget);
0188 
0189     d->m_iface = iface;
0190     d->m_url = url;
0191     d->m_ui = new Ui::VcsEventWidget();
0192     d->m_ui->setupUi(this);
0193 
0194     d->m_logModel = new VcsEventLogModel(iface, rev, url, this);
0195     d->m_ui->eventView->setModel( d->m_logModel );
0196     d->m_ui->eventView->sortByColumn(0, Qt::DescendingOrder);
0197     d->m_ui->eventView->setContextMenuPolicy( Qt::CustomContextMenu );
0198     QHeaderView* header = d->m_ui->eventView->header();
0199     header->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
0200     header->setSectionResizeMode( 1, QHeaderView::Stretch );
0201     header->setSectionResizeMode( 2, QHeaderView::ResizeToContents );
0202     header->setSectionResizeMode( 3, QHeaderView::ResizeToContents );
0203     // Select first row as soon as the model got populated
0204     connect(d->m_logModel, &QAbstractItemModel::rowsInserted, this, [this]() {
0205         Q_D(VcsEventWidget);
0206         auto view = d->m_ui->eventView;
0207         view->setCurrentIndex(view->model()->index(0, 0));
0208     });
0209 
0210     d->m_detailModel = new VcsItemEventModel(this);
0211     d->m_ui->itemEventView->setModel( d->m_detailModel );
0212 
0213     connect(d->m_ui->eventView, &QTreeView::clicked, this, [this] (const QModelIndex& index) {
0214         Q_D(VcsEventWidget);
0215         d->eventViewClicked(index);
0216     });
0217     connect(d->m_ui->eventView->selectionModel(), &QItemSelectionModel::currentRowChanged,
0218             this, [this] (const QModelIndex& start, const QModelIndex& end) {
0219         Q_D(VcsEventWidget);
0220         d->currentRowChanged(start, end);
0221     });
0222     connect(d->m_ui->eventView, &QTreeView::customContextMenuRequested,
0223             this, [this] (const QPoint& point) {
0224         Q_D(VcsEventWidget);
0225         d->eventViewCustomContextMenuRequested(point);
0226     });
0227 
0228     connect(d->m_ui->message, &QTextBrowser::anchorClicked,
0229             this, [&] (const QUrl& url) { QDesktopServices::openUrl(url); });
0230 }
0231 
0232 VcsEventWidget::~VcsEventWidget()
0233 {
0234     Q_D(VcsEventWidget);
0235 
0236     delete d->m_ui;
0237 }
0238 
0239 }
0240 
0241 
0242 #include "moc_vcseventwidget.cpp"