File indexing completed on 2024-05-12 04:38:54
0001 /* 0002 SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "vcsannotationmodel.h" 0008 0009 #include "../vcsannotation.h" 0010 #include "../vcsrevision.h" 0011 #include "../vcsjob.h" 0012 0013 #include <QDateTime> 0014 #include <QBrush> 0015 #include <QPen> 0016 #include <QHash> 0017 #include <QLocale> 0018 #include <QUrl> 0019 #include <QApplication> 0020 0021 #include <KLocalizedString> 0022 0023 #include <interfaces/icore.h> 0024 #include <interfaces/iruncontroller.h> 0025 0026 namespace KDevelop 0027 { 0028 0029 class VcsAnnotationModelPrivate 0030 { 0031 public: 0032 explicit VcsAnnotationModelPrivate( VcsAnnotationModel* q_ ) : q(q_) {} 0033 KDevelop::VcsAnnotation m_annotation; 0034 mutable QHash<KDevelop::VcsRevision, QBrush> m_brushes; 0035 VcsAnnotationModel* q; 0036 VcsJob* job; 0037 QColor foreground; 0038 QColor background; 0039 0040 const QBrush& brush(const VcsRevision& revision) const 0041 { 0042 auto brushIt = m_brushes.find(revision); 0043 if (brushIt == m_brushes.end()) { 0044 const int background_y = background.red()*0.299 + 0.587*background.green() 0045 + 0.114*background.blue(); 0046 // get random, but reproducible 8-bit values from last two bytes of the revision hash 0047 const uint revisionHash = qHash(revision); 0048 const int u = static_cast<int>((0xFF & revisionHash)); 0049 const int v = static_cast<int>((0xFF00 & revisionHash) >> 8); 0050 const int r = qRound(qMin(255.0, qMax(0.0, background_y + 1.402*(v-128)))); 0051 const int g = qRound(qMin(255.0, qMax(0.0, background_y - 0.344*(u-128) - 0.714*(v-128)))); 0052 const int b = qRound(qMin(255.0, qMax(0.0, background_y + 1.772*(u-128)))); 0053 brushIt = m_brushes.insert(revision, QBrush(QColor(r, g, b))); 0054 } 0055 return brushIt.value(); 0056 } 0057 0058 void addLines( KDevelop::VcsJob* job ) 0059 { 0060 if( job == this->job ) 0061 { 0062 const auto results = job->fetchResults().toList(); 0063 for (const QVariant& v : results) { 0064 if( v.canConvert<KDevelop::VcsAnnotationLine>() ) 0065 { 0066 VcsAnnotationLine l = v.value<KDevelop::VcsAnnotationLine>(); 0067 m_annotation.insertLine( l.lineNumber(), l ); 0068 emit q->lineChanged( l.lineNumber() ); 0069 } 0070 } 0071 } 0072 } 0073 }; 0074 0075 VcsAnnotationModel::VcsAnnotationModel(VcsJob *job, const QUrl& url, QObject* parent, 0076 const QColor &foreground, const QColor &background) 0077 : d_ptr(new VcsAnnotationModelPrivate(this)) 0078 { 0079 Q_D(VcsAnnotationModel); 0080 0081 setParent( parent ); 0082 d->m_annotation.setLocation( url ); 0083 d->job = job; 0084 d->foreground = foreground; 0085 d->background = background; 0086 connect( d->job, &VcsJob::resultsReady,this, [this] (VcsJob* job) { Q_D(VcsAnnotationModel); d->addLines(job); } ); 0087 ICore::self()->runController()->registerJob( d->job ); 0088 } 0089 0090 VcsAnnotationModel::~VcsAnnotationModel() = default; 0091 0092 static QString abbreviateLastName(const QString& author) { 0093 auto parts = author.split(QLatin1Char(' ')); 0094 bool onlyOneFragment = parts.size() == 1 || ( parts.size() == 2 && parts.at(1).isEmpty() ); 0095 return onlyOneFragment ? parts.first() : parts.first() + QStringLiteral(" %1.").arg(parts.last()[0]); 0096 } 0097 0098 static QString annotationToolTip(const VcsAnnotationLine& aline) 0099 { 0100 const bool textIsLeftToRight = (QApplication::layoutDirection() == Qt::LeftToRight); 0101 0102 const QString boldStyle = QStringLiteral(";font-weight:bold"); 0103 const QString one = QStringLiteral("1"); 0104 const QString two = QStringLiteral("2"); 0105 const QString line = QStringLiteral( 0106 "<tr>" 0107 "<td align=\"right\" style=\"white-space:nowrap%1\">%%2</td>" 0108 "<td align=\"left\" style=\"white-space:nowrap%3\">%%4</td>" 0109 "</tr>").arg( 0110 (textIsLeftToRight ? boldStyle : QString()), 0111 (textIsLeftToRight ? one : two), 0112 (textIsLeftToRight ? QString() : boldStyle), 0113 (textIsLeftToRight ? two : one) 0114 ); 0115 0116 const QString authorLabel = i18n("Author:").toHtmlEscaped(); 0117 const QString dateLabel = i18n("Date:").toHtmlEscaped(); 0118 const QString messageLabel = i18n("Commit message:").toHtmlEscaped(); 0119 0120 const QString author = aline.author().toHtmlEscaped(); 0121 const QString date = QLocale().toString(aline.date()).toHtmlEscaped(); 0122 const QString message = aline.commitMessage().toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("<br/>")); 0123 0124 return 0125 QLatin1String("<table>") + 0126 line.arg(authorLabel, author) + 0127 line.arg(dateLabel, date) + 0128 line.arg(messageLabel, message) + 0129 QLatin1String("</table>"); 0130 } 0131 0132 QVariant VcsAnnotationModel::data( int line, Qt::ItemDataRole role ) const 0133 { 0134 Q_D(const VcsAnnotationModel); 0135 0136 if( line < 0 || !d->m_annotation.containsLine( line ) ) 0137 { 0138 return QVariant(); 0139 } 0140 0141 KDevelop::VcsAnnotationLine aline = d->m_annotation.line( line ); 0142 if( role == Qt::ForegroundRole ) 0143 { 0144 return QVariant(QPen(d->foreground)); 0145 } 0146 if( role == Qt::BackgroundRole ) 0147 { 0148 return QVariant(d->brush(aline.revision())); 0149 } else if( role == Qt::DisplayRole ) 0150 { 0151 return QVariant( QStringLiteral("%1 ").arg(aline.date().date().year()) + abbreviateLastName(aline.author()) ); 0152 } else if( role == (int) KTextEditor::AnnotationModel::GroupIdentifierRole ) 0153 { 0154 return aline.revision().revisionValue(); 0155 } else if( role == Qt::ToolTipRole ) 0156 { 0157 return QVariant(annotationToolTip(aline)); 0158 } 0159 return QVariant(); 0160 } 0161 0162 VcsRevision VcsAnnotationModel::revisionForLine( int line ) const 0163 { 0164 Q_D(const VcsAnnotationModel); 0165 0166 ///FIXME: update the annotation bar on edit/reload somehow 0167 ///BUG: https://bugs.kde.org/show_bug.cgi?id=269757 0168 if (!d->m_annotation.containsLine(line)) { 0169 return VcsRevision(); 0170 } 0171 0172 Q_ASSERT(line >= 0 && d->m_annotation.containsLine(line)); 0173 return d->m_annotation.line( line ).revision(); 0174 } 0175 0176 VcsAnnotationLine VcsAnnotationModel::annotationLine(int line) const 0177 { 0178 Q_D(const VcsAnnotationModel); 0179 0180 if (line < 0 || !d->m_annotation.containsLine(line)) { 0181 return VcsAnnotationLine(); 0182 } 0183 0184 return d->m_annotation.line(line); 0185 } 0186 0187 } 0188 0189 #include "moc_vcsannotationmodel.cpp"