Replaced QGit icons
[qgit4/bulb.git] / src / domain.cpp
blob362b59f9e37e6a2a3c03af62de5329eb33740f23
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 "exceptionmanager.h"
11 #include "mainimpl.h"
12 #include "git.h"
13 #include "domain.h"
15 using namespace QGit;
17 void StateInfo::S::clear() {
19 sha = fn = dtSha = "";
20 isM = allM = false;
21 sel = true;
24 bool StateInfo::S::operator==(const StateInfo::S& st) const {
26 if (&st == this)
27 return true;
29 return ( sha == st.sha
30 && fn == st.fn
31 && dtSha == st.dtSha
32 && sel == st.sel
33 && isM == st.isM
34 && allM == st.allM);
37 bool StateInfo::S::operator!=(const StateInfo::S& st) const {
39 return !(StateInfo::S::operator==(st));
42 void StateInfo::clear() {
44 nextS.clear();
45 curS.clear();
46 prevS.clear();
47 isLocked = false;
50 StateInfo& StateInfo::operator=(const StateInfo& newState) {
52 if (&newState != this) {
53 if (isLocked)
54 nextS = newState.curS;
55 else
56 curS = newState.curS; // prevS is mot modified to allow a rollback
58 return *this;
61 bool StateInfo::operator==(const StateInfo& newState) const {
63 if (&newState == this)
64 return true;
66 return (curS == newState.curS); // compare is made on curS only
69 bool StateInfo::operator!=(const StateInfo& newState) const {
71 return !(StateInfo::operator==(newState));
74 bool StateInfo::isChanged(uint what) const {
76 bool ret = false;
77 if (what & SHA)
78 ret = (sha(true) != sha(false));
80 if (!ret && (what & FILE_NAME))
81 ret = (fileName(true) != fileName(false));
83 if (!ret && (what & DIFF_TO_SHA))
84 ret = (diffToSha(true) != diffToSha(false));
86 if (!ret && (what & ALL_MERGE_FILES))
87 ret = (allMergeFiles(true) != allMergeFiles(false));
89 return ret;
92 // ************************* Domain ****************************
94 Domain::Domain(MainImpl* m, Git* g, bool isMain) : QObject(m), git(g) {
96 EM_INIT(exDeleteRequest, "Deleting domain");
97 EM_INIT(exCancelRequest, "Canceling update");
99 fileHistory = new FileHistory(this, git);
100 if (isMain)
101 git->setDefaultModel(fileHistory);
103 st.clear();
104 busy = readyToDrag = dragging = dropping = linked = false;
105 popupType = 0;
106 container = new QWidget(NULL); // will be reparented to m()->tabWdg
109 Domain::~Domain() {
111 if (!parent())
112 return;
114 // remove before to delete, avoids a Qt warning in QInputContext()
115 m()->tabWdg->removeTab(m()->tabWdg->indexOf(container));
116 delete container;
119 void Domain::clear(bool complete) {
121 if (complete)
122 st.clear();
124 fileHistory->clear();
127 void Domain::on_closeAllTabs() {
129 delete this; // must be sync, deleteLater() does not work
132 void Domain::deleteWhenDone() {
134 if (!EM_IS_PENDING(exDeleteRequest))
135 EM_RAISE(exDeleteRequest);
137 emit cancelDomainProcesses();
139 on_deleteWhenDone();
142 void Domain::on_deleteWhenDone() {
144 if (!EM_IS_PENDING(exDeleteRequest))
145 deleteLater();
146 else
147 QTimer::singleShot(20, this, SLOT(on_deleteWhenDone()));
150 void Domain::setThrowOnDelete(bool b) {
152 if (b)
153 EM_REGISTER(exDeleteRequest);
154 else
155 EM_REMOVE(exDeleteRequest);
158 bool Domain::isThrowOnDeleteRaised(int excpId, SCRef curContext) {
160 return EM_MATCH(excpId, exDeleteRequest, curContext);
163 MainImpl* Domain::m() const {
165 return static_cast<MainImpl*>(parent());
168 void Domain::showStatusBarMessage(const QString& msg, int timeout) {
170 m()->statusBar()->showMessage(msg, timeout);
173 void Domain::setTabCaption(const QString& caption) {
175 int idx = m()->tabWdg->indexOf(container);
176 m()->tabWdg->setTabText(idx, caption);
179 bool Domain::setReadyToDrag(bool b) {
181 readyToDrag = (b && !busy && !dragging && !dropping);
182 return readyToDrag;
185 bool Domain::setDragging(bool b) {
187 bool dragFinished = (!b && dragging);
189 dragging = (b && readyToDrag && !dropping);
191 if (dragging)
192 readyToDrag = false;
194 if (dragFinished)
195 flushQueue();
197 return dragging;
200 void Domain::unlinkDomain(Domain* d) {
202 d->linked = false;
203 while (d->disconnect(SIGNAL(updateRequested(StateInfo)), this))
204 ;// a signal is emitted for every connection you make,
205 // so if you duplicate a connection, two signals will be emitted.
208 void Domain::linkDomain(Domain* d) {
210 unlinkDomain(d); // be sure only one connection is active
211 connect(d, SIGNAL(updateRequested(StateInfo)), this, SLOT(on_updateRequested(StateInfo)));
212 d->linked = true;
215 void Domain::on_updateRequested(StateInfo newSt) {
217 st = newSt;
218 UPDATE();
221 bool Domain::flushQueue() {
222 // during dragging any state update is queued, so try to flush pending now
224 if (!busy && st.flushQueue()) {
225 UPDATE();
226 return true;
228 return false;
231 bool Domain::event(QEvent* e) {
233 bool fromMaster = false;
235 switch (e->type()) {
236 case UPD_DM_MST_EV:
237 fromMaster = true;
238 // fall through
239 case UPD_DM_EV:
240 update(fromMaster, ((UpdateDomainEvent*)e)->isForced());
241 break;
242 case MSG_EV:
243 if (!busy && !st.requestPending())
244 QApplication::postEvent(m(), new MessageEvent(((MessageEvent*)e)->myData()));
245 else // waiting for the end of updating
246 statusBarRequest = ((MessageEvent*)e)->myData();
247 break;
248 default:
249 break;
251 return QObject::event(e);
254 void Domain::populateState() {
256 const Rev* r = git->revLookup(st.sha());
257 if (r)
258 st.setIsMerge(r->parentsCount() > 1);
261 void Domain::update(bool fromMaster, bool force) {
263 if (busy && st.requestPending()) {
264 // quick exit current (obsoleted) update but only if state
265 // is going to change. Without this check calling update()
266 // many times with the same data nullify the update
267 EM_RAISE(exCancelRequest);
268 emit cancelDomainProcesses();
270 if (busy || dragging)
271 return;
273 if (linked && !fromMaster) {
274 // in this case let the update to fall down from master domain
275 StateInfo tmp(st);
276 st.rollBack(); // we don't want to filter out next update sent from master
277 emit updateRequested(tmp);
278 return;
280 try {
281 EM_REGISTER_Q(exCancelRequest); // quiet, no messages when thrown
282 setThrowOnDelete(true);
283 git->setThrowOnStop(true);
284 git->setCurContext(this);
285 busy = true;
286 setReadyToDrag(false); // do not start any drag while updating
287 populateState(); // complete any missing state information
288 st.setLock(true); // any state change will be queued now
290 if (doUpdate(force))
291 st.commit();
292 else
293 st.rollBack();
295 st.setLock(false);
296 busy = false;
297 if (git->curContext() != this)
298 qDebug("ASSERT in Domain::update, context is %p "
299 "instead of %p", (void*)git->curContext(), (void*)this);
301 git->setCurContext(NULL);
302 git->setThrowOnStop(false);
303 setThrowOnDelete(false);
304 EM_REMOVE(exCancelRequest);
306 } catch (int i) {
309 If we have a cancel request because of a new update is in queue we
310 cannot roolback current state to avoid new update is filtered out
311 in case rolled back sha and new sha are the same.
312 This could happen with arrow navigation:
314 sha -> go UP (new sha) -> go DOWN (cancel) -> rollback to sha
316 And pending request 'sha' is filtered out leaving us in an
317 inconsistent state.
319 st.commit();
320 st.setLock(false);
321 busy = false;
322 git->setCurContext(NULL);
323 git->setThrowOnStop(false);
324 setThrowOnDelete(false);
325 EM_REMOVE(exCancelRequest);
327 if (QApplication::overrideCursor())
328 QApplication::restoreOverrideCursor();
330 QString context("updating ");
331 if (git->isThrowOnStopRaised(i, context + metaObject()->className())) {
332 EM_THROW_PENDING;
333 return;
335 if (isThrowOnDeleteRaised(i, context + metaObject()->className())) {
336 EM_THROW_PENDING;
337 return;
339 if (i == exCancelRequest)
340 EM_THROW_PENDING;
341 // do not return
342 else {
343 const QString info("Exception \'" + EM_DESC(i) + "\' "
344 "not handled in init...re-throw");
345 dbs(info);
346 throw;
349 bool nextRequestPending = flushQueue();
351 if (!nextRequestPending && !statusBarRequest.isEmpty()) {
352 // update status bar when we are sure no more work is pending
353 QApplication::postEvent(m(), new MessageEvent(statusBarRequest));
354 statusBarRequest = "";
356 if (!nextRequestPending && popupType)
357 sendPopupEvent();
360 void Domain::sendPopupEvent() {
362 // call an async context popup, must be executed
363 // after returning to event loop
364 DeferredPopupEvent* e = new DeferredPopupEvent(popupData, popupType);
365 QApplication::postEvent(m(), e);
366 popupType = 0;
369 void Domain::on_contextMenu(const QString& data, int type) {
371 popupType = type;
372 popupData = data;
374 if (busy)
375 return; // we are in the middle of an update
377 // if list view is already updated pop-up
378 // context menu, otherwise it means update()
379 // has still not been called, a pop-up request,
380 // will be fired up at the end of next update()
381 if ((type == POPUP_LIST_EV) && (data != st.sha()))
382 return;
384 sendPopupEvent();