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 #include <cxxtls/treeviewer.h>
00031 #include <cxxtls/viewermanager.h>
00032 #include <algorithm>
00033
00034 using namespace std;
00035
00036
00037
00038
00039 namespace cxxtls
00040 {
00041
00042
00043
00044 int
00045 TreeViewerNode::
00046 height() const
00047 {
00048 int rv= 1;
00049
00050 ChildrenList::const_iterator first = children_.begin(),
00051 last = children_.end();
00052
00053 while(first != last)
00054 {
00055 rv += (*first++).height();
00056 }
00057
00058 return rv;
00059
00060 }
00061
00062
00063
00064 void
00065 TreeViewerNode::
00066 close(bool childrenToo)
00067 {
00068 opened_ = false;
00069
00070 if(childrenToo)
00071 {
00072
00073 ChildrenList::iterator first = children_.begin(),
00074 last = children_.end();
00075
00076 while(first != last)
00077 {
00078 (*first++).close(childrenToo);
00079 }
00080
00081 }
00082 }
00083
00084
00085
00086 void
00087 TreeViewerNode::
00088 open(bool childrenToo)
00089 {
00090 opened_ = true;
00091
00092 if(childrenToo)
00093 {
00094 ChildrenList::iterator first = children_.begin(),
00095 last = children_.end();
00096
00097 while(first != last)
00098 {
00099 (*first++).open(childrenToo);
00100 }
00101 }
00102
00103 }
00104
00105
00106
00107 void
00108 TreeViewerNode::
00109 flatten(FlatForm &output, int depth)
00110 {
00111 std::pair<TreeViewerNode*, int> me(this,depth);
00112
00113 output.push_back(me);
00114
00115 if(opened_)
00116 {
00117 ChildrenList::iterator first = children_.begin(),
00118 last = children_.end();
00119
00120 while(first != last)
00121 {
00122 (*first++).flatten(output, depth+1);
00123 }
00124 }
00125 }
00126
00127
00128
00129 Viewer*
00130 TreeViewer::
00131 app(ViewerManager*vm, std::string &fullname)
00132 {
00133 TreeViewer *rv =new TreeViewer(vm);
00134
00135 rv->application_name_ = fullname;
00136
00137 return rv;
00138 }
00139
00140
00141 void
00142 TreeViewer::
00143 operator() ( CursorWindow::viewport * vp,
00144 int cmd
00145 )
00146 {
00147
00148
00149
00150 if(cmd == CursorWindow::viewport::repaint_handler::activate)
00151 active_ = true;
00152 else
00153 if(cmd == CursorWindow::viewport::repaint_handler::deactivate)
00154 {
00155 active_ = false;
00156 }
00157
00158 if(flattenedNodes_.empty())
00159 {
00160
00161
00162 nodes_.flatten(flattenedNodes_);
00163
00164 }
00165
00166 paintViewport(vp);
00167
00168 positionCursor(vp);
00169
00170 }
00171
00172
00173 void
00174 TreeViewer::
00175 acknowledgeViewport( CursorWindow::viewport * vp)
00176 {
00177
00178
00179 CursorWindow::row_col size = vp->size();
00180
00181 enum constants
00182 {
00183 TITLE_HEIGHT=1,
00184 };
00185
00186
00187
00188 treeStartRow_ = TITLE_HEIGHT;
00189
00190
00191
00192 displayRows_ = size.row_ - TITLE_HEIGHT;
00193
00194
00195
00196
00197 displayNodes_= displayRows_;
00198
00199 int nodeCount = flattenedNodes_.size();
00200
00201 if(nodeCount < displayNodes_)
00202 {
00203 displayNodes_ = nodeCount;
00204 }
00205
00206 if(topNode_ + displayNodes_ >= nodeCount)
00207 {
00208 if(topNode_ >= nodeCount)
00209 topNode_ = nodeCount - 1;
00210
00211 displayNodes_ = nodeCount - topNode_;
00212 }
00213
00214 if(cursorNode_ < topNode_)
00215 {
00216 cursorNode_ = topNode_;
00217 }
00218 else
00219 if(cursorNode_ >= topNode_ + displayNodes_)
00220 {
00221
00222
00223 if(cursorNode_ >= nodeCount)
00224 {
00225
00226
00227 if(nodeCount)
00228 {
00229 cursorNode_ = nodeCount - 1 ;
00230 }
00231 else
00232 {
00233 cursorNode_ = 0;
00234 }
00235 }
00236
00237 topNode_ = cursorNode_ - displayNodes_ / 2;
00238
00239 if(topNode_ < 0)
00240 {
00241 topNode_ = 0;
00242 }
00243
00244 }
00245
00246
00247
00248
00249
00250
00251 }
00252
00253
00254 void
00255 TreeViewer::
00256 paintViewport( CursorWindow::viewport * vp)
00257 {
00258 acknowledgeViewport(vp);
00259
00260 paintTitle(vp);
00261
00262 for(int i = 0; i < displayRows_; ++i)
00263 {
00264
00265
00266 vp->set_curpos(i + treeStartRow_, 0);
00267
00268
00269
00270 vp->set_text_attribute(manager()->inactive_mark_att);
00271
00272
00273 if( i < displayNodes_)
00274 {
00275
00276
00277 bool isCursor = false;
00278
00279 if( i == cursorNode_ - topNode_)
00280 {
00281 isCursor = true;
00282
00283 vp->set_text_attribute(active_ ? manager()->highlighted_att
00284 : manager()->normal_att
00285 );
00286 }
00287
00288 paintNodeText(vp, topNode_+i, isCursor);
00289
00290 }
00291 else
00292 {
00293 if(vp->origin().col_)
00294 {
00295
00296
00297 vp->write('|');
00298
00299 }
00300
00301 vp->fill_to_eol();
00302 }
00303
00304 }
00305
00306
00307 }
00308
00309
00310 void
00311 TreeViewer::
00312 paintNodeText(CursorWindow::viewport * vp, int nodeNumber, bool isCursor)
00313 {
00314
00315 if(vp->origin().col_)
00316 {
00317
00318
00319 vp->write('|');
00320
00321 }
00322
00323 int nodeCount = flattenedNodes_.size();
00324
00325 if(nodeNumber < nodeCount)
00326 {
00327 TreeViewerNode * node = flattenedNodes_[nodeNumber].first;
00328 int depth= flattenedNodes_[nodeNumber].second;
00329
00330 std::string const &name = node->name();
00331
00332 vp->write(' ', depth * 4);
00333
00334 if(node->hasChildren())
00335 {
00336 if(node->opened())
00337 vp->write("[-] ");
00338 else
00339 vp->write("[+] ");
00340 }
00341 else
00342 {
00343 vp->write("[ ] ");
00344 }
00345
00346 int savedTextAttribute = vp->text_attribute();
00347
00348
00349 if(node->highlighted() )
00350 {
00351 if(isCursor)
00352 vp->set_text_attribute( CursorWindow::active_mark_att );
00353 else
00354 vp->set_text_attribute( CursorWindow::normal_att );
00355 }
00356
00357
00358 *vp << name;
00359
00360 vp->set_text_attribute(savedTextAttribute);
00361
00362 }
00363
00364 vp->fill_to_eol();
00365
00366
00367 }
00368
00369
00370
00371 void
00372 TreeViewer::
00373 paintTitle( CursorWindow::viewport * vp)
00374 {
00375 vp->set_curpos(0,0);
00376 vp->set_text_attribute(active_ ? manager()->active_title_att
00377 : manager()->inactive_title_att
00378 );
00379
00380 *vp << application_name();
00381
00382 vp->fill_to_eol();
00383
00384 }
00385
00386
00387
00388
00389 bool
00390 TreeViewer::
00391 handle_event( CursorWindow::input_event const * e,
00392 CursorWindow::viewport * vp
00393 )
00394 {
00395
00396 if( e->type_ == CursorWindow::input_event::DataKey
00397 || e->type_ == CursorWindow::input_event::FunctionKey
00398 )
00399 {
00400
00401
00402
00403
00404 if(keyKludge_)
00405 {
00406 keyKludge_ = false;
00407
00408 switch(e->value_)
00409 {
00410 case 0x03:
00411 return true;
00412
00413 case 0x0b:
00414
00415 return true;
00416
00417 default: vp->beep(); break;
00418 }
00419
00420 return false;
00421 }
00422
00423
00424
00425
00426 switch(e->value_)
00427 {
00428 case 0x18:
00429 {
00430 keyKludge_ = true;
00431 }
00432 return false;
00433
00434 case 'q': return true;
00435 case 'Q': return true;
00436 case 0x1b: return true;
00437
00438
00439 case '+': toggle(vp); positionCursor(vp); return false;
00440 case '=': toggle(vp); positionCursor(vp); return false;
00441 case 'o': toggleAll(vp); positionCursor(vp); return false;
00442 case 'O': toggleAll(vp); positionCursor(vp); return false;
00443 case '<': findMyParent(vp); positionCursor(vp); return false;
00444 case '>': findNextParent(vp); positionCursor(vp); return false;
00445
00446
00447 case CursorWindow::key_up: moveUp(vp); positionCursor(vp); return false;
00448 case CursorWindow::key_down: moveDown(vp); positionCursor(vp); return false;
00449 case CursorWindow::key_home: pageTop(vp); positionCursor(vp); return false;
00450 case CursorWindow::key_next: pageDown(vp); positionCursor(vp); return false;
00451 case CursorWindow::key_prior: pageUp(vp); positionCursor(vp); return false;
00452 case CursorWindow::key_end: pageBottom(vp); positionCursor(vp); return false;
00453 case 0x02: pageBottom(vp); positionCursor(vp); return false;
00454 case 0x14: pageTop(vp); positionCursor(vp); return false;
00455 case 0x13: findFirst(vp); positionCursor(vp); return false;
00456 case 0x0e: findNext(vp); positionCursor(vp); return false;
00457
00458 default:
00459
00460
00461
00462
00463 if(!flattenedNodes_.empty() && flattenedNodes_[cursorNode_].first->info())
00464 {
00465
00466
00467 positionCursor(vp);
00468
00469 return (*flattenedNodes_[cursorNode_].first->info())(manager(), vp, e->value_, rootDir_);
00470
00471 }
00472 else
00473 if( (e->value_ == '\r' || e->value_ == '\n')
00474 && !flattenedNodes_.empty()
00475 )
00476 {
00477
00478 }
00479 else
00480 {
00481 vp->beep();
00482 }
00483
00484 }
00485 }
00486 else
00487 if(e->type_ == CursorWindow::input_event::ForceExitKey)
00488 {
00489 positionCursor(vp);
00490
00491 return true;
00492 }
00493
00494 positionCursor(vp);
00495
00496 return false;
00497 }
00498
00499
00500 void
00501 TreeViewer::
00502 moveUp(CursorWindow::viewport * vp)
00503 {
00504 if(cursorNode_ == 0)
00505 return;
00506
00507 if(cursorNode_ <= topNode_)
00508 {
00509 if(topNode_ == 0)
00510 {
00511 cursorNode_ = topNode_;
00512 return;
00513 }
00514
00515 --topNode_;
00516 cursorNode_ = topNode_;
00517
00518 if(topNode_ < 0)
00519 {
00520 topNode_ = 0;
00521 cursorNode_ = 0;
00522 }
00523
00524 paintViewport(vp);
00525 return;
00526 }
00527
00528
00529
00530 displayCurrentLine(vp,false);
00531
00532 --cursorNode_;
00533
00534 displayCurrentLine(vp,true);
00535
00536
00537 }
00538
00539
00540
00541 void
00542 TreeViewer::
00543 toggle(CursorWindow::viewport * vp)
00544 {
00545
00546 if(flattenedNodes_[cursorNode_].first->opened())
00547 close(vp);
00548 else
00549 open(vp);
00550
00551 }
00552
00553
00554
00555 void
00556 TreeViewer::
00557 toggleAll(CursorWindow::viewport * vp)
00558 {
00559 TreeViewerNode * node= flattenedNodes_[cursorNode_].first;
00560
00561
00562 if(node->opened())
00563 {
00564 node->close();
00565 }
00566 else
00567 {
00568 node->open();
00569 }
00570
00571 flattenedNodes_.clear();
00572 nodes_.flatten(flattenedNodes_);
00573
00574 paintViewport(vp);
00575
00576
00577
00578 }
00579
00580
00581
00582 void
00583 TreeViewer::
00584 close(CursorWindow::viewport * vp)
00585 {
00586 TreeViewerNode * node= flattenedNodes_[cursorNode_].first;
00587
00588 if(node->opened())
00589 {
00590 node->close();
00591 }
00592
00593 flattenedNodes_.clear();
00594 nodes_.flatten(flattenedNodes_);
00595
00596 paintViewport(vp);
00597
00598
00599 }
00600
00601
00602
00603 void
00604 TreeViewer::
00605 open(CursorWindow::viewport * vp)
00606 {
00607 TreeViewerNode * node= flattenedNodes_[cursorNode_].first;
00608
00609
00610 if(!node->opened())
00611 {
00612 node->open(false);
00613
00614 }
00615
00616 flattenedNodes_.clear();
00617
00618 nodes_.flatten(flattenedNodes_);
00619
00620
00621 paintViewport(vp);
00622
00623
00624 }
00625
00626
00627
00628 void
00629 TreeViewer::
00630 displayCurrentLine(CursorWindow::viewport * vp, bool highlighted)
00631 {
00632 int offsetFromTopNode = cursorNode_ - topNode_;
00633
00634 vp->set_curpos(offsetFromTopNode + treeStartRow_, 0);
00635
00636 if(highlighted)
00637 {
00638 vp->set_text_attribute(active_ ? manager()->highlighted_att
00639 : manager()->normal_att
00640 );
00641 }
00642 else
00643 vp->set_text_attribute(manager()->inactive_mark_att);
00644
00645 paintNodeText(vp, cursorNode_);
00646
00647 }
00648
00649
00650
00651 void
00652 TreeViewer::
00653 moveDown(CursorWindow::viewport * vp)
00654 {
00655 int nodeCount = flattenedNodes_.size();
00656
00657 if(cursorNode_ > nodeCount-1)
00658 return;
00659
00660 if(cursorNode_ == nodeCount-1)
00661 {
00662
00663
00664
00665 if(cursorNode_ == topNode_)
00666 return;
00667
00668 ++topNode_;
00669 --displayNodes_;
00670
00671 if(displayNodes_ > displayRows_)
00672 displayNodes_ = displayRows_;
00673
00674 paintViewport(vp);
00675 return;
00676 }
00677
00678 int offset = cursorNode_ - topNode_;
00679
00680 if(offset == displayNodes_-1)
00681 {
00682
00683
00684
00685 ++topNode_;
00686 ++cursorNode_;
00687 paintViewport(vp);
00688 return;
00689
00690 }
00691
00692
00693
00694
00695 displayCurrentLine(vp, false);
00696 ++cursorNode_;
00697 displayCurrentLine(vp, true);
00698
00699 }
00700
00701
00702
00703 void
00704 TreeViewer::
00705 pageTop(CursorWindow::viewport * vp)
00706 {
00707 topNode_ = 0;
00708 cursorNode_ = 0;
00709 paintViewport(vp);
00710 }
00711
00712
00713 void
00714 TreeViewer::
00715 pageBottom(CursorWindow::viewport * vp)
00716 {
00717 topNode_ = flattenedNodes_.size() - 1;
00718
00719 if(topNode_ < 0)
00720 {
00721 topNode_ = 0;
00722 cursorNode_ = 0;
00723 return;
00724 }
00725
00726
00727
00728 displayNodes_ = 1;
00729 cursorNode_ = topNode_;
00730
00731 paintViewport(vp);
00732
00733 }
00734
00735
00736 void
00737 TreeViewer::
00738 pageDown(CursorWindow::viewport * vp)
00739 {
00740 topNode_ += displayRows_;
00741 cursorNode_ += displayRows_;
00742
00743 if(topNode_ >= int(flattenedNodes_.size())-1)
00744 {
00745 topNode_ = flattenedNodes_.size()-1;
00746 }
00747
00748 if(cursorNode_ >= int(flattenedNodes_.size()) - 1)
00749 {
00750 cursorNode_ = flattenedNodes_.size()-1;
00751 }
00752
00753
00754 paintViewport(vp);
00755
00756 }
00757
00758
00759 void
00760 TreeViewer::
00761 pageUp(CursorWindow::viewport * vp)
00762 {
00763
00764 topNode_ -= displayRows_;
00765 cursorNode_ -= displayRows_;
00766
00767 if(topNode_ < 0)
00768 {
00769 topNode_ = 0;
00770 cursorNode_ = 0;
00771 }
00772
00773
00774 paintViewport(vp);
00775
00776 }
00777
00778
00779 void
00780 TreeViewer::
00781 findFirst(CursorWindow::viewport * vp)
00782 {
00783 CursorWindow::Dialog dialog("Find string");
00784
00785
00786 dialog += new CursorWindow::Dialog::String("mode",
00787 "(R)egexp, or (S)tring",
00788 (is_regex_ ? "R" : "S"),
00789 2
00790 );
00791
00792 dialog += new CursorWindow::Dialog::String("text",
00793 "Search for",
00794 search_text_,
00795 40
00796 );
00797
00798 dialog += new CursorWindow::Dialog::Ok("ok","");
00799
00800 dialog.set_first_input_field("text");
00801
00802
00803 if( dialog.popup(vp->window()) )
00804 return;
00805
00806
00807 std::string match_words = dialog.element_value("mode");
00808
00809 is_regex_ = false;
00810
00811 if( match_words.size()
00812 && (match_words[0] == 'r' || match_words[0] == 'R')
00813 )
00814 {
00815 is_regex_ = true;
00816 }
00817
00818 std::string search_text = dialog.element_value("text");
00819
00820 if(search_text.size() == 0)
00821 return;
00822
00823 search_text_ = search_text;
00824
00825 int saved_line = cursorNode_;
00826
00827 displayCurrentLine(vp,false);
00828
00829
00830 if(!findNextHelper(vp, false))
00831 {
00832 cursorNode_ = saved_line;
00833 vp->beep();
00834 }
00835 else
00836 {
00837 make_line_visible(vp);
00838 }
00839
00840 displayCurrentLine(vp,true);
00841
00842
00843
00844 }
00845
00846
00847 void
00848 TreeViewer::
00849 findNext(CursorWindow::viewport * vp)
00850 {
00851 int saved_line = cursorNode_;
00852
00853 displayCurrentLine(vp,false);
00854
00855 if(!findNextHelper(vp, true))
00856 {
00857 cursorNode_ = saved_line;
00858 vp->beep();
00859 }
00860 else
00861 {
00862 make_line_visible(vp);
00863 }
00864
00865 displayCurrentLine(vp,true);
00866
00867
00868 }
00869
00870 namespace
00871 {
00872 struct CharCompare
00873 {
00874
00875 bool operator() (char a, char b) const
00876 {
00877 {
00878 if( isupper(a) )
00879 a = tolower(a);
00880
00881 if( isupper(b) )
00882 b = tolower(b);
00883
00884 return a == b;
00885 }
00886
00887 return a == b;
00888 }
00889
00890
00891 };
00892
00893 }
00894
00895
00896
00897 bool
00898 TreeViewer::
00899 findNextHelper(CursorWindow::viewport* vp, bool skipCurrentLine)
00901 {
00902
00903
00904
00905 if(is_regex_)
00906 {
00907 SimpleRegex *p = ®exp_;
00908
00909 p->SimpleRegex::~SimpleRegex();
00910
00911 new(®exp_) SimpleRegex(search_text_, "i" );
00912
00913
00914 }
00915
00916
00917 if(skipCurrentLine)
00918 {
00919 cursorNode_++;
00920 }
00921
00922
00923 while(cursorNode_ < int(flattenedNodes_.size()) )
00924 {
00925
00926 std::string curline = flattenedNodes_[cursorNode_].first->name();
00927
00928 size_t memberStart;
00929
00930 if(curline.size() > (memberStart = curline.find_last_of(':')) )
00931 {
00932 ++memberStart;
00933
00934 if(memberStart < curline.size())
00935 {
00936 curline = curline.substr( memberStart, curline.size() - memberStart );
00937 }
00938 }
00939
00940 {
00941
00942 std::string::iterator where;
00943
00944
00945 if(is_regex_)
00946 {
00947
00948 int count = regexp_(curline, 0, curline.size());
00949
00950 if(count > 0)
00951 {
00952 where = (curline.begin() + 0 + regexp_.matches()[0].offset);
00953 }
00954 else
00955 where = curline.end();
00956
00957 }
00958 else
00959 {
00960 where = std::search(curline.begin() + 0,
00961 curline.end(),
00962 search_text_.begin(),
00963 search_text_.end(),
00964 CharCompare()
00965 );
00966 }
00967
00968 if(where != curline.end())
00969 {
00970 return true;
00971 }
00972 }
00973
00974 ++cursorNode_;
00975
00976 }
00977
00978
00979 return false;
00980
00981 }
00982
00983
00984
00985
00986
00987 void
00988 TreeViewer::make_line_visible(CursorWindow::viewport *vp)
00989 {
00990 if(cursorNode_ - topNode_ >= displayNodes_)
00991 {
00992
00993
00994 topNode_ = cursorNode_ - displayRows_ / 2;
00995
00996 if(topNode_ < 0)
00997 topNode_ = 0;
00998
00999 paintViewport(vp);
01000 }
01001 }
01002
01003
01004
01005 void
01006 TreeViewer::
01007 findMyParent(CursorWindow::viewport * vp)
01008 {
01009
01010 int myDepth = flattenedNodes_[cursorNode_].second;
01011
01012 while( cursorNode_ > 0
01013 && flattenedNodes_[cursorNode_].second >= myDepth
01014 )
01015 {
01016 --cursorNode_;
01017 }
01018
01019 if(cursorNode_ < topNode_)
01020 {
01021 topNode_ = cursorNode_;
01022 }
01023
01024 paintViewport(vp);
01025
01026
01027
01028 }
01029
01030
01031 void
01032 TreeViewer::
01033 findNextParent(CursorWindow::viewport * vp)
01034 {
01035 int myDepth = flattenedNodes_[cursorNode_].second;
01036
01037 int maxNode = flattenedNodes_.size();
01038
01039 while( cursorNode_ < maxNode
01040 && flattenedNodes_[cursorNode_].second >= myDepth
01041 )
01042 {
01043 ++cursorNode_;
01044 }
01045
01046 if(cursorNode_ >= maxNode)
01047 {
01048 cursorNode_ = maxNode-1;
01049 }
01050
01051 if(cursorNode_ >= topNode_ + displayNodes_)
01052 {
01053 topNode_ = cursorNode_;
01054 }
01055
01056 paintViewport(vp);
01057
01058 }
01059
01060
01061
01062 std::string const &
01063 TreeViewer::
01064 application_name() const
01065 {
01066 return application_name_;
01067 }
01068
01069
01070
01071
01072 TreeViewer::~TreeViewer()
01073 {
01074 }
01075
01076
01077 std::string TreeViewer::description() const
01078 {
01079 return "TreeViewer";
01080 }
01081
01082
01083 void
01084 TreeViewer::helpHeader(list<string> &help_text)
01085 {
01086 help_text.push_back("TreeViewer help");
01087 }
01088
01089
01090
01091
01092 void
01093 TreeViewer::help()
01094 {
01095 list<string> help_text;
01096
01097 helpHeader(help_text);
01098
01099 help_text.push_back("");
01100 help_text.push_back("");
01101 help_text.push_back("Command keys:");
01102 help_text.push_back(" enter -- open the file defining the symbol under the cursor.");
01103 help_text.push_back(" + -- open or close the current item (if it has children).");
01104 help_text.push_back(" o -- completely open or close the entire current item's children ");
01105 help_text.push_back(" < -- move up to the current row's parent");
01106 help_text.push_back(" > -- move down to the next parent higher than the current row");
01107 help_text.push_back(" ^T -- move to the top of the tree");
01108 help_text.push_back(" ^B -- move to the bottom of the tree");
01109 help_text.push_back(" ^S -- search for a string string the tree");
01110 help_text.push_back(" ^N -- search next");
01111
01112
01113 manager()->help_helper(help_text);
01114 }
01115
01116 TreeViewerNodeInfo::
01117 ~TreeViewerNodeInfo()
01118 {
01119 }
01120
01121
01122 void
01123 TreeViewer::
01124 positionCursor(CursorWindow::viewport *vp)
01125 {
01126 int col=0;
01127
01128 if(cursorNode_ < int(flattenedNodes_.size()))
01129 {
01130 col = 4 * flattenedNodes_[cursorNode_].second;
01131
01132 if(vp->origin().col_ != 0)
01133 ++col;
01134 }
01135
01136 ++col;
01137
01138
01139
01140
01141
01142
01143 vp->set_curpos((cursorNode_ - topNode_) + treeStartRow_, col);
01144 }
01145
01146 };
01147