Warning, file /sdk/cervisia/diffview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * Copyright (C) 1999-2002 Bernd Gehrmann 0003 * bernd@mail.berlios.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 Free Software 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "diffview.h" 0021 0022 #include <qapplication.h> 0023 #include <qevent.h> 0024 #include <qpainter.h> 0025 #include <qpixmap.h> 0026 #include <qscrollbar.h> 0027 #include <qstyle.h> 0028 #include <qstyleoption.h> 0029 0030 #include <KLocalizedString> 0031 #include <kcolorscheme.h> 0032 #include <kconfig.h> 0033 #include <kconfiggroup.h> 0034 0035 #include "cervisiasettings.h" 0036 #include "debug.h" 0037 0038 class DiffViewItem 0039 { 0040 public: 0041 QString line; 0042 DiffView::DiffType type; 0043 bool inverted; 0044 int no; 0045 }; 0046 0047 const int DiffView::BORDER = 7; 0048 0049 DiffView::DiffView(KConfig &cfg, bool withlinenos, bool withmarker, QWidget *parent, const char *name) 0050 : QtTableView(parent, name) 0051 , linenos(withlinenos) 0052 , marker(withmarker) 0053 , textwidth(0) 0054 , partner(0) 0055 , partConfig(cfg) 0056 { 0057 setNumRows(0); 0058 setNumCols(1 + (withlinenos ? 1 : 0) + (withmarker ? 1 : 0)); 0059 setTableFlags(Tbl_autoVScrollBar | Tbl_autoHScrollBar | Tbl_smoothVScrolling); 0060 setFrameStyle(QFrame::WinPanel | QFrame::Sunken); 0061 setBackgroundRole(QPalette::Base); 0062 0063 configChanged(); 0064 0065 QFontMetrics fm(font()); 0066 setCellHeight(fm.lineSpacing()); 0067 setCellWidth(0); 0068 0069 const KConfigGroup group(&partConfig, "General"); 0070 m_tabWidth = group.readEntry("TabWidth", 8); 0071 0072 connect(CervisiaSettings::self(), SIGNAL(configChanged()), this, SLOT(configChanged())); 0073 } 0074 0075 DiffView::~DiffView() 0076 { 0077 qDeleteAll(items); 0078 } 0079 0080 void DiffView::setFont(const QFont &font) 0081 { 0082 QtTableView::setFont(font); 0083 QFontMetrics fm(font); 0084 setCellHeight(fm.lineSpacing()); 0085 } 0086 0087 void DiffView::setPartner(DiffView *other) 0088 { 0089 partner = other; 0090 if (partner) { 0091 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(vertPositionChanged(int))); 0092 connect(verticalScrollBar(), SIGNAL(sliderMoved(int)), SLOT(vertPositionChanged(int))); 0093 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), SLOT(horzPositionChanged(int))); 0094 connect(horizontalScrollBar(), SIGNAL(sliderMoved(int)), SLOT(horzPositionChanged(int))); 0095 } 0096 } 0097 0098 void DiffView::vertPositionChanged(int val) 0099 { 0100 if (partner) 0101 partner->setYOffset(qMin(val, partner->maxYOffset())); 0102 } 0103 0104 void DiffView::horzPositionChanged(int val) 0105 { 0106 if (partner) 0107 partner->setXOffset(qMin(val, partner->maxXOffset())); 0108 } 0109 0110 void DiffView::configChanged() 0111 { 0112 diffChangeColor = CervisiaSettings::diffChangeColor(); 0113 diffInsertColor = CervisiaSettings::diffInsertColor(); 0114 diffDeleteColor = CervisiaSettings::diffDeleteColor(); 0115 0116 setFont(CervisiaSettings::diffFont()); 0117 } 0118 0119 // *offset methods are only for views withlineno 0120 void DiffView::removeAtOffset(int offset) 0121 { 0122 delete items.takeAt(offset); 0123 setNumRows(numRows() - 1); 0124 } 0125 0126 void DiffView::insertAtOffset(const QString &line, DiffType type, int offset) 0127 { 0128 auto item = new DiffViewItem; 0129 item->line = line; 0130 item->type = type; 0131 item->no = -1; 0132 item->inverted = false; 0133 items.insert(offset, item); 0134 setNumRows(numRows() + 1); 0135 } 0136 0137 void DiffView::setCenterOffset(int offset) 0138 { 0139 if (!rowIsVisible(offset)) { 0140 int visiblerows = viewHeight() / cellHeight(0); 0141 setTopCell(qMax(0, offset - visiblerows / 2)); 0142 } 0143 } 0144 0145 void DiffView::addLine(const QString &line, DiffType type, int no) 0146 { 0147 QFont f(font()); 0148 f.setBold(true); 0149 QFontMetrics fmbold(f); 0150 QFontMetrics fm(font()); 0151 0152 // calculate textwidth based on 'line' where tabs are expanded 0153 // 0154 // *Please note* 0155 // For some fonts, e.g. "Clean", is fm.maxWidth() greater than 0156 // fmbold.maxWidth(). 0157 QString copy(line); 0158 const int numTabs = copy.count(QLatin1Char('\t')); 0159 copy.remove(QLatin1Char('\t')); 0160 0161 const int tabSize = m_tabWidth * qMax(fm.maxWidth(), fmbold.maxWidth()); 0162 const int copyWidth = qMax(fm.width(copy), fmbold.width(copy)); 0163 textwidth = qMax(textwidth, copyWidth + numTabs * tabSize); 0164 0165 auto item = new DiffViewItem; 0166 item->line = line; 0167 item->type = type; 0168 item->no = no; 0169 item->inverted = false; 0170 items.append(item); 0171 setNumRows(numRows() + 1); 0172 } 0173 0174 QString DiffView::stringAtOffset(int offset) 0175 { 0176 if (offset >= items.count()) { 0177 qCDebug(log_cervisia) << "Internal error: lineAtOffset"; 0178 } 0179 return items.at(offset)->line; 0180 } 0181 0182 int DiffView::count() 0183 { 0184 return items.count(); 0185 } 0186 0187 int DiffView::findLine(int lineno) 0188 { 0189 for (int i = 0; i < items.count(); i++) 0190 if (items[i]->no == lineno) 0191 return i; 0192 0193 qCDebug(log_cervisia) << "Internal Error: Line" << lineno << "not found"; 0194 return -1; 0195 } 0196 0197 void DiffView::setInverted(int lineno, bool inverted) 0198 { 0199 int offset; 0200 if ((offset = findLine(lineno)) != -1) 0201 items.at(offset)->inverted = inverted; 0202 } 0203 0204 void DiffView::setCenterLine(int lineno) 0205 { 0206 int offset; 0207 if ((offset = findLine(lineno)) != -1) 0208 setCenterOffset(offset); 0209 } 0210 0211 QString DiffView::stringAtLine(int lineno) 0212 { 0213 int pos; 0214 if ((pos = findLine(lineno)) != -1) 0215 return items.at(pos)->line; 0216 else 0217 return {}; 0218 } 0219 0220 QByteArray DiffView::compressedContent() 0221 { 0222 QByteArray res(items.count(), '\0'); 0223 0224 for (int i = 0; i < items.count(); i++) { 0225 switch (items[i]->type) { 0226 case Change: 0227 res[i] = 'C'; 0228 break; 0229 case Insert: 0230 res[i] = 'I'; 0231 break; 0232 case Delete: 0233 res[i] = 'D'; 0234 break; 0235 case Neutral: 0236 res[i] = 'N'; 0237 break; 0238 case Unchanged: 0239 res[i] = 'U'; 0240 break; 0241 default: 0242 res[i] = ' '; 0243 } 0244 } 0245 return res; 0246 } 0247 0248 int DiffView::cellWidth(int col) 0249 { 0250 if (col == 0 && linenos) { 0251 QFontMetrics fm(font()); 0252 return fm.width("10000"); 0253 } else if (marker && (col == 0 || col == 1)) { 0254 QFontMetrics fm(fontMetrics()); 0255 return qMax(qMax(fm.width(i18n("Delete")), fm.width(i18n("Insert"))), fm.width(i18n("Change"))) + 2 * BORDER; 0256 } else { 0257 int rest = (linenos || marker) ? cellWidth(0) : 0; 0258 if (linenos && marker) 0259 rest += cellWidth(1); 0260 return qMax(textwidth, viewWidth() - rest); 0261 } 0262 } 0263 0264 QSize DiffView::sizeHint() const 0265 { 0266 QFontMetrics fm(font()); 0267 return {4 * fm.width("0123456789"), fm.lineSpacing() * 8}; 0268 } 0269 0270 void DiffView::paintCell(QPainter *p, int row, int col) 0271 { 0272 QFontMetrics fm(font()); 0273 0274 DiffViewItem *item = items.at(row); 0275 0276 int width = cellWidth(col); 0277 int height = cellHeight(); 0278 0279 QColor backgroundColor; 0280 bool inverted; 0281 Qt::Alignment align; 0282 int innerborder; 0283 QString str; 0284 0285 QFont oldFont(p->font()); 0286 if (item->type == Separator) { 0287 backgroundColor = KColorScheme(QPalette::Active, KColorScheme::Selection).background().color(); 0288 p->setPen(KColorScheme(QPalette::Active, KColorScheme::Selection).foreground().color()); 0289 inverted = false; 0290 align = Qt::AlignLeft; 0291 innerborder = 0; 0292 if (col == (linenos ? 1 : 0) + (marker ? 1 : 0)) 0293 str = item->line; 0294 QFont f(oldFont); 0295 f.setBold(true); 0296 p->setFont(f); 0297 } else if (col == 0 && linenos) { 0298 backgroundColor = KColorScheme(QPalette::Active, KColorScheme::Selection).background().color(); 0299 p->setPen(KColorScheme(QPalette::Active, KColorScheme::Selection).foreground().color()); 0300 inverted = false; 0301 align = Qt::AlignLeft; 0302 innerborder = 0; 0303 if (item->no == -1) 0304 str = "+++++"; 0305 else 0306 str.setNum(item->no); 0307 } else if (marker && (col == 0 || col == 1)) { 0308 backgroundColor = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::AlternateBackground).color(); 0309 p->setPen(KColorScheme(QPalette::Active, KColorScheme::View).foreground().color()); 0310 inverted = false; 0311 align = Qt::AlignRight; 0312 innerborder = BORDER; 0313 str = (item->type == Change) ? i18n("Change") : (item->type == Insert) ? i18n("Insert") : (item->type == Delete) ? i18n("Delete") : QString(); 0314 } else { 0315 backgroundColor = (item->type == Change) ? diffChangeColor 0316 : (item->type == Insert) ? diffInsertColor 0317 : (item->type == Delete) ? diffDeleteColor 0318 : (item->type == Neutral) ? KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::AlternateBackground).color() 0319 : KColorScheme(QPalette::Active, KColorScheme::View).background().color(); 0320 p->setPen(KColorScheme(QPalette::Active, KColorScheme::View).foreground().color()); 0321 inverted = item->inverted; 0322 align = Qt::AlignLeft; 0323 innerborder = 0; 0324 str = item->line; 0325 } 0326 0327 if (inverted) { 0328 p->setPen(backgroundColor); 0329 backgroundColor = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(); 0330 QFont f(oldFont); 0331 f.setBold(true); 0332 p->setFont(f); 0333 } 0334 0335 p->fillRect(0, 0, width, height, backgroundColor); 0336 QTextOption textOption(align); 0337 textOption.setTabStop(m_tabWidth * fm.width(' ')); 0338 p->drawText(QRectF(innerborder, 0, width - 2 * innerborder, height), str, textOption); 0339 p->setFont(oldFont); 0340 } 0341 0342 DiffZoomWidget::DiffZoomWidget(QWidget *parent) 0343 : QFrame(parent) 0344 { 0345 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum)); 0346 } 0347 0348 DiffZoomWidget::~DiffZoomWidget() = default; 0349 0350 void DiffZoomWidget::setDiffView(DiffView *view) 0351 { 0352 diffview = view; 0353 auto sb = const_cast<QScrollBar *>(diffview->scrollBar()); 0354 sb->installEventFilter(this); 0355 } 0356 0357 QSize DiffZoomWidget::sizeHint() const 0358 { 0359 return {25, style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, this)}; 0360 } 0361 0362 bool DiffZoomWidget::eventFilter(QObject *o, QEvent *e) 0363 { 0364 if (e->type() == QEvent::Show || e->type() == QEvent::Hide || e->type() == QEvent::Resize) 0365 update(); 0366 0367 return QFrame::eventFilter(o, e); 0368 } 0369 0370 void DiffZoomWidget::paintEvent(QPaintEvent *) 0371 { 0372 const QScrollBar *scrollBar = diffview->scrollBar(); 0373 if (!scrollBar) 0374 return; 0375 0376 const QColor diffChangeColor(CervisiaSettings::diffChangeColor()); 0377 const QColor diffInsertColor(CervisiaSettings::diffInsertColor()); 0378 const QColor diffDeleteColor(CervisiaSettings::diffDeleteColor()); 0379 0380 // only y and height are important 0381 QStyleOptionSlider option; 0382 option.init(scrollBar); 0383 const QRect scrollBarGroove(scrollBar->isVisible() ? style()->subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarGroove, scrollBar) 0384 : rect()); 0385 0386 // draw rectangles at the positions of the differences 0387 0388 const QByteArray &lineTypes(diffview->compressedContent()); 0389 0390 QPainter p(this); 0391 p.fillRect(0, scrollBarGroove.y(), width(), scrollBarGroove.height(), KColorScheme(QPalette::Active, KColorScheme::View).background().color()); 0392 if (const unsigned int numberOfLines = lineTypes.size()) { 0393 const double scale(((double)scrollBarGroove.height()) / numberOfLines); 0394 for (unsigned int index(0); index < numberOfLines;) { 0395 const char lineType(lineTypes[index]); 0396 0397 // don't use qRound() to avoid painting outside of the pixmap 0398 // (yPos1 must be lesser than scrollBarGroove.height()) 0399 const int yPos1(static_cast<int>(index * scale)); 0400 0401 // search next line with different lineType 0402 for (++index; index < numberOfLines && lineType == lineTypes[index]; ++index) 0403 ; 0404 0405 QColor color; 0406 switch (lineType) { 0407 case 'C': 0408 color = diffChangeColor; 0409 break; 0410 case 'I': 0411 color = diffInsertColor; 0412 break; 0413 case 'D': 0414 color = diffDeleteColor; 0415 break; 0416 case ' ': 0417 case 'N': 0418 color = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::AlternateBackground).color(); 0419 break; 0420 } 0421 0422 if (color.isValid()) { 0423 const int yPos2(qRound(index * scale)); 0424 const int areaHeight((yPos2 != yPos1) ? yPos2 - yPos1 : 1); 0425 0426 p.fillRect(0, yPos1 + scrollBarGroove.y(), width(), areaHeight, QBrush(color)); 0427 } 0428 } 0429 } 0430 } 0431 0432 // Local Variables: 0433 // c-basic-offset: 4 0434 // End: