Generated on Mon Nov 30 23:53:19 2009 for Gecode by doxygen 1.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: 2009-05-15 00:32:07 +0200 (Fri, 15 May 2009) $ by $Author: tack $
00011  *     $Revision: 9124 $
00012  *
00013  *  This file is part of Gecode, the generic constraint
00014  *  development environment:
00015  *     http://www.gecode.org
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining
00018  * a copy of this software and associated documentation files (the
00019  * "Software"), to deal in the Software without restriction, including
00020  * without limitation the rights to use, copy, modify, merge, publish,
00021  * distribute, sublicense, and/or sell copies of the Software, and to
00022  * permit persons to whom the Software is furnished to do so, subject to
00023  * the following conditions:
00024  *
00025  * The above copyright notice and this permission notice shall be
00026  * included in all copies or substantial portions of the Software.
00027  *
00028  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00029  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00030  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00031  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00032  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00033  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00034  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00035  *
00036  */
00037 
00038 #include <QtGui/QPainter>
00039 
00040 #include <stack>
00041 #include <fstream>
00042 
00043 #include <gecode/gist/treecanvas.hh>
00044 
00045 #include <gecode/gist/nodevisitor.hh>
00046 #include <gecode/gist/layoutcursor.hh>
00047 #include <gecode/gist/visualnode.hh>
00048 #include <gecode/gist/drawingcursor.hh>
00049 
00050 #include <gecode/search.hh>
00051 #include <gecode/search/support.hh>
00052 
00053 namespace Gecode { namespace Gist {
00054 
00055   TreeCanvas::TreeCanvas(Space* rootSpace, bool bab,
00056                          QWidget* parent, const Options& opt)
00057     : QWidget(parent)
00058     , mutex(QMutex::Recursive)
00059     , layoutMutex(QMutex::Recursive)
00060     , finishedFlag(false)
00061     , autoHideFailed(true), autoZoom(false)
00062     , refresh(500), smoothScrollAndZoom(false), nextPit(0)
00063     , targetZoom(LayoutConfig::defScale)
00064     , metaZoomCurrent(static_cast<double>(LayoutConfig::defScale))
00065     , zoomTimerId(0)
00066     , targetScrollX(0), targetScrollY(0)
00067     , metaScrollXCurrent(0.0), metaScrollYCurrent(0.0)
00068     , scrollTimerId(0)
00069     , targetW(0), targetH(0), targetScale(0)
00070     , layoutDoneTimerId(0) {
00071       QMutexLocker locker(&mutex);
00072       curBest = (bab ? new BestNode(NULL) : NULL);
00073       if (rootSpace->status() == SS_FAILED) {
00074         rootSpace = NULL;
00075       } else {
00076         rootSpace = Gecode::Search::snapshot(rootSpace,opt);
00077       }
00078       na = new Node::NodeAllocator();
00079       root = new (*na) VisualNode(rootSpace);
00080       root->layout();
00081       root->setMarked(true);
00082       currentNode = root;
00083       pathHead = root;
00084       scale = LayoutConfig::defScale / 100.0;
00085 
00086       setAutoFillBackground(true);
00087 
00088       connect(&searcher, SIGNAL(update(int,int,int)), this,
00089                          SLOT(layoutDone(int,int,int)));
00090       connect(&searcher, SIGNAL(statusChanged(bool)), this,
00091               SLOT(statusChanged(bool)));
00092 
00093       connect(&searcher, SIGNAL(solution(const Space*)),
00094               this, SIGNAL(solution(const Space*)),
00095               Qt::BlockingQueuedConnection);
00096       connect(&searcher, SIGNAL(solution(const Space*)),
00097               this, SLOT(inspectSolution(const Space*)),
00098               Qt::BlockingQueuedConnection);
00099 
00100       connect(&searcher, SIGNAL(finished(void)), this, SIGNAL(finished(void)));
00101 
00102       qRegisterMetaType<Statistics>("Statistics");
00103       update();
00104   }
00105 
00106   TreeCanvas::~TreeCanvas(void) { delete root; delete na; }
00107 
00108   void
00109   TreeCanvas::addDoubleClickInspector(Inspector* i) {
00110     doubleClickInspectors.append(QPair<Inspector*,bool>(i,false));
00111   }
00112 
00113   void
00114   TreeCanvas::activateDoubleClickInspector(int i, bool active) {
00115     doubleClickInspectors[i].second = active;
00116   }
00117 
00118   void
00119   TreeCanvas::addSolutionInspector(Inspector* i) {
00120     solutionInspectors.append(QPair<Inspector*,bool>(i,false));
00121   }
00122 
00123   void
00124   TreeCanvas::activateSolutionInspector(int i, bool active) {
00125     solutionInspectors[i].second = active;
00126   }
00127 
00128   void
00129   TreeCanvas::scaleTree(int scale0) {
00130     QMutexLocker locker(&layoutMutex);
00131     BoundingBox bb;
00132     scale0 = std::min(std::max(scale0, LayoutConfig::minScale),
00133                       LayoutConfig::maxScale);
00134     scale = (static_cast<double>(scale0)) / 100.0;
00135     bb = root->getBoundingBox();
00136     int w =
00137       static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00138     int h =
00139       static_cast<int>(2*Layout::extent+
00140         root->getShape()->depth()*Layout::dist_y*scale);
00141     resize(w,h);
00142     emit scaleChanged(scale0);
00143     QWidget::update();
00144   }
00145 
00146   void
00147   TreeCanvas::update(void) {
00148     QMutexLocker locker(&mutex);
00149     layoutMutex.lock();
00150     if (root != NULL) {
00151       root->layout();
00152       BoundingBox bb = root->getBoundingBox();
00153 
00154       int w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00155       int h =
00156         static_cast<int>(2*Layout::extent+
00157           root->getShape()->depth()*Layout::dist_y*scale);
00158       xtrans = -bb.left+(Layout::extent / 2);
00159       resize(w,h);
00160     }
00161     if (autoZoom)
00162       zoomToFit();
00163     layoutMutex.unlock();
00164     QWidget::update();
00165   }
00166 
00167   void
00168   TreeCanvas::layoutDone(int w, int h, int scale0) {
00169     targetW = w; targetH = h; targetScale = scale0;
00170     if (layoutDoneTimerId == 0)
00171       layoutDoneTimerId = startTimer(15);
00172   }
00173 
00174   void
00175   TreeCanvas::statusChanged(bool finished) {
00176     if (finished) {
00177       update();
00178       centerCurrentNode();
00179     }
00180     emit statusChanged(currentNode, stats, finished);
00181   }
00182 
00183   void
00184   SearcherThread::search(VisualNode* n, bool all, TreeCanvas* ti) {
00185     node = n;
00186     
00187     depth = -1;
00188     for (VisualNode* p = n; p != NULL; p = p->getParent())
00189       depth++;
00190     
00191     a = all;
00192     t = ti;
00193     start();
00194   }
00195 
00196   void
00197   SearcherThread::updateCanvas(void) {
00198     t->layoutMutex.lock();
00199     if (t->root == NULL)
00200       return;
00201 
00202     if (t->autoHideFailed) {
00203       t->root->hideFailed();
00204     }
00205     for (VisualNode* n = t->currentNode; n != NULL; n = n->getParent()) {
00206       if (n->isHidden()) {
00207         t->currentNode->setMarked(false);
00208         t->currentNode = n;
00209         t->currentNode->setMarked(true);
00210         break;
00211       }
00212     }
00213     
00214     t->root->layout();
00215     BoundingBox bb = t->root->getBoundingBox();
00216 
00217     int w = static_cast<int>((bb.right-bb.left+Layout::extent)*t->scale);
00218     int h = static_cast<int>(2*Layout::extent+
00219                              t->root->getShape()->depth()
00220                               *Layout::dist_y*t->scale);
00221     t->xtrans = -bb.left+(Layout::extent / 2);
00222 
00223     int scale0 = static_cast<int>(t->scale*100);
00224     if (t->autoZoom) {
00225       QWidget* p = t->parentWidget();
00226       if (p) {
00227         double newXScale =
00228           static_cast<double>(p->width()) / (bb.right - bb.left +
00229                                              Layout::extent);
00230         double newYScale =
00231           static_cast<double>(p->height()) /
00232           (t->root->getShape()->depth() * Layout::dist_y + 2*Layout::extent);
00233 
00234         scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
00235         if (scale0<LayoutConfig::minScale)
00236           scale0 = LayoutConfig::minScale;
00237         if (scale0>LayoutConfig::maxAutoZoomScale)
00238           scale0 = LayoutConfig::maxAutoZoomScale;
00239         double scale = (static_cast<double>(scale0)) / 100.0;
00240 
00241         w = static_cast<int>((bb.right-bb.left+Layout::extent)*scale);
00242         h = static_cast<int>(2*Layout::extent+
00243                              t->root->getShape()->depth()*Layout::dist_y*scale);
00244       }
00245     }
00246     t->layoutMutex.unlock();
00247     emit update(w,h,scale0);
00248   }
00249 
00250   class SearchItem {
00251   public:
00252     VisualNode* n;
00253     int i;
00254     int noOfChildren;
00255     SearchItem(VisualNode* n0, int noOfChildren0)
00256       : n(n0), i(-1), noOfChildren(noOfChildren0) {}
00257   };
00258 
00259   void
00260   SearcherThread::run() {
00261     {
00262       if (!node->isOpen())
00263         return;
00264       t->mutex.lock();
00265       emit statusChanged(false);
00266 
00267       unsigned int kids = 
00268         node->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
00269                                     t->c_d, t->a_d);
00270       if (kids == 0) {
00271         t->mutex.unlock();
00272         updateCanvas();
00273         emit statusChanged(true);
00274         return;
00275       }
00276 
00277       std::stack<SearchItem> stck;
00278       stck.push(SearchItem(node,kids));
00279       t->stats.maxDepth =
00280         std::max(static_cast<long unsigned int>(t->stats.maxDepth), 
00281                  static_cast<long unsigned int>(depth+stck.size()));
00282 
00283       VisualNode* sol = NULL;
00284       int nodeCount = 0;
00285       t->stopSearchFlag = false;
00286       while (!stck.empty() && !t->stopSearchFlag) {
00287         if (t->refresh > 0 && ++nodeCount > t->refresh) {
00288           node->dirtyUp();
00289           updateCanvas();
00290           emit statusChanged(false);
00291           nodeCount = 0;
00292         }
00293         SearchItem& si = stck.top();
00294         si.i++;
00295         if (si.i == si.noOfChildren) {
00296           stck.pop();
00297         } else {
00298           VisualNode* n = si.n->getChild(si.i);
00299           if (n->isOpen()) {
00300             kids = n->getNumberOfChildNodes(*t->na, t->curBest, t->stats,
00301                                             t->c_d, t->a_d);
00302             if (kids == 0) {
00303               if (n->getStatus() == SOLVED) {
00304                 assert(n->hasWorkingSpace());
00305                 emit solution(n->getWorkingSpace());
00306                 n->purge();
00307                 sol = n;
00308                 if (!a)
00309                   break;
00310               }
00311             } else {
00312               stck.push(SearchItem(n,kids));
00313               t->stats.maxDepth =
00314                 std::max(static_cast<long unsigned int>(t->stats.maxDepth), 
00315                          static_cast<long unsigned int>(depth+stck.size()));
00316             }
00317           }
00318         }
00319       }
00320       node->dirtyUp();
00321       t->stopSearchFlag = false;
00322       t->mutex.unlock();
00323       if (sol != NULL) {
00324         t->setCurrentNode(sol);
00325       } else {
00326         t->setCurrentNode(node);
00327       }
00328     }
00329     updateCanvas();
00330     emit statusChanged(true);
00331     if (t->finishedFlag)
00332       emit finished();
00333   }
00334 
00335   void
00336   TreeCanvas::searchAll(void) {
00337     QMutexLocker locker(&mutex);
00338     searcher.search(currentNode, true, this);
00339   }
00340 
00341   void
00342   TreeCanvas::searchOne(void) {
00343     QMutexLocker locker(&mutex);
00344     searcher.search(currentNode, false, this);
00345   }
00346 
00347   void
00348   TreeCanvas::toggleHidden(void) {
00349     QMutexLocker locker(&mutex);
00350     currentNode->toggleHidden();
00351     update();
00352     centerCurrentNode();
00353     emit statusChanged(currentNode, stats, true);
00354   }
00355 
00356   void
00357   TreeCanvas::hideFailed(void) {
00358     QMutexLocker locker(&mutex);
00359     currentNode->hideFailed();
00360     update();
00361     centerCurrentNode();
00362     emit statusChanged(currentNode, stats, true);
00363   }
00364 
00365   void
00366   TreeCanvas::unhideAll(void) {
00367     QMutexLocker locker(&mutex);
00368     QMutexLocker layoutLocker(&layoutMutex);
00369     currentNode->unhideAll();
00370     update();
00371     centerCurrentNode();
00372     emit statusChanged(currentNode, stats, true);
00373   }
00374 
00375   void
00376   TreeCanvas::timerEvent(QTimerEvent* e) {
00377     if (e->timerId() == zoomTimerId) {
00378       double offset = static_cast<double>(targetZoom - metaZoomCurrent) / 6.0;
00379       metaZoomCurrent += offset;
00380       scaleBar->setValue(static_cast<int>(metaZoomCurrent));
00381       if (static_cast<int>(metaZoomCurrent+.5) == targetZoom) {
00382         killTimer(zoomTimerId);
00383         zoomTimerId = 0;
00384       }
00385     } else if (e->timerId() == scrollTimerId) {
00386       QScrollArea* sa =
00387         static_cast<QScrollArea*>(parentWidget()->parentWidget());
00388 
00389       double xoffset =
00390         static_cast<double>(targetScrollX - metaScrollXCurrent) / 6.0;
00391       metaScrollXCurrent += xoffset;
00392       sa->horizontalScrollBar()
00393         ->setValue(static_cast<int>(metaScrollXCurrent));
00394       double yoffset =
00395         static_cast<double>(targetScrollY - metaScrollYCurrent) / 6.0;
00396       metaScrollYCurrent += yoffset;
00397       sa->verticalScrollBar()
00398         ->setValue(static_cast<int>(metaScrollYCurrent));
00399 
00400       if (static_cast<int>(metaScrollXCurrent+.5) == targetScrollX &&
00401           static_cast<int>(metaScrollYCurrent+.5) == targetScrollY) {
00402         killTimer(scrollTimerId);
00403         scrollTimerId = 0;
00404       }
00405     } else if (e->timerId() == layoutDoneTimerId) {
00406       if (!smoothScrollAndZoom) {
00407         scaleTree(targetScale);
00408       } else {
00409         metaZoomCurrent = static_cast<int>(scale*100);
00410         targetZoom = targetScale;
00411         targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00412                               LayoutConfig::maxAutoZoomScale);
00413         zoomTimerId = startTimer(15);
00414       }
00415       resize(targetW,targetH);
00416       QWidget::update();
00417       killTimer(layoutDoneTimerId);
00418       layoutDoneTimerId = 0;
00419     }
00420   }
00421 
00422   void
00423   TreeCanvas::zoomToFit(void) {
00424     QMutexLocker locker(&layoutMutex);
00425     if (root != NULL) {
00426       BoundingBox bb;
00427       bb = root->getBoundingBox();
00428       QWidget* p = parentWidget();
00429       if (p) {
00430         double newXScale =
00431           static_cast<double>(p->width()) / (bb.right - bb.left +
00432                                              Layout::extent);
00433         double newYScale =
00434           static_cast<double>(p->height()) / (root->getShape()->depth() * 
00435                                               Layout::dist_y +
00436                                               2*Layout::extent);
00437         int scale0 = static_cast<int>(std::min(newXScale, newYScale)*100);
00438         if (scale0<LayoutConfig::minScale)
00439           scale0 = LayoutConfig::minScale;
00440         if (scale0>LayoutConfig::maxAutoZoomScale)
00441           scale0 = LayoutConfig::maxAutoZoomScale;
00442 
00443         if (!smoothScrollAndZoom) {
00444           scaleTree(scale0);
00445         } else {
00446           metaZoomCurrent = static_cast<int>(scale*100);
00447           targetZoom = scale0;
00448           targetZoom = std::min(std::max(targetZoom, LayoutConfig::minScale),
00449                                 LayoutConfig::maxAutoZoomScale);
00450           zoomTimerId = startTimer(15);
00451         }
00452       }
00453     }
00454   }
00455 
00456   void
00457   TreeCanvas::centerCurrentNode(void) {
00458     QMutexLocker locker(&mutex);
00459     int x=0;
00460     int y=0;
00461 
00462     VisualNode* c = currentNode;
00463     while (c != NULL) {
00464       x += c->getOffset();
00465       y += Layout::dist_y;
00466       c = c->getParent();
00467     }
00468 
00469     x = static_cast<int>((xtrans+x)*scale); y = static_cast<int>(y*scale);
00470 
00471     QScrollArea* sa =
00472       static_cast<QScrollArea*>(parentWidget()->parentWidget());
00473 
00474     if (!smoothScrollAndZoom) {
00475       sa->ensureVisible(x,y);
00476     } else {
00477       x -= sa->viewport()->width() / 2;
00478       y -= sa->viewport()->height() / 2;
00479 
00480       metaScrollXCurrent = sa->horizontalScrollBar()->value();
00481       metaScrollYCurrent = sa->verticalScrollBar()->value();
00482       targetScrollX = std::max(sa->horizontalScrollBar()->minimum(), x);
00483       targetScrollX = std::min(sa->horizontalScrollBar()->maximum(),
00484                                targetScrollX);
00485       targetScrollY = std::max(sa->verticalScrollBar()->minimum(), y);
00486       targetScrollY = std::min(sa->verticalScrollBar()->maximum(),
00487                                targetScrollY);
00488       scrollTimerId = startTimer(15);
00489     }
00490   }
00491 
00492   void
00493   TreeCanvas::inspectCurrentNode(void) {
00494     QMutexLocker locker(&mutex);
00495 
00496     if (currentNode->isHidden()) {
00497       toggleHidden();
00498       return;
00499     }
00500 
00501     switch (currentNode->getStatus()) {
00502     case UNDETERMINED:
00503         {
00504           unsigned int kids =  
00505             currentNode->getNumberOfChildNodes(*na,curBest,stats,c_d,a_d);
00506           int depth = -1;
00507           for (VisualNode* p = currentNode; p != NULL; p = p->getParent())
00508             depth++;
00509           if (kids > 0)
00510             depth++;
00511           stats.maxDepth =
00512             std::max(stats.maxDepth, depth);
00513           if (currentNode->getStatus() == SOLVED) {
00514             assert(currentNode->hasWorkingSpace());
00515             emit solution(currentNode->getWorkingSpace());
00516             currentNode->purge();
00517           }
00518           emit statusChanged(currentNode,stats,true);
00519         }
00520         break;
00521     case FAILED:
00522     case STEP:
00523     case SPECIAL:
00524     case BRANCH:
00525     case SOLVED:
00526       {
00527         // SizeCursor sc(currentNode);
00528         // PreorderNodeVisitor<SizeCursor> pnv(sc);
00529         // int nodes = 1;
00530         // while (pnv.next()) { nodes++; }
00531         // std::cout << "sizeof(VisualNode): " << sizeof(VisualNode)
00532         //           << std::endl;
00533         // std::cout << "Size: " << (pnv.getCursor().s)/1024 << std::endl;
00534         // std::cout << "Nodes: " << nodes << std::endl;
00535         // std::cout << "Size / node: " << (pnv.getCursor().s)/nodes
00536         //           << std::endl;
00537 
00538         if (currentNode->isRoot() && currentNode->getStatus() == FAILED)
00539           break;
00540         Space* curSpace = currentNode->getSpace(curBest,c_d,a_d);
00541         if (currentNode->getStatus() == SOLVED &&
00542             curSpace->status() != SS_SOLVED) {
00543           // in the presence of weakly monotonic propagators, we may have to
00544           // use search to find the solution here
00545           Space* dfsSpace = Gecode::dfs(curSpace);
00546           delete curSpace;
00547           curSpace = dfsSpace;
00548         }
00549         saveCurrentNode();
00550 
00551         for (int i=0; i<doubleClickInspectors.size(); i++) {
00552           if (doubleClickInspectors[i].second) {
00553             doubleClickInspectors[i].first->inspect(*curSpace);
00554           }
00555         }
00556         delete curSpace;
00557       }
00558       break;
00559     }
00560 
00561     currentNode->dirtyUp();
00562     update();
00563     centerCurrentNode();
00564   }
00565 
00566   void
00567   TreeCanvas::inspectSolution(const Space* s) {
00568     Space* c = NULL;
00569     for (int i=0; i<solutionInspectors.size(); i++) {
00570       if (solutionInspectors[i].second) {
00571         if (c == NULL)
00572           c = s->clone();
00573         solutionInspectors[i].first->inspect(*c);
00574       }
00575     }
00576     delete c;
00577   }
00578 
00579   void
00580   TreeCanvas::stopSearch(void) {
00581     stopSearchFlag = true;
00582     layoutDoneTimerId = startTimer(15);
00583   }
00584 
00585   void
00586   TreeCanvas::reset(void) {
00587     QMutexLocker locker(&mutex);
00588     Space* rootSpace =
00589       root->getStatus() == FAILED ? NULL : root->getSpace(curBest,c_d,a_d);
00590     if (curBest != NULL) {
00591       delete curBest;
00592       curBest = new BestNode(NULL);
00593     }
00594     delete root;
00595     delete na;
00596     na = new Node::NodeAllocator();
00597     root = new (*na) VisualNode(rootSpace);
00598     root->setMarked(true);
00599     currentNode = root;
00600     pathHead = root;
00601     nodeMap.clear();
00602     nextPit = 0;
00603     scale = 1.0;
00604     stats = Statistics();
00605     root->layout();
00606 
00607     emit statusChanged(currentNode, stats, true);
00608     update();
00609   }
00610 
00611   void
00612   TreeCanvas::setPath(void) {
00613     QMutexLocker locker(&mutex);
00614     if(currentNode == pathHead)
00615       return;
00616 
00617     pathHead->unPathUp();
00618     pathHead = currentNode;
00619 
00620     currentNode->pathUp();
00621     currentNode->dirtyUp();
00622     update();
00623   }
00624 
00625   void
00626   TreeCanvas::inspectPath(void) {
00627     QMutexLocker locker(&mutex);
00628     setCurrentNode(root);
00629     if (currentNode->isOnPath()) {
00630       inspectCurrentNode();
00631       int nextAlt = currentNode->getPathAlternative();
00632       while (nextAlt >= 0) {
00633         setCurrentNode(currentNode->getChild(nextAlt));
00634         inspectCurrentNode();
00635         nextAlt = currentNode->getPathAlternative();
00636       }
00637     }
00638     update();
00639   }
00640 
00641   void
00642   TreeCanvas::emitStatusChanged(void) {
00643     emit statusChanged(currentNode, stats, true);
00644   }
00645 
00646   void
00647   TreeCanvas::navUp(void) {
00648     QMutexLocker locker(&mutex);
00649 
00650     VisualNode* p = currentNode->getParent();
00651 
00652     setCurrentNode(p);
00653 
00654     if (p != NULL) {
00655       centerCurrentNode();
00656     }
00657   }
00658 
00659   void
00660   TreeCanvas::navDown(void) {
00661     QMutexLocker locker(&mutex);
00662     if (!currentNode->isHidden()) {
00663       switch (currentNode->getStatus()) {
00664       case STEP:
00665       case SPECIAL:
00666         if (currentNode->getNumberOfChildren() < 1)
00667           break;
00668       case BRANCH:
00669           {
00670             int alt = std::max(0, currentNode->getPathAlternative());
00671             VisualNode* n = currentNode->getChild(alt);
00672             setCurrentNode(n);
00673             centerCurrentNode();
00674             break;
00675           }
00676       case SOLVED:
00677       case FAILED:
00678       case UNDETERMINED:
00679         break;
00680       }
00681     }
00682   }
00683 
00684   void
00685   TreeCanvas::navLeft(void) {
00686     QMutexLocker locker(&mutex);
00687     VisualNode* p = currentNode->getParent();
00688     if (p != NULL) {
00689       int alt = currentNode->getAlternative();
00690       if (alt > 0) {
00691         VisualNode* n = p->getChild(alt-1);
00692         setCurrentNode(n);
00693         centerCurrentNode();
00694       }
00695     }
00696   }
00697 
00698   void
00699   TreeCanvas::navRight(void) {
00700     QMutexLocker locker(&mutex);
00701     VisualNode* p = currentNode->getParent();
00702     if (p != NULL) {
00703       unsigned int alt = currentNode->getAlternative();
00704       if (alt + 1 < p->getNumberOfChildren()) {
00705         VisualNode* n = p->getChild(alt+1);
00706         setCurrentNode(n);
00707         centerCurrentNode();
00708       }
00709     }
00710   }
00711 
00712   void
00713   TreeCanvas::navRoot(void) {
00714     QMutexLocker locker(&mutex);
00715     setCurrentNode(root);
00716     centerCurrentNode();
00717   }
00718 
00719   void
00720   TreeCanvas::navNextSol(bool back) {
00721     QMutexLocker locker(&mutex);
00722     NextSolCursor nsc(currentNode, back);
00723     PreorderNodeVisitor<NextSolCursor> nsv(nsc);
00724     while (nsv.next()) {}
00725     if (nsv.getCursor().node() != root) {
00726       setCurrentNode(nsv.getCursor().node());
00727       centerCurrentNode();
00728     }
00729   }
00730 
00731   void
00732   TreeCanvas::navPrevSol(void) {
00733     navNextSol(true);
00734   }
00735 
00736   void
00737   TreeCanvas::markCurrentNode(int pit) {
00738     QMutexLocker locker(&mutex);
00739     if(nodeMap.size() > pit && nodeMap[pit] != NULL) {
00740       setCurrentNode(nodeMap[pit]);
00741       centerCurrentNode();
00742       emit pointInTimeChanged(pit);
00743     }
00744   }
00745 
00746   void
00747   TreeCanvas::saveCurrentNode(void) {
00748     QMutexLocker locker(&mutex);
00749     assert(nextPit == nodeMap.size());
00750     nodeMap << currentNode;
00751     nextPit++;
00752   }
00753 
00754   void
00755   TreeCanvas::exportNodePDF(VisualNode* n) {
00756 #if QT_VERSION >= 0x040400
00757     QString filename = QFileDialog::getSaveFileName(this, tr("Export tree as pdf"), "", tr("PDF (*.pdf)"));
00758     if (filename != "") {
00759       QPrinter printer(QPrinter::ScreenResolution);
00760       QMutexLocker locker(&mutex);
00761 
00762       BoundingBox bb = n->getBoundingBox();
00763       printer.setFullPage(true);
00764       printer.setPaperSize(QSizeF(bb.right-bb.left+Layout::extent,
00765                                   n->getShape()->depth() * Layout::dist_y +
00766                                   Layout::extent), QPrinter::Point);
00767       printer.setOutputFileName(filename);
00768       QPainter painter(&printer);
00769 
00770       painter.setRenderHint(QPainter::Antialiasing);
00771 
00772       QRect pageRect = printer.pageRect();
00773       double newXScale =
00774         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
00775                                                  Layout::extent);
00776       double newYScale =
00777         static_cast<double>(pageRect.height()) /
00778                             (n->getShape()->depth() * Layout::dist_y +
00779                              Layout::extent);
00780       double printScale = std::min(newXScale, newYScale);
00781       painter.scale(printScale,printScale);
00782 
00783       int printxtrans = -bb.left+(Layout::extent / 2);
00784 
00785       painter.translate(printxtrans, Layout::dist_y / 2);
00786       QRect clip(0,0,0,0);
00787       DrawingCursor dc(n, curBest, painter, clip, showCopies);
00788       currentNode->setMarked(false);
00789       PreorderNodeVisitor<DrawingCursor> v(dc);
00790       while (v.next()) {}
00791       currentNode->setMarked(true);
00792     }
00793 #endif
00794   }
00795 
00796   void
00797   TreeCanvas::exportWholeTreePDF(void) {
00798 #if QT_VERSION >= 0x040400
00799     exportNodePDF(root);
00800 #endif
00801   }
00802 
00803   void
00804   TreeCanvas::exportPDF(void) {
00805 #if QT_VERSION >= 0x040400
00806     exportNodePDF(currentNode);
00807 #endif
00808   }
00809 
00810   void
00811   TreeCanvas::print(void) {
00812     QPrinter printer;
00813     if (QPrintDialog(&printer, this).exec() == QDialog::Accepted) {
00814       QMutexLocker locker(&mutex);
00815 
00816       BoundingBox bb = root->getBoundingBox();
00817       QRect pageRect = printer.pageRect();
00818       double newXScale =
00819         static_cast<double>(pageRect.width()) / (bb.right - bb.left +
00820                                                  Layout::extent);
00821       double newYScale =
00822         static_cast<double>(pageRect.height()) /
00823                             (root->getShape()->depth() * Layout::dist_y +
00824                              2*Layout::extent);
00825       double printScale = std::min(newXScale, newYScale)*100;
00826       if (printScale<1.0)
00827         printScale = 1.0;
00828       if (printScale > 400.0)
00829         printScale = 400.0;
00830       printScale = printScale / 100.0;
00831 
00832       QPainter painter(&printer);
00833       painter.setRenderHint(QPainter::Antialiasing);
00834       painter.scale(printScale,printScale);
00835       painter.translate(xtrans, 0);
00836       QRect clip(0,0,0,0);
00837       DrawingCursor dc(root, curBest, painter, clip, showCopies);
00838       PreorderNodeVisitor<DrawingCursor> v(dc);
00839       while (v.next()) {}
00840     }
00841   }
00842 
00843   VisualNode*
00844   TreeCanvas::eventNode(QEvent* event) {
00845     int x = 0;
00846     int y = 0;
00847     switch (event->type()) {
00848     case QEvent::ToolTip:
00849         {
00850           QHelpEvent* he = static_cast<QHelpEvent*>(event);
00851           x = he->x();
00852           y = he->y();
00853           break;
00854         }
00855     case QEvent::MouseButtonDblClick:
00856     case QEvent::MouseButtonPress:
00857     case QEvent::MouseButtonRelease:
00858     case QEvent::MouseMove:
00859         {
00860           QMouseEvent* me = static_cast<QMouseEvent*>(event);
00861           x = me->x();
00862           y = me->y();
00863           break;
00864         }
00865     case QEvent::ContextMenu:
00866         {
00867           QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
00868           x = ce->x();
00869           y = ce->y();
00870           break;
00871         }
00872     default:
00873       return NULL;
00874     }
00875     VisualNode* n;
00876     n = root->findNode(static_cast<int>(x/scale-xtrans),
00877                        static_cast<int>((y-30)/scale));
00878     return n;
00879   }
00880 
00881   bool
00882   TreeCanvas::event(QEvent* event) {
00883     if (mutex.tryLock()) {
00884       if (event->type() == QEvent::ToolTip) {
00885         VisualNode* n = eventNode(event);
00886         if (n != NULL && !n->isHidden() &&
00887             (n->getStatus() == BRANCH)) {
00888           QHelpEvent* he = static_cast<QHelpEvent*>(event);
00889           QToolTip::showText(he->globalPos(),
00890                              QString(n->toolTip(curBest,c_d,a_d).c_str()));
00891         } else {
00892           QToolTip::hideText();
00893         }
00894       }
00895       mutex.unlock();
00896     }
00897     return QWidget::event(event);
00898   }
00899 
00900   void
00901   TreeCanvas::resizeToOuter(void) {
00902     if (autoZoom)
00903       zoomToFit();
00904   }
00905 
00906   void
00907   TreeCanvas::paintEvent(QPaintEvent* event) {
00908     QMutexLocker locker(&layoutMutex);
00909     QPainter painter(this);
00910     painter.setRenderHint(QPainter::Antialiasing);
00911 
00912     BoundingBox bb = root->getBoundingBox();
00913     QRect origClip = event->rect();
00914     painter.translate(0, 30);
00915     painter.scale(scale,scale);
00916     painter.translate(xtrans, 0);
00917     QRect clip(static_cast<int>(origClip.x()/scale-xtrans),
00918                static_cast<int>(origClip.y()/scale),
00919                static_cast<int>(origClip.width()/scale),
00920                static_cast<int>(origClip.height()/scale));
00921     DrawingCursor dc(root, curBest, painter, clip, showCopies);
00922     PreorderNodeVisitor<DrawingCursor> v(dc);
00923 
00924     while (v.next()) {}
00925 
00926     // int nodesLayouted = 1;
00927     // clock_t t0 = clock();
00928     // while (v.next()) { nodesLayouted++; }
00929     // double t = (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC) * 1000.0;
00930     // double nps = static_cast<double>(nodesLayouted) /
00931     //   (static_cast<double>(clock()-t0) / CLOCKS_PER_SEC);
00932     // std::cout << "Drawing done. " << nodesLayouted << " nodes in "
00933     //   << t << " ms. " << nps << " nodes/s." << std::endl;
00934 
00935   }
00936 
00937   void
00938   TreeCanvas::mouseDoubleClickEvent(QMouseEvent* event) {
00939     if (mutex.tryLock()) {
00940       if(event->button() == Qt::LeftButton) {
00941         VisualNode* n = eventNode(event);
00942         if(n == currentNode) {
00943           inspectCurrentNode();
00944           event->accept();
00945           mutex.unlock();
00946           return;
00947         }
00948       }
00949       mutex.unlock();
00950     }
00951     event->ignore();
00952   }
00953 
00954   void
00955   TreeCanvas::contextMenuEvent(QContextMenuEvent* event) {
00956     if (mutex.tryLock()) {
00957       VisualNode* n = eventNode(event);
00958       if (n != NULL) {
00959         setCurrentNode(n);
00960         emit contextMenu(event);
00961         event->accept();
00962         mutex.unlock();
00963         return;
00964       }
00965       mutex.unlock();
00966     }
00967     event->ignore();
00968   }
00969 
00970   bool
00971   TreeCanvas::finish(void) {
00972     if (finishedFlag)
00973       return true;
00974     stopSearchFlag = true;
00975     finishedFlag = true;
00976     for (int i=0; i<doubleClickInspectors.size(); i++)
00977       doubleClickInspectors[i].first->finalize();
00978     for (int i=0; i<solutionInspectors.size(); i++)
00979       solutionInspectors[i].first->finalize();
00980     return !searcher.isRunning();
00981   }
00982 
00983   void
00984   TreeCanvas::setCurrentNode(VisualNode* n) {
00985     QMutexLocker locker(&mutex);
00986     if (n != NULL) {
00987       currentNode->setMarked(false);
00988       currentNode = n;
00989       currentNode->setMarked(true);
00990       emit statusChanged(currentNode,stats,true);
00991       QWidget::update();
00992     }
00993   }
00994 
00995   void
00996   TreeCanvas::mousePressEvent(QMouseEvent* event) {
00997     if (mutex.tryLock()) {
00998       if (event->button() == Qt::LeftButton) {
00999         VisualNode* n = eventNode(event);
01000         setCurrentNode(n);
01001         if (n != NULL) {
01002           event->accept();
01003           mutex.unlock();
01004           return;
01005         }
01006       }
01007       mutex.unlock();
01008     }
01009     event->ignore();
01010   }
01011 
01012   void
01013   TreeCanvas::setRecompDistances(int c_d0, int a_d0) {
01014     c_d = c_d0; a_d = a_d0;
01015   }
01016 
01017   void
01018   TreeCanvas::setAutoHideFailed(bool b) {
01019     autoHideFailed = b;
01020   }
01021 
01022   void
01023   TreeCanvas::setAutoZoom(bool b) {
01024     autoZoom = b;
01025     if (autoZoom) {
01026       zoomToFit();
01027     }
01028     emit autoZoomChanged(b);
01029     scaleBar->setEnabled(!b);
01030   }
01031 
01032   void
01033   TreeCanvas::setShowCopies(bool b) {
01034     showCopies = b;
01035   }
01036   bool
01037   TreeCanvas::getShowCopies(void) {
01038     return showCopies;
01039   }
01040 
01041   bool
01042   TreeCanvas::getAutoHideFailed(void) {
01043     return autoHideFailed;
01044   }
01045 
01046   bool
01047   TreeCanvas::getAutoZoom(void) {
01048     return autoZoom;
01049   }
01050 
01051   void
01052   TreeCanvas::setRefresh(int i) {
01053     refresh = i;
01054   }
01055 
01056   bool
01057   TreeCanvas::getSmoothScrollAndZoom(void) {
01058     return smoothScrollAndZoom;
01059   }
01060 
01061   void
01062   TreeCanvas::setSmoothScrollAndZoom(bool b) {
01063     smoothScrollAndZoom = b;
01064   }
01065 
01066 }}
01067 
01068 // STATISTICS: gist-any