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