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