00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "katecodefoldinghelpers.h"
00020 #include "katecodefoldinghelpers.moc"
00021
00022 #include "katebuffer.h"
00023 #include "katecursor.h"
00024 #include <kdebug.h>
00025
00026 #include <qstring.h>
00027
00028 #define JW_DEBUG 0
00029
00030 bool KateCodeFoldingTree::trueVal = true;
00031
00032 KateCodeFoldingNode::KateCodeFoldingNode() :
00033 parentNode(0),
00034 startLineRel(0),
00035 endLineRel(0),
00036 startCol(0),
00037 endCol(0),
00038 startLineValid(false),
00039 endLineValid(false),
00040 type(0),
00041 visible(true),
00042 deleteOpening(false),
00043 deleteEnding(false)
00044 {
00045 }
00046
00047 KateCodeFoldingNode::KateCodeFoldingNode(KateCodeFoldingNode *par, signed char typ, unsigned int sLRel):
00048 parentNode(par),
00049 startLineRel(sLRel),
00050 endLineRel(10000),
00051 startCol(0),
00052 endCol(0),
00053 startLineValid(true),
00054 endLineValid(false),
00055 type(typ),
00056 visible(true),
00057 deleteOpening(false),
00058 deleteEnding(false)
00059 {
00060 }
00061
00062 KateCodeFoldingNode::~KateCodeFoldingNode()
00063 {
00064
00065 clearChildren ();
00066 }
00067
00068 bool KateCodeFoldingNode::getBegin(KateCodeFoldingTree *tree, KateTextCursor* begin) {
00069 if (!startLineValid) return false;
00070 unsigned int line=startLineRel;
00071 for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
00072 line+=n->startLineRel;
00073
00074 tree->m_buffer->codeFoldingColumnUpdate(line);
00075 begin->setLine(line);
00076 begin->setCol(startCol);
00077
00078 return true;
00079 }
00080
00081 bool KateCodeFoldingNode::getEnd(KateCodeFoldingTree *tree, KateTextCursor *end) {
00082 if (!endLineValid) return false;
00083 unsigned int line=startLineRel+endLineRel;
00084 for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
00085 line+=n->startLineRel;
00086
00087 tree->m_buffer->codeFoldingColumnUpdate(line);
00088 end->setLine(line);
00089 end->setCol(endCol);
00090
00091 return true;
00092 }
00093
00094 int KateCodeFoldingNode::cmpPos(KateCodeFoldingTree *tree, uint line,uint col) {
00095 KateTextCursor cur(line,col);
00096 KateTextCursor start,end;
00097 kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (1)"<<endl;
00098 bool startValid=getBegin(tree, &start);
00099 kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (2)"<<endl;
00100 bool endValid=getEnd(tree, &end);
00101 kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (3)"<<endl;
00102 if ((!endValid) && startValid) {
00103 return ((start>cur)?-1:0);
00104 }
00105 if ((!startValid) && endValid) {
00106 return ((cur>end)?1:0);
00107 }
00108
00109 Q_ASSERT(startValid && endValid);
00110 return ( (cur<start)?(-1):( (cur>end) ? 1:0));
00111 }
00112
00113 void KateCodeFoldingNode::insertChild (uint index, KateCodeFoldingNode *node)
00114 {
00115 uint s = m_children.size ();
00116
00117 if (index > s)
00118 return;
00119
00120 m_children.resize (++s);
00121
00122 for (uint i=s-1; i > index; --i)
00123 m_children[i] = m_children[i-1];
00124
00125 m_children[index] = node;
00126 }
00127
00128 KateCodeFoldingNode *KateCodeFoldingNode::takeChild (uint index)
00129 {
00130 uint s = m_children.size ();
00131
00132 if (index >= s)
00133 return 0;
00134
00135 KateCodeFoldingNode *n = m_children[index];
00136
00137 for (uint i=index; (i+1) < s; ++i)
00138 m_children[i] = m_children[i+1];
00139
00140 m_children.resize (s-1);
00141
00142 return n;
00143 }
00144
00145 void KateCodeFoldingNode::clearChildren ()
00146 {
00147 for (uint i=0; i < m_children.size(); ++i)
00148 delete m_children[i];
00149
00150 m_children.resize (0);
00151 }
00152
00153 KateCodeFoldingTree::KateCodeFoldingTree(KateBuffer *buffer): QObject(buffer), m_buffer (buffer)
00154 {
00155 clear();
00156 }
00157
00158 void KateCodeFoldingTree::fixRoot(int endLRel)
00159 {
00160 m_root.endLineRel = endLRel;
00161 }
00162
00163 void KateCodeFoldingTree::clear()
00164 {
00165 m_root.clearChildren();
00166
00167
00168 m_root.startLineValid=true;
00169 m_root.endLineValid=true;
00170 m_root.endLineRel=1;
00171
00172 hiddenLinesCountCacheValid=false;
00173 lineMapping.setAutoDelete(true);
00174 hiddenLines.clear();
00175 lineMapping.clear();
00176 nodesForLine.clear();
00177 markedForDeleting.clear();
00178 dontIgnoreUnchangedLines.clear();
00179 }
00180
00181 KateCodeFoldingTree::~KateCodeFoldingTree()
00182 {
00183 }
00184
00185 bool KateCodeFoldingTree::isTopLevel(unsigned int line)
00186 {
00187 if (m_root.noChildren())
00188 return true;
00189
00190
00191 for ( uint i=0; i < m_root.childCount(); ++i )
00192 {
00193 KateCodeFoldingNode *node = m_root.child(i);
00194
00195 if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
00196 return false;
00197 }
00198
00199 return true;
00200 }
00201
00202 void KateCodeFoldingTree::getLineInfo(KateLineInfo *info, unsigned int line)
00203 {
00204
00205
00206 info->topLevel = true;
00207 info->startsVisibleBlock = false;
00208 info->startsInVisibleBlock = false;
00209 info->endsBlock = false;
00210 info->invalidBlockEnd = false;
00211
00212 if (m_root.noChildren())
00213 return;
00214
00215
00216 for ( uint i=0; i < m_root.childCount(); ++i )
00217 {
00218 KateCodeFoldingNode *node = m_root.child(i);
00219
00220 if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
00221 {
00222 info->topLevel = false;
00223 findAllNodesOpenedOrClosedAt(line);
00224
00225 for ( KateCodeFoldingNode *node = nodesForLine.first(); node; node = nodesForLine.next() )
00226 {
00227 uint startLine = getStartLine(node);
00228
00229
00230
00231 if (node->type < 0)
00232 info->invalidBlockEnd=true;
00233 else
00234 {
00235 if (startLine != line)
00236 info->endsBlock = true;
00237 else
00238 {
00239
00240 if (node->visible)
00241 info->startsVisibleBlock=true;
00242 else
00243 info->startsInVisibleBlock=true;
00244 }
00245 }
00246 }
00247
00248 return;
00249 }
00250 }
00251
00252 return;
00253 }
00254
00255
00256 KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLine(unsigned int line)
00257 {
00258 if (m_root.noChildren())
00259 return &m_root;
00260
00261
00262 for ( uint i=0; i < m_root.childCount(); ++i )
00263 {
00264 KateCodeFoldingNode *node = m_root.child(i);
00265
00266 if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
00267 {
00268
00269 return findNodeForLineDescending(node,line,0);
00270 }
00271 }
00272
00273 return &m_root;
00274 }
00275
00276
00277 KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLineDescending ( KateCodeFoldingNode *node,
00278 unsigned int line, unsigned int offset, bool oneStepOnly )
00279 {
00280 if (node->noChildren())
00281 return node;
00282
00283
00284 offset += node->startLineRel;
00285
00286 for ( uint i=0; i < node->childCount(); ++i )
00287 {
00288 KateCodeFoldingNode *subNode = node->child(i);
00289
00290 if ((subNode->startLineRel+offset<=line) && (line<=subNode->endLineRel+subNode->startLineRel+offset))
00291 {
00292
00293
00294
00295 if (oneStepOnly)
00296 return subNode;
00297 else
00298 return findNodeForLineDescending (subNode,line,offset);
00299 }
00300 }
00301
00302 return node;
00303 }
00304
00305 KateCodeFoldingNode *KateCodeFoldingTree::findNodeForPosition(unsigned int line, unsigned int column)
00306 {
00307 KateCodeFoldingNode *node=findNodeForLine(line);
00308
00309 if (node==&m_root) return &m_root;
00310
00311 kdDebug(13000)<<"initial cmpPos"<<endl;
00312
00313 KateCodeFoldingNode *tmp;
00314 int leq=node->cmpPos(this, line,column);
00315 while (true) {
00316 switch (leq) {
00317 case 0: {
00318 if (node->noChildren())
00319 return node;
00320 else
00321 {
00322 tmp=node;
00323 for ( uint i=0; i < node->childCount(); ++i )
00324 {
00325 KateCodeFoldingNode *subNode = node->child(i);
00326 kdDebug(13000)<<"cmdPos(case0):calling"<<endl;
00327 leq=subNode->cmpPos(this, line,column);
00328 kdDebug(13000)<<"cmdPos(case0):returned"<<endl;
00329 if (leq==0) {
00330 tmp=subNode;
00331 break;
00332 } else if (leq==-1) break;
00333 }
00334 if (tmp!=node) node=tmp; else return node;
00335 }
00336 break;
00337 }
00338
00339 case -1:
00340 case 1: {
00341 if (!(node->parentNode)) return &m_root;
00342 kdDebug(13000)<<"current node type"<<node->type<<endl;
00343 node=node->parentNode;
00344 kdDebug(13000)<<"cmdPos(case-1/1):calling:"<<node<<endl;
00345 leq=node->cmpPos(this, line,column);
00346 kdDebug(13000)<<"cmdPos(case-1/1):returned"<<endl;
00347 break;
00348 }
00349 }
00350
00351 }
00352 Q_ASSERT(false);
00353 return &m_root;
00354 }
00355
00356 void KateCodeFoldingTree::debugDump()
00357 {
00358
00359 kdDebug(13000)<<"The parsed region/block tree for code folding"<<endl;
00360 dumpNode(&m_root, "");
00361 }
00362
00363 void KateCodeFoldingTree::dumpNode(KateCodeFoldingNode *node, const QString &prefix)
00364 {
00365
00366 kdDebug(13000)<<prefix<<QString("Type: %1, startLineValid %2, startLineRel %3, endLineValid %4, endLineRel %5, visible %6").
00367 arg(node->type).arg(node->startLineValid).arg(node->startLineRel).arg(node->endLineValid).
00368 arg(node->endLineRel).arg(node->visible)<<endl;
00369
00370
00371 if (node->noChildren())
00372 return;
00373
00374 QString newprefix(prefix + " ");
00375 for ( uint i=0; i < node->childCount(); ++i )
00376 dumpNode (node->child(i),newprefix);
00377 }
00378
00379
00380
00381
00382 void KateCodeFoldingTree::updateLine(unsigned int line,
00383 QMemArray<uint> *regionChanges, bool *updated,bool changed,bool colsChanged)
00384 {
00385 if ( (!changed) || colsChanged)
00386 {
00387 if (dontIgnoreUnchangedLines.isEmpty())
00388 return;
00389
00390 if (dontIgnoreUnchangedLines[line])
00391 dontIgnoreUnchangedLines.remove(line);
00392 else
00393 return;
00394 }
00395
00396 something_changed = false;
00397
00398 findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);
00399
00400 if (regionChanges->isEmpty())
00401 {
00402
00403
00404
00405 }
00406 else
00407 {
00408 for (unsigned int i=0;i<regionChanges->size() / 4;i++)
00409 {
00410 signed char tmp=(*regionChanges)[regionChanges->size()-2-i*2];
00411 uint tmppos=(*regionChanges)[regionChanges->size()-1-i*2];
00412 (*regionChanges)[regionChanges->size()-2-i*2]=(*regionChanges)[i*2];
00413 (*regionChanges)[regionChanges->size()-1-i*2]=(*regionChanges)[i*2+1];
00414 (*regionChanges)[i*2]=tmp;
00415 (*regionChanges)[i*2+1]=tmppos;
00416 }
00417
00418
00419 signed char data= (*regionChanges)[regionChanges->size()-2];
00420 uint charPos=(*regionChanges)[regionChanges->size()-1];
00421 regionChanges->resize (regionChanges->size()-2);
00422
00423 int insertPos=-1;
00424 KateCodeFoldingNode *node = findNodeForLine(line);
00425
00426 if (data<0)
00427 {
00428
00429 {
00430 unsigned int tmpLine=line-getStartLine(node);
00431
00432 for ( uint i=0; i < node->childCount(); ++i )
00433 {
00434 if (node->child(i)->startLineRel >= tmpLine)
00435 {
00436 insertPos=i;
00437 break;
00438 }
00439 }
00440 }
00441 }
00442 else
00443 {
00444 for (; (node->parentNode) && (getStartLine(node->parentNode)==line) && (node->parentNode->type!=0); node=node->parentNode);
00445
00446 if ((getStartLine(node)==line) && (node->type!=0))
00447 {
00448 insertPos=node->parentNode->findChild(node);
00449 node = node->parentNode;
00450 }
00451 else
00452 {
00453 for ( uint i=0; i < node->childCount(); ++i )
00454 {
00455 if (getStartLine(node->child(i))>=line)
00456 {
00457 insertPos=i;
00458 break;
00459 }
00460 }
00461 }
00462 }
00463
00464 do
00465 {
00466 if (data<0)
00467 {
00468 if (correctEndings(data,node,line,charPos,insertPos))
00469 {
00470 insertPos=node->parentNode->findChild(node)+1;
00471 node=node->parentNode;
00472 }
00473 else
00474 {
00475 if (insertPos!=-1) insertPos++;
00476 }
00477 }
00478 else
00479 {
00480 int startLine=getStartLine(node);
00481 if ((insertPos==-1) || (insertPos>=(int)node->childCount()))
00482 {
00483 KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
00484 something_changed = true;
00485 node->appendChild(newNode);
00486 addOpening(newNode, data, regionChanges, line,charPos);
00487 insertPos = node->findChild(newNode)+1;
00488 }
00489 else
00490 {
00491 if (node->child(insertPos)->startLineRel == line-startLine)
00492 {
00493 addOpening(node->child(insertPos), data, regionChanges, line,charPos);
00494 insertPos++;
00495 }
00496 else
00497 {
00498
00499 KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
00500 something_changed = true;
00501 node->insertChild(insertPos, newNode);
00502 addOpening(newNode, data, regionChanges, line,charPos);
00503 insertPos++;
00504 }
00505 }
00506 }
00507
00508 if (regionChanges->isEmpty())
00509 data = 0;
00510 else
00511 {
00512 data = (*regionChanges)[regionChanges->size()-2];
00513 charPos=(*regionChanges)[regionChanges->size()-1];
00514 regionChanges->resize (regionChanges->size()-2);
00515 }
00516 } while (data!=0);
00517 }
00518
00519 cleanupUnneededNodes(line);
00520
00521 (*updated) = something_changed;
00522 }
00523
00524
00525 bool KateCodeFoldingTree::removeOpening(KateCodeFoldingNode *node,unsigned int line)
00526 {
00527 signed char type;
00528 if ((type=node->type) == 0)
00529 {
00530 dontDeleteOpening(node);
00531 dontDeleteEnding(node);
00532 return false;
00533 }
00534
00535 if (!node->visible)
00536 {
00537 toggleRegionVisibility(getStartLine(node));
00538 }
00539
00540 KateCodeFoldingNode *parent = node->parentNode;
00541 int mypos = parent->findChild(node);
00542
00543 if (mypos > -1)
00544 {
00545
00546 for(; node->childCount()>0 ;)
00547 {
00548 KateCodeFoldingNode *tmp;
00549 parent->insertChild(mypos, tmp=node->takeChild(0));
00550 tmp->parentNode = parent;
00551 tmp->startLineRel += node->startLineRel;
00552 mypos++;
00553 }
00554
00555
00556
00557 bool endLineValid = node->endLineValid;
00558 int endLineRel = node->endLineRel;
00559 uint endCol=node->endCol;
00560
00561
00562 delete parent->takeChild(mypos);
00563
00564 if ((type>0) && (endLineValid))
00565 correctEndings(-type, parent, line+endLineRel,endCol, mypos);
00566 }
00567
00568 return true;
00569 }
00570
00571 bool KateCodeFoldingTree::removeEnding(KateCodeFoldingNode *node,unsigned int )
00572 {
00573 KateCodeFoldingNode *parent = node->parentNode;
00574
00575 if (!parent)
00576 return false;
00577
00578 if (node->type == 0)
00579 return false;
00580
00581 if (node->type < 0)
00582 {
00583
00584 int i = parent->findChild (node);
00585 if (i >= 0)
00586 delete parent->takeChild (i);
00587
00588 return true;
00589 }
00590
00591 int mypos = parent->findChild(node);
00592 int count = parent->childCount();
00593
00594 for (int i=mypos+1; i<count; i++)
00595 {
00596 if (parent->child(i)->type == -node->type)
00597 {
00598 node->endLineValid = true;
00599 node->endLineRel = parent->child(i)->startLineRel - node->startLineRel;
00600
00601 delete parent->takeChild(i);
00602
00603 count = i-mypos-1;
00604 if (count > 0)
00605 {
00606 for (int i=0; i<count; i++)
00607 {
00608 KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
00609 tmp->startLineRel -= node->startLineRel;
00610 tmp->parentNode = node;
00611 node->appendChild(tmp);
00612 }
00613 }
00614 return false;
00615 }
00616 }
00617
00618 if ( (parent->type == node->type) || (!parent->parentNode))
00619 {
00620 for (int i=mypos+1; i<(int)parent->childCount(); i++)
00621 {
00622 KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
00623 tmp->startLineRel -= node->startLineRel;
00624 tmp->parentNode = node;
00625 node->appendChild(tmp);
00626 }
00627
00628
00629 if (!parent->parentNode)
00630 node->endLineValid=false;
00631 else
00632 node->endLineValid = parent->endLineValid;
00633
00634 node->endLineRel = parent->endLineRel-node->startLineRel;
00635
00636 if (node->endLineValid)
00637 return removeEnding(parent, getStartLine(parent)+parent->endLineRel);
00638
00639 return false;
00640 }
00641
00642 node->endLineValid = false;
00643 node->endLineRel = parent->endLineRel - node->startLineRel;
00644
00645 return false;
00646 }
00647
00648
00649 bool KateCodeFoldingTree::correctEndings(signed char data, KateCodeFoldingNode *node,unsigned int line,unsigned int endCol,int insertPos)
00650 {
00651
00652 uint startLine = getStartLine(node);
00653 if (data != -node->type)
00654 {
00655 #if JW_DEBUG
00656 kdDebug(13000)<<"data!=-node->type (correctEndings)"<<endl;
00657 #endif
00658
00659 dontDeleteEnding(node);
00660 if (data == node->type) {
00661 node->endCol=endCol;
00662 return false;
00663 }
00664 KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
00665 something_changed = true;
00666 newNode->startLineValid = false;
00667 newNode->endLineValid = true;
00668 newNode->endLineRel = 0;
00669 newNode->endCol=endCol;
00670
00671 if ((insertPos==-1) || (insertPos==(int)node->childCount()))
00672 node->appendChild(newNode);
00673 else
00674 node->insertChild(insertPos,newNode);
00675
00676
00677 return false;
00678 }
00679 else
00680 {
00681 something_changed = true;
00682 dontDeleteEnding(node);
00683
00684
00685 if (!node->endLineValid)
00686 {
00687 node->endLineValid = true;
00688 node->endLineRel = line - startLine;
00689 node->endCol=endCol;
00690
00691
00692 moveSubNodesUp(node);
00693 }
00694 else
00695 {
00696 #if JW_DEBUG
00697 kdDebug(13000)<<"Closing a node which had already a valid end"<<endl;
00698 #endif
00699
00700 if (startLine+node->endLineRel == line)
00701 {
00702 node->endCol=endCol;
00703
00704 #if JW_DEBUG
00705 kdDebug(13000)<< "We won, just skipping (correctEndings)"<<endl;
00706 #endif
00707 }
00708 else
00709 {
00710 int bakEndLine = node->endLineRel+startLine;
00711 uint bakEndCol = node->endCol;
00712 node->endLineRel = line-startLine;
00713 node->endCol=endCol;
00714
00715 #if JW_DEBUG
00716 kdDebug(13000)<< "reclosed node had childnodes()"<<endl;
00717 kdDebug(13000)<<"It could be, that childnodes() need to be moved up"<<endl;
00718 #endif
00719 moveSubNodesUp(node);
00720
00721 if (node->parentNode)
00722 {
00723 correctEndings(data,node->parentNode,bakEndLine, bakEndCol,node->parentNode->findChild(node)+1);
00724 }
00725 else
00726 {
00727
00728 }
00729 }
00730 }
00731 }
00732 return true;
00733 }
00734
00735 void KateCodeFoldingTree::moveSubNodesUp(KateCodeFoldingNode *node)
00736 {
00737 int mypos = node->parentNode->findChild(node);
00738 int removepos=-1;
00739 int count = node->childCount();
00740 for (int i=0; i<count; i++)
00741 if (node->child(i)->startLineRel >= node->endLineRel)
00742 {
00743 removepos=i;
00744 break;
00745 }
00746 #if JW_DEBUG
00747 kdDebug(13000)<<QString("remove pos: %1").arg(removepos)<<endl;
00748 #endif
00749 if (removepos>-1)
00750 {
00751 #if JW_DEBUG
00752 kdDebug(13000)<<"Children need to be moved"<<endl;
00753 #endif
00754 KateCodeFoldingNode *moveNode;
00755 if (mypos == (int)node->parentNode->childCount()-1)
00756 {
00757 while (removepos<(int)node->childCount())
00758 {
00759 node->parentNode->appendChild(moveNode=node->takeChild(removepos));
00760 moveNode->parentNode = node->parentNode;
00761 moveNode->startLineRel += node->startLineRel;
00762 }
00763 }
00764 else
00765 {
00766 int insertPos=mypos;
00767 while (removepos < (int)node->childCount())
00768 {
00769 insertPos++;
00770 node->parentNode->insertChild(insertPos, moveNode=node->takeChild(removepos));
00771 moveNode->parentNode = node->parentNode;
00772 moveNode->startLineRel += node->startLineRel;
00773 }
00774 }
00775 }
00776
00777 }
00778
00779
00780
00781 void KateCodeFoldingTree::addOpening(KateCodeFoldingNode *node,signed char nType, QMemArray<uint>* list,unsigned int line,unsigned int charPos)
00782 {
00783 uint startLine = getStartLine(node);
00784 if ((startLine==line) && (node->type!=0))
00785 {
00786 #if JW_DEBUG
00787 kdDebug(13000)<<"startLine equals line"<<endl;
00788 #endif
00789 if (nType == node->type)
00790 {
00791 #if JW_DEBUG
00792 kdDebug(13000)<<"Node exists"<<endl;
00793 #endif
00794 node->deleteOpening = false;
00795 node->startCol=charPos;
00796 KateCodeFoldingNode *parent = node->parentNode;
00797
00798 if (!node->endLineValid)
00799 {
00800 int current = parent->findChild(node);
00801 int count = parent->childCount()-(current+1);
00802 node->endLineRel = parent->endLineRel - node->startLineRel;
00803
00804
00805
00806 if (parent)
00807 if (parent->type == node->type)
00808 {
00809 if (parent->endLineValid)
00810 {
00811 removeEnding(parent, line);
00812 node->endLineValid = true;
00813 }
00814 }
00815
00816
00817
00818 if (current != (int)parent->childCount()-1)
00819 {
00820
00821 #ifdef __GNUC__
00822 #warning "FIXME: why does this seem to work?"
00823 #endif
00824
00825 {
00826 for (int i=current+1; i<(int)parent->childCount(); i++)
00827 {
00828 if (parent->child(i)->type == -node->type)
00829 {
00830 count = (i-current-1);
00831 node->endLineValid = true;
00832 node->endLineRel = getStartLine(parent->child(i))-line;
00833 node->endCol = parent->child(i)->endCol;
00834 delete parent->takeChild(i);
00835 break;
00836 }
00837 }
00838 }
00839
00840
00841
00842
00843
00844
00845 if (count>0)
00846 {
00847 for (int i=0;i<count;i++)
00848 {
00849 KateCodeFoldingNode *tmp;
00850 node->appendChild(tmp=parent->takeChild(current+1));
00851 tmp->startLineRel -= node->startLineRel;
00852 tmp->parentNode = node;
00853 }
00854 }
00855 }
00856
00857 }
00858
00859 addOpening_further_iterations(node, nType, list, line, 0, startLine,node->startCol);
00860
00861 }
00862 }
00863 else
00864 {
00865 KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,nType,line-startLine);
00866 something_changed = true;
00867
00868 int insert_position=-1;
00869 for (int i=0; i<(int)node->childCount(); i++)
00870 {
00871 if (startLine+node->child(i)->startLineRel > line)
00872 {
00873 insert_position=i;
00874 break;
00875 }
00876 }
00877
00878 int current;
00879 if (insert_position==-1)
00880 {
00881 node->appendChild(newNode);
00882 current = node->childCount()-1;
00883 }
00884 else
00885 {
00886 node->insertChild(insert_position, newNode);
00887 current = insert_position;
00888 }
00889
00890
00891
00892
00893
00894
00895
00896
00897 int count = node->childCount() - (current+1);
00898 newNode->endLineRel -= newNode->startLineRel;
00899 if (current != (int)node->childCount()-1)
00900 {
00901 if (node->type != newNode->type)
00902 {
00903 for (int i=current+1; i<(int)node->childCount(); i++)
00904 {
00905 if (node->child(i)->type == -newNode->type)
00906 {
00907 count = node->childCount() - i - 1;
00908 newNode->endLineValid = true;
00909 newNode->endLineRel = line - getStartLine(node->child(i));
00910 delete node->takeChild(i);
00911 break;
00912 }
00913 }
00914 }
00915 else
00916 {
00917 node->endLineValid = false;
00918 node->endLineRel = 10000;
00919 }
00920 if (count > 0)
00921 {
00922 for (int i=0;i<count;i++)
00923 {
00924 KateCodeFoldingNode *tmp;
00925 newNode->appendChild(tmp=node->takeChild(current+1));
00926 tmp->parentNode=newNode;
00927 }
00928 }
00929
00930 }
00931
00932 addOpening(newNode, nType, list, line,charPos);
00933
00934 addOpening_further_iterations(node, node->type, list, line, current, startLine,node->startCol);
00935 }
00936 }
00937
00938
00939 void KateCodeFoldingTree::addOpening_further_iterations(KateCodeFoldingNode *node,signed char , QMemArray<uint>*
00940 list,unsigned int line,int current, unsigned int startLine,unsigned int charPos)
00941 {
00942 while (!(list->isEmpty()))
00943 {
00944 if (list->isEmpty())
00945 return;
00946 else
00947 {
00948 signed char data = (*list)[list->size()-2];
00949 uint charPos=(*list)[list->size()-1];
00950 list->resize (list->size()-2);
00951
00952 if (data<0)
00953 {
00954 #if JW_DEBUG
00955 kdDebug(13000)<<"An ending was found"<<endl;
00956 #endif
00957
00958 if (correctEndings(data,node,line,charPos,-1))
00959 return;
00960
00961 #if 0
00962 if(data == -nType)
00963 {
00964 if (node->endLineValid)
00965 {
00966 if (node->endLineRel+startLine==line)
00967 {
00968
00969 }
00970 else
00971 {
00972 node->endLineRel=line-startLine;
00973 node->endLineValid=true;
00974 }
00975 return;
00976 }
00977 else
00978 {
00979 node->endLineRel=line-startLine;
00980 node->endLineValid=true;
00981
00982 }
00983 }
00984 #endif
00985 }
00986 else
00987 {
00988 bool needNew = true;
00989 if (current < (int)node->childCount())
00990 {
00991 if (getStartLine(node->child(current)) == line)
00992 needNew=false;
00993 }
00994 if (needNew)
00995 {
00996 something_changed = true;
00997 KateCodeFoldingNode *newNode = new KateCodeFoldingNode(node, data, line-startLine);
00998 node->insertChild(current, newNode);
00999 }
01000
01001 addOpening(node->child(current), data, list, line,charPos);
01002 current++;
01003
01004 }
01005 }
01006 }
01007 }
01008
01009 unsigned int KateCodeFoldingTree::getStartLine(KateCodeFoldingNode *node)
01010 {
01011 unsigned int lineStart=0;
01012 for (KateCodeFoldingNode *iter=node; iter->type != 0; iter=iter->parentNode)
01013 lineStart += iter->startLineRel;
01014
01015 return lineStart;
01016 }
01017
01018
01019 void KateCodeFoldingTree::lineHasBeenRemoved(unsigned int line)
01020 {
01021 lineMapping.clear();
01022 dontIgnoreUnchangedLines.insert(line, &trueVal);
01023 dontIgnoreUnchangedLines.insert(line-1, &trueVal);
01024 dontIgnoreUnchangedLines.insert(line+1, &trueVal);
01025 hiddenLinesCountCacheValid = false;
01026 #if JW_DEBUG
01027 kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenRemoved: %1").arg(line)<<endl;
01028 #endif
01029
01030
01031 findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);
01032 cleanupUnneededNodes(line);
01033
01034 KateCodeFoldingNode *node = findNodeForLine(line);
01035
01036 {
01037 int startLine = getStartLine(node);
01038 if (startLine == (int)line)
01039 node->startLineRel--;
01040 else
01041 {
01042 if (node->endLineRel == 0)
01043 node->endLineValid = false;
01044 node->endLineRel--;
01045 }
01046
01047 int count = node->childCount();
01048 for (int i=0; i<count; i++)
01049 {
01050 if (node->child(i)->startLineRel+startLine >= line)
01051 node->child(i)->startLineRel--;
01052 }
01053 }
01054
01055 if (node->parentNode)
01056 decrementBy1(node->parentNode, node);
01057
01058 for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
01059 {
01060 if ((*it).start > line)
01061 (*it).start--;
01062 else if ((*it).start+(*it).length > line)
01063 (*it).length--;
01064 }
01065 }
01066
01067
01068 void KateCodeFoldingTree::decrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
01069 {
01070 if (node->endLineRel == 0)
01071 node->endLineValid = false;
01072 node->endLineRel--;
01073
01074 for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
01075 node->child(i)->startLineRel--;
01076
01077 if (node->parentNode)
01078 decrementBy1(node->parentNode,node);
01079 }
01080
01081
01082 void KateCodeFoldingTree::lineHasBeenInserted(unsigned int line)
01083 {
01084 lineMapping.clear();
01085 dontIgnoreUnchangedLines.insert(line, &trueVal);
01086 dontIgnoreUnchangedLines.insert(line-1, &trueVal);
01087 dontIgnoreUnchangedLines.insert(line+1, &trueVal);
01088 hiddenLinesCountCacheValid = false;
01089
01090 #if JW_DEBUG
01091 kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenInserted: %1").arg(line)<<endl;
01092 #endif
01093
01094
01095
01096
01097 KateCodeFoldingNode *node = findNodeForLine(line);
01098
01099 {
01100 int startLine=getStartLine(node);
01101 if (node->type < 0)
01102 node->startLineRel++;
01103 else
01104 node->endLineRel++;
01105
01106 for (uint i=0; i < node->childCount(); ++i)
01107 {
01108 KateCodeFoldingNode *iter = node->child(i);
01109
01110 if (iter->startLineRel+startLine >= line)
01111 iter->startLineRel++;
01112 }
01113 }
01114
01115 if (node->parentNode)
01116 incrementBy1(node->parentNode, node);
01117
01118 for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
01119 {
01120 if ((*it).start > line)
01121 (*it).start++;
01122 else if ((*it).start+(*it).length > line)
01123 (*it).length++;
01124 }
01125 }
01126
01127 void KateCodeFoldingTree::incrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
01128 {
01129 node->endLineRel++;
01130
01131 for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
01132 node->child(i)->startLineRel++;
01133
01134 if (node->parentNode)
01135 incrementBy1(node->parentNode,node);
01136 }
01137
01138
01139 void KateCodeFoldingTree::findAndMarkAllNodesforRemovalOpenedOrClosedAt(unsigned int line)
01140 {
01141 #ifdef __GNUC__
01142 #warning "FIXME: make this multiple region changes per line save";
01143 #endif
01144
01145 markedForDeleting.clear();
01146 KateCodeFoldingNode *node = findNodeForLine(line);
01147 if (node->type == 0)
01148 return;
01149
01150 addNodeToRemoveList(node, line);
01151
01152 while (((node->parentNode) && (node->parentNode->type!=0)) && (getStartLine(node->parentNode)==line))
01153 {
01154 node = node->parentNode;
01155 addNodeToRemoveList(node, line);
01156 }
01157 #if JW_DEBUG
01158 kdDebug(13000)<<" added line to markedForDeleting list"<<endl;
01159 #endif
01160 }
01161
01162
01163 void KateCodeFoldingTree::addNodeToRemoveList(KateCodeFoldingNode *node,unsigned int line)
01164 {
01165 bool add=false;
01166 #ifdef __GNUC__
01167 #warning "FIXME: make this multiple region changes per line save";
01168 #endif
01169 unsigned int startLine=getStartLine(node);
01170 if ((startLine==line) && (node->startLineValid))
01171 {
01172 add=true;
01173 node->deleteOpening = true;
01174 }
01175 if ((startLine+node->endLineRel==line) || ((node->endLineValid==false) && (node->deleteOpening)))
01176 {
01177 int myPos=node->parentNode->findChild(node);
01178 if ((int)node->parentNode->childCount()>myPos+1)
01179 addNodeToRemoveList(node->parentNode->child(myPos+1),line);
01180 add=true;
01181 node->deleteEnding = true;
01182 }
01183
01184 if(add)
01185 markedForDeleting.append(node);
01186
01187 }
01188
01189
01190 void KateCodeFoldingTree::findAllNodesOpenedOrClosedAt(unsigned int line)
01191 {
01192 nodesForLine.clear();
01193 KateCodeFoldingNode *node = findNodeForLine(line);
01194 if (node->type == 0)
01195 return;
01196
01197 unsigned int startLine = getStartLine(node);
01198 if (startLine == line)
01199 nodesForLine.append(node);
01200 else if ((startLine+node->endLineRel == line))
01201 nodesForLine.append(node);
01202
01203 while (node->parentNode)
01204 {
01205 addNodeToFoundList(node->parentNode, line, node->parentNode->findChild(node));
01206 node = node->parentNode;
01207 }
01208 #if JW_DEBUG
01209 kdDebug(13000)<<" added line to nodesForLine list"<<endl;
01210 #endif
01211 }
01212
01213
01214 void KateCodeFoldingTree::addNodeToFoundList(KateCodeFoldingNode *node,unsigned int line,int childpos)
01215 {
01216 unsigned int startLine = getStartLine(node);
01217
01218 if ((startLine==line) && (node->type!=0))
01219 nodesForLine.append(node);
01220 else if ((startLine+node->endLineRel==line) && (node->type!=0))
01221 nodesForLine.append(node);
01222
01223 for (int i=childpos+1; i<(int)node->childCount(); i++)
01224 {
01225 KateCodeFoldingNode *child = node->child(i);
01226
01227 if (startLine+child->startLineRel == line)
01228 {
01229 nodesForLine.append(child);
01230 addNodeToFoundList(child, line, 0);
01231 }
01232 else
01233 break;
01234 }
01235 }
01236
01237
01238 void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)
01239 {
01240 #if JW_DEBUG
01241 kdDebug(13000)<<"void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)"<<endl;
01242 #endif
01243
01244
01245 if (markedForDeleting.isEmpty())
01246 return;
01247
01248 for (int i=0; i<(int)markedForDeleting.count(); i++)
01249 {
01250 KateCodeFoldingNode *node = markedForDeleting.at(i);
01251 if (node->deleteOpening)
01252 kdDebug(13000)<<"DELETE OPENING SET"<<endl;
01253 if (node->deleteEnding)
01254 kdDebug(13000)<<"DELETE ENDING SET"<<endl;
01255
01256 if ((node->deleteOpening) && (node->deleteEnding))
01257 {
01258 #if JW_DEBUG
01259 kdDebug(13000)<<"Deleting complete node"<<endl;
01260 #endif
01261 if (node->endLineValid)
01262 {
01263 int f = node->parentNode->findChild (node);
01264
01265 if (f >= 0)
01266 delete node->parentNode->takeChild(f);
01267 }
01268 else
01269 {
01270 removeOpening(node, line);
01271
01272 }
01273 something_changed = true;
01274 }
01275 else
01276 {
01277 if ((node->deleteOpening) && (node->startLineValid))
01278 {
01279 #if JW_DEBUG
01280 kdDebug(13000)<<"calling removeOpening"<<endl;
01281 #endif
01282 removeOpening(node, line);
01283 something_changed = true;
01284 }
01285 else
01286 {
01287 dontDeleteOpening(node);
01288
01289 if ((node->deleteEnding) && (node->endLineValid))
01290 {
01291 dontDeleteEnding(node);
01292 removeEnding(node, line);
01293 something_changed = true;
01294 }
01295 else
01296 dontDeleteEnding(node);
01297 }
01298 }
01299 }
01300 }
01301
01302 void KateCodeFoldingTree::dontDeleteEnding(KateCodeFoldingNode* node)
01303 {
01304 node->deleteEnding = false;
01305 }
01306
01307
01308 void KateCodeFoldingTree::dontDeleteOpening(KateCodeFoldingNode* node)
01309 {
01310 node->deleteOpening = false;
01311 }
01312
01313
01314 void KateCodeFoldingTree::toggleRegionVisibility(unsigned int line)
01315 {
01316
01317 m_buffer->line (m_buffer->count()-1);
01318
01319 lineMapping.clear();
01320 hiddenLinesCountCacheValid = false;
01321 kdDebug(13000)<<QString("KateCodeFoldingTree::toggleRegionVisibility() %1").arg(line)<<endl;
01322
01323 findAllNodesOpenedOrClosedAt(line);
01324 for (int i=0; i<(int)nodesForLine.count(); i++)
01325 {
01326 KateCodeFoldingNode *node=nodesForLine.at(i);
01327 if ( (!node->startLineValid) || (getStartLine(node) != line) )
01328 {
01329 nodesForLine.remove(i);
01330 i--;
01331 }
01332 }
01333
01334 if (nodesForLine.isEmpty())
01335 return;
01336
01337 nodesForLine.at(0)->visible = !nodesForLine.at(0)->visible;
01338
01339 if (!nodesForLine.at(0)->visible)
01340 addHiddenLineBlock(nodesForLine.at(0),line);
01341 else
01342 {
01343 for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end();++it)
01344 if ((*it).start == line+1)
01345 {
01346 hiddenLines.remove(it);
01347 break;
01348 }
01349
01350 updateHiddenSubNodes(nodesForLine.at(0));
01351 }
01352
01353 emit regionVisibilityChangedAt(line);
01354 }
01355
01356 void KateCodeFoldingTree::updateHiddenSubNodes(KateCodeFoldingNode *node)
01357 {
01358 for (uint i=0; i < node->childCount(); ++i)
01359 {
01360 KateCodeFoldingNode *iter = node->child(i);
01361
01362 if (!iter->visible)
01363 addHiddenLineBlock(iter, getStartLine(iter));
01364 else
01365 updateHiddenSubNodes(iter);
01366 }
01367 }
01368
01369 void KateCodeFoldingTree::addHiddenLineBlock(KateCodeFoldingNode *node,unsigned int line)
01370 {
01371 KateHiddenLineBlock data;
01372 data.start = line+1;
01373 data.length = node->endLineRel-(existsOpeningAtLineAfter(line+node->endLineRel,node)?1:0);
01374 bool inserted = false;
01375
01376 for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
01377 {
01378 if (((*it).start>=data.start) && ((*it).start<=data.start+data.length-1))
01379 {
01380
01381
01382 it=hiddenLines.remove(it);
01383 --it;
01384 }
01385 else
01386 {
01387 if ((*it).start > line)
01388 {
01389 hiddenLines.insert(it, data);
01390 inserted = true;
01391
01392 break;
01393 }
01394 }
01395 }
01396
01397 if (!inserted)
01398 hiddenLines.append(data);
01399 }
01400
01401 bool KateCodeFoldingTree::existsOpeningAtLineAfter(unsigned int line, KateCodeFoldingNode *node)
01402 {
01403 for(KateCodeFoldingNode *tmp = node->parentNode; tmp; tmp=tmp->parentNode)
01404 {
01405 KateCodeFoldingNode *tmp2;
01406 unsigned int startLine=getStartLine(tmp);
01407
01408 if ((tmp2 = tmp->child(tmp->findChild(node) + 1))
01409 && ((tmp2->startLineRel + startLine) == line))
01410 return true;
01411
01412 if ((startLine + tmp->endLineRel) > line)
01413 return false;
01414 }
01415
01416 return false;
01417 }
01418
01419
01420
01421
01422
01423 unsigned int KateCodeFoldingTree::getRealLine(unsigned int virtualLine)
01424 {
01425
01426 if (hiddenLines.isEmpty())
01427 return virtualLine;
01428
01429
01430
01431 unsigned int *real=lineMapping[virtualLine];
01432 if (real)
01433 return (*real);
01434
01435 unsigned int tmp = virtualLine;
01436 for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
01437 {
01438 if ((*it).start<=virtualLine)
01439 virtualLine += (*it).length;
01440 else
01441 break;
01442 }
01443
01444
01445
01446 lineMapping.insert(tmp, new unsigned int(virtualLine));
01447 return virtualLine;
01448 }
01449
01450
01451
01452
01453 unsigned int KateCodeFoldingTree::getVirtualLine(unsigned int realLine)
01454 {
01455
01456 if (hiddenLines.isEmpty())
01457 return realLine;
01458
01459
01460
01461 for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.fromLast(); it!=hiddenLines.end(); --it)
01462 {
01463 if ((*it).start <= realLine)
01464 realLine -= (*it).length;
01465
01466
01467 }
01468
01469
01470
01471 return realLine;
01472 }
01473
01474
01475
01476
01477 unsigned int KateCodeFoldingTree::getHiddenLinesCount(unsigned int doclen)
01478 {
01479
01480 if (hiddenLines.isEmpty())
01481 return 0;
01482
01483 if (hiddenLinesCountCacheValid)
01484 return hiddenLinesCountCache;
01485
01486 hiddenLinesCountCacheValid = true;
01487 hiddenLinesCountCache = 0;
01488
01489 for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
01490 {
01491 if ((*it).start+(*it).length<=doclen)
01492 hiddenLinesCountCache += (*it).length;
01493 else
01494 {
01495 hiddenLinesCountCache += ((*it).length- ((*it).length + (*it).start - doclen));
01496 break;
01497 }
01498 }
01499
01500 return hiddenLinesCountCache;
01501 }
01502
01503 void KateCodeFoldingTree::collapseToplevelNodes()
01504 {
01505
01506 m_buffer->line (m_buffer->count()-1);
01507
01508 if (m_root.noChildren ())
01509 return;
01510
01511 for ( uint i=0; i < m_root.childCount(); ++i )
01512 {
01513 KateCodeFoldingNode *node = m_root.child(i);
01514
01515 if (node->visible && node->startLineValid && node->endLineValid)
01516 {
01517 node->visible=false;
01518 lineMapping.clear();
01519 hiddenLinesCountCacheValid = false;
01520 addHiddenLineBlock(node,node->startLineRel);
01521 emit regionVisibilityChangedAt(node->startLineRel);
01522 }
01523 }
01524 }
01525
01526 void KateCodeFoldingTree::expandToplevelNodes(int numLines)
01527 {
01528
01529 m_buffer->line (m_buffer->count()-1);
01530
01531 KateLineInfo line;
01532 for (int i = 0; i < numLines; i++) {
01533 getLineInfo(&line, i);
01534
01535 if (line.startsInVisibleBlock)
01536 toggleRegionVisibility(i);
01537 }
01538 }
01539
01540 int KateCodeFoldingTree::collapseOne(int realLine)
01541 {
01542
01543 m_buffer->line (m_buffer->count()-1);
01544
01545 KateLineInfo line;
01546 int unrelatedBlocks = 0;
01547 for (int i = realLine; i >= 0; i--) {
01548 getLineInfo(&line, i);
01549
01550 if (line.topLevel && !line.endsBlock)
01551
01552 break;
01553
01554 if (line.endsBlock && ( line.invalidBlockEnd ) && (i != realLine)) {
01555 unrelatedBlocks++;
01556 }
01557
01558 if (line.startsVisibleBlock) {
01559 unrelatedBlocks--;
01560 if (unrelatedBlocks == -1) {
01561 toggleRegionVisibility(i);
01562 return i;
01563 }
01564 }
01565 }
01566 return -1;
01567 }
01568
01569 void KateCodeFoldingTree::expandOne(int realLine, int numLines)
01570 {
01571
01572 m_buffer->line (m_buffer->count()-1);
01573
01574 KateLineInfo line;
01575 int blockTrack = 0;
01576 for (int i = realLine; i >= 0; i--) {
01577 getLineInfo(&line, i);
01578
01579 if (line.topLevel)
01580
01581 break;
01582
01583 if (line.startsInVisibleBlock && i != realLine) {
01584 if (blockTrack == 0)
01585 toggleRegionVisibility(i);
01586
01587 blockTrack--;
01588 }
01589
01590 if (line.endsBlock)
01591 blockTrack++;
01592
01593 if (blockTrack < 0)
01594
01595 break;
01596 }
01597
01598 blockTrack = 0;
01599 for (int i = realLine; i < numLines; i++) {
01600 getLineInfo(&line, i);
01601
01602 if (line.topLevel)
01603
01604 break;
01605
01606 if (line.startsInVisibleBlock) {
01607 if (blockTrack == 0)
01608 toggleRegionVisibility(i);
01609
01610 blockTrack++;
01611 }
01612
01613 if (line.endsBlock)
01614 blockTrack--;
01615
01616 if (blockTrack < 0)
01617
01618 break;
01619 }
01620 }
01621
01622 void KateCodeFoldingTree::ensureVisible( uint line )
01623 {
01624
01625 bool found=false;
01626 for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
01627 {
01628 if ( ((*it).start<=line) && ((*it).start+(*it).length>line) )
01629 {
01630 found=true;
01631 break;
01632 }
01633 }
01634
01635
01636 if (!found) return;
01637
01638 kdDebug(13000)<<"line "<<line<<" is really hidden ->show block"<<endl;
01639
01640
01641 KateCodeFoldingNode *n = findNodeForLine( line );
01642 do {
01643 if ( ! n->visible )
01644 toggleRegionVisibility( getStartLine( n ) );
01645 n = n->parentNode;
01646 } while( n );
01647
01648 }
01649
01650