Generated on Thu Apr 11 13:59:03 2019 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  *  This file is part of Gecode, the generic constraint
00010  *  development environment:
00011  *     http://www.gecode.org
00012  *
00013  * Permission is hereby granted, free of charge, to any person obtaining
00014  * a copy of this software and associated documentation files (the
00015  * "Software"), to deal in the Software without restriction, including
00016  * without limitation the rights to use, copy, modify, merge, publish,
00017  * distribute, sublicense, and/or sell copies of the Software, and to
00018  * permit persons to whom the Software is furnished to do so, subject to
00019  * the following conditions:
00020  *
00021  * The above copyright notice and this permission notice shall be
00022  * included in all copies or substantial portions of the Software.
00023  *
00024  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00025  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00026  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00027  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00028  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00029  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00030  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00031  *
00032  */
00033 
00034 #include <QtGui/QPainter>
00035 #include <QPrinter>
00036 #include <QPrintDialog>
00037 
00038 #include <stack>
00039 #include <fstream>
00040 
00041 #include <gecode/gist/treecanvas.hh>
00042 
00043 #include <gecode/gist/nodevisitor.hh>
00044 #include <gecode/gist/visualnode.hh>
00045 #include <gecode/gist/drawingcursor.hh>
00046 
00047 #include <gecode/search.hh>
00048 #include <gecode/search/support.hh>
00049 
00050 namespace Gecode { namespace Gist {
00051 
00052   TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
00053                          QWidget* parent, const Options& opt)
00054     : QWidget(parent)
00055     , mutex(QMutex::Recursive)
00056     , layoutMutex(QMutex::Recursive)
00057     , finishedFlag(false)
00058     , compareNodes(false), compareNodesBeforeFP(false)
00059     , autoHideFailed(true), autoZoom(false)
00060     , refresh(500), refreshPause(0), smoothScrollAndZoom(false)
00061     , moveDuringSearch(false)
00062     , zoomTimeLine(500)
00063     , scrollTimeLine(1000), targetX(0), sourceX(0), targetY(0), sourceY(0)
00064     , targetW(0), targetH(0), targetScale(0)
00065     , layoutDoneTimerId(0) {
00066       QMutexLocker locker(&mutex);
00067       curBest = (bab ? new BestNode(NULL) : NULL);
00068       if (rootSpace->status() == SS_FAILED) {
00069         if (!opt.clone)
00070           delete rootSpace;
00071         rootSpace = NULL;
00072       } else {
00073         rootSpace = Gecode::Search::snapshot(rootSpace,opt);
00074       }
00075       na = new Node::NodeAllocator(bab);
00076       int rootIdx = na->allocate(rootSpace);
00077       assert(rootIdx == 0); (void) rootIdx;
00078       root = (*na)[0];
00079       root->layout(*na);
00080       root->setMarked(true);
00081       currentNode = root;
00082       pathHead = root;
00083       scale = LayoutConfig::defScale / 100.0;
00084 
00085       setAutoFillBackground(true);
00086 
00087       connect(&searcher, SIGNAL(update(int,int,int)), this,
00088                          SLOT(layoutDone(int,int,int)));
00089       connect(&searcher, SIGNAL(statusChanged(bool)), this,
00090               SLOT(statusChanged(bool)));
00091 
00092       connect(&searcher, SIGNAL(solution(const Space*)),
00093               this, SIGNAL(solution(const Space*)),
00094               Qt::BlockingQueuedConnection);
00095       connect(this, SIGNAL(solution(const Space*)),
00096               this, SLOT(inspectSolution(const Space*)));
00097 
00098       connect(&searcher, SIGNAL(moveToNode(VisualNode*,bool)),
00099               this, SLOT(setCurrentNode(VisualNode*,bool)),
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(void) {
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 (t->moveDuringSearch)
00411               emit moveToNode(n,false);
00412             if (kids == 0) {
00413               if (n->getStatus() == SOLVED) {
00414                 assert(n->hasCopy());
00415                 emit solution(n->getWorkingSpace());
00416                 n->purge(*t->na);
00417                 sol = n;
00418                 if (!a)
00419                   break;
00420               }
00421             } else {
00422               if ( n->getStatus() != STOP )
00423                 stck.push(SearchItem(n,kids));
00424               else if (!a)
00425                 break;
00426               t->stats.maxDepth =
00427                 std::max(static_cast<long unsigned int>(t->stats.maxDepth),
00428                          static_cast<long unsigned int>(depth+stck.size()));
00429             }
00430           }
00431         }
00432       }
00433       node->dirtyUp(*t->na);
00434       t->stopSearchFlag = false;
00435       t->mutex.unlock();
00436       if (sol != NULL) {
00437         t->setCurrentNode(sol,true,false);
00438       } else {
00439         t->setCurrentNode(node,true,false);
00440       }
00441     }
00442     updateCanvas();
00443     emit statusChanged(true);
00444     if (t->finishedFlag)
00445       emit searchFinished();
00446   }
00447 
00448   void
00449   TreeCanvas::searchAll(void) {
00450     QMutexLocker locker(&mutex);
00451     searcher.search(currentNode, true, this);
00452   }
00453 
00454   void
00455   TreeCanvas::searchOne(void) {
00456     QMutexLocker locker(&mutex);
00457     searcher.search(currentNode, false, this);
00458   }
00459 
00460   void
00461   TreeCanvas::toggleHidden(void) {
00462     QMutexLocker locker(&mutex);
00463     currentNode->toggleHidden(*na);
00464     update();
00465     centerCurrentNode();
00466     emit statusChanged(currentNode, stats, true);
00467   }
00468 
00469   void
00470   TreeCanvas::hideFailed(void) {
00471     QMutexLocker locker(&mutex);
00472     currentNode->hideFailed(*na);
00473     update();
00474     centerCurrentNode();
00475     emit statusChanged(currentNode, stats, true);
00476   }
00477 
00478   void
00479   TreeCanvas::unhideAll(void) {
00480     QMutexLocker locker(&mutex);
00481     QMutexLocker layoutLocker(&layoutMutex);
00482     currentNode->unhideAll(*na);
00483     update();
00484     centerCurrentNode();
00485     emit statusChanged(currentNode, stats, true);
00486   }
00487 
00488   void
00489   TreeCanvas::toggleStop(void) {
00490     QMutexLocker locker(&mutex);
00491     currentNode->toggleStop(*na);
00492     update();
00493     centerCurrentNode();
00494     emit statusChanged(currentNode, stats, true);
00495   }
00496 
00497   void
00498   TreeCanvas::unstopAll(void) {
00499     QMutexLocker locker(&mutex);
00500     QMutexLocker layoutLocker(&layoutMutex);
00501     currentNode->unstopAll(*na);
00502     update();
00503     centerCurrentNode();
00504     emit statusChanged(currentNode, stats, true);
00505   }
00506 
00507   void
00508   TreeCanvas::timerEvent(QTimerEvent* e) {
00509     if (e->timerId() == layoutDoneTimerId) {
00510       if (!smoothScrollAndZoom) {
00511         scaleTree(targetScale);
00512       } else {
00513         zoomTimeLine.stop();
00514         int zoomCurrent = static_cast<int>(scale*100);
00515         int targetZoom = targetScale;
00516         targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00517                               LayoutConfig::maxAutoZoomScale);
00518         zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
00519         zoomTimeLine.start();
00520       }
00521       QWidget::update();
00522       killTimer(layoutDoneTimerId);
00523       layoutDoneTimerId = 0;
00524     }
00525   }
00526 
00527   void
00528   TreeCanvas::zoomToFit(void) {
00529     QMutexLocker locker(&layoutMutex);
00530     if (root != NULL) {
00531       BoundingBox bb;
00532       bb = root->getBoundingBox();
00533       QWidget* p = parentWidget();
00534       if (p) {
00535         double newXScale =
00536           static_cast<double>(p->width()) / (bb.right - bb.left +
00537                                              Layout::extent);
00538         double newYScale =
00539           static_cast<double>(p->height()) / (root->getShape()->depth() *
00540                                               Layout::dist_y +
00541                                               2*Layout::extent);
00542         int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
00543         if (scale0<LayoutConfig::minScale)
00544           scale0 = LayoutConfig::minScale;
00545         if (scale0>LayoutConfig::maxAutoZoomScale)
00546           scale0 = LayoutConfig::maxAutoZoomScale;
00547 
00548         if (!smoothScrollAndZoom) {
00549           scaleTree(scale0);
00550         } else {
00551           zoomTimeLine.stop();
00552           int zoomCurrent = static_cast<int>(scale*100);
00553           int targetZoom = scale0;
00554           targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00555                                 LayoutConfig::maxAutoZoomScale);
00556           zoomTimeLine.setFrameRange(zoomCurrent,targetZoom);
00557           zoomTimeLine.start();
00558         }
00559       }
00560     }
00561   }
00562 
00563   void
00564   TreeCanvas::centerCurrentNode(void) {
00565     QMutexLocker locker(&mutex);
00566     int x=0;
00567     int y=0;
00568 
00569     VisualNode* c = currentNode;
00570     while (c != NULL) {
00571       x += c->getOffset();
00572       y += Layout::dist_y;
00573       c = c->getParent(*na);
00574     }
00575 
00576     x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
00577 
00578     QAbstractScrollArea* sa =
00579       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00580 
00581     x -= sa->viewport()->width() / 2;
00582     y -= sa->viewport()->height() / 2;
00583 
00584     sourceX = sa->horizontalScrollBar()->value();
00585     targetX = std::max(sa->horizontalScrollBar()->minimum(), x);
00586     targetX = std::min(sa->horizontalScrollBar()->maximum(),
00587                        targetX);
00588     sourceY = sa->verticalScrollBar()->value();
00589     targetY = std::max(sa->verticalScrollBar()->minimum(), y);
00590     targetY = std::min(sa->verticalScrollBar()->maximum(),
00591                        targetY);
00592     if (!smoothScrollAndZoom) {
00593       sa->horizontalScrollBar()->setValue(targetX);
00594       sa->verticalScrollBar()->setValue(targetY);
00595     } else {
00596       scrollTimeLine.stop();
00597       scrollTimeLine.setFrameRange(0,100);
00598       scrollTimeLine.setDuration(std::max(200,
00599         std::min(1000,
00600         std::min(std::abs(sourceX-targetX),
00601                  std::abs(sourceY-targetY)))));
00602       scrollTimeLine.start();
00603     }
00604   }
00605 
00606   void
00607   TreeCanvas::scroll(int i) {
00608     QAbstractScrollArea* sa =
00609       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
00610     double p = static_cast<double>(i)/100.0;
00611     double xdiff = static_cast<double>(targetX-sourceX)*p;
00612     double ydiff = static_cast<double>(targetY-sourceY)*p;
00613     sa->horizontalScrollBar()->setValue(sourceX+static_cast<int>(xdiff));
00614     sa->verticalScrollBar()->setValue(sourceY+static_cast<int>(ydiff));
00615   }
00616 
00617   void
00618   TreeCanvas::inspectCurrentNode(bool fix, int inspectorNo) {
00619     QMutexLocker locker(&mutex);
00620 
00621     if (currentNode->isHidden()) {
00622       toggleHidden();
00623       return;
00624     }
00625 
00626     int failedInspectorType = -1;
00627     int failedInspector = -1;
00628     bool needCentering = false;
00629     try {
00630       switch (currentNode->getStatus()) {
00631       case UNDETERMINED:
00632           {
00633             unsigned int kids =
00634               currentNode->getNumberOfChildNodes(*na,curBest,stats,c_d,a_d);
00635             int depth = -1;
00636             for (VisualNode* p = currentNode; p != NULL; p=p->getParent(*na))
00637               depth++;
00638             if (kids > 0) {
00639               needCentering = true;
00640               depth++;
00641             }
00642             stats.maxDepth =
00643               std::max(stats.maxDepth, depth);
00644             if (currentNode->getStatus() == SOLVED) {
00645               assert(currentNode->hasCopy());
00646               emit solution(currentNode->getWorkingSpace());
00647             }
00648             emit statusChanged(currentNode,stats,true);
00649             for (int i=0; i<moveInspectors.size(); i++) {
00650               if (moveInspectors[i].second) {
00651                 failedInspectorType = 0;
00652                 failedInspector = i;
00653                 if (currentNode->getStatus() == FAILED) {
00654                   if (!currentNode->isRoot()) {
00655                     Space* curSpace =
00656                       currentNode->getSpace(*na,curBest,c_d,a_d);
00657                     moveInspectors[i].first->inspect(*curSpace);
00658                     delete curSpace;
00659                   }
00660                 } else {
00661                   moveInspectors[i].first->
00662                     inspect(*currentNode->getWorkingSpace());
00663                 }
00664                 failedInspectorType = -1;
00665               }
00666             }
00667             if (currentNode->getStatus() == SOLVED) {
00668               currentNode->purge(*na);
00669             }
00670           }
00671           break;
00672       case FAILED:
00673       case STOP:
00674       case UNSTOP:
00675       case BRANCH:
00676       case SOLVED:
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
00687               // to use search to find the solution here
00688               assert(curSpace->status() == SS_BRANCH &&
00689                      "Something went wrong - probably an incorrect brancher");
00690               Space* dfsSpace = Gecode::dfs(curSpace);
00691               delete curSpace;
00692               curSpace = dfsSpace;
00693             }
00694           } else {
00695             if (currentNode->isRoot())
00696               break;
00697             VisualNode* p = currentNode->getParent(*na);
00698             curSpace = p->getSpace(*na,curBest,c_d,a_d);
00699             switch (curSpace->status()) {
00700             case SS_SOLVED:
00701             case SS_FAILED:
00702               break;
00703             case SS_BRANCH:
00704               curSpace->commit(*p->getChoice(),
00705                                currentNode->getAlternative(*na));
00706               break;
00707             default:
00708               GECODE_NEVER;
00709             }
00710           }
00711 
00712           if (inspectorNo==-1) {
00713             for (int i=0; i<doubleClickInspectors.size(); i++) {
00714               if (doubleClickInspectors[i].second) {
00715                 failedInspectorType = 1;
00716                 failedInspector = i;
00717                 doubleClickInspectors[i].first->inspect(*curSpace);
00718                 failedInspectorType = -1;
00719               }
00720             }
00721           } else {
00722             failedInspectorType = 1;
00723             failedInspector = inspectorNo;
00724             doubleClickInspectors[inspectorNo].first->inspect(*curSpace);
00725             failedInspectorType = -1;
00726           }
00727           delete curSpace;
00728         }
00729         break;
00730       }
00731     } catch (Exception& e) {
00732       switch (failedInspectorType) {
00733       case 0:
00734         qFatal("Exception in move inspector %d: %s.\n Stopping.",
00735                failedInspector, e.what());
00736         break;
00737       case 1:
00738         qFatal("Exception in double click inspector %d: %s.\n Stopping.",
00739                failedInspector, e.what());
00740         break;
00741       default:
00742         qFatal("Exception: %s.\n Stopping.", e.what());
00743         break;
00744       }
00745     }
00746 
00747     currentNode->dirtyUp(*na);
00748     update();
00749     if (needCentering)
00750       centerCurrentNode();
00751   }
00752 
00753   void
00754   TreeCanvas::inspectBeforeFP(void) {
00755     inspectCurrentNode(false);
00756   }
00757 
00758   void
00759   TreeCanvas::labelBranches(void) {
00760     QMutexLocker locker(&mutex);
00761     currentNode->labelBranches(*na,curBest,c_d,a_d);
00762     update();
00763     centerCurrentNode();
00764     emit statusChanged(currentNode, stats, true);
00765   }
00766   void
00767   TreeCanvas::labelPath(void) {
00768     QMutexLocker locker(&mutex);
00769     currentNode->labelPath(*na,curBest,c_d,a_d);
00770     update();
00771     centerCurrentNode();
00772     emit statusChanged(currentNode, stats, true);
00773   }
00774 
00775   void
00776   TreeCanvas::inspectSolution(const Space* s) {
00777     int failedInspectorType = -1;
00778     int failedInspector = -1;
00779     try {
00780       Space* c = NULL;
00781       for (int i=0; i<solutionInspectors.size(); i++) {
00782         if (solutionInspectors[i].second) {
00783           if (c == NULL)
00784             c = s->clone();
00785           failedInspectorType = 1;
00786           failedInspector = i;
00787           solutionInspectors[i].first->inspect(*c);
00788           failedInspectorType = -1;
00789         }
00790       }
00791       delete c;
00792     } catch (Exception& e) {
00793       switch (failedInspectorType) {
00794       case 0:
00795         qFatal("Exception in move inspector %d: %s.\n Stopping.",
00796                failedInspector, e.what());
00797         break;
00798       case 1:
00799         qFatal("Exception in solution inspector %d: %s.\n Stopping.",
00800                failedInspector, e.what());
00801         break;
00802       default:
00803         qFatal("Exception: %s.\n Stopping.", e.what());
00804         break;
00805       }
00806     }
00807   }
00808 
00809   void
00810   TreeCanvas::stopSearch(void) {
00811     stopSearchFlag = true;
00812     layoutDoneTimerId = startTimer(15);
00813   }
00814 
00815   void
00816   TreeCanvas::reset(void) {
00817     QMutexLocker locker(&mutex);
00818     Space* rootSpace =
00819       root->getStatus() == FAILED ? NULL :
00820                            root->getSpace(*na,curBest,c_d,a_d);
00821     if (curBest != NULL) {
00822       delete curBest;
00823       curBest = new BestNode(NULL);
00824     }
00825     if (root) {
00826       DisposeCursor dc(root,*na);
00827       PreorderNodeVisitor<DisposeCursor>(dc).run();
00828     }
00829     delete na;
00830     na = new Node::NodeAllocator(curBest != NULL);
00831     int rootIdx = na->allocate(rootSpace);
00832     assert(rootIdx == 0); (void) rootIdx;
00833     root = (*na)[0];
00834     root->setMarked(true);
00835     currentNode = root;
00836     pathHead = root;
00837     scale = 1.0;
00838     stats = Statistics();
00839     for (int i=bookmarks.size(); i--;)
00840       emit removedBookmark(i);
00841     bookmarks.clear();
00842     root->layout(*na);
00843 
00844     emit statusChanged(currentNode, stats, true);
00845     update();
00846   }
00847 
00848   void
00849   TreeCanvas::bookmarkNode(void) {
00850     QMutexLocker locker(&mutex);
00851     if (!currentNode->isBookmarked()) {
00852       bool ok;
00853       QString text =
00854         QInputDialog::getText(this, "Add bookmark", "Name:",
00855                               QLineEdit::Normal,"",&ok);
00856       if (ok) {
00857         currentNode->setBookmarked(true);
00858         bookmarks.append(currentNode);
00859         if (text == "")
00860           text = QString("Node ")+QString().setNum(bookmarks.size());
00861         emit addedBookmark(text);
00862       }
00863     } else {
00864       currentNode->setBookmarked(false);
00865       int idx = bookmarks.indexOf(currentNode);
00866       bookmarks.remove(idx);
00867       emit removedBookmark(idx);
00868     }
00869     currentNode->dirtyUp(*na);
00870     update();
00871   }
00872 
00873   void
00874   TreeCanvas::setPath(void) {
00875     QMutexLocker locker(&mutex);
00876     if(currentNode == pathHead)
00877       return;
00878 
00879     pathHead->unPathUp(*na);
00880     pathHead = currentNode;
00881 
00882     currentNode->pathUp(*na);
00883     currentNode->dirtyUp(*na);
00884     update();
00885   }
00886 
00887   void
00888   TreeCanvas::inspectPath(void) {
00889     QMutexLocker locker(&mutex);
00890     setCurrentNode(root);
00891     if (currentNode->isOnPath()) {
00892       inspectCurrentNode();
00893       int nextAlt = currentNode->getPathAlternative(*na);
00894       while (nextAlt >= 0) {
00895         setCurrentNode(currentNode->getChild(*na,nextAlt));
00896         inspectCurrentNode();
00897         nextAlt = currentNode->getPathAlternative(*na);
00898       }
00899     }
00900     update();
00901   }
00902 
00903   void
00904   TreeCanvas::startCompareNodes(void) {
00905     QMutexLocker locker(&mutex);
00906     compareNodes = true;
00907     compareNodesBeforeFP = false;
00908     setCursor(QCursor(Qt::CrossCursor));
00909   }
00910 
00911   void
00912   TreeCanvas::startCompareNodesBeforeFP(void) {
00913     QMutexLocker locker(&mutex);
00914     compareNodes = true;
00915     compareNodesBeforeFP = true;
00916     setCursor(QCursor(Qt::CrossCursor));
00917   }
00918 
00919   void
00920   TreeCanvas::emitStatusChanged(void) {
00921     emit statusChanged(currentNode, stats, true);
00922   }
00923 
00924   void
00925   TreeCanvas::navUp(void) {
00926     QMutexLocker locker(&mutex);
00927 
00928     VisualNode* p = currentNode->getParent(*na);
00929 
00930     setCurrentNode(p);
00931 
00932     if (p != NULL) {
00933       centerCurrentNode();
00934     }
00935   }
00936 
00937   void
00938   TreeCanvas::navDown(void) {
00939     QMutexLocker locker(&mutex);
00940     if (!currentNode->isHidden()) {
00941       switch (currentNode->getStatus()) {
00942       case STOP:
00943       case UNSTOP:
00944       case BRANCH:
00945           {
00946             int alt = std::max(0, currentNode->getPathAlternative(*na));
00947             VisualNode* n = currentNode->getChild(*na,alt);
00948             setCurrentNode(n);
00949             centerCurrentNode();
00950             break;
00951           }
00952       case SOLVED:
00953       case FAILED:
00954       case UNDETERMINED:
00955         break;
00956       }
00957     }
00958   }
00959 
00960   void
00961   TreeCanvas::navLeft(void) {
00962     QMutexLocker locker(&mutex);
00963     VisualNode* p = currentNode->getParent(*na);
00964     if (p != NULL) {
00965       int alt = currentNode->getAlternative(*na);
00966       if (alt > 0) {
00967         VisualNode* n = p->getChild(*na,alt-1);
00968         setCurrentNode(n);
00969         centerCurrentNode();
00970       }
00971     }
00972   }
00973 
00974   void
00975   TreeCanvas::navRight(void) {
00976     QMutexLocker locker(&mutex);
00977     VisualNode* p = currentNode->getParent(*na);
00978     if (p != NULL) {
00979       unsigned int alt = currentNode->getAlternative(*na);
00980       if (alt + 1 < p->getNumberOfChildren()) {
00981         VisualNode* n = p->getChild(*na,alt+1);
00982         setCurrentNode(n);
00983         centerCurrentNode();
00984       }
00985     }
00986   }
00987 
00988   void
00989   TreeCanvas::navRoot(void) {
00990     QMutexLocker locker(&mutex);
00991     setCurrentNode(root);
00992     centerCurrentNode();
00993   }
00994 
00995   void
00996   TreeCanvas::navNextSol(bool back) {
00997     QMutexLocker locker(&mutex);
00998     NextSolCursor nsc(currentNode,back,*na);
00999     PreorderNodeVisitor<NextSolCursor> nsv(nsc);
01000     nsv.run();
01001     VisualNode* n = nsv.getCursor().node();
01002     if (n != root) {
01003       setCurrentNode(n);
01004       centerCurrentNode();
01005     }
01006   }
01007 
01008   void
01009   TreeCanvas::navPrevSol(void) {
01010     navNextSol(true);
01011   }
01012 
01013   void
01014   TreeCanvas::exportNodePDF(VisualNode* n) {
01015 #if QT_VERSION >= 0x040400
01016     QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
01017     if (filename != "") {
01018       QPrinter printer(QPrinter::ScreenResolution);
01019       QMutexLocker locker(&mutex);
01020 
01021       BoundingBox bb = n->getBoundingBox();
01022       printer.setFullPage(true);
01023       printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
01024                                   n->getShape()->depth() * Layout::dist_y +
01025                                   Layout::extent), QPrinter::Point);
01026       printer.setOutputFileName(filename);
01027       QPainter painter(&printer);
01028 
01029       painter.setRenderHint(QPainter::Antialiasing);
01030 
01031       QRect pageRect = printer.pageRect();
01032       double newXScale =
01033         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
01034                                                  Layout::extent);
01035       double newYScale =
01036         static_cast<double>(pageRect.height()) /
01037                             (n->getShape()->depth() * Layout::dist_y +
01038                              Layout::extent);
01039       double printScale = std::min(newXScale, newYScale);
01040       painter.scale(printScale,printScale);
01041 
01042       int printxtrans = -bb.left+(Layout::extent / 2);
01043 
01044       painter.translate(printxtrans, Layout::dist_y / 2);
01045       QRect clip(0,0,0,0);
01046       DrawingCursor dc(n, *na, curBest, painter, clip, showCopies);
01047       currentNode->setMarked(false);
01048       PreorderNodeVisitor<DrawingCursor>(dc).run();
01049       currentNode->setMarked(true);
01050     }
01051 #else
01052     (void) n;
01053 #endif
01054   }
01055 
01056   void
01057   TreeCanvas::exportWholeTreePDF(void) {
01058 #if QT_VERSION >= 0x040400
01059     exportNodePDF(root);
01060 #endif
01061   }
01062 
01063   void
01064   TreeCanvas::exportPDF(void) {
01065 #if QT_VERSION >= 0x040400
01066     exportNodePDF(currentNode);
01067 #endif
01068   }
01069 
01070   void
01071   TreeCanvas::print(void) {
01072     QPrinter printer;
01073     if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
01074       QMutexLocker locker(&mutex);
01075 
01076       BoundingBox bb = root->getBoundingBox();
01077       QRect pageRect = printer.pageRect();
01078       double newXScale =
01079         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
01080                                                  Layout::extent);
01081       double newYScale =
01082         static_cast<double>(pageRect.height()) /
01083                             (root->getShape()->depth() * Layout::dist_y +
01084                              2*Layout::extent);
01085       double printScale = std::min(newXScale, newYScale)*100;
01086       if (printScale<1.0)
01087         printScale = 1.0;
01088       if (printScale > 400.0)
01089         printScale = 400.0;
01090       printScale = printScale / 100.0;
01091 
01092       QPainter painter(&printer);
01093       painter.setRenderHint(QPainter::Antialiasing);
01094       painter.scale(printScale,printScale);
01095       painter.translate(xtrans, 0);
01096       QRect clip(0,0,0,0);
01097       DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
01098       PreorderNodeVisitor<DrawingCursor>(dc).run();
01099     }
01100   }
01101 
01102   VisualNode*
01103   TreeCanvas::eventNode(QEvent* event) {
01104     int x = 0;
01105     int y = 0;
01106     switch (event->type()) {
01107     case QEvent::ToolTip:
01108         {
01109           QHelpEvent* he = static_cast<QHelpEvent*>(event);
01110           x = he->x();
01111           y = he->y();
01112           break;
01113         }
01114     case QEvent::MouseButtonDblClick:
01115     case QEvent::MouseButtonPress:
01116     case QEvent::MouseButtonRelease:
01117     case QEvent::MouseMove:
01118         {
01119           QMouseEvent* me = static_cast<QMouseEvent*>(event);
01120           x = me->x();
01121           y = me->y();
01122           break;
01123         }
01124     case QEvent::ContextMenu:
01125         {
01126           QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
01127           x = ce->x();
01128           y = ce->y();
01129           break;
01130         }
01131     default:
01132       return NULL;
01133     }
01134     QAbstractScrollArea* sa =
01135       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01136     int xoff = sa->horizontalScrollBar()->value()/scale;
01137     int yoff = sa->verticalScrollBar()->value()/scale;
01138 
01139     BoundingBox bb = root->getBoundingBox();
01140     int w =
01141       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
01142     if (w < sa->viewport()->width())
01143       xoff -= (sa->viewport()->width()-w)/2;
01144 
01145     VisualNode* n;
01146     n = root->findNode(*na,
01147                        static_cast<int>(x/scale-xtrans+xoff),
01148                        static_cast<int>((y-30)/scale+yoff));
01149     return n;
01150   }
01151 
01152   bool
01153   TreeCanvas::event(QEvent* event) {
01154     if (mutex.tryLock()) {
01155       if (event->type() == QEvent::ToolTip) {
01156         VisualNode* n = eventNode(event);
01157         if (n != NULL) {
01158           QHelpEvent* he = static_cast<QHelpEvent*>(event);
01159           QToolTip::showText(he->globalPos(),
01160                              QString(n->toolTip(*na,curBest,
01161                                                 c_d,a_d).c_str()));
01162         } else {
01163           QToolTip::hideText();
01164         }
01165       }
01166       mutex.unlock();
01167     }
01168     return QWidget::event(event);
01169   }
01170 
01171   void
01172   TreeCanvas::resizeToOuter(void) {
01173     if (autoZoom)
01174       zoomToFit();
01175   }
01176 
01177   void
01178   TreeCanvas::paintEvent(QPaintEvent* event) {
01179     QMutexLocker locker(&layoutMutex);
01180     QPainter painter(this);
01181     painter.setRenderHint(QPainter::Antialiasing);
01182 
01183     QAbstractScrollArea* sa =
01184       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01185     int xoff = sa->horizontalScrollBar()->value()/scale;
01186     int yoff = sa->verticalScrollBar()->value()/scale;
01187 
01188     BoundingBox bb = root->getBoundingBox();
01189     int w =
01190       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
01191     if (w < sa->viewport()->width())
01192       xoff -= (sa->viewport()->width()-w)/2;
01193 
01194     QRect origClip = event->rect();
01195     painter.translate(0, 30);
01196     painter.scale(scale,scale);
01197     painter.translate(xtrans-xoff, -yoff);
01198     QRect clip(static_cast<int>(origClip.x()/scale-xtrans+xoff),
01199                static_cast<int>(origClip.y()/scale+yoff),
01200                static_cast<int>(origClip.width()/scale),
01201                static_cast<int>(origClip.height()/scale));
01202     DrawingCursor dc(root, *na, curBest, painter, clip, showCopies);
01203     PreorderNodeVisitor<DrawingCursor>(dc).run();
01204 
01205     // int nodesLayouted = 1;
01206     // clock_t t0 = clock();
01207     // while (v.next()) { nodesLayouted++; }
01208     // double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
01209     // double nps = static_cast<double>(nodesLayouted) /
01210     //   (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
01211     // std::cout << "Drawing done. " << nodesLayouted << " nodes in "
01212     //   << t << " ms. " << nps << " nodes/s." << std::endl;
01213 
01214   }
01215 
01216   void
01217   TreeCanvas::mouseDoubleClickEvent(QMouseEvent* event) {
01218     if (mutex.tryLock()) {
01219       if(event->button() == Qt::LeftButton) {
01220         VisualNode* n = eventNode(event);
01221         if(n == currentNode) {
01222           inspectCurrentNode();
01223           event->accept();
01224           mutex.unlock();
01225           return;
01226         }
01227       }
01228       mutex.unlock();
01229     }
01230     event->ignore();
01231   }
01232 
01233   void
01234   TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
01235     if (mutex.tryLock()) {
01236       VisualNode* n = eventNode(event);
01237       if (n != NULL) {
01238         setCurrentNode(n);
01239         emit contextMenu(event);
01240         event->accept();
01241         mutex.unlock();
01242         return;
01243       }
01244       mutex.unlock();
01245     }
01246     event->ignore();
01247   }
01248 
01249   void
01250   TreeCanvas::resizeEvent(QResizeEvent* e) {
01251     QAbstractScrollArea* sa =
01252       static_cast<QAbstractScrollArea*>(parentWidget()->parentWidget());
01253 
01254     int w = sa->horizontalScrollBar()->maximum()+e->oldSize().width();
01255     int h = sa->verticalScrollBar()->maximum()+e->oldSize().height();
01256 
01257     sa->horizontalScrollBar()->setRange(0,w-e->size().width());
01258     sa->verticalScrollBar()->setRange(0,h-e->size().height());
01259     sa->horizontalScrollBar()->setPageStep(e->size().width());
01260     sa->verticalScrollBar()->setPageStep(e->size().height());
01261   }
01262 
01263   void
01264   TreeCanvas::wheelEvent(QWheelEvent* event) {
01265     if (event->modifiers() & Qt::ShiftModifier) {
01266       event->accept();
01267       if (event->orientation() == Qt::Vertical && !autoZoom)
01268         scaleTree(scale*100+ceil(static_cast<double>(event->delta())/4.0),
01269                   event->x(), event->y());
01270     } else {
01271       event->ignore();
01272     }
01273   }
01274 
01275   bool
01276   TreeCanvas::finish(void) {
01277     if (finishedFlag)
01278       return true;
01279     stopSearchFlag = true;
01280     finishedFlag = true;
01281     for (int i=0; i<doubleClickInspectors.size(); i++)
01282       doubleClickInspectors[i].first->finalize();
01283     for (int i=0; i<solutionInspectors.size(); i++)
01284       solutionInspectors[i].first->finalize();
01285     for (int i=0; i<moveInspectors.size(); i++)
01286       moveInspectors[i].first->finalize();
01287     for (int i=0; i<comparators.size(); i++)
01288       comparators[i].first->finalize();
01289     return !searcher.isRunning();
01290   }
01291 
01292   void
01293   TreeCanvas::setCurrentNode(VisualNode* n, bool finished, bool update) {
01294     if (finished)
01295       mutex.lock();
01296     if (update && n != NULL && n != currentNode &&
01297         n->getStatus() != UNDETERMINED && !n->isHidden()) {
01298       Space* curSpace = NULL;
01299       for (int i=0; i<moveInspectors.size(); i++) {
01300         if (moveInspectors[i].second) {
01301           if (curSpace == NULL)
01302             curSpace = n->getSpace(*na,curBest,c_d,a_d);
01303           try {
01304             moveInspectors[i].first->inspect(*curSpace);
01305           } catch (Exception& e) {
01306             qFatal("Exception in move inspector %d: %s.\n Stopping.",
01307                    i, e.what());
01308           }
01309         }
01310       }
01311     }
01312     if (n != NULL) {
01313       currentNode->setMarked(false);
01314       currentNode = n;
01315       currentNode->setMarked(true);
01316       emit statusChanged(currentNode,stats,finished);
01317       if (update) {
01318         compareNodes = false;
01319         setCursor(QCursor(Qt::ArrowCursor));
01320         QWidget::update();
01321       }
01322     }
01323     if (finished)
01324       mutex.unlock();
01325   }
01326 
01327   void
01328   TreeCanvas::mousePressEvent(QMouseEvent* event) {
01329     if (mutex.tryLock()) {
01330       if (event->button() == Qt::LeftButton) {
01331         VisualNode* n = eventNode(event);
01332         if (compareNodes) {
01333           if (n != NULL && n->getStatus() != UNDETERMINED &&
01334               currentNode != NULL &&
01335               currentNode->getStatus() != UNDETERMINED) {
01336             Space* curSpace = NULL;
01337             Space* compareSpace = NULL;
01338             for (int i=0; i<comparators.size(); i++) {
01339               if (comparators[i].second) {
01340                 if (curSpace == NULL) {
01341                   curSpace = currentNode->getSpace(*na,curBest,c_d,a_d);
01342 
01343                   if (!compareNodesBeforeFP || n->isRoot()) {
01344                     compareSpace = n->getSpace(*na,curBest,c_d,a_d);
01345                   } else {
01346                     VisualNode* p = n->getParent(*na);
01347                     compareSpace = p->getSpace(*na,curBest,c_d,a_d);
01348                     switch (compareSpace->status()) {
01349                     case SS_SOLVED:
01350                     case SS_FAILED:
01351                       break;
01352                     case SS_BRANCH:
01353                       compareSpace->commit(*p->getChoice(),
01354                                            n->getAlternative(*na));
01355                       break;
01356                     default:
01357                       GECODE_NEVER;
01358                     }
01359                   }
01360                 }
01361                 try {
01362                   comparators[i].first->compare(*curSpace,*compareSpace);
01363                 } catch (Exception& e) {
01364                   qFatal("Exception in comparator %d: %s.\n Stopping.",
01365                     i, e.what());
01366                 }
01367               }
01368             }
01369           }
01370         } else {
01371           setCurrentNode(n);
01372         }
01373         compareNodes = false;
01374         setCursor(QCursor(Qt::ArrowCursor));
01375         if (n != NULL) {
01376           event->accept();
01377           mutex.unlock();
01378           return;
01379         }
01380       }
01381       mutex.unlock();
01382     }
01383     event->ignore();
01384   }
01385 
01386   void
01387   TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
01388     c_d = c_d0; a_d = a_d0;
01389   }
01390 
01391   void
01392   TreeCanvas::setAutoHideFailed(bool b) {
01393     autoHideFailed = b;
01394   }
01395 
01396   void
01397   TreeCanvas::setAutoZoom(bool b) {
01398     autoZoom = b;
01399     if (autoZoom) {
01400       zoomToFit();
01401     }
01402     emit autoZoomChanged(b);
01403     scaleBar->setEnabled(!b);
01404   }
01405 
01406   void
01407   TreeCanvas::setShowCopies(bool b) {
01408     showCopies = b;
01409   }
01410   bool
01411   TreeCanvas::getShowCopies(void) {
01412     return showCopies;
01413   }
01414 
01415   bool
01416   TreeCanvas::getAutoHideFailed(void) {
01417     return autoHideFailed;
01418   }
01419 
01420   bool
01421   TreeCanvas::getAutoZoom(void) {
01422     return autoZoom;
01423   }
01424 
01425   void
01426   TreeCanvas::setRefresh(int i) {
01427     refresh = i;
01428   }
01429 
01430   void
01431   TreeCanvas::setRefreshPause(int i) {
01432     refreshPause = i;
01433     if (refreshPause > 0)
01434       refresh = 1;
01435   }
01436 
01437   bool
01438   TreeCanvas::getSmoothScrollAndZoom(void) {
01439     return smoothScrollAndZoom;
01440   }
01441 
01442   void
01443   TreeCanvas::setSmoothScrollAndZoom(bool b) {
01444     smoothScrollAndZoom = b;
01445   }
01446 
01447   bool
01448   TreeCanvas::getMoveDuringSearch(void) {
01449     return moveDuringSearch;
01450   }
01451 
01452   void
01453   TreeCanvas::setMoveDuringSearch(bool b) {
01454     moveDuringSearch = b;
01455   }
01456 
01457 }}
01458 
01459 // STATISTICS: gist-any