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@244
|
8 Copyright (c) 2011 Chris Cannam
|
Chris@244
|
9 Copyright (c) 2011 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@116
|
26
|
Chris@116
|
27 #include <iostream>
|
Chris@116
|
28
|
Chris@116
|
29 #include <QGridLayout>
|
Chris@513
|
30 #include <QSettings>
|
Chris@116
|
31
|
Chris@154
|
32 HistoryWidget::HistoryWidget() :
|
Chris@154
|
33 m_showUncommitted(false),
|
Chris@154
|
34 m_refreshNeeded(false)
|
Chris@116
|
35 {
|
Chris@397
|
36 m_panned = new ChangesetView;
|
Chris@116
|
37 m_panner = new Panner;
|
Chris@116
|
38
|
Chris@168
|
39 m_panned->setDragMode(QGraphicsView::ScrollHandDrag);
|
Chris@250
|
40 m_panned->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
|
Chris@397
|
41 m_panned->setCacheMode(QGraphicsView::CacheNone);
|
Chris@168
|
42
|
Chris@513
|
43 int row = 0;
|
Chris@513
|
44
|
Chris@116
|
45 QGridLayout *layout = new QGridLayout;
|
Chris@513
|
46 layout->setMargin(10);
|
Chris@513
|
47 layout->addWidget(m_panned, row, 0);
|
Chris@513
|
48 layout->addWidget(m_panner, row, 1);
|
Chris@116
|
49 m_panner->setMaximumWidth(80);
|
Chris@116
|
50 m_panner->connectToPanned(m_panned);
|
Chris@116
|
51
|
Chris@513
|
52 layout->setRowStretch(row, 20);
|
Chris@513
|
53
|
Chris@513
|
54 QSettings settings;
|
Chris@513
|
55 settings.beginGroup("Presentation");
|
Chris@513
|
56 bool showClosed = (settings.value("showclosedbranches", false).toBool());
|
Chris@513
|
57
|
Chris@513
|
58 m_showClosedBranches = new QCheckBox(tr("Show closed branches"), this);
|
Chris@513
|
59 m_showClosedBranches->setChecked(showClosed);
|
Chris@513
|
60 connect(m_showClosedBranches, SIGNAL(toggled(bool)),
|
Chris@513
|
61 this, SLOT(showClosedChanged(bool)));
|
Chris@513
|
62 layout->addWidget(m_showClosedBranches, ++row, 0, Qt::AlignLeft);
|
Chris@513
|
63 m_showClosedBranches->hide();
|
Chris@513
|
64
|
Chris@116
|
65 setLayout(layout);
|
Chris@116
|
66 }
|
Chris@116
|
67
|
Chris@116
|
68 HistoryWidget::~HistoryWidget()
|
Chris@116
|
69 {
|
Chris@116
|
70 clearChangesets();
|
Chris@145
|
71 }
|
Chris@145
|
72
|
Chris@145
|
73 QGraphicsScene *HistoryWidget::scene()
|
Chris@145
|
74 {
|
Chris@145
|
75 return m_panned->scene();
|
Chris@116
|
76 }
|
Chris@116
|
77
|
Chris@116
|
78 void HistoryWidget::clearChangesets()
|
Chris@116
|
79 {
|
Chris@116
|
80 foreach (Changeset *cs, m_changesets) delete cs;
|
Chris@116
|
81 m_changesets.clear();
|
Chris@116
|
82 }
|
Chris@128
|
83
|
Chris@153
|
84 void HistoryWidget::setCurrent(QStringList ids, QString branch,
|
Chris@153
|
85 bool showUncommitted)
|
Chris@128
|
86 {
|
Chris@153
|
87 if (m_currentIds == ids &&
|
Chris@153
|
88 m_currentBranch == branch &&
|
Chris@153
|
89 m_showUncommitted == showUncommitted) return;
|
Chris@145
|
90
|
Chris@145
|
91 DEBUG << "HistoryWidget::setCurrent: " << ids.size() << " ids, "
|
Chris@145
|
92 << "showUncommitted: " << showUncommitted << endl;
|
Chris@145
|
93
|
Chris@133
|
94 m_currentIds.clear();
|
Chris@153
|
95 m_currentBranch = branch;
|
Chris@145
|
96 m_showUncommitted = showUncommitted;
|
Chris@145
|
97
|
Chris@145
|
98 if (ids.empty()) return;
|
Chris@145
|
99
|
Chris@133
|
100 foreach (QString id, ids) {
|
Chris@133
|
101 m_currentIds.push_back(id);
|
Chris@133
|
102 }
|
Chris@128
|
103
|
Chris@154
|
104 m_refreshNeeded = true;
|
Chris@128
|
105 }
|
Chris@505
|
106
|
Chris@519
|
107 void HistoryWidget::setBookmarks(QHash<QString, QStringList> bookmarks)
|
Chris@519
|
108 {
|
Chris@519
|
109 m_bookmarks = bookmarks;
|
Chris@519
|
110 }
|
Chris@519
|
111
|
Chris@506
|
112 void HistoryWidget::setClosedHeadIds(QSet<QString> closed)
|
Chris@506
|
113 {
|
Chris@506
|
114 if (closed == m_closedIds) return;
|
Chris@506
|
115 m_closedIds = closed;
|
Chris@513
|
116 m_showClosedBranches->setVisible(!closed.empty());
|
Chris@506
|
117 m_refreshNeeded = true;
|
Chris@506
|
118 }
|
Chris@506
|
119
|
Chris@505
|
120 void HistoryWidget::setShowUncommitted(bool showUncommitted)
|
Chris@505
|
121 {
|
Chris@505
|
122 setCurrent(m_currentIds, m_currentBranch, showUncommitted);
|
Chris@505
|
123 }
|
Chris@513
|
124
|
Chris@513
|
125 void HistoryWidget::showClosedChanged(bool show)
|
Chris@513
|
126 {
|
Chris@513
|
127 QSettings settings;
|
Chris@513
|
128 settings.beginGroup("Presentation");
|
Chris@513
|
129 settings.setValue("showclosedbranches", show);
|
Chris@513
|
130 layoutAll();
|
Chris@513
|
131 }
|
Chris@116
|
132
|
Chris@120
|
133 void HistoryWidget::parseNewLog(QString log)
|
Chris@120
|
134 {
|
Chris@120
|
135 DEBUG << "HistoryWidget::parseNewLog: log has " << log.length() << " chars" << endl;
|
Chris@122
|
136 Changesets csets = Changeset::parseChangesets(log);
|
Chris@120
|
137 DEBUG << "HistoryWidget::parseNewLog: log has " << csets.size() << " changesets" << endl;
|
Chris@133
|
138 replaceChangesets(csets);
|
Chris@154
|
139 m_refreshNeeded = true;
|
Chris@120
|
140 }
|
Chris@120
|
141
|
Chris@120
|
142 void HistoryWidget::parseIncrementalLog(QString log)
|
Chris@120
|
143 {
|
Chris@120
|
144 DEBUG << "HistoryWidget::parseIncrementalLog: log has " << log.length() << " chars" << endl;
|
Chris@122
|
145 Changesets csets = Changeset::parseChangesets(log);
|
Chris@120
|
146 DEBUG << "HistoryWidget::parseIncrementalLog: log has " << csets.size() << " changesets" << endl;
|
Chris@120
|
147 if (!csets.empty()) {
|
Chris@133
|
148 addChangesets(csets);
|
Chris@120
|
149 }
|
Chris@154
|
150 m_refreshNeeded = true;
|
Chris@120
|
151 }
|
Chris@120
|
152
|
Chris@133
|
153 void HistoryWidget::replaceChangesets(Changesets csets)
|
Chris@133
|
154 {
|
Chris@133
|
155 QSet<QString> oldIds;
|
Chris@133
|
156 foreach (Changeset *cs, m_changesets) {
|
Chris@133
|
157 oldIds.insert(cs->id());
|
Chris@133
|
158 }
|
Chris@133
|
159
|
Chris@133
|
160 QSet<QString> newIds;
|
Chris@133
|
161 foreach (Changeset *cs, csets) {
|
Chris@133
|
162 if (!oldIds.contains(cs->id())) {
|
Chris@133
|
163 newIds.insert(cs->id());
|
Chris@133
|
164 }
|
Chris@133
|
165 }
|
Chris@133
|
166
|
Chris@133
|
167 if (newIds.size() == csets.size()) {
|
Chris@133
|
168 // completely new set, unrelated to the old: don't mark new
|
Chris@133
|
169 m_newIds.clear();
|
Chris@133
|
170 } else {
|
Chris@133
|
171 m_newIds = newIds;
|
Chris@133
|
172 }
|
Chris@133
|
173
|
Chris@133
|
174 clearChangesets();
|
Chris@133
|
175 m_changesets = csets;
|
Chris@133
|
176 }
|
Chris@133
|
177
|
Chris@133
|
178 void HistoryWidget::addChangesets(Changesets csets)
|
Chris@133
|
179 {
|
Chris@133
|
180 m_newIds.clear();
|
Chris@138
|
181
|
Chris@138
|
182 if (csets.empty()) return;
|
Chris@138
|
183
|
Chris@133
|
184 foreach (Changeset *cs, csets) {
|
Chris@133
|
185 m_newIds.insert(cs->id());
|
Chris@133
|
186 }
|
Chris@133
|
187
|
Chris@305
|
188 DEBUG << "addChangesets: " << csets.size() << " new changesets have ("
|
Chris@305
|
189 << m_changesets.size() << " already)" << endl;
|
Chris@138
|
190
|
Chris@133
|
191 csets << m_changesets;
|
Chris@133
|
192 m_changesets = csets;
|
Chris@133
|
193 }
|
Chris@133
|
194
|
Chris@154
|
195 void HistoryWidget::update()
|
Chris@154
|
196 {
|
Chris@154
|
197 if (m_refreshNeeded) {
|
Chris@154
|
198 layoutAll();
|
Chris@154
|
199 }
|
Chris@154
|
200 }
|
Chris@154
|
201
|
Chris@120
|
202 void HistoryWidget::layoutAll()
|
Chris@116
|
203 {
|
Chris@154
|
204 m_refreshNeeded = false;
|
Chris@154
|
205
|
Chris@122
|
206 setChangesetParents();
|
Chris@122
|
207
|
Chris@119
|
208 ChangesetScene *scene = new ChangesetScene();
|
Chris@116
|
209 ChangesetItem *tipItem = 0;
|
Chris@116
|
210
|
Chris@138
|
211 QGraphicsScene *oldScene = m_panned->scene();
|
Chris@138
|
212
|
Chris@138
|
213 m_panned->setScene(0);
|
Chris@138
|
214 m_panner->setScene(0);
|
Chris@138
|
215
|
Chris@138
|
216 delete oldScene;
|
Chris@138
|
217
|
Chris@145
|
218 QGraphicsItem *toFocus = 0;
|
Chris@145
|
219
|
Chris@120
|
220 if (!m_changesets.empty()) {
|
Chris@116
|
221 Grapher g(scene);
|
Chris@506
|
222 g.setClosedHeadIds(m_closedIds);
|
Chris@116
|
223 try {
|
Chris@153
|
224 g.layout(m_changesets,
|
Chris@153
|
225 m_showUncommitted ? m_currentIds : QStringList(),
|
Chris@153
|
226 m_currentBranch);
|
Chris@116
|
227 } catch (std::string s) {
|
Chris@116
|
228 std::cerr << "Internal error: Layout failed: " << s << std::endl;
|
Chris@116
|
229 }
|
Chris@145
|
230 toFocus = g.getUncommittedItem();
|
Chris@145
|
231 if (!toFocus) {
|
Chris@371
|
232 if (!m_currentIds.empty()) {
|
Chris@371
|
233 toFocus = g.getItemFor(m_currentIds[0]);
|
Chris@371
|
234 } else {
|
Chris@371
|
235 toFocus = g.getItemFor(m_changesets[0]);
|
Chris@371
|
236 }
|
Chris@145
|
237 }
|
Chris@134
|
238 }
|
Chris@134
|
239
|
Chris@116
|
240 m_panned->setScene(scene);
|
Chris@116
|
241 m_panner->setScene(scene);
|
Chris@116
|
242
|
Chris@133
|
243 updateNewAndCurrentItems();
|
Chris@134
|
244
|
Chris@145
|
245 if (toFocus) {
|
Chris@145
|
246 toFocus->ensureVisible();
|
Chris@134
|
247 }
|
Chris@141
|
248
|
Chris@141
|
249 connectSceneSignals();
|
Chris@116
|
250 }
|
Chris@116
|
251
|
Chris@122
|
252 void HistoryWidget::setChangesetParents()
|
Chris@116
|
253 {
|
Chris@139
|
254 for (int i = 0; i < m_changesets.size(); ++i) {
|
Chris@122
|
255 Changeset *cs = m_changesets[i];
|
Chris@123
|
256 // Need to reset this, as Grapher::layout will recalculate it
|
Chris@123
|
257 // and we don't want to end up with twice the children for
|
Chris@123
|
258 // each parent...
|
Chris@123
|
259 cs->setChildren(QStringList());
|
Chris@139
|
260 }
|
Chris@139
|
261 for (int i = 0; i+1 < m_changesets.size(); ++i) {
|
Chris@139
|
262 Changeset *cs = m_changesets[i];
|
Chris@116
|
263 if (cs->parents().empty()) {
|
Chris@116
|
264 QStringList list;
|
Chris@122
|
265 list.push_back(m_changesets[i+1]->id());
|
Chris@116
|
266 cs->setParents(list);
|
Chris@116
|
267 }
|
Chris@116
|
268 }
|
Chris@116
|
269 }
|
Chris@128
|
270
|
Chris@133
|
271 void HistoryWidget::updateNewAndCurrentItems()
|
Chris@128
|
272 {
|
Chris@128
|
273 QGraphicsScene *scene = m_panned->scene();
|
Chris@128
|
274 if (!scene) return;
|
Chris@133
|
275
|
Chris@128
|
276 QList<QGraphicsItem *> items = scene->items();
|
Chris@128
|
277 foreach (QGraphicsItem *it, items) {
|
Chris@133
|
278
|
Chris@128
|
279 ChangesetItem *csit = dynamic_cast<ChangesetItem *>(it);
|
Chris@133
|
280 if (!csit) continue;
|
Chris@133
|
281
|
Chris@133
|
282 QString id = csit->getChangeset()->id();
|
Chris@133
|
283
|
Chris@133
|
284 bool current = m_currentIds.contains(id);
|
Chris@133
|
285 if (current) {
|
Chris@133
|
286 DEBUG << "id " << id << " is current" << endl;
|
Chris@133
|
287 }
|
Chris@133
|
288 bool newid = m_newIds.contains(id);
|
Chris@133
|
289 if (newid) {
|
Chris@133
|
290 DEBUG << "id " << id << " is new" << endl;
|
Chris@133
|
291 }
|
Chris@147
|
292
|
Chris@519
|
293 if (m_bookmarks.contains(id)) {
|
Chris@519
|
294 csit->setBookmarks(m_bookmarks[id]);
|
Chris@519
|
295 } else {
|
Chris@519
|
296 csit->setBookmarks(QStringList());
|
Chris@519
|
297 }
|
Chris@519
|
298
|
Chris@506
|
299 if (csit->isCurrent() != current ||
|
Chris@506
|
300 csit->isNew() != newid) {
|
Chris@147
|
301 csit->setCurrent(current);
|
Chris@147
|
302 csit->setNew(newid);
|
Chris@147
|
303 csit->update();
|
Chris@147
|
304 }
|
Chris@128
|
305 }
|
Chris@128
|
306 }
|
Chris@141
|
307
|
Chris@141
|
308 void HistoryWidget::connectSceneSignals()
|
Chris@141
|
309 {
|
Chris@141
|
310 ChangesetScene *scene = qobject_cast<ChangesetScene *>(m_panned->scene());
|
Chris@141
|
311 if (!scene) return;
|
Chris@141
|
312
|
Chris@141
|
313 connect(scene, SIGNAL(commit()),
|
Chris@141
|
314 this, SIGNAL(commit()));
|
Chris@141
|
315
|
Chris@141
|
316 connect(scene, SIGNAL(revert()),
|
Chris@141
|
317 this, SIGNAL(revert()));
|
Chris@141
|
318
|
Chris@141
|
319 connect(scene, SIGNAL(diffWorkingFolder()),
|
Chris@141
|
320 this, SIGNAL(diffWorkingFolder()));
|
Chris@141
|
321
|
Chris@168
|
322 connect(scene, SIGNAL(showSummary()),
|
Chris@168
|
323 this, SIGNAL(showSummary()));
|
Chris@168
|
324
|
Chris@153
|
325 connect(scene, SIGNAL(showWork()),
|
Chris@153
|
326 this, SIGNAL(showWork()));
|
Chris@311
|
327
|
Chris@311
|
328 connect(scene, SIGNAL(newBranch()),
|
Chris@311
|
329 this, SIGNAL(newBranch()));
|
Chris@311
|
330
|
Chris@311
|
331 connect(scene, SIGNAL(noBranch()),
|
Chris@311
|
332 this, SIGNAL(noBranch()));
|
Chris@153
|
333
|
Chris@141
|
334 connect(scene, SIGNAL(updateTo(QString)),
|
Chris@141
|
335 this, SIGNAL(updateTo(QString)));
|
Chris@141
|
336
|
Chris@141
|
337 connect(scene, SIGNAL(diffToCurrent(QString)),
|
Chris@141
|
338 this, SIGNAL(diffToCurrent(QString)));
|
Chris@141
|
339
|
Chris@148
|
340 connect(scene, SIGNAL(diffToParent(QString, QString)),
|
Chris@148
|
341 this, SIGNAL(diffToParent(QString, QString)));
|
Chris@141
|
342
|
Chris@289
|
343 connect(scene, SIGNAL(showSummary(Changeset *)),
|
Chris@289
|
344 this, SIGNAL(showSummary(Changeset *)));
|
Chris@288
|
345
|
Chris@141
|
346 connect(scene, SIGNAL(mergeFrom(QString)),
|
Chris@141
|
347 this, SIGNAL(mergeFrom(QString)));
|
Chris@141
|
348
|
Chris@278
|
349 connect(scene, SIGNAL(newBranch(QString)),
|
Chris@278
|
350 this, SIGNAL(newBranch(QString)));
|
Chris@278
|
351
|
Chris@514
|
352 connect(scene, SIGNAL(closeBranch(QString)),
|
Chris@514
|
353 this, SIGNAL(closeBranch(QString)));
|
Chris@514
|
354
|
Chris@141
|
355 connect(scene, SIGNAL(tag(QString)),
|
Chris@141
|
356 this, SIGNAL(tag(QString)));
|
Chris@141
|
357 }
|