| Chris@57 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@57 | 2 | 
| Chris@57 | 3 /* | 
| Chris@57 | 4     EasyMercurial | 
| Chris@57 | 5 | 
| Chris@57 | 6     Based on HgExplorer by Jari Korhonen | 
| Chris@57 | 7     Copyright (c) 2010 Jari Korhonen | 
| Chris@244 | 8     Copyright (c) 2011 Chris Cannam | 
| Chris@244 | 9     Copyright (c) 2011 Queen Mary, University of London | 
| Chris@57 | 10 | 
| Chris@57 | 11     This program is free software; you can redistribute it and/or | 
| Chris@57 | 12     modify it under the terms of the GNU General Public License as | 
| Chris@57 | 13     published by the Free Software Foundation; either version 2 of the | 
| Chris@57 | 14     License, or (at your option) any later version.  See the file | 
| Chris@57 | 15     COPYING included with this distribution for more information. | 
| Chris@57 | 16 */ | 
| Chris@57 | 17 | 
| Chris@43 | 18 #include "changesetitem.h" | 
| Chris@281 | 19 #include "changesetscene.h" | 
| Chris@117 | 20 #include "changesetdetailitem.h" | 
| Chris@43 | 21 #include "changeset.h" | 
| Chris@53 | 22 #include "textabbrev.h" | 
| Chris@53 | 23 #include "colourset.h" | 
| Chris@117 | 24 #include "debug.h" | 
| Chris@43 | 25 | 
| Chris@43 | 26 #include <QPainter> | 
| Chris@117 | 27 #include <QGraphicsScene> | 
| Chris@132 | 28 #include <QGraphicsSceneMouseEvent> | 
| Chris@140 | 29 #include <QMenu> | 
| Chris@140 | 30 #include <QAction> | 
| Chris@140 | 31 #include <QLabel> | 
| Chris@140 | 32 #include <QWidgetAction> | 
| Chris@153 | 33 #include <QApplication> | 
| Chris@153 | 34 #include <QClipboard> | 
| Chris@43 | 35 | 
| Chris@390 | 36 QImage *ChangesetItem::m_star = 0; | 
| Chris@390 | 37 | 
| Chris@53 | 38 ChangesetItem::ChangesetItem(Changeset *cs) : | 
| Chris@117 | 39     m_changeset(cs), m_detail(0), | 
| Chris@133 | 40     m_showBranch(false), m_column(0), m_row(0), m_wide(false), | 
| Chris@510 | 41     m_current(false), m_closing(false), m_new(false) | 
| Chris@53 | 42 { | 
| Chris@53 | 43     m_font = QFont(); | 
| Chris@53 | 44     m_font.setPixelSize(11); | 
| Chris@53 | 45     m_font.setBold(false); | 
| Chris@53 | 46     m_font.setItalic(false); | 
| Chris@168 | 47     setCursor(Qt::ArrowCursor); | 
| Chris@390 | 48 | 
| Chris@390 | 49     if (!m_star) m_star = new QImage(":images/star.png"); | 
| Chris@53 | 50 } | 
| Chris@53 | 51 | 
| Chris@141 | 52 QString | 
| Chris@141 | 53 ChangesetItem::getId() | 
| Chris@141 | 54 { | 
| Chris@141 | 55     return m_changeset->id(); | 
| Chris@141 | 56 } | 
| Chris@141 | 57 | 
| Chris@43 | 58 QRectF | 
| Chris@43 | 59 ChangesetItem::boundingRect() const | 
| Chris@43 | 60 { | 
| Chris@55 | 61     int w = 100; | 
| Chris@55 | 62     if (m_wide) w = 180; | 
| Chris@250 | 63     return QRectF(-((w-50)/2 - 1), -30, w - 3, 90); | 
| Chris@43 | 64 } | 
| Chris@43 | 65 | 
| Chris@43 | 66 void | 
| Chris@119 | 67 ChangesetItem::showDetail() | 
| Chris@117 | 68 { | 
| Chris@119 | 69     if (m_detail) return; | 
| Chris@117 | 70     m_detail = new ChangesetDetailItem(m_changeset); | 
| Chris@117 | 71     m_detail->setZValue(zValue() + 1); | 
| Chris@117 | 72     scene()->addItem(m_detail); | 
| Chris@117 | 73     int w = 100; | 
| Chris@117 | 74     if (m_wide) w = 180; | 
| Chris@508 | 75     if (isMerge() || isClosingCommit()) w = 60; | 
| Chris@124 | 76     int h = 80; | 
| Chris@124 | 77 //    m_detail->moveBy(x() - (m_detail->boundingRect().width() - 50) / 2, | 
| Chris@124 | 78 //                     y() + 60); | 
| Chris@124 | 79     m_detail->moveBy(x() + (w + 50) / 2 + 10 + 0.5, | 
| Chris@430 | 80                      y() - (m_detail->boundingRect().height() - h) / 3 + 0.5); | 
| Chris@119 | 81     emit detailShown(); | 
| Chris@119 | 82 } | 
| Chris@119 | 83 | 
| Chris@119 | 84 void | 
| Chris@119 | 85 ChangesetItem::hideDetail() | 
| Chris@119 | 86 { | 
| Chris@124 | 87     if (!m_detail) return; | 
| Chris@124 | 88     scene()->removeItem(m_detail); | 
| Chris@119 | 89     delete m_detail; | 
| Chris@119 | 90     m_detail = 0; | 
| Chris@119 | 91     emit detailHidden(); | 
| Chris@119 | 92 } | 
| Chris@119 | 93 | 
| Chris@119 | 94 void | 
| Chris@119 | 95 ChangesetItem::mousePressEvent(QGraphicsSceneMouseEvent *e) | 
| Chris@119 | 96 { | 
| Chris@119 | 97     DEBUG << "ChangesetItem::mousePressEvent" << endl; | 
| Chris@132 | 98     if (e->button() == Qt::LeftButton) { | 
| Chris@132 | 99         if (m_detail) { | 
| Chris@132 | 100             hideDetail(); | 
| Chris@132 | 101         } else { | 
| Chris@132 | 102             showDetail(); | 
| Chris@132 | 103         } | 
| Chris@119 | 104     } | 
| Chris@117 | 105 } | 
| Chris@117 | 106 | 
| Chris@117 | 107 void | 
| Chris@474 | 108 ChangesetItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *) | 
| Chris@140 | 109 { | 
| Chris@474 | 110     if (m_detail) { | 
| Chris@474 | 111         hideDetail(); | 
| Chris@474 | 112     } | 
| Chris@474 | 113 | 
| Chris@153 | 114     m_parentDiffActions.clear(); | 
| Chris@288 | 115     m_summaryActions.clear(); | 
| Chris@153 | 116 | 
| Chris@140 | 117     QMenu *menu = new QMenu; | 
| Chris@165 | 118     QLabel *label = new QLabel(tr("<qt><b> Revision: </b>%1</qt>") | 
| Chris@165 | 119                                .arg(Changeset::hashOf(m_changeset->id()))); | 
| Chris@141 | 120     QWidgetAction *wa = new QWidgetAction(menu); | 
| Chris@140 | 121     wa->setDefaultWidget(label); | 
| Chris@140 | 122     menu->addAction(wa); | 
| Chris@140 | 123     menu->addSeparator(); | 
| Chris@141 | 124 | 
| Chris@153 | 125     QAction *copyId = menu->addAction(tr("Copy identifier to clipboard")); | 
| Chris@153 | 126     connect(copyId, SIGNAL(triggered()), this, SLOT(copyIdActivated())); | 
| Chris@141 | 127 | 
| Chris@289 | 128     QAction *stat = menu->addAction(tr("Summarise changes")); | 
| Chris@289 | 129     connect(stat, SIGNAL(triggered()), this, SLOT(showSummaryActivated())); | 
| Chris@289 | 130 | 
| Chris@289 | 131     menu->addSeparator(); | 
| Chris@289 | 132 | 
| Chris@281 | 133     QStringList parents = m_changeset->parents(); | 
| Chris@153 | 134 | 
| Chris@288 | 135     QString leftId, rightId; | 
| Chris@288 | 136     bool havePositions = false; | 
| Chris@288 | 137 | 
| Chris@281 | 138     if (parents.size() > 1) { | 
| Chris@281 | 139         ChangesetScene *cs = dynamic_cast<ChangesetScene *>(scene()); | 
| Chris@281 | 140         if (cs && parents.size() == 2) { | 
| Chris@281 | 141             ChangesetItem *i0 = cs->getItemById(parents[0]); | 
| Chris@281 | 142             ChangesetItem *i1 = cs->getItemById(parents[1]); | 
| Chris@281 | 143             if (i0 && i1) { | 
| Chris@281 | 144                 if (i0->x() < i1->x()) { | 
| Chris@281 | 145                     leftId = parents[0]; | 
| Chris@281 | 146                     rightId = parents[1]; | 
| Chris@281 | 147                 } else { | 
| Chris@281 | 148                     leftId = parents[1]; | 
| Chris@281 | 149                     rightId = parents[0]; | 
| Chris@281 | 150                 } | 
| Chris@281 | 151                 havePositions = true; | 
| Chris@281 | 152             } | 
| Chris@281 | 153         } | 
| Chris@288 | 154     } | 
| Chris@281 | 155 | 
| Chris@288 | 156     if (parents.size() > 1) { | 
| Chris@288 | 157         if (havePositions) { | 
| Chris@281 | 158 | 
| Chris@288 | 159             QAction *diff = menu->addAction(tr("Diff to left parent")); | 
| Chris@288 | 160             connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated())); | 
| Chris@288 | 161             m_parentDiffActions[diff] = leftId; | 
| Chris@281 | 162 | 
| Chris@288 | 163             diff = menu->addAction(tr("Diff to right parent")); | 
| Chris@288 | 164             connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated())); | 
| Chris@288 | 165             m_parentDiffActions[diff] = rightId; | 
| Chris@281 | 166 | 
| Chris@281 | 167         } else { | 
| Chris@281 | 168 | 
| Chris@281 | 169             foreach (QString parentId, parents) { | 
| Chris@288 | 170                 QString text = tr("Diff to parent %1").arg(Changeset::hashOf(parentId)); | 
| Chris@288 | 171                 QAction *diff = menu->addAction(text); | 
| Chris@288 | 172                 connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated())); | 
| Chris@288 | 173                 m_parentDiffActions[diff] = parentId; | 
| Chris@281 | 174             } | 
| Chris@153 | 175         } | 
| Chris@153 | 176 | 
| Chris@153 | 177     } else { | 
| Chris@153 | 178 | 
| Chris@288 | 179         QAction *diff = menu->addAction(tr("Diff to parent")); | 
| Chris@288 | 180         connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated())); | 
| Chris@153 | 181     } | 
| Chris@153 | 182 | 
| Chris@153 | 183     QAction *diffCurrent = menu->addAction(tr("Diff to current working folder")); | 
| Chris@141 | 184     connect(diffCurrent, SIGNAL(triggered()), this, SLOT(diffToCurrentActivated())); | 
| Chris@141 | 185 | 
| Chris@140 | 186     menu->addSeparator(); | 
| Chris@141 | 187 | 
| Chris@153 | 188     QAction *update = menu->addAction(tr("Update to this revision")); | 
| Chris@153 | 189     connect(update, SIGNAL(triggered()), this, SLOT(updateActivated())); | 
| Chris@153 | 190 | 
| Chris@140 | 191     QAction *merge = menu->addAction(tr("Merge from here to current")); | 
| Chris@141 | 192     connect(merge, SIGNAL(triggered()), this, SLOT(mergeActivated())); | 
| Chris@153 | 193 | 
| Chris@153 | 194     menu->addSeparator(); | 
| Chris@153 | 195 | 
| Chris@278 | 196     QAction *branch = menu->addAction(tr("Start new branch...")); | 
| Chris@278 | 197     branch->setEnabled(m_current); | 
| Chris@278 | 198     connect(branch, SIGNAL(triggered()), this, SLOT(newBranchActivated())); | 
| Chris@278 | 199 | 
| Chris@514 | 200     QAction *closebranch = menu->addAction(tr("Close branch...")); | 
| Chris@514 | 201     closebranch->setEnabled(m_current); | 
| Chris@514 | 202     connect(closebranch, SIGNAL(triggered()), this, SLOT(closeBranchActivated())); | 
| Chris@514 | 203 | 
| Chris@153 | 204     QAction *tag = menu->addAction(tr("Add tag...")); | 
| Chris@141 | 205     connect(tag, SIGNAL(triggered()), this, SLOT(tagActivated())); | 
| Chris@141 | 206 | 
| Chris@474 | 207     ungrabMouse(); | 
| Chris@474 | 208 | 
| Chris@148 | 209     menu->exec(QCursor::pos()); | 
| Chris@140 | 210 } | 
| Chris@140 | 211 | 
| Chris@153 | 212 void | 
| Chris@153 | 213 ChangesetItem::copyIdActivated() | 
| Chris@153 | 214 { | 
| Chris@153 | 215     QClipboard *clipboard = QApplication::clipboard(); | 
| Chris@153 | 216     clipboard->setText(Changeset::hashOf(m_changeset->id())); | 
| Chris@153 | 217 } | 
| Chris@153 | 218 | 
| Chris@153 | 219 void ChangesetItem::diffToParentActivated() | 
| Chris@153 | 220 { | 
| Chris@153 | 221     QAction *a = qobject_cast<QAction *>(sender()); | 
| Chris@153 | 222     QString parentId; | 
| Chris@153 | 223     if (m_parentDiffActions.contains(a)) { | 
| Chris@153 | 224         parentId = m_parentDiffActions[a]; | 
| Chris@153 | 225         DEBUG << "ChangesetItem::diffToParentActivated: specific parent " | 
| Chris@153 | 226               << parentId << " selected" << endl; | 
| Chris@153 | 227     } else { | 
| Chris@153 | 228         parentId = m_changeset->parents()[0]; | 
| Chris@153 | 229         DEBUG << "ChangesetItem::diffToParentActivated: " | 
| Chris@153 | 230               << "no specific parent selected, using first parent " | 
| Chris@153 | 231               << parentId << endl; | 
| Chris@153 | 232     } | 
| Chris@153 | 233 | 
| Chris@153 | 234     emit diffToParent(getId(), parentId); | 
| Chris@153 | 235 } | 
| Chris@153 | 236 | 
| Chris@289 | 237 void ChangesetItem::showSummaryActivated() | 
| Chris@288 | 238 { | 
| Chris@289 | 239     emit showSummary(m_changeset); | 
| Chris@288 | 240 } | 
| Chris@288 | 241 | 
| Chris@141 | 242 void ChangesetItem::updateActivated() { emit updateTo(getId()); } | 
| Chris@141 | 243 void ChangesetItem::diffToCurrentActivated() { emit diffToCurrent(getId()); } | 
| Chris@141 | 244 void ChangesetItem::mergeActivated() { emit mergeFrom(getId()); } | 
| Chris@141 | 245 void ChangesetItem::tagActivated() { emit tag(getId()); } | 
| Chris@278 | 246 void ChangesetItem::newBranchActivated() { emit newBranch(getId()); } | 
| Chris@514 | 247 void ChangesetItem::closeBranchActivated() { emit closeBranch(getId()); } | 
| Chris@141 | 248 | 
| Chris@140 | 249 void | 
| Chris@288 | 250 ChangesetItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *, QWidget *) | 
| Chris@43 | 251 { | 
| Chris@508 | 252     if (isClosingCommit() || isMerge()) { | 
| Chris@508 | 253         paintSimple(paint); | 
| Chris@387 | 254     } else { | 
| Chris@387 | 255         paintNormal(paint); | 
| Chris@387 | 256     } | 
| Chris@387 | 257 } | 
| Chris@387 | 258 | 
| Chris@387 | 259 bool | 
| Chris@387 | 260 ChangesetItem::isMerge() const | 
| Chris@387 | 261 { | 
| Chris@387 | 262     return (m_changeset && m_changeset->parents().size() > 1); | 
| Chris@387 | 263 } | 
| Chris@387 | 264 | 
| Chris@510 | 265 bool | 
| Chris@510 | 266 ChangesetItem::isClosed() const | 
| Chris@510 | 267 { | 
| Chris@510 | 268     return (m_changeset && m_changeset->closed()); | 
| Chris@510 | 269 } | 
| Chris@510 | 270 | 
| Chris@387 | 271 void | 
| Chris@387 | 272 ChangesetItem::paintNormal(QPainter *paint) | 
| Chris@387 | 273 { | 
| Chris@53 | 274     paint->save(); | 
| Chris@53 | 275 | 
| Chris@506 | 276     int alpha = 255; | 
| Chris@510 | 277     if (isClosed()) alpha = 90; | 
| Chris@506 | 278 | 
| Chris@53 | 279     ColourSet *colourSet = ColourSet::instance(); | 
| Chris@53 | 280     QColor branchColour = colourSet->getColourFor(m_changeset->branch()); | 
| Chris@128 | 281     QColor userColour = colourSet->getColourFor(m_changeset->author()); | 
| Chris@53 | 282 | 
| Chris@506 | 283     branchColour.setAlpha(alpha); | 
| Chris@506 | 284     userColour.setAlpha(alpha); | 
| Chris@506 | 285 | 
| Chris@53 | 286     QFont f(m_font); | 
| Chris@53 | 287 | 
| Chris@54 | 288     QTransform t = paint->worldTransform(); | 
| Chris@53 | 289     float scale = std::min(t.m11(), t.m22()); | 
| Chris@53 | 290     if (scale > 1.0) { | 
| Chris@53 | 291 	int ps = int((f.pixelSize() / scale) + 0.5); | 
| Chris@53 | 292 	if (ps < 8) ps = 8; | 
| Chris@53 | 293 	f.setPixelSize(ps); | 
| Chris@53 | 294     } | 
| Chris@54 | 295 | 
| Chris@250 | 296     bool showText = (scale >= 0.2); | 
| Chris@250 | 297     bool showProperLines = (scale >= 0.1); | 
| Chris@250 | 298 | 
| Chris@250 | 299     if (!showProperLines) { | 
| Chris@54 | 300 	paint->setPen(QPen(branchColour, 0)); | 
| Chris@54 | 301     } else { | 
| Chris@54 | 302 	paint->setPen(QPen(branchColour, 2)); | 
| Chris@54 | 303     } | 
| Chris@53 | 304 | 
| Chris@53 | 305     paint->setFont(f); | 
| Chris@53 | 306     QFontMetrics fm(f); | 
| Chris@53 | 307     int fh = fm.height(); | 
| Chris@55 | 308 | 
| Chris@55 | 309     int width = 100; | 
| Chris@55 | 310     if (m_wide) width = 180; | 
| Chris@55 | 311     int x0 = -((width - 50) / 2 - 1); | 
| Chris@55 | 312 | 
| Chris@250 | 313     int textwid = width - 7; | 
| Chris@250 | 314 | 
| Chris@250 | 315     QString comment; | 
| Chris@250 | 316     QStringList lines; | 
| Chris@250 | 317     int lineCount = 3; | 
| Chris@250 | 318 | 
| Chris@250 | 319     if (showText) { | 
| Chris@250 | 320 | 
| Chris@250 | 321         comment = m_changeset->comment().trimmed(); | 
| Chris@250 | 322         comment = comment.replace("\\n", " "); | 
| Chris@250 | 323         comment = comment.replace(QRegExp("^\"\\s*\\**\\s*"), ""); | 
| Chris@250 | 324         comment = comment.replace(QRegExp("\"$"), ""); | 
| Chris@250 | 325         comment = comment.replace("\\\"", "\""); | 
| Chris@250 | 326 | 
| Chris@250 | 327         comment = TextAbbrev::abbreviate(comment, fm, textwid, | 
| Chris@250 | 328                                          TextAbbrev::ElideEnd, "...", 3); | 
| Chris@250 | 329         // abbreviate() changes this (ouch!), restore it | 
| Chris@250 | 330         textwid = width - 5; | 
| Chris@250 | 331 | 
| Chris@250 | 332         lines = comment.split('\n'); | 
| Chris@250 | 333         lineCount = lines.size(); | 
| Chris@250 | 334 | 
| Chris@250 | 335         if (lineCount < 2) lineCount = 2; | 
| Chris@250 | 336     } | 
| Chris@250 | 337 | 
| Chris@250 | 338     int height = (lineCount + 1) * fh + 2; | 
| Chris@56 | 339     QRectF r(x0, 0, width - 3, height); | 
| Chris@250 | 340 | 
| Chris@250 | 341     if (showProperLines) { | 
| Chris@250 | 342 | 
| Chris@393 | 343         if (m_new) { | 
| Chris@396 | 344             paint->setBrush(QColor(255, 255, 220)); | 
| Chris@393 | 345         } else { | 
| Chris@393 | 346             paint->setBrush(Qt::white); | 
| Chris@393 | 347         } | 
| Chris@250 | 348 | 
| Chris@250 | 349         if (m_current) { | 
| Chris@386 | 350             paint->drawRoundedRect(QRectF(x0 - 4, -4, width + 5, height + 8), | 
| Chris@386 | 351                                    10, 10); | 
| Chris@393 | 352             if (m_new) { | 
| Chris@393 | 353                 paint->save(); | 
| Chris@393 | 354                 paint->setPen(Qt::yellow); | 
| Chris@393 | 355                 paint->setBrush(Qt::NoBrush); | 
| Chris@393 | 356                 paint->drawRoundedRect(QRectF(x0 - 2, -2, width + 1, height + 4), | 
| Chris@393 | 357                                        10, 10); | 
| Chris@393 | 358                 paint->restore(); | 
| Chris@393 | 359             } | 
| Chris@250 | 360         } | 
| Chris@250 | 361     } | 
| Chris@250 | 362 | 
| Chris@250 | 363     if (!showText) { | 
| Chris@386 | 364         paint->drawRoundedRect(r, 7, 7); | 
| Chris@53 | 365 	paint->restore(); | 
| Chris@53 | 366 	return; | 
| Chris@53 | 367     } | 
| Chris@53 | 368 | 
| Chris@386 | 369     paint->save(); | 
| Chris@386 | 370     paint->setPen(Qt::NoPen); | 
| Chris@386 | 371     paint->drawRoundedRect(r, 7, 7); | 
| Chris@386 | 372     paint->setBrush(QBrush(userColour)); | 
| Chris@386 | 373     paint->drawRoundedRect(QRectF(x0 + 0.5, 0.5, width - 4, fh - 0.5), 7, 7); | 
| chris@391 | 374     paint->drawRect(QRectF(x0 + 0.5, fh/2.0, width - 4, fh/2.0)); | 
| Chris@386 | 375     paint->restore(); | 
| Chris@53 | 376 | 
| Chris@53 | 377     paint->setPen(QPen(Qt::white)); | 
| Chris@53 | 378 | 
| Chris@250 | 379     QString person = TextAbbrev::abbreviate(m_changeset->authorName(), | 
| Chris@250 | 380                                             fm, textwid); | 
| Chris@55 | 381     paint->drawText(x0 + 3, fm.ascent(), person); | 
| Chris@53 | 382 | 
| Chris@506 | 383     QColor textColour = Qt::black; | 
| Chris@506 | 384     textColour.setAlpha(alpha); | 
| Chris@506 | 385 | 
| Chris@506 | 386     paint->setPen(QPen(textColour)); | 
| Chris@53 | 387 | 
| Chris@147 | 388     QStringList tags = m_changeset->tags(); | 
| Chris@147 | 389     if (!tags.empty()) { | 
| Chris@147 | 390         QStringList nonTipTags; | 
| Chris@147 | 391         foreach (QString t, tags) { | 
| Chris@147 | 392             // I'm not convinced that showing the tip tag really | 
| Chris@147 | 393             // works; I think perhaps it confuses as much as it | 
| Chris@147 | 394             // illuminates.  But also, our current implementation | 
| Chris@147 | 395             // doesn't interact well with it because it moves -- it's | 
| Chris@147 | 396             // the one thing that can actually (in normal use) change | 
| Chris@147 | 397             // inside an existing changeset record even during an | 
| Chris@147 | 398             // incremental update | 
| Chris@147 | 399             if (t != "tip") nonTipTags.push_back(t); | 
| Chris@147 | 400         } | 
| Chris@147 | 401         if (!nonTipTags.empty()) { | 
| Chris@147 | 402             QString tagText = nonTipTags.join(" ").trimmed(); | 
| Chris@147 | 403             int tw = fm.width(tagText); | 
| Chris@147 | 404             paint->fillRect(QRectF(x0 + width - 8 - tw, 1, tw + 4, fh - 1), | 
| Chris@147 | 405                             QBrush(Qt::yellow)); | 
| Chris@147 | 406             paint->drawText(x0 + width - 6 - tw, fm.ascent(), tagText); | 
| Chris@147 | 407         } | 
| Chris@128 | 408     } | 
| Chris@128 | 409 | 
| Chris@519 | 410     if (!m_bookmarks.empty()) { | 
| Chris@519 | 411         QString bmText = m_bookmarks.join(" ").trimmed(); | 
| Chris@519 | 412         int bw = fm.width(bmText); | 
| Chris@519 | 413         paint->fillRect(QRectF(x0 + width - 8 - bw, 1, bw + 4, fh - 1), | 
| Chris@519 | 414                         QBrush(Qt::yellow)); paint->drawText(x0 + width - 6 - bw, fm.ascent(), bmText); | 
| Chris@519 | 415     } | 
| Chris@519 | 416 | 
| Chris@386 | 417     paint->setPen(QPen(branchColour, 2)); | 
| Chris@386 | 418     paint->setBrush(Qt::NoBrush); | 
| Chris@386 | 419     paint->drawRoundedRect(r, 7, 7); | 
| Chris@386 | 420 | 
| Chris@74 | 421     if (m_showBranch) { | 
| Chris@56 | 422 	// write branch name | 
| Chris@153 | 423         paint->save(); | 
| Chris@56 | 424 	f.setBold(true); | 
| Chris@56 | 425 	paint->setFont(f); | 
| Chris@153 | 426 	paint->setPen(QPen(branchColour)); | 
| Chris@56 | 427 	QString branch = m_changeset->branch(); | 
| Chris@74 | 428         if (branch == "") branch = "default"; | 
| Chris@56 | 429 	int wid = width - 3; | 
| Chris@56 | 430 	branch = TextAbbrev::abbreviate(branch, QFontMetrics(f), wid); | 
| Chris@56 | 431 	paint->drawText(x0, -fh + fm.ascent() - 4, branch); | 
| Chris@56 | 432 	f.setBold(false); | 
| Chris@153 | 433         paint->restore(); | 
| Chris@56 | 434     } | 
| Chris@56 | 435 | 
| chris@392 | 436     if (m_current && showProperLines) { | 
| chris@392 | 437         paint->setRenderHint(QPainter::SmoothPixmapTransform, true); | 
| chris@392 | 438         int starSize = fh * 1.5; | 
| chris@392 | 439         paint->drawImage(QRectF(x0 + width - starSize, | 
| chris@392 | 440                                 -fh, starSize, starSize), | 
| chris@392 | 441                          *m_star); | 
| chris@392 | 442     } | 
| chris@392 | 443 | 
| Chris@53 | 444     paint->setFont(f); | 
| Chris@53 | 445 | 
| Chris@53 | 446     for (int i = 0; i < lines.size(); ++i) { | 
| Chris@55 | 447 	paint->drawText(x0 + 3, i * fh + fh + fm.ascent(), lines[i].trimmed()); | 
| Chris@53 | 448     } | 
| Chris@53 | 449 | 
| Chris@53 | 450     paint->restore(); | 
| Chris@43 | 451 } | 
| Chris@387 | 452 | 
| Chris@387 | 453 void | 
| Chris@508 | 454 ChangesetItem::paintSimple(QPainter *paint) | 
| Chris@387 | 455 { | 
| Chris@387 | 456     paint->save(); | 
| Chris@506 | 457 | 
| Chris@506 | 458     int alpha = 255; | 
| Chris@510 | 459     if (isClosed()) alpha = 90; | 
| Chris@387 | 460 | 
| Chris@387 | 461     ColourSet *colourSet = ColourSet::instance(); | 
| Chris@387 | 462     QColor branchColour = colourSet->getColourFor(m_changeset->branch()); | 
| Chris@387 | 463     QColor userColour = colourSet->getColourFor(m_changeset->author()); | 
| Chris@387 | 464 | 
| Chris@506 | 465     branchColour.setAlpha(alpha); | 
| Chris@506 | 466     userColour.setAlpha(alpha); | 
| Chris@506 | 467 | 
| Chris@387 | 468     QFont f(m_font); | 
| Chris@387 | 469 | 
| Chris@387 | 470     QTransform t = paint->worldTransform(); | 
| Chris@387 | 471     float scale = std::min(t.m11(), t.m22()); | 
| Chris@387 | 472     if (scale > 1.0) { | 
| Chris@387 | 473 	int ps = int((f.pixelSize() / scale) + 0.5); | 
| Chris@387 | 474 	if (ps < 8) ps = 8; | 
| Chris@387 | 475 	f.setPixelSize(ps); | 
| Chris@387 | 476     } | 
| Chris@387 | 477 | 
| Chris@387 | 478     bool showText = (scale >= 0.2); | 
| Chris@387 | 479     bool showProperLines = (scale >= 0.1); | 
| Chris@387 | 480 | 
| Chris@387 | 481     if (!showProperLines) { | 
| Chris@387 | 482 	paint->setPen(QPen(branchColour, 0)); | 
| Chris@387 | 483     } else { | 
| Chris@387 | 484 	paint->setPen(QPen(branchColour, 2)); | 
| Chris@387 | 485     } | 
| Chris@387 | 486 | 
| Chris@387 | 487     paint->setFont(f); | 
| Chris@387 | 488     QFontMetrics fm(f); | 
| Chris@387 | 489     int fh = fm.height(); | 
| Chris@387 | 490     int size = fh * 2; | 
| Chris@387 | 491     int x0 = -size/2 + 25; | 
| Chris@387 | 492 | 
| Chris@393 | 493     if (m_new) { | 
| Chris@396 | 494         paint->setBrush(QColor(255, 255, 220)); | 
| Chris@393 | 495     } else { | 
| Chris@393 | 496         paint->setBrush(Qt::white); | 
| Chris@393 | 497     } | 
| Chris@390 | 498 | 
| Chris@390 | 499     if (showProperLines) { | 
| Chris@390 | 500 | 
| Chris@390 | 501         if (m_current) { | 
| Chris@508 | 502             if (isClosingCommit()) { | 
| Chris@508 | 503                 paint->drawRect(QRectF(x0 - 4, fh - 4, size + 8, size + 8)); | 
| Chris@508 | 504             } else { | 
| Chris@508 | 505                 paint->drawEllipse(QRectF(x0 - 4, fh - 4, size + 8, size + 8)); | 
| Chris@508 | 506             } | 
| Chris@508 | 507 | 
| Chris@393 | 508             if (m_new) { | 
| Chris@393 | 509                 paint->save(); | 
| Chris@393 | 510                 paint->setPen(Qt::yellow); | 
| Chris@393 | 511                 paint->setBrush(Qt::NoBrush); | 
| Chris@508 | 512                 if (isClosingCommit()) { | 
| Chris@508 | 513                     paint->drawRect(QRectF(x0 - 2, fh - 2, size + 4, size + 4)); | 
| Chris@508 | 514                 } else { | 
| Chris@508 | 515                     paint->drawEllipse(QRectF(x0 - 2, fh - 2, size + 4, size + 4)); | 
| Chris@508 | 516                 } | 
| Chris@393 | 517                 paint->restore(); | 
| Chris@393 | 518             } | 
| Chris@390 | 519         } | 
| Chris@390 | 520     } | 
| Chris@390 | 521 | 
| Chris@508 | 522     if (isClosingCommit()) { | 
| Chris@508 | 523         paint->drawRect(QRectF(x0, fh, size, size)); | 
| Chris@508 | 524     } else { | 
| Chris@508 | 525         paint->drawEllipse(QRectF(x0, fh, size, size)); | 
| Chris@508 | 526     } | 
| Chris@387 | 527 | 
| Chris@387 | 528     if (m_showBranch) { | 
| Chris@387 | 529 	// write branch name | 
| Chris@387 | 530         paint->save(); | 
| Chris@387 | 531 	f.setBold(true); | 
| Chris@387 | 532 	paint->setFont(f); | 
| Chris@387 | 533 	paint->setPen(QPen(branchColour)); | 
| Chris@387 | 534 	QString branch = m_changeset->branch(); | 
| Chris@387 | 535         if (branch == "") branch = "default"; | 
| Chris@387 | 536 	int wid = size * 3; | 
| Chris@387 | 537 	branch = TextAbbrev::abbreviate(branch, QFontMetrics(f), wid); | 
| Chris@387 | 538 	paint->drawText(-wid/2 + 25, fm.ascent() - 4, branch); | 
| Chris@387 | 539 	f.setBold(false); | 
| Chris@387 | 540         paint->restore(); | 
| Chris@387 | 541     } | 
| Chris@387 | 542 | 
| Chris@390 | 543     if (m_current && showProperLines) { | 
| Chris@390 | 544         paint->setRenderHint(QPainter::SmoothPixmapTransform, true); | 
| Chris@390 | 545         int starSize = fh * 1.5; | 
| Chris@390 | 546         paint->drawImage(QRectF(x0 + size - starSize/2, | 
| Chris@390 | 547                                 0, starSize, starSize), | 
| Chris@390 | 548                          *m_star); | 
| Chris@390 | 549     } | 
| Chris@390 | 550 | 
| Chris@387 | 551     paint->restore(); | 
| Chris@387 | 552 } | 
| Chris@387 | 553 |