Logo Search packages:      
Sourcecode: qgit version File versions  Download package

revsview.cpp

/*
      Description: qgit revision list view

      Author: Marco Costalba (C) 2005-2006

      Copyright: See COPYING file that comes with this distribution

*/
#include <qlineedit.h>
#include <qtextbrowser.h>
#include <qlistview.h>
#include <qpainter.h>
#include <qstringlist.h>
#include <qlistbox.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include <qstatusbar.h>
#include <qheader.h>
#include <qpopupmenu.h>
#include <qcursor.h>
#include <qaction.h>
#include <qdragobject.h>
#include <qtoolbutton.h>
#include <qtabwidget.h>
#include "common.h"
#include "git.h"
#include "domain.h"
#include "treeview.h"
#include "listview.h"
#include "filelist.h"
#include "revbase.h"
#include "revdesc.h"
#include "patchbase.h"
#include "patchview.h"
#include "mainimpl.h"
#include "revsview.h"

RevsView::RevsView(MainImpl* mi, Git* g) : Domain(mi, g) {

      revTab = new TabRev(m());
      m()->tabWdg->addTab(revTab, "&Rev list");
      tabPosition = m()->tabWdg->count() - 1;

      listViewLog = new ListView(this, git, tab()->listViewLog, NULL);
      tab()->textBrowserDesc->setDomain(this);
      listBoxFiles = new ListBoxFiles(this, git, tab()->listBoxFiles);
      treeView = new TreeView(this, git, m()->treeView);

      connect(git, SIGNAL(newRevsAdded(const FileHistory*, const QValueVector<QString>&)),
      listViewLog, SLOT(on_newRevsAdded(const FileHistory*, const QValueVector<QString>&)));

      connect(git, SIGNAL(loadCompleted(const FileHistory*, const QString&)),
            this, SLOT(on_loadCompleted(const FileHistory*, const QString&)));

      connect(listViewLog, SIGNAL(lanesContextMenuRequested(const QStringList&,
              const QStringList&)), this, SLOT(on_lanesContextMenuRequested
              (const QStringList&, const QStringList&)));

      connect(listViewLog, SIGNAL(droppedRevisions(const QString&,
              const QStringList&, const QString&)), this, SLOT(on_droppedRevisions
              (const QString&, const QStringList&, const QString&)));

      connect(listViewLog, SIGNAL(contextMenu(const QString&, int)),
              this, SLOT(on_contextMenu(const QString&, int)));

      connect(treeView, SIGNAL(contextMenu(const QString&, int)),
              this, SLOT(on_contextMenu(const QString&, int)));

      connect(listBoxFiles, SIGNAL(contextMenu(const QString&, int)),
              this, SLOT(on_contextMenu(const QString&, int)));
}

RevsView::~RevsView() {

      if (!parent())
            return;

      delete linkedPatchView;
      delete revTab;
}

void RevsView::clear(bool keepState) {

      if (!keepState)
            st.clear();

      listViewLog->clear();
      tab()->textBrowserDesc->clear();
      listBoxFiles->clear();
      treeView->clear();
      updateLineEditSHA(true);
      if (linkedPatchView)
            linkedPatchView->clear();
}

void RevsView::setEnabled(bool b) {

      revTab->setEnabled(b);
      if (linkedPatchView)
            linkedPatchView->tab()->setEnabled(b);
}

void RevsView::viewPatch(bool newTab) {

      if (!newTab && linkedPatchView) {
            m()->tabWdg->setCurrentPage(linkedPatchView->tabPos());
            return;
      }
      PatchView* pv = new PatchView(m(), git);
      m()->tabWdg->setCurrentPage(pv->tabPos());

      if (!newTab) { // linkedPatchView == NULL
            linkedPatchView = pv;
            linkDomain(linkedPatchView);

            connect(m(), SIGNAL(highlightPatch(const QString&, bool)),
                  linkedPatchView, SLOT(on_highlightPatch(const QString&, bool)));

            connect(linkedPatchView->tab()->listBoxFiles, SIGNAL(doubleClicked(QListBoxItem*)),
                  m(), SLOT(on_fileList_doubleClicked(QListBoxItem*)));
      }
      connect(m(), SIGNAL(closeAllTabs()), pv, SLOT(on_closeAllTabs()));
      pv->st = st;
      UPDATE_DM_MASTER(pv);
}

void RevsView::on_loadCompleted(const FileHistory* fh, const QString& stats) {

      if (fh)
            return;

      if (st.sha().isEmpty()) { // point to first one in list
            if (tab()->listViewLog->firstChild()) {
                  SCRef firstRev(tab()->listViewLog->firstChild()->text(QGit::COMMIT_COL));
                  st.setSha(firstRev);
                  st.setSelectItem(true);
            }
      }
      UPDATE();
      QApplication::postEvent(this, new MessageEvent(stats));
}

bool RevsView::doUpdate() {

      try {
            EM_REGISTER(m()->exSetRepositoryCalled);
            EM_REGISTER(m()->exExiting);

            bool dataCleared = m()->lineEditSHA->text().isEmpty();
            bool found = listViewLog->update();

            if (!found) { // views are updated only if sha is found

                  const QString tmp("Sorry, revision " + st.sha() +
                                    " has not been found in main view");
                  m()->statusBar()->message(tmp);

            } else {
                  bool shaChanged = (st.sha(true) != st.sha(false));
                  bool diffToChanged = (st.diffToSha(true) != st.diffToSha(false));
                  bool allChanged = (st.allMergeFiles(true) != st.allMergeFiles(false));

                  if (shaChanged || dataCleared) {

                        updateLineEditSHA();
                        SCRef d(git->getDesc(st.sha(), m()->shortLogRE, m()->longLogRE));
                        tab()->textBrowserDesc->setText(d);
                        tab()->textBrowserDesc->setCursorPosition(0, 0);

                        m()->statusBar()->message(git->getRevInfo(st.sha(), false));
                  }
                  const RevFile* files = NULL;

                  if (shaChanged || diffToChanged || allChanged || dataCleared)
                        // could call processEvents()
                        files = git->getFiles(st.sha(), st.diffToSha(), st.allMergeFiles());

                  listBoxFiles->update(files);

                  if (m()->treeView->isVisible())
                        treeView->update(); // blocking call

                  if (st.selectItem()) {
                        bool isDir = treeView->isDir(st.fileName());
                        m()->updateContextActions(st.sha(), st.fileName(), isDir, found);
                  }
                  // at the end update diffs that is the slowest and must be
                  // run after update of file list for 'diff to sha' to work
                  if (linkedPatchView) {
                        linkedPatchView->st = st;
                        UPDATE_DM_MASTER(linkedPatchView); // async call
                  }
            }
            if (!found && dataCleared && tab()->listViewLog->currentItem()) {
                  // we are in an inconsistent state: list view current item is
                  // not selected and secondary panes are empty.
                  // This could happen as example after removing a tree filter.
                  // At least populate secondary panes
                  SCRef firstRev(tab()->listViewLog->currentItem()->text(QGit::COMMIT_COL));
                  st.setSha(firstRev);
                  st.setSelectItem(false);
                  customEvent(new UpdateDomainEvent(false)); // will be queued immediately
            }
            EM_REMOVE(m()->exExiting);
            EM_REMOVE(m()->exSetRepositoryCalled);

            return found;

      } catch(int i) {

            EM_REMOVE(m()->exExiting);
            EM_REMOVE(m()->exSetRepositoryCalled);

            if (EM_MATCH(i, m()->exSetRepositoryCalled, "update views")) {
                  EM_CHECK_PENDING;
                  return false;
            }
            if (EM_MATCH(i, m()->exExiting, "update main view")) {
                  EM_CHECK_PENDING;
                  return false;
            }
            const QString info("Exception \'" + EM_DESC(i) + "\' "
                               "not handled in main view update...re-throw");
            dbp("%1", info);
            throw i;
      }
}

void RevsView::updateLineEditSHA(bool clear) {

      QLineEdit* l = m()->lineEditSHA;

      if (clear)
            l->setText(""); // clears history

      else if (l->text() != st.sha()) {

            if (l->text().isEmpty())
                  l->setText(st.sha()); // first rev clears history
            else {
                  // setText() clears undo/redo history so
                  // we use clear() + insert() instead
                  l->clear();
                  l->insert(st.sha());
            }
      }
      m()->ActBack->setEnabled(l->isUndoAvailable());
      m()->ActForward->setEnabled(l->isRedoAvailable());
}

void RevsView::on_lanesContextMenuRequested(SCList parents, SCList childs) {

      QPopupMenu contextMenu;
      uint i = 0;
      QStringList::const_iterator it(childs.constBegin());
      for ( ; it != childs.constEnd(); ++it, i++)
            contextMenu.insertItem("Child: " + git->getShortLog(*it), i);

      for (it = parents.constBegin() ; it != parents.constEnd(); ++it, i++) {

            QString log(git->getShortLog(*it));
            if (log.isEmpty())
                  log = *it;

            contextMenu.insertItem("Parent: " + log, i);
      }
      int id = contextMenu.exec(QCursor::pos()); // modal exec
      if (id == -1)
            return;

      int cc = (int)childs.count();
      SCRef target((id < cc) ? childs[id] : parents[id - cc]);
      st.setSha(target);
      UPDATE();
}

void RevsView::on_droppedRevisions(const QString& sha, const QStringList& remoteRevs,
                                   const QString& remoteRepo) {

      if (isDropping()) // avoid reentrancy
            return;

      QDir dr;
      if (!dr.exists(remoteRepo))
            return;

      dr.setPath(m()->curDir + QGit::PATCHES_DIR);
      if (dr.exists()) {
            const QString tmp("Please remove stale import directory " + dr.absPath());
            m()->statusBar()->message(tmp);
            return;
      }
      bool commit, fold;
      if (!m()->askApplyPatchParameters(&commit, &fold))
            return;

      // ok, let's go.
      dr.setFilter(QDir::Files);
      setDropping(true);
      QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
      m()->raise();
      EM_PROCESS_EVENTS;

      uint applied = 0;
      for (uint i = 1; i <= remoteRevs.count(); i++) {

            SCRef sha(remoteRevs[remoteRevs.count() - i].section('@', 0, 0));
            const QStringList shaList(sha); // we create patches one by one

            const QString tmp(QString("Importing revision %1 of %2")
                              .arg(i).arg(remoteRevs.count()));
            m()->statusBar()->message(tmp);

            if (!git->formatPatch(shaList, dr.absPath(), remoteRepo))
                  break;

            dr.refresh();
            if (dr.count() != 1) {
                  qDebug("ASSERT in on_droppedRevisions: found %i files "
                         "in %s", dr.count(), QGit::PATCHES_DIR.latin1());
                  break;
            }
            bool ok = git->applyPatchFile(dr.absFilePath(dr[0]), commit, fold, !Git::optSign);
            dr.remove(dr[0]);
            if (!ok)
                  break;

            applied = i;
      }
      if (applied != remoteRevs.count())
            m()->statusBar()->message("Failed to import revision " + sha);
      else
            m()->statusBar()->clear();

      if (!commit && (applied > 0))
            git->resetCommits(applied);

      dr.rmdir(dr.absPath());
      QApplication::restoreOverrideCursor();
      setDropping(false);
      m()->refreshRepo();
}

Generated by  Doxygen 1.6.0   Back to index