annotate src/historywidget.cpp @ 600:641ccce7c771

Avoid messing with font size when zooming, let it zoom naturally; don't delete detail item when removing it, just let it wait to be shown again (and do delete it when deleting main item)
author Chris Cannam
date Fri, 11 May 2012 17:44:33 +0100
parents 88ec0ae91dcc
children 6f90bb52eee6
rev   line source
Chris@116 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@116 2
Chris@116 3 /*
Chris@116 4 EasyMercurial
Chris@116 5
Chris@116 6 Based on HgExplorer by Jari Korhonen
Chris@116 7 Copyright (c) 2010 Jari Korhonen
Chris@560 8 Copyright (c) 2012 Chris Cannam
Chris@560 9 Copyright (c) 2012 Queen Mary, University of London
Chris@116 10
Chris@116 11 This program is free software; you can redistribute it and/or
Chris@116 12 modify it under the terms of the GNU General Public License as
Chris@116 13 published by the Free Software Foundation; either version 2 of the
Chris@116 14 License, or (at your option) any later version. See the file
Chris@116 15 COPYING included with this distribution for more information.
Chris@116 16 */
Chris@116 17
Chris@116 18 #include "historywidget.h"
Chris@116 19
Chris@119 20 #include "changesetscene.h"
Chris@397 21 #include "changesetview.h"
Chris@116 22 #include "panner.h"
Chris@116 23 #include "grapher.h"
Chris@116 24 #include "debug.h"
Chris@129 25 #include "uncommitteditem.h"
Chris@558 26 #include "findwidget.h"
Chris@116 27
Chris@116 28 #include <iostream>
Chris@116 29
Chris@116 30 #include <QGridLayout>
Chris@513 31 #include <QSettings>
Chris@116 32
Chris@154 33 HistoryWidget::HistoryWidget() :
Chris@154 34 m_showUncommitted(false),
Chris@154 35 m_refreshNeeded(false)
Chris@116 36 {
Chris@397 37 m_panned = new ChangesetView;
Chris@116 38 m_panner = new Panner;
Chris@116 39
Chris@168 40 m_panned->setDragMode(QGraphicsView::ScrollHandDrag);
Chris@250 41 m_panned->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
Chris@397 42 m_panned->setCacheMode(QGraphicsView::CacheNone);
Chris@168 43
Chris@513 44 int row = 0;
Chris@513 45
Chris@116 46 QGridLayout *layout = new QGridLayout;
Chris@513 47 layout->setMargin(10);
Chris@513 48 layout->addWidget(m_panned, row, 0);
Chris@513 49 layout->addWidget(m_panner, row, 1);
Chris@116 50 m_panner->setMaximumWidth(80);
Chris@116 51 m_panner->connectToPanned(m_panned);
Chris@116 52
Chris@513 53 layout->setRowStretch(row, 20);
Chris@513 54
Chris@513 55 QSettings settings;
Chris@513 56 settings.beginGroup("Presentation");
Chris@513 57 bool showClosed = (settings.value("showclosedbranches", false).toBool());
Chris@513 58
Chris@558 59 QWidget *opts = new QWidget;
Chris@558 60 QGridLayout *optLayout = new QGridLayout(opts);
Chris@558 61 optLayout->setMargin(0);
Chris@558 62 layout->addWidget(opts, ++row, 0, 1, 2);
Chris@558 63
Chris@558 64 m_findWidget = new FindWidget(this);
Chris@558 65 optLayout->addWidget(m_findWidget, 0, 0, Qt::AlignLeft);
Chris@558 66 connect(m_findWidget, SIGNAL(findTextChanged(QString)),
Chris@558 67 this, SLOT(setSearchText(QString)));
Chris@558 68
Chris@513 69 m_showClosedBranches = new QCheckBox(tr("Show closed branches"), this);
Chris@513 70 m_showClosedBranches->setChecked(showClosed);
Chris@513 71 connect(m_showClosedBranches, SIGNAL(toggled(bool)),
Chris@513 72 this, SLOT(showClosedChanged(bool)));
Chris@558 73 optLayout->addWidget(m_showClosedBranches, 0, 1, Qt::AlignRight);
Chris@513 74 m_showClosedBranches->hide();
Chris@513 75
Chris@116 76 setLayout(layout);
Chris@116 77 }
Chris@116 78
Chris@116 79 HistoryWidget::~HistoryWidget()
Chris@116 80 {
Chris@116 81 clearChangesets();
Chris@145 82 }
Chris@145 83
Chris@145 84 QGraphicsScene *HistoryWidget::scene()
Chris@145 85 {
Chris@145 86 return m_panned->scene();
Chris@116 87 }
Chris@116 88
Chris@116 89 void HistoryWidget::clearChangesets()
Chris@116 90 {
Chris@116 91 foreach (Changeset *cs, m_changesets) delete cs;
Chris@116 92 m_changesets.clear();
Chris@116 93 }
Chris@128 94
Chris@153 95 void HistoryWidget::setCurrent(QStringList ids, QString branch,
Chris@153 96 bool showUncommitted)
Chris@128 97 {
Chris@153 98 if (m_currentIds == ids &&
Chris@153 99 m_currentBranch == branch &&
Chris@153 100 m_showUncommitted == showUncommitted) return;
Chris@145 101
Chris@145 102 DEBUG << "HistoryWidget::setCurrent: " << ids.size() << " ids, "
Chris@145 103 << "showUncommitted: " << showUncommitted << endl;
Chris@145 104
Chris@133 105 m_currentIds.clear();
Chris@153 106 m_currentBranch = branch;
Chris@145 107 m_showUncommitted = showUncommitted;
Chris@145 108
Chris@145 109 if (ids.empty()) return;
Chris@145 110
Chris@133 111 foreach (QString id, ids) {
Chris@133 112 m_currentIds.push_back(id);
Chris@133 113 }
Chris@128 114
Chris@154 115 m_refreshNeeded = true;
Chris@128 116 }
Chris@505 117
Chris@506 118 void HistoryWidget::setClosedHeadIds(QSet<QString> closed)
Chris@506 119 {
Chris@506 120 if (closed == m_closedIds) return;
Chris@506 121 m_closedIds = closed;
Chris@513 122 m_showClosedBranches->setVisible(!closed.empty());
Chris@506 123 m_refreshNeeded = true;
Chris@506 124 }
Chris@506 125
Chris@505 126 void HistoryWidget::setShowUncommitted(bool showUncommitted)
Chris@505 127 {
Chris@505 128 setCurrent(m_currentIds, m_currentBranch, showUncommitted);
Chris@505 129 }
Chris@513 130
Chris@513 131 void HistoryWidget::showClosedChanged(bool show)
Chris@513 132 {
Chris@513 133 QSettings settings;
Chris@513 134 settings.beginGroup("Presentation");
Chris@513 135 settings.setValue("showclosedbranches", show);
Chris@513 136 layoutAll();
Chris@513 137 }
Chris@116 138
Chris@120 139 void HistoryWidget::parseNewLog(QString log)
Chris@120 140 {
Chris@120 141 DEBUG << "HistoryWidget::parseNewLog: log has " << log.length() << " chars" << endl;
Chris@122 142 Changesets csets = Changeset::parseChangesets(log);
Chris@120 143 DEBUG << "HistoryWidget::parseNewLog: log has " << csets.size() << " changesets" << endl;
Chris@133 144 replaceChangesets(csets);
Chris@154 145 m_refreshNeeded = true;
Chris@120 146 }
Chris@120 147
Chris@120 148 void HistoryWidget::parseIncrementalLog(QString log)
Chris@120 149 {
Chris@120 150 DEBUG << "HistoryWidget::parseIncrementalLog: log has " << log.length() << " chars" << endl;
Chris@122 151 Changesets csets = Changeset::parseChangesets(log);
Chris@120 152 DEBUG << "HistoryWidget::parseIncrementalLog: log has " << csets.size() << " changesets" << endl;
Chris@120 153 if (!csets.empty()) {
Chris@133 154 addChangesets(csets);
Chris@120 155 }
Chris@154 156 m_refreshNeeded = true;
Chris@120 157 }
Chris@120 158
Chris@133 159 void HistoryWidget::replaceChangesets(Changesets csets)
Chris@133 160 {
Chris@133 161 QSet<QString> oldIds;
Chris@133 162 foreach (Changeset *cs, m_changesets) {
Chris@133 163 oldIds.insert(cs->id());
Chris@133 164 }
Chris@133 165
Chris@133 166 QSet<QString> newIds;
Chris@133 167 foreach (Changeset *cs, csets) {
Chris@133 168 if (!oldIds.contains(cs->id())) {
Chris@133 169 newIds.insert(cs->id());
Chris@133 170 }
Chris@133 171 }
Chris@133 172
Chris@133 173 if (newIds.size() == csets.size()) {
Chris@133 174 // completely new set, unrelated to the old: don't mark new
Chris@133 175 m_newIds.clear();
Chris@133 176 } else {
Chris@133 177 m_newIds = newIds;
Chris@133 178 }
Chris@133 179
Chris@133 180 clearChangesets();
Chris@133 181 m_changesets = csets;
Chris@133 182 }
Chris@133 183
Chris@133 184 void HistoryWidget::addChangesets(Changesets csets)
Chris@133 185 {
Chris@133 186 m_newIds.clear();
Chris@138 187
Chris@138 188 if (csets.empty()) return;
Chris@138 189
Chris@133 190 foreach (Changeset *cs, csets) {
Chris@133 191 m_newIds.insert(cs->id());
Chris@133 192 }
Chris@133 193
Chris@305 194 DEBUG << "addChangesets: " << csets.size() << " new changesets have ("
Chris@305 195 << m_changesets.size() << " already)" << endl;
Chris@138 196
Chris@133 197 csets << m_changesets;
Chris@133 198 m_changesets = csets;
Chris@133 199 }
Chris@133 200
Chris@154 201 void HistoryWidget::update()
Chris@154 202 {
Chris@154 203 if (m_refreshNeeded) {
Chris@154 204 layoutAll();
Chris@154 205 }
Chris@154 206 }
Chris@154 207
Chris@120 208 void HistoryWidget::layoutAll()
Chris@116 209 {
Chris@154 210 m_refreshNeeded = false;
Chris@154 211
Chris@122 212 setChangesetParents();
Chris@122 213
Chris@119 214 ChangesetScene *scene = new ChangesetScene();
Chris@116 215 ChangesetItem *tipItem = 0;
Chris@116 216
Chris@138 217 QGraphicsScene *oldScene = m_panned->scene();
Chris@138 218
Chris@138 219 m_panned->setScene(0);
Chris@138 220 m_panner->setScene(0);
Chris@138 221
Chris@138 222 delete oldScene;
Chris@138 223
Chris@145 224 QGraphicsItem *toFocus = 0;
Chris@145 225
Chris@120 226 if (!m_changesets.empty()) {
Chris@116 227 Grapher g(scene);
Chris@506 228 g.setClosedHeadIds(m_closedIds);
Chris@116 229 try {
Chris@153 230 g.layout(m_changesets,
Chris@153 231 m_showUncommitted ? m_currentIds : QStringList(),
Chris@153 232 m_currentBranch);
Chris@116 233 } catch (std::string s) {
Chris@116 234 std::cerr << "Internal error: Layout failed: " << s << std::endl;
Chris@116 235 }
Chris@145 236 toFocus = g.getUncommittedItem();
Chris@145 237 if (!toFocus) {
Chris@371 238 if (!m_currentIds.empty()) {
Chris@371 239 toFocus = g.getItemFor(m_currentIds[0]);
Chris@371 240 } else {
Chris@371 241 toFocus = g.getItemFor(m_changesets[0]);
Chris@371 242 }
Chris@145 243 }
Chris@134 244 }
Chris@134 245
Chris@116 246 m_panned->setScene(scene);
Chris@116 247 m_panner->setScene(scene);
Chris@116 248
Chris@133 249 updateNewAndCurrentItems();
Chris@134 250
Chris@145 251 if (toFocus) {
Chris@145 252 toFocus->ensureVisible();
Chris@134 253 }
Chris@141 254
Chris@572 255 if (m_searchText != "") {
Chris@572 256 updateSearchStatus();
Chris@572 257 }
Chris@141 258 connectSceneSignals();
Chris@116 259 }
Chris@116 260
Chris@122 261 void HistoryWidget::setChangesetParents()
Chris@116 262 {
Chris@139 263 for (int i = 0; i < m_changesets.size(); ++i) {
Chris@122 264 Changeset *cs = m_changesets[i];
Chris@123 265 // Need to reset this, as Grapher::layout will recalculate it
Chris@123 266 // and we don't want to end up with twice the children for
Chris@123 267 // each parent...
Chris@123 268 cs->setChildren(QStringList());
Chris@139 269 }
Chris@139 270 for (int i = 0; i+1 < m_changesets.size(); ++i) {
Chris@139 271 Changeset *cs = m_changesets[i];
Chris@116 272 if (cs->parents().empty()) {
Chris@116 273 QStringList list;
Chris@122 274 list.push_back(m_changesets[i+1]->id());
Chris@116 275 cs->setParents(list);
Chris@116 276 }
Chris@116 277 }
Chris@116 278 }
Chris@128 279
Chris@133 280 void HistoryWidget::updateNewAndCurrentItems()
Chris@128 281 {
Chris@128 282 QGraphicsScene *scene = m_panned->scene();
Chris@128 283 if (!scene) return;
Chris@133 284
Chris@128 285 QList<QGraphicsItem *> items = scene->items();
Chris@128 286 foreach (QGraphicsItem *it, items) {
Chris@133 287
Chris@128 288 ChangesetItem *csit = dynamic_cast<ChangesetItem *>(it);
Chris@133 289 if (!csit) continue;
Chris@133 290
Chris@133 291 QString id = csit->getChangeset()->id();
Chris@133 292
Chris@133 293 bool current = m_currentIds.contains(id);
Chris@133 294 if (current) {
Chris@133 295 DEBUG << "id " << id << " is current" << endl;
Chris@133 296 }
Chris@133 297 bool newid = m_newIds.contains(id);
Chris@133 298 if (newid) {
Chris@133 299 DEBUG << "id " << id << " is new" << endl;
Chris@133 300 }
Chris@147 301
Chris@506 302 if (csit->isCurrent() != current ||
Chris@506 303 csit->isNew() != newid) {
Chris@147 304 csit->setCurrent(current);
Chris@147 305 csit->setNew(newid);
Chris@147 306 csit->update();
Chris@147 307 }
Chris@128 308 }
Chris@128 309 }
Chris@141 310
Chris@555 311 void HistoryWidget::setSearchText(QString text)
Chris@555 312 {
Chris@555 313 if (m_searchText == text) return;
Chris@555 314 m_searchText = text;
Chris@555 315 updateSearchStatus();
Chris@555 316 }
Chris@555 317
Chris@555 318 void HistoryWidget::updateSearchStatus()
Chris@555 319 {
Chris@555 320 QGraphicsScene *scene = m_panned->scene();
Chris@555 321 if (!scene) return;
Chris@555 322
Chris@555 323 ChangesetItem *toFocus = 0;
Chris@555 324
Chris@555 325 QList<QGraphicsItem *> items = scene->items();
Chris@555 326 foreach (QGraphicsItem *it, items) {
Chris@555 327
Chris@555 328 ChangesetItem *csit = dynamic_cast<ChangesetItem *>(it);
Chris@555 329 if (!csit) continue;
Chris@555 330
Chris@566 331 bool matched = csit->matchSearchText(m_searchText);
Chris@555 332 if (matched && (!toFocus || csit->row() < toFocus->row())) {
Chris@555 333 toFocus = csit;
Chris@555 334 }
Chris@555 335 csit->update();
Chris@555 336 }
Chris@555 337
Chris@555 338 if (toFocus) {
Chris@555 339 toFocus->ensureVisible();
Chris@555 340 }
Chris@555 341 }
Chris@555 342
Chris@141 343 void HistoryWidget::connectSceneSignals()
Chris@141 344 {
Chris@141 345 ChangesetScene *scene = qobject_cast<ChangesetScene *>(m_panned->scene());
Chris@141 346 if (!scene) return;
Chris@141 347
Chris@141 348 connect(scene, SIGNAL(commit()),
Chris@141 349 this, SIGNAL(commit()));
Chris@141 350
Chris@141 351 connect(scene, SIGNAL(revert()),
Chris@141 352 this, SIGNAL(revert()));
Chris@141 353
Chris@141 354 connect(scene, SIGNAL(diffWorkingFolder()),
Chris@141 355 this, SIGNAL(diffWorkingFolder()));
Chris@141 356
Chris@168 357 connect(scene, SIGNAL(showSummary()),
Chris@168 358 this, SIGNAL(showSummary()));
Chris@168 359
Chris@153 360 connect(scene, SIGNAL(showWork()),
Chris@153 361 this, SIGNAL(showWork()));
Chris@311 362
Chris@311 363 connect(scene, SIGNAL(newBranch()),
Chris@311 364 this, SIGNAL(newBranch()));
Chris@311 365
Chris@311 366 connect(scene, SIGNAL(noBranch()),
Chris@311 367 this, SIGNAL(noBranch()));
Chris@153 368
Chris@141 369 connect(scene, SIGNAL(updateTo(QString)),
Chris@141 370 this, SIGNAL(updateTo(QString)));
Chris@141 371
Chris@141 372 connect(scene, SIGNAL(diffToCurrent(QString)),
Chris@141 373 this, SIGNAL(diffToCurrent(QString)));
Chris@141 374
Chris@148 375 connect(scene, SIGNAL(diffToParent(QString, QString)),
Chris@148 376 this, SIGNAL(diffToParent(QString, QString)));
Chris@141 377
Chris@289 378 connect(scene, SIGNAL(showSummary(Changeset *)),
Chris@289 379 this, SIGNAL(showSummary(Changeset *)));
Chris@288 380
Chris@141 381 connect(scene, SIGNAL(mergeFrom(QString)),
Chris@141 382 this, SIGNAL(mergeFrom(QString)));
Chris@141 383
Chris@278 384 connect(scene, SIGNAL(newBranch(QString)),
Chris@278 385 this, SIGNAL(newBranch(QString)));
Chris@278 386
Chris@514 387 connect(scene, SIGNAL(closeBranch(QString)),
Chris@514 388 this, SIGNAL(closeBranch(QString)));
Chris@514 389
Chris@141 390 connect(scene, SIGNAL(tag(QString)),
Chris@141 391 this, SIGNAL(tag(QString)));
Chris@141 392 }