FIX issue #19 where multiple widget values led to bad replacements in the command
[qgit4/redivivus.git] / src / revsview.cpp
blob2e69278bcb773d304ad972bd5030d72ce0f61a99
1 /*
2 Description: qgit revision list view
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
9 #include <QMenu>
10 #include <QStackedWidget>
11 #include "common.h"
12 #include "git.h"
13 #include "domain.h"
14 #include "treeview.h"
15 #include "listview.h"
16 #include "filelist.h"
17 #include "revdesc.h"
18 #include "patchview.h"
19 #include "smartbrowse.h"
20 #include "mainimpl.h"
21 #include "revsview.h"
23 RevsView::RevsView(MainImpl* mi, Git* g, bool isMain) : Domain(mi, g, isMain) {
25 revTab = new Ui_TabRev();
26 revTab->setupUi(container);
28 tab()->listViewLog->setup(this, g);
29 tab()->textBrowserDesc->setup(this);
30 tab()->textEditDiff->setup(this, git);
31 tab()->fileList->setup(this, git);
32 m()->treeView->setup(this, git);
34 setTabLogDiffVisible(QGit::testFlag(QGit::LOG_DIFF_TAB_F));
36 SmartBrowse* sb = new SmartBrowse(this);
38 // restore geometry
39 QVector<QSplitter*> v;
40 v << tab()->horizontalSplitter << tab()->verticalSplitter;
41 QGit::restoreGeometrySetting(QGit::REV_GEOM_KEY, NULL, &v);
43 connect(m(), SIGNAL(typeWriterFontChanged()),
44 tab()->textEditDiff, SLOT(typeWriterFontChanged()));
46 connect(m(), SIGNAL(flagChanged(uint)),
47 sb, SLOT(flagChanged(uint)));
49 connect(m(), SIGNAL(flagChanged(uint)), SLOT(on_flagChanged(uint)));
51 connect(git, SIGNAL(newRevsAdded(const FileHistory*, const QVector<ShaString>&)),
52 this, SLOT(on_newRevsAdded(const FileHistory*, const QVector<ShaString>&)));
54 connect(git, SIGNAL(loadCompleted(const FileHistory*, const QString&)),
55 this, SLOT(on_loadCompleted(const FileHistory*, const QString&)));
57 connect(m(), SIGNAL(changeFont(const QFont&)),
58 tab()->listViewLog, SLOT(on_changeFont(const QFont&)));
60 connect(m(), SIGNAL(updateRevDesc()), this, SLOT(on_updateRevDesc()));
62 connect(tab()->listViewLog, SIGNAL(lanesContextMenuRequested(const QStringList&,
63 const QStringList&)), this, SLOT(on_lanesContextMenuRequested
64 (const QStringList&, const QStringList&)));
66 connect(tab()->listViewLog, SIGNAL(applyRevisions(const QStringList&, const QString&)),
67 m(), SLOT(applyRevisions(const QStringList&, const QString&)));
68 connect(tab()->listViewLog, SIGNAL(applyPatches(QStringList)),
69 m(), SLOT(applyPatches(const QStringList&)));
71 connect(tab()->listViewLog, SIGNAL(rebase(QString,QString,QString)),
72 m(), SLOT(rebase(QString,QString,QString)));
73 connect(tab()->listViewLog, SIGNAL(merge(QStringList,QString)),
74 m(), SLOT(merge(QStringList,QString)));
75 connect(tab()->listViewLog, SIGNAL(moveRef(QString,QString)),
76 m(), SLOT(moveRef(QString,QString)));
78 connect(tab()->listViewLog, SIGNAL(contextMenu(const QString&, int)),
79 this, SLOT(on_contextMenu(const QString&, int)));
81 connect(m()->treeView, SIGNAL(contextMenu(const QString&, int)),
82 this, SLOT(on_contextMenu(const QString&, int)));
84 connect(tab()->fileList, SIGNAL(contextMenu(const QString&, int)),
85 this, SLOT(on_contextMenu(const QString&, int)));
87 connect(m(), SIGNAL(changeFont(const QFont&)),
88 tab()->fileList, SLOT(on_changeFont(const QFont&)));
90 connect(m(), SIGNAL(highlightPatch(const QString&, bool)),
91 tab()->textEditDiff, SLOT(on_highlightPatch(const QString&, bool)));
94 RevsView::~RevsView() {
96 if (!parent())
97 return;
99 QVector<QSplitter*> v;
100 v << tab()->horizontalSplitter << tab()->verticalSplitter;
101 QGit::saveGeometrySetting(QGit::REV_GEOM_KEY, NULL, &v);
103 // manually delete before container is removed in Domain
104 // d'tor to avoid a crash due to spurious events in
105 // SmartBrowse::eventFilter()
106 delete tab()->textBrowserDesc;
107 delete tab()->textEditDiff;
109 delete linkedPatchView;
110 delete revTab;
113 void RevsView::clear(bool complete) {
115 Domain::clear(complete);
117 tab()->textBrowserDesc->clear();
118 tab()->textEditDiff->clear();
119 tab()->fileList->clear();
120 m()->treeView->clear();
121 updateLineEditSHA(true);
122 if (linkedPatchView)
123 linkedPatchView->clear();
126 void RevsView::setEnabled(bool b) {
128 container->setEnabled(b);
129 if (linkedPatchView)
130 linkedPatchView->tabPage()->setEnabled(b);
133 void RevsView::toggleDiffView() {
135 QStackedWidget* s = tab()->stackedPanes;
136 QTabWidget* t = tab()->tabLogDiff;
138 bool isTabPage = (s->currentIndex() == 0);
139 int idx = (isTabPage ? t->currentIndex() : s->currentIndex());
141 bool old = container->updatesEnabled();
142 container->setUpdatesEnabled(false);
144 if (isTabPage)
145 t->setCurrentIndex(1 - idx);
146 else
147 s->setCurrentIndex(3 - idx);
149 container->setUpdatesEnabled(old);
152 void RevsView::setTabLogDiffVisible(bool b) {
154 QStackedWidget* s = tab()->stackedPanes;
155 QTabWidget* t = tab()->tabLogDiff;
157 bool isTabPage = (s->currentIndex() == 0);
158 int idx = (isTabPage ? t->currentIndex() : s->currentIndex());
160 container->setUpdatesEnabled(false);
162 if (b && !isTabPage) {
164 t->addTab(tab()->textBrowserDesc, "Log");
165 t->addTab(tab()->textEditDiff, "Diff");
167 t->setCurrentIndex(idx - 1);
168 s->setCurrentIndex(0);
170 if (!b && isTabPage) {
172 s->addWidget(tab()->textBrowserDesc);
173 s->addWidget(tab()->textEditDiff);
175 // manually remove the two remaining empty pages
176 t->removeTab(0); t->removeTab(0);
178 s->setCurrentIndex(idx + 1);
180 container->setUpdatesEnabled(true);
183 void RevsView::viewPatch(bool newTab) {
185 if (!newTab && linkedPatchView) {
186 m()->tabWdg->setCurrentWidget(linkedPatchView->tabPage());
187 return;
189 PatchView* pv = new PatchView(m(), git);
190 m()->tabWdg->addTab(pv->tabPage(), "&Patch");
191 m()->tabWdg->setCurrentWidget(pv->tabPage());
193 if (!newTab) { // linkedPatchView == NULL
194 linkedPatchView = pv;
195 linkDomain(linkedPatchView);
197 connect(m(), SIGNAL(highlightPatch(const QString&, bool)),
198 pv->tab()->textEditDiff, SLOT(on_highlightPatch(const QString&, bool)));
200 connect(pv->tab()->fileList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
201 m(), SLOT(fileList_itemDoubleClicked(QListWidgetItem*)));
203 connect(m(), SIGNAL(updateRevDesc()), pv, SLOT(on_updateRevDesc()));
204 connect(m(), SIGNAL(closeAllTabs()), pv, SLOT(on_closeAllTabs()));
205 pv->st = st;
206 UPDATE_DM_MASTER(pv, false);
209 void RevsView::on_newRevsAdded(const FileHistory* fh, const QVector<ShaString>&) {
211 if (!git->isMainHistory(fh) || !st.sha().isEmpty())
212 return;
214 ListView* lv = tab()->listViewLog;
215 if (lv->model()->rowCount() == 0)
216 return;
218 st.setSha(lv->sha(0));
219 st.setSelectItem(true);
220 UPDATE(); // give feedback to user as soon as possible
223 void RevsView::on_loadCompleted(const FileHistory* fh, const QString& stats) {
225 if (!git->isMainHistory(fh))
226 return;
228 UPDATE(); // restore revision after a refresh
229 QApplication::postEvent(this, new MessageEvent(stats));
232 void RevsView::on_updateRevDesc() {
234 SCRef d = m()->getRevisionDesc(st.sha());
235 tab()->textBrowserDesc->setHtml(d);
238 void RevsView::on_flagChanged(uint flag) {
240 if (flag == QGit::ENABLE_DRAGNDROP_F) {
241 if (QGit::testFlag(QGit::ENABLE_DRAGNDROP_F)) {
242 tab()->listViewLog->setSelectionMode(QAbstractItemView::ExtendedSelection);
243 } else {
244 tab()->listViewLog->setSelectionMode(QAbstractItemView::SingleSelection);
249 bool RevsView::doUpdate(bool force) {
251 force = force || m()->lineEditSHA->text().isEmpty();
253 bool found = tab()->listViewLog->update();
255 if (!found && !st.sha().isEmpty()) {
257 const QString tmp("Sorry, revision " + st.sha() +
258 " has not been found in main view");
259 showStatusBarMessage(tmp);
261 } else { // sha could be NULL
263 if (st.isChanged(StateInfo::SHA) || force) {
265 updateLineEditSHA();
266 on_updateRevDesc();
267 showStatusBarMessage(git->getRevInfo(st.sha()));
269 if ( testFlag(QGit::MSG_ON_NEW_F)
270 && tab()->textEditDiff->isVisible())
271 toggleDiffView();
273 const RevFile* files = NULL;
274 bool newFiles = false;
276 if (st.isChanged(StateInfo::ANY & ~StateInfo::FILE_NAME) || force) {
278 tab()->fileList->clear();
280 if (linkedPatchView) // give some feedback while waiting
281 linkedPatchView->clear();
283 // blocking call, could be slow in case of all merge files
284 files = git->getFiles(st.sha(), st.diffToSha(), st.allMergeFiles());
285 newFiles = true;
287 tab()->textEditDiff->update(st);
289 // call always to allow a simple refresh
290 tab()->fileList->update(files, newFiles);
292 // update the tree at startup or when releasing a no-match toolbar search
293 if (m()->treeView->isVisible() || st.sha(false).isEmpty())
294 m()->treeView->updateTree(); // blocking call
296 if (st.selectItem()) {
297 bool isDir = m()->treeView->isDir(st.fileName());
298 m()->updateContextActions(st.sha(), st.fileName(), isDir, found);
300 if (st.isChanged() || force) {
301 // activate log or diff tab depending on file selection
302 bool sha_changed = st.sha(true) != st.sha(false);
303 tab()->tabLogDiff->setCurrentIndex(sha_changed ? 0 : 1);
304 tab()->textEditDiff->centerOnFileHeader(st);
307 // at the end update diffs that is the slowest and must be
308 // run after update of file list for 'diff to sha' to work
309 if (linkedPatchView) {
310 linkedPatchView->st = st;
311 UPDATE_DM_MASTER(linkedPatchView, force); // async call
314 return (found || st.sha().isEmpty());
317 void RevsView::updateLineEditSHA(bool clear) {
319 QLineEdit* l = m()->lineEditSHA;
321 if (clear)
322 l->setText(""); // clears history
324 else if (l->text() != st.sha()) {
325 auto hash = QGit::testFlag(QGit::ENABLE_SHORTREF_F)
326 ? st.sha().left(git->shortHashLength())
327 : st.sha();
329 if (l->text().isEmpty())
330 l->setText(hash); // first rev clears history
331 else {
332 // setText() clears undo/redo history so
333 // we use clear() + insert() instead
334 l->clear();
335 l->insert(hash);
338 m()->ActBack->setEnabled(l->isUndoAvailable());
339 m()->ActForward->setEnabled(l->isRedoAvailable());
342 void RevsView::on_lanesContextMenuRequested(SCList parents, SCList children) {
344 QMenu contextMenu;
345 FOREACH_SL (it, children)
346 contextMenu.addAction("Child: " + git->getShortLog(*it));
348 FOREACH_SL (it, parents) {
349 QString log(git->getShortLog(*it));
350 contextMenu.addAction("Parent: " + (log.isEmpty() ? *it : log));
352 QAction* act = contextMenu.exec(QCursor::pos()); // modal exec
353 if (!act)
354 return;
356 int cc = children.count();
357 int id = contextMenu.actions().indexOf(act);
358 SCRef target(id < cc ? children[id] : parents[id - cc]);
359 st.setSha(target);
360 UPDATE();