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: