Version 2.6
[qgit4/redivivus.git] / src / domain.cpp
blob60daf1c950c20ea29bde45b95ad33929f0c5b079
1 /*
2 Author: Marco Costalba (C) 2005-2007
4 Copyright: See COPYING file that comes with this distribution
6 */
7 #include <QApplication>
8 #include <QStatusBar>
9 #include <QTimer>
10 #include "FileHistory.h"
11 #include "exceptionmanager.h"
12 #include "mainimpl.h"
13 #include "git.h"
14 #include "domain.h"
16 using namespace QGit;
18 void StateInfo::S::clear() {
20 sha = fn = dtSha = "";
21 isM = allM = false;
22 sel = true;
25 bool StateInfo::S::operator==(const StateInfo::S& st) const {
27 if (&st == this)
28 return true;
30 return ( sha == st.sha
31 && fn == st.fn
32 && dtSha == st.dtSha
33 && sel == st.sel
34 && isM == st.isM
35 && allM == st.allM);
38 bool StateInfo::S::operator!=(const StateInfo::S& st) const {
40 return !(StateInfo::S::operator==(st));
43 void StateInfo::clear() {
45 nextS.clear();
46 curS.clear();
47 prevS.clear();
48 isLocked = false;
51 StateInfo& StateInfo::operator=(const StateInfo& newState) {
53 if (&newState != this) {
54 if (isLocked)
55 nextS = newState.curS;
56 else
57 curS = newState.curS; // prevS is mot modified to allow a rollback
59 return *this;
62 bool StateInfo::operator==(const StateInfo& newState) const {
64 if (&newState == this)
65 return true;
67 return (curS == newState.curS); // compare is made on curS only
70 bool StateInfo::operator!=(const StateInfo& newState) const {
72 return !(StateInfo::operator==(newState));
75 bool StateInfo::isChanged(uint what) const {
77 bool ret = false;
78 if (what & SHA)
79 ret = (sha(true) != sha(false));
81 if (!ret && (what & FILE_NAME))
82 ret = (fileName(true) != fileName(false));
84 if (!ret && (what & DIFF_TO_SHA))
85 ret = (diffToSha(true) != diffToSha(false));
87 if (!ret && (what & ALL_MERGE_FILES))
88 ret = (allMergeFiles(true) != allMergeFiles(false));
90 return ret;
93 // ************************* Domain ****************************
95 Domain::Domain(MainImpl* m, Git* g, bool isMain) : QObject(m), git(g) {
97 EM_INIT(exDeleteRequest, "Deleting domain");
98 EM_INIT(exCancelRequest, "Canceling update");
100 fileHistory = new FileHistory(this, git);
101 if (isMain)
102 git->setDefaultModel(fileHistory);
104 st.clear();
105 busy = readyToDrag = dragging = dropping = linked = false;
106 popupType = 0;
107 container = new QWidget(NULL); // will be reparented to m()->tabWdg
110 Domain::~Domain() {
112 if (!parent())
113 return;
115 // remove before to delete, avoids a Qt warning in QInputContext()
116 m()->tabWdg->removeTab(m()->tabWdg->indexOf(container));
117 container->deleteLater();
120 void Domain::clear(bool complete) {
122 if (complete)
123 st.clear();
125 fileHistory->clear();
128 void Domain::on_closeAllTabs() {
130 delete this; // must be sync, deleteLater() does not work
133 void Domain::deleteWhenDone() {
135 if (!EM_IS_PENDING(exDeleteRequest))
136 EM_RAISE(exDeleteRequest);
138 emit cancelDomainProcesses();
140 on_deleteWhenDone();
143 void Domain::on_deleteWhenDone() {
145 if (!EM_IS_PENDING(exDeleteRequest))
146 deleteLater();
147 else
148 QTimer::singleShot(20, this, SLOT(on_deleteWhenDone()));
151 void Domain::setThrowOnDelete(bool b) {
153 if (b)
154 EM_REGISTER(exDeleteRequest);
155 else
156 EM_REMOVE(exDeleteRequest);
159 bool Domain::isThrowOnDeleteRaised(int excpId, SCRef curContext) {
161 return EM_MATCH(excpId, exDeleteRequest, curContext);
164 MainImpl* Domain::m() const {
166 return static_cast<MainImpl*>(parent());
169 void Domain::showStatusBarMessage(const QString& msg, int timeout) {
171 m()->statusBar()->showMessage(msg, timeout);
174 void Domain::setTabCaption(const QString& caption) {
176 int idx = m()->tabWdg->indexOf(container);
177 m()->tabWdg->setTabText(idx, caption);
180 bool Domain::setReadyToDrag(bool b) {
182 readyToDrag = (b && !busy && !dragging && !dropping);
183 return readyToDrag;
186 bool Domain::setDragging(bool b) {
188 bool dragFinished = (!b && dragging);
190 dragging = (b && readyToDrag && !dropping);
192 if (dragging)
193 readyToDrag = false;
195 if (dragFinished)
196 flushQueue();
198 return dragging;
201 void Domain::unlinkDomain(Domain* d) {
203 d->linked = false;
204 while (d->disconnect(SIGNAL(updateRequested(StateInfo)), this))
205 ;// a signal is emitted for every connection you make,
206 // so if you duplicate a connection, two signals will be emitted.
209 void Domain::linkDomain(Domain* d) {
211 unlinkDomain(d); // be sure only one connection is active
212 connect(d, SIGNAL(updateRequested(StateInfo)), this, SLOT(on_updateRequested(StateInfo)));
213 d->linked = true;
216 void Domain::on_updateRequested(StateInfo newSt) {
218 st = newSt;
219 UPDATE();
222 bool Domain::flushQueue() {
223 // during dragging any state update is queued, so try to flush pending now
225 if (!busy && st.flushQueue()) {
226 UPDATE();
227 return true;
229 return false;
232 bool Domain::event(QEvent* e) {
234 bool fromMaster = false;
236 switch (e->type()) {
237 case UPD_DM_MST_EV:
238 fromMaster = true;
239 // fall through
240 case UPD_DM_EV:
241 update(fromMaster, ((UpdateDomainEvent*)e)->isForced());
242 break;
243 case MSG_EV:
244 if (!busy && !st.requestPending())
245 QApplication::postEvent(m(), new MessageEvent(((MessageEvent*)e)->myData()));
246 else // waiting for the end of updating
247 statusBarRequest = ((MessageEvent*)e)->myData();
248 break;
249 default:
250 break;
252 return QObject::event(e);
255 void Domain::populateState() {
257 const Rev* r = git->revLookup(st.sha());
258 if (r)
259 st.setIsMerge(r->parentsCount() > 1);
262 void Domain::update(bool fromMaster, bool force) {
264 if (busy && st.requestPending()) {
265 // quick exit current (obsoleted) update but only if state
266 // is going to change. Without this check calling update()
267 // many times with the same data nullify the update
268 EM_RAISE(exCancelRequest);
269 emit cancelDomainProcesses();
271 if (busy || dragging)
272 return;
274 if (linked && !fromMaster) {
275 // in this case let the update to fall down from master domain
276 StateInfo tmp(st);
277 st.rollBack(); // we don't want to filter out next update sent from master
278 emit updateRequested(tmp);
279 return;
281 try {
282 EM_REGISTER_Q(exCancelRequest); // quiet, no messages when thrown
283 setThrowOnDelete(true);
284 git->setThrowOnStop(true);
285 git->setCurContext(this);
286 busy = true;
287 setReadyToDrag(false); // do not start any drag while updating
288 populateState(); // complete any missing state information
289 st.setLock(true); // any state change will be queued now
291 if (doUpdate(force))
292 st.commit();
293 else
294 st.rollBack();
296 st.setLock(false);
297 busy = false;
298 if (git->curContext() != this)
299 qDebug("ASSERT in Domain::update, context is %p "
300 "instead of %p", (void*)git->curContext(), (void*)this);
302 git->setCurContext(NULL);
303 git->setThrowOnStop(false);
304 setThrowOnDelete(false);
305 EM_REMOVE(exCancelRequest);
307 } catch (int i) {
310 If we have a cancel request because of a new update is in queue we
311 cannot roolback current state to avoid new update is filtered out
312 in case rolled back sha and new sha are the same.
313 This could happen with arrow navigation:
315 sha -> go UP (new sha) -> go DOWN (cancel) -> rollback to sha
317 And pending request 'sha' is filtered out leaving us in an
318 inconsistent state.
320 st.commit();
321 st.setLock(false);
322 busy = false;
323 git->setCurContext(NULL);
324 git->setThrowOnStop(false);
325 setThrowOnDelete(false);
326 EM_REMOVE(exCancelRequest);
328 if (QApplication::overrideCursor())
329 QApplication::restoreOverrideCursor();
331 QString context("updating ");
332 if (git->isThrowOnStopRaised(i, context + metaObject()->className())) {
333 EM_THROW_PENDING;
334 return;
336 if (isThrowOnDeleteRaised(i, context + metaObject()->className())) {
337 EM_THROW_PENDING;
338 return;
340 if (i == exCancelRequest)
341 EM_THROW_PENDING;
342 // do not return
343 else {
344 const QString info("Exception \'" + EM_DESC(i) + "\' "
345 "not handled in init...re-throw");
346 dbs(info);
347 throw;
350 bool nextRequestPending = flushQueue();
352 if (!nextRequestPending && !statusBarRequest.isEmpty()) {
353 // update status bar when we are sure no more work is pending
354 QApplication::postEvent(m(), new MessageEvent(statusBarRequest));
355 statusBarRequest = "";
357 if (!nextRequestPending && popupType)
358 sendPopupEvent();
361 void Domain::sendPopupEvent() {
363 // call an async context popup, must be executed
364 // after returning to event loop
365 DeferredPopupEvent* e = new DeferredPopupEvent(popupData, popupType);
366 QApplication::postEvent(m(), e);
367 popupType = 0;
370 void Domain::on_contextMenu(const QString& data, int type) {
372 popupType = type;
373 popupData = data;
375 if (busy)
376 return; // we are in the middle of an update
378 // if list view is already updated pop-up
379 // context menu, otherwise it means update()
380 // has still not been called, a pop-up request,
381 // will be fired up at the end of next update()
382 if ((type == POPUP_LIST_EV) && (data != st.sha()))
383 return;
385 sendPopupEvent();