Generated on Thu Mar 22 10:39:32 2012 for Gecode by doxygen 1.6.3

treecanvas.cpp

Go to the documentation of this file.
00001 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
00002 /*
00003  *  Main authors:
00004  *     Guido Tack <tack@gecode.org>
00005  *
00006  *  Copyright:
00007  *     Guido Tack, 2006
00008  *
00009  *  Last modified:
00010  *     $Date: 2011-08-30 11:40:00 +0200 (Tue, 30 Aug 2011) $ by $Author: tack $
00011  *     $Revision: 12374 $
00012  *
00013  *  This file is part of Gecode, the generic constraint
00014  *  development environment:
00015  *     http://www.gecode.org
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining
00018  * a copy of this software and associated documentation files (the
00019  * "Software"), to deal in the Software without restriction, including
00020  * without limitation the rights to use, copy, modify, merge, publish,
00021  * distribute, sublicense, and/or sell copies of the Software, and to
00022  * permit persons to whom the Software is furnished to do so, subject to
00023  * the following conditions:
00024  *
00025  * The above copyright notice and this permission notice shall be
00026  * included in all copies or substantial portions of the Software.
00027  *
00028  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00029  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00030  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00031  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00032  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00033  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00034  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00035  *
00036  */
00037 
00038 #include <QtGui/QPainter>
00039 
00040 #include <stack>
00041 #include <fstream>
00042 
00043 #include <gecode/gist/treecanvas.hh>
00044 
00045 #include <gecode/gist/nodevisitor.hh>
00046 #include <gecode/gist/visualnode.hh>
00047 #include <gecode/gist/drawingcursor.hh>
00048 
00049 #include <gecode/search.hh>
00050 #include <gecode/search/support.hh>
00051 
00052 namespace Gecode { namespace Gist {
00053 
00054   TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
00055                          QWidget* parent, const Options& opt)
00056     : QWidget(parent)
00057     , mutex(QMutex::Recursive)
00058     , layoutMutex(QMutex::Recursive)
00059     , finishedFlag(false)
00060     , compareNodes(false), compareNodesBeforeFP(false)
00061     , autoHideFailed(true), autoZoom(false)
00062     , refresh(500), refreshPause(0), smoothScrollAndZoom(false)
00063     , zoomTimeLine(500)
00064     , scrollTimeLine(1000), targetX(0), sourceX(0), targetY(0), sourceY(0)
00065     , targetW(0), targetH(0), targetScale(0)
00066     , layoutDoneTimerId(0) {
00067       QMutexLocker locker(&mutex);
00068       curBest = (bab ? new BestNode(NULL) : NULL);
00069       if (rootSpace->status() == SS_FAILED) {
00070         if (!opt.clone)
00071           delete rootSpace;
00072         rootSpace = NULL;
00073       } else {
00074         rootSpace = Gecode::Search::snapshot(rootSpace,opt);
00075       }
00076       na = new Node::NodeAllocator(bab);
00077       int rootIdx = na->allocate(rootSpace);
00078       assert(rootIdx == 0); (void) rootIdx;
00079       root = (*na)[0];
00080       root->layout(*na);
00081       root->setMarked(true);
00082       currentNode = root;
00083       pathHead = root;
00084       scale = LayoutConfig::defScale / 100.0;
00085 
00086       setAutoFillBackground(true);
00087 
00088       connect(&searcher, SIGNAL(update(int,int,int)), this,
00089                          SLOT(layoutDone(int,int,int)));
00090       connect(&searcher, SIGNAL(statusChanged(bool)), this,
00091               SLOT(statusChanged(bool)));
00092 
00093       connect(&searcher, SIGNAL(solution(const Space*)),
00094               this, SIGNAL(solution(const Space*)),
00095               Qt::BlockingQueuedConnection);
00096       connect(this, SIGNAL(solution(const Space*)),
00097               this, SLOT(inspectSolution(const Space*)));
00098       connect(&searcher, SIGNAL(solution(const Space*)),
00099               this, SLOT(inspectSolution(const Space*)),
00100               Qt::BlockingQueuedConnection);
00101 
00102       connect(&searcher, SIGNAL(searchFinished(void)), this, SIGNAL(searchFinished(void)));
00103 
00104       connect(&scrollTimeLine, SIGNAL(frameChanged(int)),
00105               this, SLOT(scroll(int)));
00106       scrollTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
00107 
00108       scaleBar = new QSlider(Qt::Vertical, this);
00109       scaleBar->setObjectName("scaleBar");
00110       scaleBar->setMinimum(LayoutConfig::minScale);
00111       scaleBar->setMaximum(LayoutConfig::maxScale);
00112       scaleBar->setValue(LayoutConfig::defScale);
00113       connect(scaleBar, SIGNAL(valueChanged(int)),
00114               this, SLOT(scaleTree(int)));
00115       connect(this, SIGNAL(scaleChanged(int)), scaleBar, SLOT(setValue(int)));
00116       connect(&searcher, SIGNAL(scaleChanged(int)),
00117               scaleBar, SLOT(setValue(int)));
00118 
00119       connect(&zoomTimeLine, SIGNAL(frameChanged(int)),
00120               scaleBar, SLOT(setValue(int)));
00121       zoomTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
00122 
00123       qRegisterMetaType<Statistics>("Statistics");
00124       update();
00125   }
00126 
00127   TreeCanvas::~TreeCanvas(void) {
00128     if (root) {
00129       DisposeCursor dc(root,*na);
00130       PreorderNodeVisitor<DisposeCursor>(dc).run();
00131     }
00132     delete na;
00133   }
00134 
00135   void
00136   TreeCanvas::addDoubleClickInspector(Inspector* i) {
00137     doubleClickInspectors.append(QPair<Inspector*,bool>(i,false));
00138   }
00139 
00140   void
00141   TreeCanvas::activateDoubleClickInspector(int i, bool active) {
00142     assert(i < doubleClickInspectors.size());
00143     doubleClickInspectors[i].second = active;
00144   }
00145 
00146   void
00147   TreeCanvas::addSolutionInspector(Inspector* i) {
00148     solutionInspectors.append(QPair<Inspector*,bool>(i,false));
00149   }
00150 
00151   void
00152   TreeCanvas::activateSolutionInspector(int i, bool active) {
00153     assert(i < solutionInspectors.size());
00154     solutionInspectors[i].second = active;
00155   }
00156 
00157   void
00158   TreeCanvas::addMoveInspector(Inspector* i) {
00159     moveInspectors.append(QPair<Inspector*,bool>(i,false));
00160   }
00161 
00162   void
00163   TreeCanvas::activateMoveInspector(int i, bool active) {
00164     assert(i < moveInspectors.size());
00165     moveInspectors[i].second = active;
00166   }
00167 
00168   void
00169   TreeCanvas::addComparator(Comparator* c) {
00170     comparators.append(QPair<Comparator*,bool>(c,false));
00171   }
00172 
00173   void
00174   TreeCanvas::activateComparator(int i, bool active) {
00175     assert(i < comparators.size());
00176     comparators[i].second = active;
00177   }
00178 
00179   void
00180   TreeCanvas::scaleTree(int scale0, int zoomx, int zoomy) {
00181     QMutexLocker locker(&layoutMutex);
00182 
00183     QSize viewport_size = size();
00184     QAbstractScrollArea* sa =
00185       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00186 
00187     if (zoomx==-1)
00188       zoomx = viewport_size.width()/2;
00189     if (zoomy==-1)
00190       zoomy = viewport_size.height()/2;
00191 
00192     int xoff = (sa->horizontalScrollBar()->value()+zoomx)/scale;
00193     int yoff = (sa->verticalScrollBar()->value()+zoomy)/scale;
00194 
00195     BoundingBox bb;
00196     scale0 = std::min(std::max(scale0, LayoutConfig::minScale),
00197                       LayoutConfig::maxScale);
00198     scale = (static_cast<double>(scale0)) / 100.0;
00199     bb = root->getBoundingBox();
00200     int w =
00201       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00202     int h =
00203       static_cast<int>(2*Layout::extent+
00204         root->getShape()->depth()*Layout::dist_y*scale);
00205 
00206     sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
00207     sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
00208     sa->horizontalScrollBar()->setPageStep(viewport_size.width());
00209     sa->verticalScrollBar()->setPageStep(viewport_size.height());
00210     sa->horizontalScrollBar()->setSingleStep(Layout::extent);
00211     sa->verticalScrollBar()->setSingleStep(Layout::extent);
00212 
00213     xoff *= scale;
00214     yoff *= scale;
00215 
00216     sa->horizontalScrollBar()->setValue(xoff-zoomx);
00217     sa->verticalScrollBar()->setValue(yoff-zoomy);
00218 
00219     emit scaleChanged(scale0);
00220     QWidget::update();
00221   }
00222 
00223   void
00224   TreeCanvas::update(void) {
00225     QMutexLocker locker(&mutex);
00226     layoutMutex.lock();
00227     if (root != NULL) {
00228       root->layout(*na);
00229       BoundingBox bb = root->getBoundingBox();
00230 
00231       int w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00232       int h =
00233         static_cast<int>(2*Layout::extent+
00234           root->getShape()->depth()*Layout::dist_y*scale);
00235       xtrans = -bb.left+(Layout::extent / 2);
00236       
00237       QSize viewport_size = size();
00238       QAbstractScrollArea* sa =
00239         static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00240       sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
00241       sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
00242       sa->horizontalScrollBar()->setPageStep(viewport_size.width());
00243       sa->verticalScrollBar()->setPageStep(viewport_size.height());
00244       sa->horizontalScrollBar()->setSingleStep(Layout::extent);
00245       sa->verticalScrollBar()->setSingleStep(Layout::extent);
00246     }
00247     if (autoZoom)
00248       zoomToFit();
00249     layoutMutex.unlock();
00250     QWidget::update();
00251   }
00252 
00253   void
00254   TreeCanvas::scroll(void) {
00255     QWidget::update();
00256   }
00257 
00258   void
00259   TreeCanvas::layoutDone(int w, int h, int scale0) {
00260     targetW = w; targetH = h; targetScale = scale0;
00261 
00262     QSize viewport_size = size();
00263     QAbstractScrollArea* sa =
00264       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00265     sa->horizontalScrollBar()->setRange(0,w-viewport_size.width());
00266     sa->verticalScrollBar()->setRange(0,h-viewport_size.height());
00267 
00268     if (layoutDoneTimerId == 0)
00269       layoutDoneTimerId = startTimer(15);
00270   }
00271 
00272   void
00273   TreeCanvas::statusChanged(bool finished) {
00274     if (finished) {
00275       update();
00276       centerCurrentNode();
00277     }
00278     emit statusChanged(currentNode, stats, finished);
00279   }
00280 
00281   void
00282   SearcherThread::search(VisualNode* n, bool all, TreeCanvas* ti) {
00283     node = n;
00284     
00285     depth = -1;
00286     for (VisualNode* p = n; p != NULL; p = p->getParent(*ti->na))
00287       depth++;
00288     
00289     a = all;
00290     t = ti;
00291     start();
00292   }
00293 
00294   void
00295   SearcherThread::updateCanvas(void) {
00296     t->layoutMutex.lock();
00297     if (t->root == NULL)
00298       return;
00299 
00300     if (t->autoHideFailed) {
00301       t->root->hideFailed(*t->na,true);
00302     }
00303     for (VisualNode* n = t->currentNode; n != NULL; n=n->getParent(*t->na)) {
00304       if (n->isHidden()) {
00305         t->currentNode->setMarked(false);
00306         t->currentNode = n;
00307         t->currentNode->setMarked(true);
00308         break;
00309       }
00310     }
00311     
00312     t->root->layout(*t->na);
00313     BoundingBox bb = t->root->getBoundingBox();
00314 
00315     int w = static_cast<int>((bb.right-bb.left+Layout::extent)*t->scale);
00316     int h = static_cast<int>(2*Layout::extent+
00317                              t->root->getShape()->depth()
00318                               *Layout::dist_y*t->scale);
00319     t->xtrans = -bb.left+(Layout::extent / 2);
00320 
00321     int scale0 = static_cast<int>(t->scale*100);
00322     if (t->autoZoom) {
00323       QWidget* p = t->parentWidget();
00324       if (p) {
00325         double newXScale =
00326           static_cast<double>(p->width()) / (bb.right - bb.left +
00327                                              Layout::extent);
00328         double newYScale =
00329           static_cast<double>(p->height()) /
00330           (t->root->getShape()->depth() * Layout::dist_y + 2*Layout::extent);
00331 
00332         scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
00333         if (scale0<LayoutConfig::minScale)
00334           scale0 = LayoutConfig::minScale;
00335         if (scale0>LayoutConfig::maxAutoZoomScale)
00336           scale0 = LayoutConfig::maxAutoZoomScale;
00337         double scale = (static_cast<double>(scale0)) / 100.0;
00338 
00339         w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00340         h = static_cast<int>(2*Layout::extent+
00341                              t->root->getShape()->depth()*Layout::dist_y*scale);
00342       }
00343     }
00344 
00345     t->layoutMutex.unlock();
00346     emit update(w,h,scale0);
00347   }
00348 
00350   class SearchItem {
00351   public:
00353     VisualNode* n;
00355     int i;
00357     int noOfChildren;
00359     SearchItem(VisualNode* n0, int noOfChildren0)
00360       : n(n0), i(-1), noOfChildren(noOfChildren0) {}
00361   };
00362 
00363   void
00364   SearcherThread::run() {
00365     {
00366       if (!node->isOpen())
00367         return;
00368       t->mutex.lock();
00369       emit statusChanged(false);
00370 
00371       unsigned int kids = 
00372         node->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
00373                                     t->c_d, t->a_d);
00374       if (kids == 0 || node->getStatus() == STOP) {
00375         t->mutex.unlock();
00376         updateCanvas();
00377         emit statusChanged(true);
00378         return;
00379       }
00380 
00381       std::stack<SearchItem> stck;
00382       stck.push(SearchItem(node,kids));
00383       t->stats.maxDepth =
00384         std::max(static_cast<long unsigned int>(t->stats.maxDepth), 
00385                  static_cast<long unsigned int>(depth+stck.size()));
00386 
00387       VisualNode* sol = NULL;
00388       int nodeCount = 0;
00389       t->stopSearchFlag = false;
00390       while (!stck.empty() && !t->stopSearchFlag) {
00391         if (t->refresh > 0 && nodeCount >= t->refresh) {
00392           node->dirtyUp(*t->na);
00393           updateCanvas();
00394           emit statusChanged(false);
00395           nodeCount = 0;
00396           if (t->refreshPause > 0)
00397             msleep(t->refreshPause);
00398         }
00399         SearchItem& si = stck.top();
00400         si.i++;
00401         if (si.i == si.noOfChildren) {
00402           stck.pop();
00403         } else {
00404           VisualNode* n = si.n->getChild(*t->na,si.i);
00405           if (n->isOpen()) {
00406             if (n->getStatus() == UNDETERMINED)
00407               nodeCount++;
00408             kids = n->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
00409                                             t->c_d, t->a_d);
00410             if (kids == 0) {
00411               if (n->getStatus() == SOLVED) {
00412                 assert(n->hasCopy());
00413                 emit solution(n->getWorkingSpace());
00414                 n->purge(*t->na);
00415                 sol = n;
00416                 if (!a)
00417                   break;
00418               }
00419             } else {
00420               if ( n->getStatus() != STOP )
00421                 stck.push(SearchItem(n,kids));
00422               else if (!a)
00423                 break;
00424               t->stats.maxDepth =
00425                 std::max(static_cast<long unsigned int>(t->stats.maxDepth), 
00426                          static_cast<long unsigned int>(depth+stck.size()));
00427             }
00428           }
00429         }
00430       }
00431       node->dirtyUp(*t->na);
00432       t->stopSearchFlag = false;
00433       t->mutex.unlock();
00434       if (sol != NULL) {
00435         t->setCurrentNode(sol,false);
00436       } else {
00437         t->setCurrentNode(node,false);
00438       }
00439     }
00440     updateCanvas();
00441     emit statusChanged(true);
00442     if (t->finishedFlag)
00443       emit searchFinished();
00444   }
00445 
00446   void
00447   TreeCanvas::searchAll(void) {
00448     QMutexLocker locker(&mutex);
00449     searcher.search(currentNode, true, this);
00450   }
00451 
00452   void
00453   TreeCanvas::searchOne(void) {
00454     QMutexLocker locker(&mutex);
00455     searcher.search(currentNode, false, this);
00456   }
00457 
00458   void
00459   TreeCanvas::toggleHidden(void) {
00460     QMutexLocker locker(&mutex);
00461     currentNode->toggleHidden(*na);
00462     update();
00463     centerCurrentNode();
00464     emit statusChanged(currentNode, stats, true);
00465   }
00466 
00467   void
00468   TreeCanvas::hideFailed(void) {
00469     QMutexLocker locker(&mutex);
00470     currentNode->hideFailed(*na);
00471     update();
00472     centerCurrentNode();
00473     emit statusChanged(currentNode, stats, true);
00474   }
00475 
00476   void
00477   TreeCanvas::unhideAll(void) {
00478     QMutexLocker locker(&mutex);
00479     QMutexLocker layoutLocker(&layoutMutex);
00480     currentNode->unhideAll(*na);
00481     update();
00482     centerCurrentNode();
00483     emit statusChanged(currentNode, stats, true);
00484   }
00485 
00486   void
00487   TreeCanvas::toggleStop(void) {
00488     QMutexLocker locker(&mutex);
00489     currentNode->toggleStop(*na);
00490     update();
00491     centerCurrentNode();
00492     emit statusChanged(currentNode, stats, true);
00493   }
00494 
00495   void
00496   TreeCanvas::unstopAll(void) {
00497     QMutexLocker locker(&mutex);
00498     QMutexLocker layoutLocker(&layoutMutex);
00499     currentNode->unstopAll(*na);
00500     update();
00501     centerCurrentNode();
00502     emit statusChanged(currentNode, stats, true);    
00503   }
00504 
00505   void
00506   TreeCanvas::timerEvent(QTimerEvent* e) {
00507     if (e->timerId() == layoutDoneTimerId) {
00508       if (!smoothScrollAndZoom) {
00509         scaleTree(targetScale);
00510       } else {
00511         zoomTimeLine.stop();
00512         int zoomCurrent = static_cast<int>(scale*100);
00513         int targetZoom = targetScale;
00514         targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00515                               LayoutConfig::maxAutoZoomScale);
00516         zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
00517         zoomTimeLine.start();
00518       }
00519       QWidget::update();
00520       killTimer(layoutDoneTimerId);
00521       layoutDoneTimerId = 0;
00522     }
00523   }
00524 
00525   void
00526   TreeCanvas::zoomToFit(void) {
00527     QMutexLocker locker(&layoutMutex);
00528     if (root != NULL) {
00529       BoundingBox bb;
00530       bb = root->getBoundingBox();
00531       QWidget* p = parentWidget();
00532       if (p) {
00533         double newXScale =
00534           static_cast<double>(p->width()) / (bb.right - bb.left +
00535                                              Layout::extent);
00536         double newYScale =
00537           static_cast<double>(p->height()) / (root->getShape()->depth() * 
00538                                               Layout::dist_y +
00539                                               2*Layout::extent);
00540         int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
00541         if (scale0<LayoutConfig::minScale)
00542           scale0 = LayoutConfig::minScale;
00543         if (scale0>LayoutConfig::maxAutoZoomScale)
00544           scale0 = LayoutConfig::maxAutoZoomScale;
00545 
00546         if (!smoothScrollAndZoom) {
00547           scaleTree(scale0);
00548         } else {
00549           zoomTimeLine.stop();
00550           int zoomCurrent = static_cast<int>(scale*100);
00551           int targetZoom = scale0;
00552           targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00553                                 LayoutConfig::maxAutoZoomScale);
00554           zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
00555           zoomTimeLine.start();
00556         }
00557       }
00558     }
00559   }
00560 
00561   void
00562   TreeCanvas::centerCurrentNode(void) {
00563     QMutexLocker locker(&mutex);
00564     int x=0;
00565     int y=0;
00566 
00567     VisualNode* c = currentNode;
00568     while (c != NULL) {
00569       x += c->getOffset();
00570       y += Layout::dist_y;
00571       c = c->getParent(*na);
00572     }
00573 
00574     x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
00575 
00576     QAbstractScrollArea* sa =
00577       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00578 
00579     x -= sa->viewport()->width() / 2;
00580     y -= sa->viewport()->height() / 2;
00581 
00582     sourceX = sa->horizontalScrollBar()->value();
00583     targetX = std::max(sa->horizontalScrollBar()->minimum(), x);
00584     targetX = std::min(sa->horizontalScrollBar()->maximum(),
00585                        targetX);
00586     sourceY = sa->verticalScrollBar()->value();
00587     targetY = std::max(sa->verticalScrollBar()->minimum(), y);
00588     targetY = std::min(sa->verticalScrollBar()->maximum(),
00589                        targetY);
00590     if (!smoothScrollAndZoom) {
00591       sa->horizontalScrollBar()->setValue(targetX);
00592       sa->verticalScrollBar()->setValue(targetY);
00593     } else {
00594       scrollTimeLine.stop();
00595       scrollTimeLine.setFrameRange(0,100);
00596       scrollTimeLine.setDuration(std::max(200,
00597         std::min(1000,
00598         std::min(std::abs(sourceX-targetX),
00599                  std::abs(sourceY-targetY)))));
00600       scrollTimeLine.start();
00601     }
00602   }
00603 
00604   void
00605   TreeCanvas::scroll(int i) {
00606     QAbstractScrollArea* sa =
00607       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00608     double p = static_cast<double>(i)/100.0;
00609     double xdiff = static_cast<double>(targetX-sourceX)*p;
00610     double ydiff = static_cast<double>(targetY-sourceY)*p;
00611     sa->horizontalScrollBar()->setValue(sourceX+static_cast<int>(xdiff));
00612     sa->verticalScrollBar()->setValue(sourceY+static_cast<int>(ydiff));
00613   }
00614 
00615   void
00616   TreeCanvas::inspectCurrentNode(bool fix, int inspectorNo) {
00617     QMutexLocker locker(&mutex);
00618 
00619     if (currentNode->isHidden()) {
00620       toggleHidden();
00621       return;
00622     }
00623 
00624     int failedInspectorType = -1;
00625     int failedInspector = -1;
00626     bool needCentering = false;
00627     try {
00628       switch (currentNode->getStatus()) {
00629       case UNDETERMINED:
00630           {
00631             unsigned int kids =  
00632               currentNode->getNumberOfChildNodes(*na,curBest,stats,c_d,a_d);
00633             int depth = -1;
00634             for (VisualNode* p = currentNode; p != NULL; p=p->getParent(*na))
00635               depth++;
00636             if (kids > 0) {
00637               needCentering = true;
00638               depth++;
00639             }
00640             stats.maxDepth =
00641               std::max(stats.maxDepth, depth);
00642             if (currentNode->getStatus() == SOLVED) {
00643               assert(currentNode->hasCopy());
00644               emit solution(currentNode->getWorkingSpace());
00645             }
00646             emit statusChanged(currentNode,stats,true);
00647             for (int i=0; i<moveInspectors.size(); i++) {
00648               if (moveInspectors[i].second) {
00649                 failedInspectorType = 0;
00650                 failedInspector = i;
00651                 moveInspectors[i].first->
00652                   inspect(*currentNode->getWorkingSpace());
00653                 failedInspectorType = -1;
00654               }
00655             }
00656             if (currentNode->getStatus() == SOLVED) {
00657               currentNode->purge(*na);
00658             }
00659           }
00660           break;
00661       case FAILED:
00662       case STOP:
00663       case UNSTOP:
00664       case BRANCH:
00665       case SOLVED:
00666         {
00667           // SizeCursor sc(currentNode);
00668           // PreorderNodeVisitor<SizeCursor> pnv(sc);
00669           // int nodes = 1;
00670           // while (pnv.next()) { nodes++; }
00671           // std::cout << "sizeof(VisualNode): " << sizeof(VisualNode)
00672           //           << std::endl;
00673           // std::cout << "Size: " << (pnv.getCursor().s)/1024 << std::endl;
00674           // std::cout << "Nodes: " << nodes << std::endl;
00675           // std::cout << "Size / node: " << (pnv.getCursor().s)/nodes
00676           //           << std::endl;
00677 
00678           Space* curSpace;
00679         
00680           if (fix) {
00681             if (currentNode->isRoot() && currentNode->getStatus() == FAILED)
00682               break;
00683             curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
00684             if (currentNode->getStatus() == SOLVED &&
00685                 curSpace->status() != SS_SOLVED) {
00686               // in the presence of weakly monotonic propagators, we may have to
00687               // use search to find the solution here
00688               Space* dfsSpace = Gecode::dfs(curSpace);
00689               delete curSpace;
00690               curSpace = dfsSpace;
00691             }          
00692           } else {
00693             if (currentNode->isRoot())
00694               break;
00695             VisualNode* p = currentNode->getParent(*na);
00696             curSpace = p->getSpace(*na,curBest,c_d,a_d);
00697             switch (curSpace->status()) {
00698             case SS_SOLVED:
00699             case SS_FAILED:
00700               break;
00701             case SS_BRANCH:
00702               curSpace->commit(*p->getChoice(), 
00703                                currentNode->getAlternative(*na));
00704               break;
00705             default:
00706               GECODE_NEVER;
00707             }
00708           }
00709 
00710           if (inspectorNo==-1) {
00711             for (int i=0; i<doubleClickInspectors.size(); i++) {
00712               if (doubleClickInspectors[i].second) {
00713                 failedInspectorType = 1;
00714                 failedInspector = i;
00715                 doubleClickInspectors[i].first->inspect(*curSpace);
00716                 failedInspectorType = -1;
00717               }
00718             }
00719           } else {
00720             failedInspectorType = 1;
00721             failedInspector = inspectorNo;
00722             doubleClickInspectors[inspectorNo].first->inspect(*curSpace);
00723             failedInspectorType = -1;
00724           }
00725           delete curSpace;
00726         }
00727         break;
00728       }
00729     } catch (Exception e) {
00730       switch (failedInspectorType) {
00731       case 0:
00732         std::cerr << "Exception in move inspector "
00733                   << failedInspector;
00734         break;
00735       case 1:
00736         std::cerr << "Exception in double-click inspector "
00737                   << failedInspector;
00738         break;
00739       default:
00740         std::cerr << "Exception ";
00741         break;
00742       }
00743       std::cerr << ": " << e.what() << "." << std::endl;
00744       std::cerr << "Stopping..." << std::endl;
00745       std::exit(EXIT_FAILURE);
00746     }
00747 
00748     currentNode->dirtyUp(*na);
00749     update();
00750     if (needCentering)
00751       centerCurrentNode();
00752   }
00753   
00754   void
00755   TreeCanvas::inspectBeforeFP(void) {
00756     inspectCurrentNode(false);
00757   }
00758 
00759   void
00760   TreeCanvas::inspectSolution(const Space* s) {
00761     int failedInspectorType = -1;
00762     int failedInspector = -1;
00763     try {
00764       Space* c = NULL;
00765       for (int i=0; i<solutionInspectors.size(); i++) {
00766         if (solutionInspectors[i].second) {
00767           if (c == NULL)
00768             c = s->clone();
00769           failedInspectorType = 1;
00770           failedInspector = i;
00771           solutionInspectors[i].first->inspect(*c);
00772           failedInspectorType = -1;
00773         }
00774       }
00775       delete c;
00776     } catch (Exception e) {
00777       switch (failedInspectorType) {
00778       case 0:
00779         std::cerr << "Exception in move inspector "
00780                   << failedInspector;
00781         break;
00782       case 1:
00783         std::cerr << "Exception in solution inspector "
00784                   << failedInspector;
00785         break;
00786       default:
00787         std::cerr << "Exception ";
00788         break;
00789       }
00790       std::cerr << ": " << e.what() << "." << std::endl;
00791       std::cerr << "Stopping..." << std::endl;
00792       std::exit(EXIT_FAILURE);      
00793     }
00794   }
00795 
00796   void
00797   TreeCanvas::stopSearch(void) {
00798     stopSearchFlag = true;
00799     layoutDoneTimerId = startTimer(15);
00800   }
00801 
00802   void
00803   TreeCanvas::reset(void) {
00804     QMutexLocker locker(&mutex);
00805     Space* rootSpace =
00806       root->getStatus() == FAILED ? NULL : 
00807                            root->getSpace(*na,curBest,c_d,a_d);
00808     if (curBest != NULL) {
00809       delete curBest;
00810       curBest = new BestNode(NULL);
00811     }
00812     if (root) {
00813       DisposeCursor dc(root,*na);
00814       PreorderNodeVisitor<DisposeCursor>(dc).run();
00815     }
00816     delete na;
00817     na = new Node::NodeAllocator(curBest != NULL);
00818     int rootIdx = na->allocate(rootSpace);
00819     assert(rootIdx == 0); (void) rootIdx;
00820     root = (*na)[0];
00821     root->setMarked(true);
00822     currentNode = root;
00823     pathHead = root;
00824     scale = 1.0;
00825     stats = Statistics();
00826     for (int i=bookmarks.size(); i--;)
00827       emit removedBookmark(i);
00828     bookmarks.clear();
00829     root->layout(*na);
00830 
00831     emit statusChanged(currentNode, stats, true);
00832     update();
00833   }
00834 
00835   void
00836   TreeCanvas::bookmarkNode(void) {
00837     QMutexLocker locker(&mutex);
00838     if (!currentNode->isBookmarked()) {
00839       bool ok;
00840       QString text =
00841         QInputDialog::getText(this, "Add bookmark", "Name:", 
00842                               QLineEdit::Normal,"",&ok);
00843       if (ok) {
00844         currentNode->setBookmarked(true);
00845         bookmarks.append(currentNode);
00846         if (text == "")
00847           text = QString("Node ")+QString().setNum(bookmarks.size());
00848         emit addedBookmark(text);
00849       }
00850     } else {
00851       currentNode->setBookmarked(false);
00852       int idx = bookmarks.indexOf(currentNode);
00853       bookmarks.remove(idx);
00854       emit removedBookmark(idx);
00855     }
00856     currentNode->dirtyUp(*na);
00857     update();
00858   }
00859   
00860   void
00861   TreeCanvas::setPath(void) {
00862     QMutexLocker locker(&mutex);
00863     if(currentNode == pathHead)
00864       return;
00865 
00866     pathHead->unPathUp(*na);
00867     pathHead = currentNode;
00868 
00869     currentNode->pathUp(*na);
00870     currentNode->dirtyUp(*na);
00871     update();
00872   }
00873 
00874   void
00875   TreeCanvas::inspectPath(void) {
00876     QMutexLocker locker(&mutex);
00877     setCurrentNode(root);
00878     if (currentNode->isOnPath()) {
00879       inspectCurrentNode();
00880       int nextAlt = currentNode->getPathAlternative(*na);
00881       while (nextAlt >= 0) {
00882         setCurrentNode(currentNode->getChild(*na,nextAlt));
00883         inspectCurrentNode();
00884         nextAlt = currentNode->getPathAlternative(*na);
00885       }
00886     }
00887     update();
00888   }
00889 
00890   void
00891   TreeCanvas::startCompareNodes(void) {
00892     QMutexLocker locker(&mutex);
00893     compareNodes = true;
00894     compareNodesBeforeFP = false;
00895     setCursor(QCursor(Qt::CrossCursor));
00896   }
00897 
00898   void
00899   TreeCanvas::startCompareNodesBeforeFP(void) {
00900     QMutexLocker locker(&mutex);
00901     compareNodes = true;
00902     compareNodesBeforeFP = true;
00903     setCursor(QCursor(Qt::CrossCursor));
00904   }
00905 
00906   void
00907   TreeCanvas::emitStatusChanged(void) {
00908     emit statusChanged(currentNode, stats, true);
00909   }
00910 
00911   void
00912   TreeCanvas::navUp(void) {
00913     QMutexLocker locker(&mutex);
00914 
00915     VisualNode* p = currentNode->getParent(*na);
00916 
00917     setCurrentNode(p);
00918 
00919     if (p != NULL) {
00920       centerCurrentNode();
00921     }
00922   }
00923 
00924   void
00925   TreeCanvas::navDown(void) {
00926     QMutexLocker locker(&mutex);
00927     if (!currentNode->isHidden()) {
00928       switch (currentNode->getStatus()) {
00929       case STOP:
00930       case UNSTOP:
00931       case BRANCH:
00932           {
00933             int alt = std::max(0, currentNode->getPathAlternative(*na));
00934             VisualNode* n = currentNode->getChild(*na,alt);
00935             setCurrentNode(n);
00936             centerCurrentNode();
00937             break;
00938           }
00939       case SOLVED:
00940       case FAILED:
00941       case UNDETERMINED:
00942         break;
00943       }
00944     }
00945   }
00946 
00947   void
00948   TreeCanvas::navLeft(void) {
00949     QMutexLocker locker(&mutex);
00950     VisualNode* p = currentNode->getParent(*na);
00951     if (p != NULL) {
00952       int alt = currentNode->getAlternative(*na);
00953       if (alt > 0) {
00954         VisualNode* n = p->getChild(*na,alt-1);
00955         setCurrentNode(n);
00956         centerCurrentNode();
00957       }
00958     }
00959   }
00960 
00961   void
00962   TreeCanvas::navRight(void) {
00963     QMutexLocker locker(&mutex);
00964     VisualNode* p = currentNode->getParent(*na);
00965     if (p != NULL) {
00966       unsigned int alt = currentNode->getAlternative(*na);
00967       if (alt + 1 < p->getNumberOfChildren()) {
00968         VisualNode* n = p->getChild(*na,alt+1);
00969         setCurrentNode(n);
00970         centerCurrentNode();
00971       }
00972     }
00973   }
00974 
00975   void
00976   TreeCanvas::navRoot(void) {
00977     QMutexLocker locker(&mutex);
00978     setCurrentNode(root);
00979     centerCurrentNode();
00980   }
00981 
00982   void
00983   TreeCanvas::navNextSol(bool back) {
00984     QMutexLocker locker(&mutex);
00985     NextSolCursor nsc(currentNode,back,*na);
00986     PreorderNodeVisitor<NextSolCursor> nsv(nsc);
00987     nsv.run();
00988     VisualNode* n = nsv.getCursor().node();
00989     if (n != root) {
00990       setCurrentNode(n);
00991       centerCurrentNode();
00992     }
00993   }
00994 
00995   void
00996   TreeCanvas::navPrevSol(void) {
00997     navNextSol(true);
00998   }
00999 
01000   void
01001   TreeCanvas::exportNodePDF(VisualNode* n) {
01002 #if QT_VERSION >= 0x040400
01003     QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
01004     if (filename != "") {
01005       QPrinter printer(QPrinter::ScreenResolution);
01006       QMutexLocker locker(&mutex);
01007 
01008       BoundingBox bb = n->getBoundingBox();
01009       printer.setFullPage(true);
01010       printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
01011                                   n->getShape()->depth() * Layout::dist_y +
01012                                   Layout::extent), QPrinter::Point);
01013       printer.setOutputFileName(filename);
01014       QPainter painter(&printer);
01015 
01016       painter.setRenderHint(QPainter::Antialiasing);
01017 
01018       QRect pageRect = printer.pageRect();
01019       double newXScale =
01020         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
01021                                                  Layout::extent);
01022       double newYScale =
01023         static_cast<double>(pageRect.height()) /
01024                             (n->getShape()->depth() * Layout::dist_y +
01025                              Layout::extent);
01026       double printScale = std::min(newXScale, newYScale);
01027       painter.scale(printScale,printScale);
01028 
01029       int printxtrans = -bb.left+(Layout::extent / 2);
01030 
01031       painter.translate(printxtrans, Layout::dist_y / 2);
01032       QRect clip(0,0,0,0);
01033       DrawingCursor dc(n, *na, curBest, painter, clip, showCopies);
01034       currentNode->setMarked(false);
01035       PreorderNodeVisitor<DrawingCursor>(dc).run();
01036       currentNode->setMarked(true);
01037     }
01038 #else
01039     (void) n;
01040 #endif
01041   }
01042 
01043   void
01044   TreeCanvas::exportWholeTreePDF(void) {
01045 #if QT_VERSION >= 0x040400
01046     exportNodePDF(root);
01047 #endif
01048   }
01049 
01050   void
01051   TreeCanvas::exportPDF(void) {
01052 #if QT_VERSION >= 0x040400
01053     exportNodePDF(currentNode);
01054 #endif
01055   }
01056 
01057   void
01058   TreeCanvas::print(void) {
01059     QPrinter printer;
01060     if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
01061       QMutexLocker locker(&mutex);
01062 
01063       BoundingBox bb = root->getBoundingBox();
01064       QRect pageRect = printer.pageRect();
01065       double newXScale =
01066         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
01067                                                  Layout::extent);
01068       double newYScale =
01069         static_cast<double>(pageRect.height()) /
01070                             (root->getShape()->depth() * Layout::dist_y +
01071                              2*Layout::extent);
01072       double printScale = std::min(newXScale, newYScale)*100;
01073       if (printScale<1.0)
01074         printScale = 1.0;
01075       if (printScale > 400.0)
01076         printScale = 400.0;
01077       printScale = printScale / 100.0;
01078 
01079       QPainter painter(&printer);
01080       painter.setRenderHint(QPainter::Antialiasing);
01081       painter.scale(printScale,printScale);
01082       painter.translate(xtrans, 0);
01083       QRect clip(0,0,0,0);
01084       DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
01085       PreorderNodeVisitor<DrawingCursor>(dc).run();
01086     }
01087   }
01088 
01089   VisualNode*
01090   TreeCanvas::eventNode(QEvent* event) {
01091     int x = 0;
01092     int y = 0;
01093     switch (event->type()) {
01094     case QEvent::ToolTip:
01095         {
01096           QHelpEvent* he = static_cast<QHelpEvent*>(event);
01097           x = he->x();
01098           y = he->y();
01099           break;
01100         }
01101     case QEvent::MouseButtonDblClick:
01102     case QEvent::MouseButtonPress:
01103     case QEvent::MouseButtonRelease:
01104     case QEvent::MouseMove:
01105         {
01106           QMouseEvent* me = static_cast<QMouseEvent*>(event);
01107           x = me->x();
01108           y = me->y();
01109           break;
01110         }
01111     case QEvent::ContextMenu:
01112         {
01113           QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
01114           x = ce->x();
01115           y = ce->y();
01116           break;
01117         }
01118     default:
01119       return NULL;
01120     }
01121     QAbstractScrollArea* sa =
01122       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01123     int xoff = sa->horizontalScrollBar()->value()/scale;
01124     int yoff = sa->verticalScrollBar()->value()/scale;
01125 
01126     BoundingBox bb = root->getBoundingBox();
01127     int w =
01128       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
01129     if (w < sa->viewport()->width())
01130       xoff -= (sa->viewport()->width()-w)/2;
01131     
01132     VisualNode* n;
01133     n = root->findNode(*na,
01134                        static_cast<int>(x/scale-xtrans+xoff),
01135                        static_cast<int>((y-30)/scale+yoff));
01136     return n;
01137   }
01138 
01139   bool
01140   TreeCanvas::event(QEvent* event) {
01141     if (mutex.tryLock()) {
01142       if (event->type() == QEvent::ToolTip) {
01143         VisualNode* n = eventNode(event);
01144         if (n != NULL && !n->isHidden() &&
01145             (n->getStatus() == BRANCH || n->getStatus() == STOP)) {
01146           QHelpEvent* he = static_cast<QHelpEvent*>(event);
01147           QToolTip::showText(he->globalPos(),
01148                              QString(n->toolTip(curBest,c_d,a_d).c_str()));
01149         } else {
01150           QToolTip::hideText();
01151         }
01152       }
01153       mutex.unlock();
01154     }
01155     return QWidget::event(event);
01156   }
01157 
01158   void
01159   TreeCanvas::resizeToOuter(void) {
01160     if (autoZoom)
01161       zoomToFit();
01162   }
01163 
01164   void
01165   TreeCanvas::paintEvent(QPaintEvent* event) {
01166     QMutexLocker locker(&layoutMutex);
01167     QPainter painter(this);
01168     painter.setRenderHint(QPainter::Antialiasing);
01169 
01170     QAbstractScrollArea* sa =
01171       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01172     int xoff = sa->horizontalScrollBar()->value()/scale;
01173     int yoff = sa->verticalScrollBar()->value()/scale;
01174 
01175     BoundingBox bb = root->getBoundingBox();
01176     int w =
01177       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
01178     if (w < sa->viewport()->width())
01179       xoff -= (sa->viewport()->width()-w)/2;
01180 
01181     QRect origClip = event->rect();
01182     painter.translate(0, 30);
01183     painter.scale(scale,scale);
01184     painter.translate(xtrans-xoff, -yoff);
01185     QRect clip(static_cast<int>(origClip.x()/scale-xtrans+xoff),
01186                static_cast<int>(origClip.y()/scale+yoff),
01187                static_cast<int>(origClip.width()/scale),
01188                static_cast<int>(origClip.height()/scale));
01189     DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
01190     PreorderNodeVisitor<DrawingCursor>(dc).run();
01191 
01192     // int nodesLayouted = 1;
01193     // clock_t t0 = clock();
01194     // while (v.next()) { nodesLayouted++; }
01195     // double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
01196     // double nps = static_cast<double>(nodesLayouted) /
01197     //   (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
01198     // std::cout << "Drawing done. " << nodesLayouted << " nodes in "
01199     //   << t << " ms. " << nps << " nodes/s." << std::endl;
01200 
01201   }
01202 
01203   void
01204   TreeCanvas::mouseDoubleClickEvent(QMouseEvent* event) {
01205     if (mutex.tryLock()) {
01206       if(event->button() == Qt::LeftButton) {
01207         VisualNode* n = eventNode(event);
01208         if(n == currentNode) {
01209           inspectCurrentNode();
01210           event->accept();
01211           mutex.unlock();
01212           return;
01213         }
01214       }
01215       mutex.unlock();
01216     }
01217     event->ignore();
01218   }
01219 
01220   void
01221   TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
01222     if (mutex.tryLock()) {
01223       VisualNode* n = eventNode(event);
01224       if (n != NULL) {
01225         setCurrentNode(n);
01226         emit contextMenu(event);
01227         event->accept();
01228         mutex.unlock();
01229         return;
01230       }
01231       mutex.unlock();
01232     }
01233     event->ignore();
01234   }
01235 
01236   void
01237   TreeCanvas::resizeEvent(QResizeEvent* e) {
01238     QAbstractScrollArea* sa =
01239       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01240 
01241     int w = sa->horizontalScrollBar()->maximum()+e->oldSize().width();
01242     int h = sa->verticalScrollBar()->maximum()+e->oldSize().height();
01243 
01244     sa->horizontalScrollBar()->setRange(0,w-e->size().width());
01245     sa->verticalScrollBar()->setRange(0,h-e->size().height());
01246     sa->horizontalScrollBar()->setPageStep(e->size().width());
01247     sa->verticalScrollBar()->setPageStep(e->size().height());
01248   }
01249 
01250   void
01251   TreeCanvas::wheelEvent(QWheelEvent* event) {
01252     if (event->modifiers() & Qt::ShiftModifier) {
01253       event->accept();
01254       if (event->orientation() == Qt::Vertical && !autoZoom)
01255         scaleTree(scale*100+ceil(static_cast<double>(event->delta())/4.0),
01256                   event->x(), event->y());
01257     } else {
01258       event->ignore();
01259     }
01260   }
01261 
01262   bool
01263   TreeCanvas::finish(void) {
01264     if (finishedFlag)
01265       return true;
01266     stopSearchFlag = true;
01267     finishedFlag = true;
01268     for (int i=0; i<doubleClickInspectors.size(); i++)
01269       doubleClickInspectors[i].first->finalize();
01270     for (int i=0; i<solutionInspectors.size(); i++)
01271       solutionInspectors[i].first->finalize();
01272     for (int i=0; i<moveInspectors.size(); i++)
01273       moveInspectors[i].first->finalize();
01274     for (int i=0; i<comparators.size(); i++)
01275       comparators[i].first->finalize();
01276     return !searcher.isRunning();
01277   }
01278 
01279   void
01280   TreeCanvas::setCurrentNode(VisualNode* n, bool update) {
01281     QMutexLocker locker(&mutex);
01282     if (update && n != NULL && n != currentNode &&
01283         n->getStatus() != UNDETERMINED && !n->isHidden()) {
01284       Space* curSpace = NULL;
01285       for (int i=0; i<moveInspectors.size(); i++) {
01286         if (moveInspectors[i].second) {
01287           if (curSpace == NULL)
01288             curSpace = n->getSpace(*na,curBest,c_d,a_d);
01289           try {
01290             moveInspectors[i].first->inspect(*curSpace);
01291           } catch (Exception e) {
01292             std::cerr << "Exception in move inspector " << i
01293                       << ": " << e.what() << "." << std::endl;
01294             std::cerr << "Stopping..." << std::endl;
01295             std::exit(EXIT_FAILURE);
01296           }
01297         }
01298       }
01299     }
01300     if (n != NULL) {
01301       currentNode->setMarked(false);
01302       currentNode = n;
01303       currentNode->setMarked(true);
01304       emit statusChanged(currentNode,stats,true);
01305       if (update) {
01306         compareNodes = false;
01307         setCursor(QCursor(Qt::ArrowCursor));
01308         QWidget::update();
01309       }
01310     }
01311   }
01312 
01313   void
01314   TreeCanvas::mousePressEvent(QMouseEvent* event) {
01315     if (mutex.tryLock()) {
01316       if (event->button() == Qt::LeftButton) {
01317         VisualNode* n = eventNode(event);
01318         if (compareNodes) {
01319           if (n != NULL && n->getStatus() != UNDETERMINED &&
01320               currentNode != NULL &&
01321               currentNode->getStatus() != UNDETERMINED) {
01322             Space* curSpace = NULL;
01323             Space* compareSpace = NULL;
01324             for (int i=0; i<comparators.size(); i++) {
01325               if (comparators[i].second) {
01326                 if (curSpace == NULL) {
01327                   curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
01328 
01329                   if (!compareNodesBeforeFP || n->isRoot()) {
01330                     compareSpace = n->getSpace(*na,curBest,c_d,a_d);
01331                   } else {
01332                     VisualNode* p = n->getParent(*na);
01333                     compareSpace = p->getSpace(*na,curBest,c_d,a_d);
01334                     switch (compareSpace->status()) {
01335                     case SS_SOLVED:
01336                     case SS_FAILED:
01337                       break;
01338                     case SS_BRANCH:
01339                       compareSpace->commit(*p->getChoice(), 
01340                                            n->getAlternative(*na));
01341                       break;
01342                     default:
01343                       GECODE_NEVER;
01344                     }
01345                   }
01346                 }
01347                 comparators[i].first->compare(*curSpace,*compareSpace);
01348               }
01349             }
01350           }
01351         } else {
01352           setCurrentNode(n);
01353         }
01354         compareNodes = false;
01355         setCursor(QCursor(Qt::ArrowCursor));
01356         if (n != NULL) {
01357           event->accept();
01358           mutex.unlock();
01359           return;
01360         }
01361       }
01362       mutex.unlock();
01363     }
01364     event->ignore();
01365   }
01366 
01367   void
01368   TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
01369     c_d = c_d0; a_d = a_d0;
01370   }
01371 
01372   void
01373   TreeCanvas::setAutoHideFailed(bool b) {
01374     autoHideFailed = b;
01375   }
01376 
01377   void
01378   TreeCanvas::setAutoZoom(bool b) {
01379     autoZoom = b;
01380     if (autoZoom) {
01381       zoomToFit();
01382     }
01383     emit autoZoomChanged(b);
01384     scaleBar->setEnabled(!b);
01385   }
01386 
01387   void
01388   TreeCanvas::setShowCopies(bool b) {
01389     showCopies = b;
01390   }
01391   bool
01392   TreeCanvas::getShowCopies(void) {
01393     return showCopies;
01394   }
01395 
01396   bool
01397   TreeCanvas::getAutoHideFailed(void) {
01398     return autoHideFailed;
01399   }
01400 
01401   bool
01402   TreeCanvas::getAutoZoom(void) {
01403     return autoZoom;
01404   }
01405 
01406   void
01407   TreeCanvas::setRefresh(int i) {
01408     refresh = i;
01409   }
01410 
01411   void
01412   TreeCanvas::setRefreshPause(int i) {
01413     refreshPause = i;
01414     if (refreshPause > 0)
01415       refresh = 1;
01416   }
01417 
01418   bool
01419   TreeCanvas::getSmoothScrollAndZoom(void) {
01420     return smoothScrollAndZoom;
01421   }
01422 
01423   void
01424   TreeCanvas::setSmoothScrollAndZoom(bool b) {
01425     smoothScrollAndZoom = b;
01426   }
01427 
01428 }}
01429 
01430 // STATISTICS: gist-any