00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <qlineedit.h>
00010 #include <qtextbrowser.h>
00011 #include <qlistview.h>
00012 #include <qpainter.h>
00013 #include <qstringlist.h>
00014 #include <qlistbox.h>
00015 #include <qapplication.h>
00016 #include <qmessagebox.h>
00017 #include <qstatusbar.h>
00018 #include <qheader.h>
00019 #include <qpopupmenu.h>
00020 #include <qcursor.h>
00021 #include <qaction.h>
00022 #include <qdragobject.h>
00023 #include <qtoolbutton.h>
00024 #include <qtabwidget.h>
00025 #include <qaction.h>
00026 #include "common.h"
00027 #include "git.h"
00028 #include "domain.h"
00029 #include "treeview.h"
00030 #include "listview.h"
00031 #include "filelist.h"
00032 #include "revbase.h"
00033 #include "revdesc.h"
00034 #include "patchbase.h"
00035 #include "patchview.h"
00036 #include "mainimpl.h"
00037 #include "revsview.h"
00038
00039 RevsView::RevsView(MainImpl* mi, Git* g) : Domain(mi, g) {
00040
00041 revTab = new TabRev(m());
00042 m()->tabWdg->addTab(revTab, "&Rev list");
00043 tabPosition = m()->tabWdg->count() - 1;
00044
00045 listViewLog = new ListView(this, g, tab()->listViewLog, &(g->revData), m()->listViewFont);
00046 tab()->textBrowserDesc->setDomain(this);
00047 listBoxFiles = new ListBoxFiles(this, git, tab()->listBoxFiles);
00048 treeView = new TreeView(this, git, m()->treeView);
00049
00050 connect(git, SIGNAL(newRevsAdded(const FileHistory*, const QValueVector<QString>&)),
00051 listViewLog, SLOT(on_newRevsAdded(const FileHistory*, const QValueVector<QString>&)));
00052
00053 connect(git, SIGNAL(loadCompleted(const FileHistory*, const QString&)),
00054 this, SLOT(on_loadCompleted(const FileHistory*, const QString&)));
00055
00056 connect(m(), SIGNAL(repaintListViews(const QFont&)),
00057 listViewLog, SLOT(on_repaintListViews(const QFont&)));
00058
00059 connect(m(), SIGNAL(updateRevDesc()), this, SLOT(on_updateRevDesc()));
00060
00061 connect(listViewLog, SIGNAL(lanesContextMenuRequested(const QStringList&,
00062 const QStringList&)), this, SLOT(on_lanesContextMenuRequested
00063 (const QStringList&, const QStringList&)));
00064
00065 connect(listViewLog, SIGNAL(droppedRevisions(const QStringList&)),
00066 this, SLOT(on_droppedRevisions(const QStringList&)));
00067
00068 connect(listViewLog, SIGNAL(contextMenu(const QString&, int)),
00069 this, SLOT(on_contextMenu(const QString&, int)));
00070
00071 connect(treeView, SIGNAL(contextMenu(const QString&, int)),
00072 this, SLOT(on_contextMenu(const QString&, int)));
00073
00074 connect(listBoxFiles, SIGNAL(contextMenu(const QString&, int)),
00075 this, SLOT(on_contextMenu(const QString&, int)));
00076 }
00077
00078 RevsView::~RevsView() {
00079
00080 if (!parent())
00081 return;
00082
00083 delete linkedPatchView;
00084 delete revTab;
00085 }
00086
00087 void RevsView::clear(bool keepState) {
00088
00089 if (!keepState)
00090 st.clear();
00091
00092 listViewLog->clear();
00093 tab()->textBrowserDesc->clear();
00094 listBoxFiles->clear();
00095 treeView->clear();
00096 updateLineEditSHA(true);
00097 if (linkedPatchView)
00098 linkedPatchView->clear();
00099 }
00100
00101 void RevsView::setEnabled(bool b) {
00102
00103 revTab->setEnabled(b);
00104 if (linkedPatchView)
00105 linkedPatchView->tab()->setEnabled(b);
00106 }
00107
00108 void RevsView::viewPatch(bool newTab) {
00109
00110 if (!newTab && linkedPatchView) {
00111 m()->tabWdg->setCurrentPage(linkedPatchView->tabPos());
00112 return;
00113 }
00114 PatchView* pv = new PatchView(m(), git);
00115 m()->tabWdg->setCurrentPage(pv->tabPos());
00116
00117 if (!newTab) {
00118 linkedPatchView = pv;
00119 linkDomain(linkedPatchView);
00120
00121 connect(m(), SIGNAL(highlightPatch(const QString&, bool)),
00122 linkedPatchView, SLOT(on_highlightPatch(const QString&, bool)));
00123
00124 connect(linkedPatchView->tab()->listBoxFiles, SIGNAL(doubleClicked(QListBoxItem*)),
00125 m(), SLOT(on_fileList_doubleClicked(QListBoxItem*)));
00126 }
00127 connect(m(), SIGNAL(updateRevDesc()), pv, SLOT(on_updateRevDesc()));
00128 connect(m(), SIGNAL(closeAllTabs()), pv, SLOT(on_closeAllTabs()));
00129 pv->st = st;
00130 UPDATE_DM_MASTER(pv, false);
00131 }
00132
00133 void RevsView::on_loadCompleted(const FileHistory* fh, const QString& stats) {
00134
00135 if (!git->isMainHistory(fh))
00136 return;
00137
00138 if (st.sha().isEmpty()) {
00139
00140 QListViewItem* item = tab()->listViewLog->firstChild();
00141 if (item) {
00142 st.setSha(((ListViewItem*)item)->sha());
00143 st.setSelectItem(true);
00144 }
00145 }
00146 UPDATE();
00147 QApplication::postEvent(this, new MessageEvent(stats));
00148 }
00149
00150 void RevsView::on_updateRevDesc() {
00151
00152 bool showHeader = m()->ActShowDescHeader->isOn();
00153 SCRef d(git->getDesc(st.sha(), m()->shortLogRE, m()->longLogRE, showHeader));
00154 tab()->textBrowserDesc->setText(d);
00155 tab()->textBrowserDesc->setCursorPosition(0, 0);
00156 }
00157
00158 bool RevsView::doUpdate(bool force) {
00159
00160 force = force || m()->lineEditSHA->text().isEmpty();
00161
00162 bool found = listViewLog->update();
00163
00164 if (!found && !st.sha().isEmpty()) {
00165
00166 const QString tmp("Sorry, revision " + st.sha() +
00167 " has not been found in main view");
00168 m()->statusBar()->message(tmp);
00169
00170 } else {
00171
00172 if (st.isChanged(StateInfo::SHA) || force) {
00173
00174 updateLineEditSHA();
00175 on_updateRevDesc();
00176 m()->statusBar()->message(git->getRevInfo(st.sha()));
00177 }
00178 const RevFile* files = NULL;
00179 bool newFiles = false;
00180
00181 if (st.isChanged(StateInfo::ANY & ~StateInfo::FILE_NAME) || force) {
00182
00183 listBoxFiles->clear();
00184 files = git->getFiles(st.sha(), st.diffToSha(), st.allMergeFiles());
00185 newFiles = true;
00186 }
00187
00188 listBoxFiles->update(files, newFiles);
00189
00190
00191 if (m()->treeView->isVisible() || st.sha(false).isEmpty())
00192 treeView->update();
00193
00194 if (st.selectItem()) {
00195 bool isDir = treeView->isDir(st.fileName());
00196 m()->updateContextActions(st.sha(), st.fileName(), isDir, found);
00197 }
00198
00199
00200 if (linkedPatchView) {
00201 linkedPatchView->st = st;
00202 UPDATE_DM_MASTER(linkedPatchView, force);
00203 }
00204 }
00205 QListViewItem* item = tab()->listViewLog->currentItem();
00206
00207 if (item && item->isVisible() && !found && force) {
00208
00209
00210
00211
00212 st.setSha(((ListViewItem*)item)->sha());
00213
00214 UpdateDomainEvent* e = new UpdateDomainEvent(false);
00215 customEvent(e);
00216 delete e;
00217 }
00218 return (found || st.sha().isEmpty());
00219 }
00220
00221 void RevsView::updateLineEditSHA(bool clear) {
00222
00223 QLineEdit* l = m()->lineEditSHA;
00224
00225 if (clear)
00226 l->setText("");
00227
00228 else if (l->text() != st.sha()) {
00229
00230 if (l->text().isEmpty())
00231 l->setText(st.sha());
00232 else {
00233
00234
00235 l->clear();
00236 l->insert(st.sha());
00237 }
00238 }
00239 m()->ActBack->setEnabled(l->isUndoAvailable());
00240 m()->ActForward->setEnabled(l->isRedoAvailable());
00241 }
00242
00243 void RevsView::on_lanesContextMenuRequested(SCList parents, SCList childs) {
00244
00245 QPopupMenu contextMenu;
00246 uint i = 0;
00247 FOREACH_SL (it, childs)
00248 contextMenu.insertItem("Child: " + git->getShortLog(*it), i++);
00249
00250 FOREACH_SL (it, parents) {
00251
00252 QString log(git->getShortLog(*it));
00253 if (log.isEmpty())
00254 log = *it;
00255
00256 contextMenu.insertItem("Parent: " + log, i++);
00257 }
00258 int id = contextMenu.exec(QCursor::pos());
00259 if (id == -1)
00260 return;
00261
00262 int cc = (int)childs.count();
00263 SCRef target(id < cc ? childs[id] : parents[id - cc]);
00264 st.setSha(target);
00265 UPDATE();
00266 }
00267
00268 void RevsView::on_droppedRevisions(SCList remoteRevs) {
00269
00270
00271 if (isDropping())
00272 return;
00273
00274 QDir dr(m()->curDir + QGit::PATCHES_DIR);
00275 if (dr.exists()) {
00276 const QString tmp("Please remove stale import directory " + dr.absPath());
00277 m()->statusBar()->message(tmp);
00278 return;
00279 }
00280 bool workDirOnly, fold;
00281 if (!m()->askApplyPatchParameters(&workDirOnly, &fold))
00282 return;
00283
00284
00285 setDropping(true);
00286 dr.setFilter(QDir::Files);
00287 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
00288 m()->raise();
00289 EM_PROCESS_EVENTS;
00290
00291 uint revNum = 0;
00292 QStringList::const_iterator it(remoteRevs.constEnd());
00293 do {
00294 --it;
00295
00296 QString tmp("Importing revision %1 of %2");
00297 m()->statusBar()->message(tmp.arg(++revNum).arg(remoteRevs.count()));
00298
00299 SCRef sha((*it).section('@', 0, 0));
00300 SCRef remoteRepo((*it).section('@', 1));
00301
00302 if (!dr.exists(remoteRepo))
00303 break;
00304
00305
00306 if (!git->formatPatch(QStringList(sha), dr.absPath(), remoteRepo))
00307 break;
00308
00309 dr.refresh();
00310 if (dr.count() != 1) {
00311 qDebug("ASSERT in on_droppedRevisions: found %i files "
00312 "in %s", dr.count(), QGit::PATCHES_DIR.latin1());
00313 break;
00314 }
00315 SCRef fn(dr.absFilePath(dr[0]));
00316 if (!git->applyPatchFile(fn, fold, Git::optDragDrop))
00317 break;
00318
00319 dr.remove(fn);
00320
00321 } while (it != remoteRevs.constBegin());
00322
00323 if (it == remoteRevs.constBegin())
00324 m()->statusBar()->clear();
00325 else
00326 m()->statusBar()->message("Failed to import revision " + QString::number(revNum--));
00327
00328 if (workDirOnly && (revNum > 0))
00329 git->resetCommits(revNum);
00330
00331 dr.rmdir(dr.absPath());
00332 QApplication::restoreOverrideCursor();
00333 setDropping(false);
00334 m()->refreshRepo();
00335 }