Chris@116: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@116: Chris@116: /* Chris@116: EasyMercurial Chris@116: Chris@116: Based on HgExplorer by Jari Korhonen Chris@116: Copyright (c) 2010 Jari Korhonen Chris@244: Copyright (c) 2011 Chris Cannam Chris@244: Copyright (c) 2011 Queen Mary, University of London Chris@116: Chris@116: This program is free software; you can redistribute it and/or Chris@116: modify it under the terms of the GNU General Public License as Chris@116: published by the Free Software Foundation; either version 2 of the Chris@116: License, or (at your option) any later version. See the file Chris@116: COPYING included with this distribution for more information. Chris@116: */ Chris@116: Chris@116: #include "historywidget.h" Chris@116: Chris@119: #include "changesetscene.h" Chris@397: #include "changesetview.h" Chris@116: #include "panner.h" Chris@116: #include "grapher.h" Chris@116: #include "debug.h" Chris@129: #include "uncommitteditem.h" Chris@116: Chris@116: #include Chris@116: Chris@116: #include Chris@513: #include Chris@116: Chris@154: HistoryWidget::HistoryWidget() : Chris@154: m_showUncommitted(false), Chris@154: m_refreshNeeded(false) Chris@116: { Chris@397: m_panned = new ChangesetView; Chris@116: m_panner = new Panner; Chris@116: Chris@168: m_panned->setDragMode(QGraphicsView::ScrollHandDrag); Chris@250: m_panned->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); Chris@397: m_panned->setCacheMode(QGraphicsView::CacheNone); Chris@168: Chris@513: int row = 0; Chris@513: Chris@116: QGridLayout *layout = new QGridLayout; Chris@513: layout->setMargin(10); Chris@513: layout->addWidget(m_panned, row, 0); Chris@513: layout->addWidget(m_panner, row, 1); Chris@116: m_panner->setMaximumWidth(80); Chris@116: m_panner->connectToPanned(m_panned); Chris@116: Chris@513: layout->setRowStretch(row, 20); Chris@513: Chris@513: QSettings settings; Chris@513: settings.beginGroup("Presentation"); Chris@513: bool showClosed = (settings.value("showclosedbranches", false).toBool()); Chris@513: Chris@513: m_showClosedBranches = new QCheckBox(tr("Show closed branches"), this); Chris@513: m_showClosedBranches->setChecked(showClosed); Chris@513: connect(m_showClosedBranches, SIGNAL(toggled(bool)), Chris@513: this, SLOT(showClosedChanged(bool))); Chris@513: layout->addWidget(m_showClosedBranches, ++row, 0, Qt::AlignLeft); Chris@513: m_showClosedBranches->hide(); Chris@513: Chris@116: setLayout(layout); Chris@116: } Chris@116: Chris@116: HistoryWidget::~HistoryWidget() Chris@116: { Chris@116: clearChangesets(); Chris@145: } Chris@145: Chris@145: QGraphicsScene *HistoryWidget::scene() Chris@145: { Chris@145: return m_panned->scene(); Chris@116: } Chris@116: Chris@116: void HistoryWidget::clearChangesets() Chris@116: { Chris@116: foreach (Changeset *cs, m_changesets) delete cs; Chris@116: m_changesets.clear(); Chris@116: } Chris@128: Chris@153: void HistoryWidget::setCurrent(QStringList ids, QString branch, Chris@153: bool showUncommitted) Chris@128: { Chris@153: if (m_currentIds == ids && Chris@153: m_currentBranch == branch && Chris@153: m_showUncommitted == showUncommitted) return; Chris@145: Chris@145: DEBUG << "HistoryWidget::setCurrent: " << ids.size() << " ids, " Chris@145: << "showUncommitted: " << showUncommitted << endl; Chris@145: Chris@133: m_currentIds.clear(); Chris@153: m_currentBranch = branch; Chris@145: m_showUncommitted = showUncommitted; Chris@145: Chris@145: if (ids.empty()) return; Chris@145: Chris@133: foreach (QString id, ids) { Chris@133: m_currentIds.push_back(id); Chris@133: } Chris@128: Chris@154: m_refreshNeeded = true; Chris@128: } Chris@505: Chris@506: void HistoryWidget::setClosedHeadIds(QSet closed) Chris@506: { Chris@506: if (closed == m_closedIds) return; Chris@506: m_closedIds = closed; Chris@513: m_showClosedBranches->setVisible(!closed.empty()); Chris@506: m_refreshNeeded = true; Chris@506: } Chris@506: Chris@505: void HistoryWidget::setShowUncommitted(bool showUncommitted) Chris@505: { Chris@505: setCurrent(m_currentIds, m_currentBranch, showUncommitted); Chris@505: } Chris@513: Chris@513: void HistoryWidget::showClosedChanged(bool show) Chris@513: { Chris@513: QSettings settings; Chris@513: settings.beginGroup("Presentation"); Chris@513: settings.setValue("showclosedbranches", show); Chris@513: layoutAll(); Chris@513: } Chris@116: Chris@120: void HistoryWidget::parseNewLog(QString log) Chris@120: { Chris@120: DEBUG << "HistoryWidget::parseNewLog: log has " << log.length() << " chars" << endl; Chris@122: Changesets csets = Changeset::parseChangesets(log); Chris@120: DEBUG << "HistoryWidget::parseNewLog: log has " << csets.size() << " changesets" << endl; Chris@133: replaceChangesets(csets); Chris@154: m_refreshNeeded = true; Chris@120: } Chris@120: Chris@120: void HistoryWidget::parseIncrementalLog(QString log) Chris@120: { Chris@120: DEBUG << "HistoryWidget::parseIncrementalLog: log has " << log.length() << " chars" << endl; Chris@122: Changesets csets = Changeset::parseChangesets(log); Chris@120: DEBUG << "HistoryWidget::parseIncrementalLog: log has " << csets.size() << " changesets" << endl; Chris@120: if (!csets.empty()) { Chris@133: addChangesets(csets); Chris@120: } Chris@154: m_refreshNeeded = true; Chris@120: } Chris@120: Chris@133: void HistoryWidget::replaceChangesets(Changesets csets) Chris@133: { Chris@133: QSet oldIds; Chris@133: foreach (Changeset *cs, m_changesets) { Chris@133: oldIds.insert(cs->id()); Chris@133: } Chris@133: Chris@133: QSet newIds; Chris@133: foreach (Changeset *cs, csets) { Chris@133: if (!oldIds.contains(cs->id())) { Chris@133: newIds.insert(cs->id()); Chris@133: } Chris@133: } Chris@133: Chris@133: if (newIds.size() == csets.size()) { Chris@133: // completely new set, unrelated to the old: don't mark new Chris@133: m_newIds.clear(); Chris@133: } else { Chris@133: m_newIds = newIds; Chris@133: } Chris@133: Chris@133: clearChangesets(); Chris@133: m_changesets = csets; Chris@133: } Chris@133: Chris@133: void HistoryWidget::addChangesets(Changesets csets) Chris@133: { Chris@133: m_newIds.clear(); Chris@138: Chris@138: if (csets.empty()) return; Chris@138: Chris@133: foreach (Changeset *cs, csets) { Chris@133: m_newIds.insert(cs->id()); Chris@133: } Chris@133: Chris@305: DEBUG << "addChangesets: " << csets.size() << " new changesets have (" Chris@305: << m_changesets.size() << " already)" << endl; Chris@138: Chris@133: csets << m_changesets; Chris@133: m_changesets = csets; Chris@133: } Chris@133: Chris@154: void HistoryWidget::update() Chris@154: { Chris@154: if (m_refreshNeeded) { Chris@154: layoutAll(); Chris@154: } Chris@154: } Chris@154: Chris@120: void HistoryWidget::layoutAll() Chris@116: { Chris@154: m_refreshNeeded = false; Chris@154: Chris@122: setChangesetParents(); Chris@122: Chris@119: ChangesetScene *scene = new ChangesetScene(); Chris@116: ChangesetItem *tipItem = 0; Chris@116: Chris@138: QGraphicsScene *oldScene = m_panned->scene(); Chris@138: Chris@138: m_panned->setScene(0); Chris@138: m_panner->setScene(0); Chris@138: Chris@138: delete oldScene; Chris@138: Chris@145: QGraphicsItem *toFocus = 0; Chris@145: Chris@120: if (!m_changesets.empty()) { Chris@116: Grapher g(scene); Chris@506: g.setClosedHeadIds(m_closedIds); Chris@116: try { Chris@153: g.layout(m_changesets, Chris@153: m_showUncommitted ? m_currentIds : QStringList(), Chris@153: m_currentBranch); Chris@116: } catch (std::string s) { Chris@116: std::cerr << "Internal error: Layout failed: " << s << std::endl; Chris@116: } Chris@145: toFocus = g.getUncommittedItem(); Chris@145: if (!toFocus) { Chris@371: if (!m_currentIds.empty()) { Chris@371: toFocus = g.getItemFor(m_currentIds[0]); Chris@371: } else { Chris@371: toFocus = g.getItemFor(m_changesets[0]); Chris@371: } Chris@145: } Chris@134: } Chris@134: Chris@116: m_panned->setScene(scene); Chris@116: m_panner->setScene(scene); Chris@116: Chris@133: updateNewAndCurrentItems(); Chris@134: Chris@145: if (toFocus) { Chris@145: toFocus->ensureVisible(); Chris@134: } Chris@141: Chris@141: connectSceneSignals(); Chris@116: } Chris@116: Chris@122: void HistoryWidget::setChangesetParents() Chris@116: { Chris@139: for (int i = 0; i < m_changesets.size(); ++i) { Chris@122: Changeset *cs = m_changesets[i]; Chris@123: // Need to reset this, as Grapher::layout will recalculate it Chris@123: // and we don't want to end up with twice the children for Chris@123: // each parent... Chris@123: cs->setChildren(QStringList()); Chris@139: } Chris@139: for (int i = 0; i+1 < m_changesets.size(); ++i) { Chris@139: Changeset *cs = m_changesets[i]; Chris@116: if (cs->parents().empty()) { Chris@116: QStringList list; Chris@122: list.push_back(m_changesets[i+1]->id()); Chris@116: cs->setParents(list); Chris@116: } Chris@116: } Chris@116: } Chris@128: Chris@133: void HistoryWidget::updateNewAndCurrentItems() Chris@128: { Chris@128: QGraphicsScene *scene = m_panned->scene(); Chris@128: if (!scene) return; Chris@133: Chris@128: QList items = scene->items(); Chris@128: foreach (QGraphicsItem *it, items) { Chris@133: Chris@128: ChangesetItem *csit = dynamic_cast(it); Chris@133: if (!csit) continue; Chris@133: Chris@133: QString id = csit->getChangeset()->id(); Chris@133: Chris@133: bool current = m_currentIds.contains(id); Chris@133: if (current) { Chris@133: DEBUG << "id " << id << " is current" << endl; Chris@133: } Chris@133: bool newid = m_newIds.contains(id); Chris@133: if (newid) { Chris@133: DEBUG << "id " << id << " is new" << endl; Chris@133: } Chris@147: Chris@506: if (csit->isCurrent() != current || Chris@506: csit->isNew() != newid) { Chris@147: csit->setCurrent(current); Chris@147: csit->setNew(newid); Chris@147: csit->update(); Chris@147: } Chris@128: } Chris@128: } Chris@141: Chris@141: void HistoryWidget::connectSceneSignals() Chris@141: { Chris@141: ChangesetScene *scene = qobject_cast(m_panned->scene()); Chris@141: if (!scene) return; Chris@141: Chris@141: connect(scene, SIGNAL(commit()), Chris@141: this, SIGNAL(commit())); Chris@141: Chris@141: connect(scene, SIGNAL(revert()), Chris@141: this, SIGNAL(revert())); Chris@141: Chris@141: connect(scene, SIGNAL(diffWorkingFolder()), Chris@141: this, SIGNAL(diffWorkingFolder())); Chris@141: Chris@168: connect(scene, SIGNAL(showSummary()), Chris@168: this, SIGNAL(showSummary())); Chris@168: Chris@153: connect(scene, SIGNAL(showWork()), Chris@153: this, SIGNAL(showWork())); Chris@311: Chris@311: connect(scene, SIGNAL(newBranch()), Chris@311: this, SIGNAL(newBranch())); Chris@311: Chris@311: connect(scene, SIGNAL(noBranch()), Chris@311: this, SIGNAL(noBranch())); Chris@153: Chris@141: connect(scene, SIGNAL(updateTo(QString)), Chris@141: this, SIGNAL(updateTo(QString))); Chris@141: Chris@141: connect(scene, SIGNAL(diffToCurrent(QString)), Chris@141: this, SIGNAL(diffToCurrent(QString))); Chris@141: Chris@148: connect(scene, SIGNAL(diffToParent(QString, QString)), Chris@148: this, SIGNAL(diffToParent(QString, QString))); Chris@141: Chris@289: connect(scene, SIGNAL(showSummary(Changeset *)), Chris@289: this, SIGNAL(showSummary(Changeset *))); Chris@288: Chris@141: connect(scene, SIGNAL(mergeFrom(QString)), Chris@141: this, SIGNAL(mergeFrom(QString))); Chris@141: Chris@278: connect(scene, SIGNAL(newBranch(QString)), Chris@278: this, SIGNAL(newBranch(QString))); Chris@278: Chris@514: connect(scene, SIGNAL(closeBranch(QString)), Chris@514: this, SIGNAL(closeBranch(QString))); Chris@514: Chris@141: connect(scene, SIGNAL(tag(QString)), Chris@141: this, SIGNAL(tag(QString))); Chris@141: }