domain.cpp

Go to the documentation of this file.
00001 /*
00002         Author: Marco Costalba (C) 2005-2006
00003 
00004         Copyright: See COPYING file that comes with this distribution
00005 
00006 */
00007 #include <qapplication.h>
00008 #include <qlistview.h>
00009 #include "exceptionmanager.h"
00010 #include "mainimpl.h"
00011 #include "git.h"
00012 #include "domain.h"
00013 
00014 using namespace QGit;
00015 
00016 void StateInfo::S::clear() {
00017 
00018         sha = fn = dtSha = "";
00019         isM = allM = false;
00020         sel = true;
00021 }
00022 
00023 bool StateInfo::S::operator==(const StateInfo::S& st) const {
00024 
00025         if (&st == this)
00026                 return true;
00027 
00028         return (   sha   == st.sha
00029                 && fn    == st.fn
00030                 && dtSha == st.dtSha
00031                 && sel   == st.sel
00032                 && isM   == st.isM
00033                 && allM  == st.allM);
00034 }
00035 
00036 bool StateInfo::S::operator!=(const StateInfo::S& st) const {
00037 
00038         return !(StateInfo::S::operator==(st));
00039 }
00040 
00041 void StateInfo::clear() {
00042 
00043         nextS.clear();
00044         curS.clear();
00045         prevS.clear();
00046         isLocked = false;
00047 }
00048 
00049 StateInfo& StateInfo::operator=(const StateInfo& newState) {
00050 
00051         if (&newState != this) {
00052                 if (isLocked)
00053                         nextS = newState.curS;
00054                 else
00055                         curS = newState.curS; // prevS is mot modified to allow a rollback
00056         }
00057         return *this;
00058 }
00059 
00060 bool StateInfo::operator==(const StateInfo& newState) const {
00061 
00062         if (&newState == this)
00063                 return true;
00064 
00065         return (curS == newState.curS); // compare is made on curS only
00066 }
00067 
00068 bool StateInfo::operator!=(const StateInfo& newState) const {
00069 
00070         return !(StateInfo::operator==(newState));
00071 }
00072 
00073 bool StateInfo::isChanged(uint what) const {
00074 
00075         bool ret = false;
00076         if (what & SHA)
00077                 ret = (sha(true) != sha(false));
00078 
00079         if (!ret && (what & FILE_NAME))
00080                 ret = (fileName(true) != fileName(false));
00081 
00082         if (!ret && (what & DIFF_TO_SHA))
00083                 ret = (diffToSha(true) != diffToSha(false));
00084 
00085         if (!ret && (what & ALL_MERGE_FILES))
00086                 ret = (allMergeFiles(true) != allMergeFiles(false));
00087 
00088         return ret;
00089 }
00090 
00091 // ************************* Domain ****************************
00092 
00093 Domain::Domain(MainImpl* m, Git* g) : QObject(m), git(g) {
00094 
00095         EM_INIT(exDeleteRequest, "Deleting domain");
00096         EM_INIT(exCancelRequest, "Canceling update");
00097 
00098         st.clear();
00099         busy = readyToDrag = dragging = dropping = linked = false;
00100         popupType = 0;
00101         tabPosition = -1;
00102 
00103         connect(m, SIGNAL(tabClosed(int)), this, SLOT(on_tabClosed(int)));
00104 }
00105 
00106 void Domain::on_closeAllTabs() {
00107 
00108         delete this; // must be sync, deleteLater() does not work
00109 }
00110 
00111 void Domain::deleteWhenDone() {
00112 
00113         if (!EM_IS_PENDING(exDeleteRequest))
00114                 EM_RAISE(exDeleteRequest);
00115 
00116         emit cancelDomainProcesses();
00117 
00118         on_deleteWhenDone();
00119 }
00120 
00121 void Domain::on_deleteWhenDone() {
00122 
00123         if (!EM_IS_PENDING(exDeleteRequest))
00124                 deleteLater();
00125         else
00126                 QTimer::singleShot(20, this, SLOT(on_deleteWhenDone()));
00127 }
00128 
00129 void Domain::on_tabClosed(int tabPos) {
00130 
00131         if (tabPosition > tabPos)
00132                 tabPosition--;
00133 }
00134 
00135 void Domain::setThrowOnDelete(bool b) {
00136 
00137         if (b)
00138                 EM_REGISTER(exDeleteRequest);
00139         else
00140                 EM_REMOVE(exDeleteRequest);
00141 }
00142 
00143 bool Domain::isThrowOnDeleteRaised(int excpId, SCRef curContext) {
00144 
00145         return EM_MATCH(excpId, exDeleteRequest, curContext);
00146 }
00147 
00148 MainImpl* Domain::m() const {
00149 
00150         return static_cast<MainImpl*>(parent());
00151 }
00152 
00153 const QString Domain::dragHostName() const {
00154 
00155         return QString::fromLatin1("@") + m()->curWorkDir();
00156 }
00157 
00158 bool Domain::setReadyToDrag(bool b) {
00159 
00160         readyToDrag = (b && !busy && !dragging && !dropping);
00161         return readyToDrag;
00162 }
00163 
00164 bool Domain::setDragging(bool b) {
00165 
00166         bool dragFinished = (!b && dragging);
00167 
00168         dragging = (b && readyToDrag && !dropping);
00169 
00170         if (dragging)
00171                 readyToDrag = false;
00172 
00173         if (dragFinished)
00174                 flushQueue();
00175 
00176         return dragging;
00177 }
00178 
00179 void Domain::unlinkDomain(Domain* d) {
00180 
00181         d->linked = false;
00182         while (d->disconnect(SIGNAL(updateRequested(StateInfo)), this))
00183                 ;// a signal is emitted for every connection you make,
00184                  // so if you duplicate a connection, two signals will be emitted.
00185 }
00186 
00187 void Domain::linkDomain(Domain* d) {
00188 
00189         unlinkDomain(d); // be sure only one connection is active
00190         connect(d, SIGNAL(updateRequested(StateInfo)), this, SLOT(on_updateRequested(StateInfo)));
00191         d->linked = true;
00192 }
00193 
00194 void Domain::on_updateRequested(StateInfo newSt) {
00195 
00196         st = newSt;
00197         UPDATE();
00198 }
00199 
00200 bool Domain::flushQueue() {
00201 // during dragging any state update is queued, so try to flush pending now
00202 
00203         if (!busy && st.flushQueue()) {
00204                 UPDATE();
00205                 return true;
00206         }
00207         return false;
00208 }
00209 
00210 void Domain::customEvent(QCustomEvent* e) {
00211 
00212         bool fromMaster = false;
00213 
00214         switch (e->type()) {
00215         case UPD_DM_MST_EV:
00216                 fromMaster = true;
00217                 // fall through
00218         case UPD_DM_EV:
00219                 update(fromMaster, ((UpdateDomainEvent*)e)->isForced());
00220                 break;
00221         case MSG_EV:
00222                 if (!busy && !st.requestPending())
00223                         QApplication::postEvent(m(), new MessageEvent(((MessageEvent*)e)->myData()));
00224                 else // waiting for the end of updating
00225                         statusBarRequest = ((MessageEvent*)e)->myData();
00226                 break;
00227         default:
00228                 break;
00229         }
00230         QObject::customEvent(e);
00231 }
00232 
00233 void Domain::populateState() {
00234 
00235         const Rev* r = git->revLookup(st.sha());
00236         if (r)
00237                 st.setIsMerge(r->parentsCount() > 1);
00238 }
00239 
00240 void Domain::update(bool fromMaster, bool force) {
00241 
00242         if (busy && st.requestPending()) {
00243                 // quick exit current (obsoleted) update but only if state
00244                 // is going to change. Without this check calling update()
00245                 // many times with the same data nullify the update
00246                 EM_RAISE(exCancelRequest);
00247                 emit cancelDomainProcesses();
00248         }
00249         if (busy || dragging)
00250                 return;
00251 
00252         if (linked && !fromMaster) {
00253                 // in this case let the update to fall down from master domain
00254                 StateInfo tmp(st);
00255                 st.rollBack(); // we don't want to filter out next update sent from master
00256                 emit updateRequested(tmp);
00257                 return;
00258         }
00259         try {
00260                 EM_REGISTER_Q(exCancelRequest); // quiet, no messages when thrown
00261                 setThrowOnDelete(true);
00262                 git->setThrowOnStop(true);
00263                 git->setCurContext(this);
00264                 busy = true;
00265                 setReadyToDrag(false); // do not start any drag while updating
00266                 populateState(); // complete any missing state information
00267                 st.setLock(true); // any state change will be queued now
00268 
00269                 if (doUpdate(force))
00270                         st.commit();
00271                 else
00272                         st.rollBack();
00273 
00274                 st.setLock(false);
00275                 busy = false;
00276                 if (git->curContext() != this)
00277                         qDebug("ASSERT in Domain::update, context is %p "
00278                                "instead of %p", (void*)git->curContext(), (void*)this);
00279 
00280                 git->setCurContext(NULL);
00281                 git->setThrowOnStop(false);
00282                 setThrowOnDelete(false);
00283                 EM_REMOVE(exCancelRequest);
00284 
00285         } catch (int i) {
00286 
00287                 /*
00288                    If we have a cancel request because of a new update is in queue we
00289                    cannot roolback current state to avoid new update is filtered out
00290                    in case rolled back sha and new sha are the same.
00291                    This could happen with arrow navigation:
00292 
00293                    sha -> go UP (new sha) -> go DOWN (cancel) -> rollback to sha
00294 
00295                    And pending request 'sha' is filtered out leaving us in an
00296                    inconsistent state.
00297                 */
00298                 st.commit();
00299                 st.setLock(false);
00300                 busy = false;
00301                 git->setCurContext(NULL);
00302                 git->setThrowOnStop(false);
00303                 setThrowOnDelete(false);
00304                 EM_REMOVE(exCancelRequest);
00305 
00306                 if (QApplication::overrideCursor())
00307                         QApplication::restoreOverrideCursor();
00308 
00309                 QString context("updating ");
00310                 if (git->isThrowOnStopRaised(i,  context + className())) {
00311                         EM_THROW_PENDING;
00312                         return;
00313                 }
00314                 if (isThrowOnDeleteRaised(i,  context + className())) {
00315                         EM_THROW_PENDING;
00316                         return;
00317                 }
00318                 if (i == exCancelRequest)
00319                         EM_THROW_PENDING;
00320                         // do not return
00321                 else {
00322                         const QString info("Exception \'" + EM_DESC(i) + "\' "
00323                                            "not handled in init...re-throw");
00324                         dbs(info);
00325                         throw;
00326                 }
00327         }
00328         bool nextRequestPending = flushQueue();
00329 
00330         if (!nextRequestPending && !statusBarRequest.isEmpty()) {
00331                 // update status bar when we are sure no more work is pending
00332                 QApplication::postEvent(m(), new MessageEvent(statusBarRequest));
00333                 statusBarRequest = "";
00334         }
00335         if (!nextRequestPending && popupType)
00336                 sendPopupEvent();
00337 }
00338 
00339 void Domain::sendPopupEvent() {
00340 
00341         // call an async context popup, must be executed
00342         // after returning to event loop
00343         DeferredPopupEvent* e = new DeferredPopupEvent(popupData, popupType);
00344         QApplication::postEvent(m(), e);
00345         popupType = 0;
00346 }
00347 
00348 void Domain::on_contextMenu(const QString& data, int type) {
00349 
00350         popupType = type;
00351         popupData = data;
00352 
00353         if (busy)
00354                 return; // we are in the middle of an update
00355 
00356         // if list view is already updated pop-up
00357         // context menu, otherwise it means update()
00358         // has still not been called, a pop-up request,
00359         // will be fired up at the end of next update()
00360         if ((type == POPUP_LIST_EV) && (data != st.sha()))
00361                 return;
00362 
00363         sendPopupEvent();
00364 }

Generated on Fri Dec 7 21:57:37 2007 for QGit by  doxygen 1.5.3