2 Author: Marco Costalba (C) 2005-2007
4 Copyright: See COPYING file that comes with this distribution
7 #include <QApplication>
10 #include "FileHistory.h"
11 #include "exceptionmanager.h"
18 void StateInfo::S::clear() {
20 sha
= fn
= dtSha
= "";
25 bool StateInfo::S::operator==(const StateInfo::S
& st
) const {
30 return ( sha
== st
.sha
38 bool StateInfo::S::operator!=(const StateInfo::S
& st
) const {
40 return !(StateInfo::S::operator==(st
));
43 void StateInfo::clear() {
51 StateInfo
& StateInfo::operator=(const StateInfo
& newState
) {
53 if (&newState
!= this) {
55 nextS
= newState
.curS
;
57 curS
= newState
.curS
; // prevS is mot modified to allow a rollback
62 bool StateInfo::operator==(const StateInfo
& newState
) const {
64 if (&newState
== this)
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 {
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));
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
);
102 git
->setDefaultModel(fileHistory
);
105 busy
= readyToDrag
= dragging
= dropping
= linked
= false;
107 container
= new QWidget(NULL
); // will be reparented to m()->tabWdg
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
) {
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();
143 void Domain::on_deleteWhenDone() {
145 if (!EM_IS_PENDING(exDeleteRequest
))
148 QTimer::singleShot(20, this, SLOT(on_deleteWhenDone()));
151 void Domain::setThrowOnDelete(bool b
) {
154 EM_REGISTER(exDeleteRequest
);
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
);
186 bool Domain::setDragging(bool b
) {
188 bool dragFinished
= (!b
&& dragging
);
190 dragging
= (b
&& readyToDrag
&& !dropping
);
201 void Domain::unlinkDomain(Domain
* d
) {
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
)));
216 void Domain::on_updateRequested(StateInfo newSt
) {
222 bool Domain::flushQueue() {
223 // during dragging any state update is queued, so try to flush pending now
225 if (!busy
&& st
.flushQueue()) {
232 bool Domain::event(QEvent
* e
) {
234 bool fromMaster
= false;
241 update(fromMaster
, ((UpdateDomainEvent
*)e
)->isForced());
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();
252 return QObject::event(e
);
255 void Domain::populateState() {
257 const Rev
* r
= git
->revLookup(st
.sha());
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
)
274 if (linked
&& !fromMaster
) {
275 // in this case let the update to fall down from master domain
277 st
.rollBack(); // we don't want to filter out next update sent from master
278 emit
updateRequested(tmp
);
282 EM_REGISTER_Q(exCancelRequest
); // quiet, no messages when thrown
283 setThrowOnDelete(true);
284 git
->setThrowOnStop(true);
285 git
->setCurContext(this);
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
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
);
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
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())) {
336 if (isThrowOnDeleteRaised(i
, context
+ metaObject()->className())) {
340 if (i
== exCancelRequest
)
344 const QString
info("Exception \'" + EM_DESC(i
) + "\' "
345 "not handled in init...re-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
)
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
);
370 void Domain::on_contextMenu(const QString
& data
, int type
) {
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()))