annotate changesetitem.cpp @ 363:f89e50d748ed feature_93

Enable Push button whenever the repo is non-empty, even when there is no remote location -- ask for remote location when it is pressed. Also change "Change Remote..." to "Set Remote..." to be consistent with this new usage
author Chris Cannam
date Thu, 17 Mar 2011 17:48:18 +0000
parents 5b4aa1c24407
children
rev   line source
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@53 36 ChangesetItem::ChangesetItem(Changeset *cs) :
Chris@117 37 m_changeset(cs), m_detail(0),
Chris@133 38 m_showBranch(false), m_column(0), m_row(0), m_wide(false),
Chris@133 39 m_current(false), m_new(false)
Chris@53 40 {
Chris@53 41 m_font = QFont();
Chris@53 42 m_font.setPixelSize(11);
Chris@53 43 m_font.setBold(false);
Chris@53 44 m_font.setItalic(false);
Chris@168 45 setCursor(Qt::ArrowCursor);
Chris@53 46 }
Chris@53 47
Chris@141 48 QString
Chris@141 49 ChangesetItem::getId()
Chris@141 50 {
Chris@141 51 return m_changeset->id();
Chris@141 52 }
Chris@141 53
Chris@43 54 QRectF
Chris@43 55 ChangesetItem::boundingRect() const
Chris@43 56 {
Chris@55 57 int w = 100;
Chris@55 58 if (m_wide) w = 180;
Chris@250 59 return QRectF(-((w-50)/2 - 1), -30, w - 3, 90);
Chris@43 60 }
Chris@43 61
Chris@43 62 void
Chris@119 63 ChangesetItem::showDetail()
Chris@117 64 {
Chris@119 65 if (m_detail) return;
Chris@117 66 m_detail = new ChangesetDetailItem(m_changeset);
Chris@117 67 m_detail->setZValue(zValue() + 1);
Chris@117 68 scene()->addItem(m_detail);
Chris@117 69 int w = 100;
Chris@117 70 if (m_wide) w = 180;
Chris@124 71 int h = 80;
Chris@124 72 // m_detail->moveBy(x() - (m_detail->boundingRect().width() - 50) / 2,
Chris@124 73 // y() + 60);
Chris@124 74 m_detail->moveBy(x() + (w + 50) / 2 + 10 + 0.5,
Chris@124 75 y() - (m_detail->boundingRect().height() - h) / 2 + 0.5);
Chris@119 76 emit detailShown();
Chris@119 77 }
Chris@119 78
Chris@119 79 void
Chris@119 80 ChangesetItem::hideDetail()
Chris@119 81 {
Chris@124 82 if (!m_detail) return;
Chris@124 83 scene()->removeItem(m_detail);
Chris@119 84 delete m_detail;
Chris@119 85 m_detail = 0;
Chris@119 86 emit detailHidden();
Chris@119 87 }
Chris@119 88
Chris@119 89 void
Chris@119 90 ChangesetItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
Chris@119 91 {
Chris@119 92 DEBUG << "ChangesetItem::mousePressEvent" << endl;
Chris@132 93 if (e->button() == Qt::LeftButton) {
Chris@132 94 if (m_detail) {
Chris@132 95 hideDetail();
Chris@132 96 } else {
Chris@132 97 showDetail();
Chris@132 98 }
Chris@140 99 } else if (e->button() == Qt::RightButton) {
Chris@141 100 if (m_detail) {
Chris@141 101 hideDetail();
Chris@141 102 }
Chris@140 103 activateMenu();
Chris@119 104 }
Chris@117 105 }
Chris@117 106
Chris@117 107 void
Chris@140 108 ChangesetItem::activateMenu()
Chris@140 109 {
Chris@153 110 m_parentDiffActions.clear();
Chris@288 111 m_summaryActions.clear();
Chris@153 112
Chris@140 113 QMenu *menu = new QMenu;
Chris@165 114 QLabel *label = new QLabel(tr("<qt><b>&nbsp;Revision: </b>%1</qt>")
Chris@165 115 .arg(Changeset::hashOf(m_changeset->id())));
Chris@141 116 QWidgetAction *wa = new QWidgetAction(menu);
Chris@140 117 wa->setDefaultWidget(label);
Chris@140 118 menu->addAction(wa);
Chris@140 119 menu->addSeparator();
Chris@141 120
Chris@153 121 QAction *copyId = menu->addAction(tr("Copy identifier to clipboard"));
Chris@153 122 connect(copyId, SIGNAL(triggered()), this, SLOT(copyIdActivated()));
Chris@141 123
Chris@289 124 QAction *stat = menu->addAction(tr("Summarise changes"));
Chris@289 125 connect(stat, SIGNAL(triggered()), this, SLOT(showSummaryActivated()));
Chris@289 126
Chris@289 127 menu->addSeparator();
Chris@289 128
Chris@281 129 QStringList parents = m_changeset->parents();
Chris@153 130
Chris@288 131 QString leftId, rightId;
Chris@288 132 bool havePositions = false;
Chris@288 133
Chris@281 134 if (parents.size() > 1) {
Chris@281 135 ChangesetScene *cs = dynamic_cast<ChangesetScene *>(scene());
Chris@281 136 if (cs && parents.size() == 2) {
Chris@281 137 ChangesetItem *i0 = cs->getItemById(parents[0]);
Chris@281 138 ChangesetItem *i1 = cs->getItemById(parents[1]);
Chris@281 139 if (i0 && i1) {
Chris@281 140 if (i0->x() < i1->x()) {
Chris@281 141 leftId = parents[0];
Chris@281 142 rightId = parents[1];
Chris@281 143 } else {
Chris@281 144 leftId = parents[1];
Chris@281 145 rightId = parents[0];
Chris@281 146 }
Chris@281 147 havePositions = true;
Chris@281 148 }
Chris@281 149 }
Chris@288 150 }
Chris@281 151
Chris@288 152 if (parents.size() > 1) {
Chris@288 153 if (havePositions) {
Chris@281 154
Chris@288 155 QAction *diff = menu->addAction(tr("Diff to left parent"));
Chris@288 156 connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated()));
Chris@288 157 m_parentDiffActions[diff] = leftId;
Chris@281 158
Chris@288 159 diff = menu->addAction(tr("Diff to right parent"));
Chris@288 160 connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated()));
Chris@288 161 m_parentDiffActions[diff] = rightId;
Chris@281 162
Chris@281 163 } else {
Chris@281 164
Chris@281 165 foreach (QString parentId, parents) {
Chris@288 166 QString text = tr("Diff to parent %1").arg(Changeset::hashOf(parentId));
Chris@288 167 QAction *diff = menu->addAction(text);
Chris@288 168 connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated()));
Chris@288 169 m_parentDiffActions[diff] = parentId;
Chris@281 170 }
Chris@153 171 }
Chris@153 172
Chris@153 173 } else {
Chris@153 174
Chris@288 175 QAction *diff = menu->addAction(tr("Diff to parent"));
Chris@288 176 connect(diff, SIGNAL(triggered()), this, SLOT(diffToParentActivated()));
Chris@153 177 }
Chris@153 178
Chris@153 179 QAction *diffCurrent = menu->addAction(tr("Diff to current working folder"));
Chris@141 180 connect(diffCurrent, SIGNAL(triggered()), this, SLOT(diffToCurrentActivated()));
Chris@141 181
Chris@140 182 menu->addSeparator();
Chris@141 183
Chris@153 184 QAction *update = menu->addAction(tr("Update to this revision"));
Chris@153 185 connect(update, SIGNAL(triggered()), this, SLOT(updateActivated()));
Chris@153 186
Chris@140 187 QAction *merge = menu->addAction(tr("Merge from here to current"));
Chris@141 188 connect(merge, SIGNAL(triggered()), this, SLOT(mergeActivated()));
Chris@153 189
Chris@153 190 menu->addSeparator();
Chris@153 191
Chris@278 192 QAction *branch = menu->addAction(tr("Start new branch..."));
Chris@278 193 branch->setEnabled(m_current);
Chris@278 194 connect(branch, SIGNAL(triggered()), this, SLOT(newBranchActivated()));
Chris@278 195
Chris@153 196 QAction *tag = menu->addAction(tr("Add tag..."));
Chris@141 197 connect(tag, SIGNAL(triggered()), this, SLOT(tagActivated()));
Chris@141 198
Chris@148 199 menu->exec(QCursor::pos());
Chris@148 200
Chris@141 201 ungrabMouse();
Chris@140 202 }
Chris@140 203
Chris@153 204 void
Chris@153 205 ChangesetItem::copyIdActivated()
Chris@153 206 {
Chris@153 207 QClipboard *clipboard = QApplication::clipboard();
Chris@153 208 clipboard->setText(Changeset::hashOf(m_changeset->id()));
Chris@153 209 }
Chris@153 210
Chris@153 211 void ChangesetItem::diffToParentActivated()
Chris@153 212 {
Chris@153 213 QAction *a = qobject_cast<QAction *>(sender());
Chris@153 214 QString parentId;
Chris@153 215 if (m_parentDiffActions.contains(a)) {
Chris@153 216 parentId = m_parentDiffActions[a];
Chris@153 217 DEBUG << "ChangesetItem::diffToParentActivated: specific parent "
Chris@153 218 << parentId << " selected" << endl;
Chris@153 219 } else {
Chris@153 220 parentId = m_changeset->parents()[0];
Chris@153 221 DEBUG << "ChangesetItem::diffToParentActivated: "
Chris@153 222 << "no specific parent selected, using first parent "
Chris@153 223 << parentId << endl;
Chris@153 224 }
Chris@153 225
Chris@153 226 emit diffToParent(getId(), parentId);
Chris@153 227 }
Chris@153 228
Chris@289 229 void ChangesetItem::showSummaryActivated()
Chris@288 230 {
Chris@289 231 emit showSummary(m_changeset);
Chris@288 232 }
Chris@288 233
Chris@141 234 void ChangesetItem::updateActivated() { emit updateTo(getId()); }
Chris@141 235 void ChangesetItem::diffToCurrentActivated() { emit diffToCurrent(getId()); }
Chris@141 236 void ChangesetItem::mergeActivated() { emit mergeFrom(getId()); }
Chris@141 237 void ChangesetItem::tagActivated() { emit tag(getId()); }
Chris@278 238 void ChangesetItem::newBranchActivated() { emit newBranch(getId()); }
Chris@141 239
Chris@140 240 void
Chris@288 241 ChangesetItem::paint(QPainter *paint, const QStyleOptionGraphicsItem *, QWidget *)
Chris@43 242 {
Chris@53 243 paint->save();
Chris@53 244
Chris@53 245 ColourSet *colourSet = ColourSet::instance();
Chris@53 246 QColor branchColour = colourSet->getColourFor(m_changeset->branch());
Chris@128 247 QColor userColour = colourSet->getColourFor(m_changeset->author());
Chris@53 248
Chris@53 249 QFont f(m_font);
Chris@53 250
Chris@54 251 QTransform t = paint->worldTransform();
Chris@53 252 float scale = std::min(t.m11(), t.m22());
Chris@53 253 if (scale > 1.0) {
Chris@53 254 int ps = int((f.pixelSize() / scale) + 0.5);
Chris@53 255 if (ps < 8) ps = 8;
Chris@53 256 f.setPixelSize(ps);
Chris@53 257 }
Chris@54 258
Chris@250 259 bool showText = (scale >= 0.2);
Chris@250 260 bool showProperLines = (scale >= 0.1);
Chris@250 261
Chris@250 262 if (!showProperLines) {
Chris@54 263 paint->setPen(QPen(branchColour, 0));
Chris@54 264 } else {
Chris@54 265 paint->setPen(QPen(branchColour, 2));
Chris@54 266 }
Chris@53 267
Chris@53 268 paint->setFont(f);
Chris@53 269 QFontMetrics fm(f);
Chris@53 270 int fh = fm.height();
Chris@55 271
Chris@55 272 int width = 100;
Chris@55 273 if (m_wide) width = 180;
Chris@55 274 int x0 = -((width - 50) / 2 - 1);
Chris@55 275
Chris@250 276 int textwid = width - 7;
Chris@250 277
Chris@250 278 QString comment;
Chris@250 279 QStringList lines;
Chris@250 280 int lineCount = 3;
Chris@250 281
Chris@250 282 if (showText) {
Chris@250 283
Chris@250 284 comment = m_changeset->comment().trimmed();
Chris@250 285 comment = comment.replace("\\n", " ");
Chris@250 286 comment = comment.replace(QRegExp("^\"\\s*\\**\\s*"), "");
Chris@250 287 comment = comment.replace(QRegExp("\"$"), "");
Chris@250 288 comment = comment.replace("\\\"", "\"");
Chris@250 289
Chris@250 290 comment = TextAbbrev::abbreviate(comment, fm, textwid,
Chris@250 291 TextAbbrev::ElideEnd, "...", 3);
Chris@250 292 // abbreviate() changes this (ouch!), restore it
Chris@250 293 textwid = width - 5;
Chris@250 294
Chris@250 295 lines = comment.split('\n');
Chris@250 296 lineCount = lines.size();
Chris@250 297
Chris@250 298 if (lineCount < 2) lineCount = 2;
Chris@250 299 }
Chris@250 300
Chris@250 301 int height = (lineCount + 1) * fh + 2;
Chris@56 302 QRectF r(x0, 0, width - 3, height);
Chris@250 303
Chris@250 304 if (showProperLines) {
Chris@250 305
Chris@250 306 paint->setBrush(Qt::white);
Chris@250 307
Chris@250 308 if (m_current) {
Chris@250 309 paint->drawRect(QRectF(x0 - 4, -4, width + 5, height + 8));
Chris@250 310 }
Chris@250 311
Chris@250 312 if (m_new) {
Chris@250 313 paint->save();
Chris@250 314 paint->setPen(Qt::yellow);
Chris@250 315 paint->setBrush(Qt::NoBrush);
Chris@250 316 paint->drawRect(QRectF(x0 - 2, -2, width + 1, height + 4));
Chris@250 317 paint->restore();
Chris@250 318 }
Chris@250 319 }
Chris@250 320
Chris@53 321 paint->drawRect(r);
Chris@53 322
Chris@250 323 if (!showText) {
Chris@53 324 paint->restore();
Chris@53 325 return;
Chris@53 326 }
Chris@53 327
Chris@55 328 paint->fillRect(QRectF(x0 + 0.5, 0.5, width - 4, fh - 0.5),
Chris@55 329 QBrush(userColour));
Chris@53 330
Chris@53 331 paint->setPen(QPen(Qt::white));
Chris@53 332
Chris@250 333 QString person = TextAbbrev::abbreviate(m_changeset->authorName(),
Chris@250 334 fm, textwid);
Chris@55 335 paint->drawText(x0 + 3, fm.ascent(), person);
Chris@53 336
Chris@53 337 paint->setPen(QPen(Qt::black));
Chris@53 338
Chris@147 339 QStringList tags = m_changeset->tags();
Chris@147 340 if (!tags.empty()) {
Chris@147 341 QStringList nonTipTags;
Chris@147 342 foreach (QString t, tags) {
Chris@147 343 // I'm not convinced that showing the tip tag really
Chris@147 344 // works; I think perhaps it confuses as much as it
Chris@147 345 // illuminates. But also, our current implementation
Chris@147 346 // doesn't interact well with it because it moves -- it's
Chris@147 347 // the one thing that can actually (in normal use) change
Chris@147 348 // inside an existing changeset record even during an
Chris@147 349 // incremental update
Chris@147 350 if (t != "tip") nonTipTags.push_back(t);
Chris@147 351 }
Chris@147 352 if (!nonTipTags.empty()) {
Chris@147 353 QString tagText = nonTipTags.join(" ").trimmed();
Chris@147 354 int tw = fm.width(tagText);
Chris@147 355 paint->fillRect(QRectF(x0 + width - 8 - tw, 1, tw + 4, fh - 1),
Chris@147 356 QBrush(Qt::yellow));
Chris@147 357 paint->drawText(x0 + width - 6 - tw, fm.ascent(), tagText);
Chris@147 358 }
Chris@128 359 }
Chris@128 360
Chris@74 361 if (m_showBranch) {
Chris@56 362 // write branch name
Chris@153 363 paint->save();
Chris@56 364 f.setBold(true);
Chris@56 365 paint->setFont(f);
Chris@153 366 paint->setPen(QPen(branchColour));
Chris@56 367 QString branch = m_changeset->branch();
Chris@74 368 if (branch == "") branch = "default";
Chris@56 369 int wid = width - 3;
Chris@56 370 branch = TextAbbrev::abbreviate(branch, QFontMetrics(f), wid);
Chris@56 371 paint->drawText(x0, -fh + fm.ascent() - 4, branch);
Chris@56 372 f.setBold(false);
Chris@153 373 paint->restore();
Chris@56 374 }
Chris@56 375
Chris@53 376 paint->setFont(f);
Chris@53 377
Chris@53 378 for (int i = 0; i < lines.size(); ++i) {
Chris@55 379 paint->drawText(x0 + 3, i * fh + fh + fm.ascent(), lines[i].trimmed());
Chris@53 380 }
Chris@53 381
Chris@53 382 paint->restore();
Chris@43 383 }