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