home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tv20os2.zip / src / TOutline.cpp < prev    next >
C/C++ Source or Header  |  1998-05-03  |  19KB  |  749 lines

  1. /*
  2.  * TOutline.cc
  3.  *
  4.  * Turbo Vision - Version 2.0
  5.  *
  6.  * Copyright (c) 1994 by Borland International
  7.  * All Rights Reserved.
  8.  *
  9.  * Modified by Sergio Sigala <ssigala@globalnet.it>
  10.  */
  11.  
  12. #define cpOutlineViewer "\x6\x7\x3\x8"
  13. #define Uses_TOutlineViewer
  14. #define Uses_TOutline
  15. #define Uses_TEvent
  16. #define Uses_TDrawBuffer
  17. #define Uses_TKeys
  18. #include <tvision/tv.h>
  19.  
  20. #include <string.h>
  21.  
  22. TOutlineViewer::TOutlineViewer(const TRect& bounds, TScrollBar* aHScrollBar,
  23.   TScrollBar* aVScrollBar) : TScroller(bounds, aHScrollBar, aVScrollBar)
  24. {
  25.   growMode = gfGrowHiX + gfGrowHiY;
  26.   foc = 0;
  27. }
  28.  
  29. // Called internally to ensure the focus is within range and displayed
  30.  
  31. void TOutlineViewer::adjustFocus(int newFocus)
  32. {
  33.   if (newFocus < 0)
  34.    newFocus = 0;
  35.   else if (newFocus >= limit.y)
  36.    newFocus = limit.y - 1;
  37.   if (foc != newFocus)
  38.    focused(newFocus);
  39.   if (newFocus < delta.y)
  40.     scrollTo(delta.x, newFocus);
  41.   else if ((newFocus - size.y) >= delta.y)
  42.     scrollTo(delta.x, newFocus - size.y + 1);
  43. }
  44.  
  45. static TDrawBuffer dBuf;
  46. static int auxPos;
  47.  
  48. // Called to draw the outline
  49.  
  50. Boolean drawTree( TOutlineViewer *beingDrawn, TNode* cur, int level,
  51.                   int position, long lines, ushort flags )
  52. {
  53.   ushort  color;
  54.   char s[256];
  55.   char* graph;
  56.  
  57.   if (position >= beingDrawn->delta.y)
  58.   {
  59.       if (position >= beingDrawn->delta.y + beingDrawn->size.y)
  60.         return True;
  61.  
  62.       if ((position == beingDrawn->foc) && ((beingDrawn->state & sfFocused)!=0))
  63.         color = beingDrawn->getColor(0x0202);
  64.       else if (beingDrawn->isSelected(position))
  65.         color = beingDrawn->getColor(0x0303);
  66.       else
  67.         color = beingDrawn->getColor(0x0401);
  68.       dBuf.moveChar(0, ' ', color, beingDrawn->size.x);
  69.  
  70.       graph = beingDrawn->getGraph(level, lines, flags);
  71.       strcpy(s, graph);
  72.       delete graph;
  73.  
  74.       if ( (flags & ovExpanded) == 0)
  75.       {
  76.         strcat(s, "~");
  77.         strcat(s, beingDrawn->getText(cur));
  78.         strcat(s, "~");
  79.       }
  80.       else
  81.         strcat(s, beingDrawn->getText(cur));
  82. //      if (beingDrawn->delta.x<=strlen(s)) /* XXX */
  83.       if (beingDrawn->delta.x <= (int)strlen(s)) /* XXX */
  84.         dBuf.moveCStr(0, &s[beingDrawn->delta.x], color);
  85.       else
  86.         dBuf.moveCStr(0, "", color );
  87.       beingDrawn->writeLine(0, position-beingDrawn->delta.y,
  88.                                beingDrawn->size.x, 1, dBuf);
  89.       auxPos = position;
  90.   }
  91.  
  92.   return False;
  93. }
  94.  
  95. void TOutlineViewer::draw()
  96. {
  97.   ushort nrmColor = getColor(0x0401);
  98.  
  99.   firstThat(drawTree);
  100.   dBuf.moveChar(0, ' ', nrmColor, size.x);
  101.   writeLine(0, auxPos + 1, size.x, size.y - (auxPos - delta.y), dBuf);
  102. }
  103.  
  104. // ExpandAll expands the current node and all child nodes
  105.  
  106. void TOutlineViewer::expandAll(TNode* node)
  107. {
  108.   int i, n;
  109.  
  110.   if (hasChildren(node))
  111.   {
  112.     adjust(node, True);
  113.     n = getNumChildren(node) - 1;
  114.     for (i=0; i <= n; i++)
  115.       expandAll(getChild(node, i));
  116.   }
  117. }
  118.  
  119. /*Draws a graph string suitable for returning from getGraph.  Level
  120.   indicates the outline level.    Lines is the set of bits decribing
  121.   the which levels have a "continuation" mark (usually a vertical
  122.   lines).  If bit 3 is set, level 3 is continued beyond this level.
  123.   Flags gives extra information about how to draw the end of the
  124.   graph (see the ovXXX constants).    LevWidth is how many characters
  125.   to indent for each level.     endWidth is the length the end characters.
  126.  
  127.   The graphics is divided into two parts: the level marks, and the end
  128.   or node graphic.    The level marks consist of the Level Mark character
  129.   separated by Level Filler.  What marks are present is determined by
  130.   Lines.  The end graphic is constructed by placing on of the End First
  131.   charcters followed by endWidth-4 End Filler characters, followed by the
  132.   End Child character, followed by the Retract/Expand character.  If
  133.   endWidth equals 2, End First and Retract/Expand are used.     If endWidth
  134.   equals 1, only the Retract/Expand character is used.    Which characters
  135.   are selected is determined by Flags.
  136.  
  137.   The layout for the characters in the Chars is:
  138.  
  139.    1: Level Filler
  140.      Typically a space.     Used between level markers.
  141.    2: Level Mark
  142.      Typically a vertical bar.    Used to mark the levels currenly active.
  143.    3: End First (not last child)
  144.      Typically a sideways T.  Used as the first character of the end part
  145.      of a node graphic if the node is not the last child of the parent.
  146.    4: End First (last child)
  147.      Typically a L shape.  Used as the first character of the end part
  148.      of a node graphic if the node is the last child of the parent.
  149.    5: End Filler
  150.      Typically a horizontal line.  Used as filler for the end part of a
  151.      node graphic.
  152.    6: End Child position
  153.      Typically not used.  If endWidth > LevWidth this character will
  154.      be placed on top of the markers for next level.  If used it is
  155.      typically a T.
  156.    7: Retracted character
  157.      Typically a '+'.  Displayed as the last character of the end
  158.      node if the level has children and they are not expanded.
  159.    8: Expanded character
  160.      Typically as straight line. Displayed as the last character of
  161.      the end node if the level has children and they are expanded.
  162.  
  163. */
  164.  
  165. char* TOutlineViewer::createGraph(int level, long lines, ushort flags,
  166.  int levWidth, int endWidth,  const char* chars)
  167. {
  168.   static const int
  169.     FillerOrBar      = 0,
  170.     YorL          = 2,
  171.     StraightOrTee = 4,
  172.     retracted      = 6;
  173.  
  174.   char* graph = new char[level*levWidth+endWidth+1];
  175.   char* p;
  176.  
  177.   Boolean expanded = Boolean((flags & ovExpanded) != 0);
  178.   Boolean children = Boolean((flags & ovChildren) != 0);
  179.   Boolean last       = Boolean((flags & ovLast)       != 0);
  180.  
  181.   for (p=graph; level > 0; level--, lines >>= 1)
  182.   {
  183.     *p++ = (lines & 1) ? chars[FillerOrBar+1]:chars[FillerOrBar];
  184.     memset(p, chars[FillerOrBar], levWidth-1);
  185.     p += levWidth-1;
  186.   }
  187.  
  188.   if (--endWidth > 0)
  189.   {
  190.     *p++ = last ? chars[YorL+1]:chars[YorL];
  191.     if (--endWidth > 0)
  192.     {
  193.         if (--endWidth > 0)
  194.         {
  195.                 memset(p, chars[StraightOrTee], endWidth);
  196.                 p += endWidth;
  197.         }
  198.         *p++ = children ? chars[StraightOrTee+1]:chars[StraightOrTee];
  199.     }
  200.     *p++ = expanded ? chars[retracted+1]:chars[retracted];
  201.   }
  202.   *p = 0;
  203.  
  204.   return graph;
  205. }
  206.  
  207. /*
  208.   FirstThat iterates over the nodes of the outline until the given
  209.   local function returns true. The declaration for the local function
  210.   must look like (save for the names, of course):
  211.  
  212.     function MyIter(Cur: Pointer; Level, Position: Integer;
  213.       Lines: LongInt; Flags: Word); far;
  214.  
  215.   The parameters are as follows:
  216.  
  217.     Cur:       A pointer to the node being checked.
  218.     Level:     The level of the node (how many node above it it has)
  219.                Level is 0 based.  This can be used to a call to
  220.                either getGraph or createGraph.
  221.     Position:  The display order position of the node in the list.
  222.                This can be used in a call to Focused or Selected.
  223.                If in range, Position - Delta.Y is location the node
  224.                is displayed on the view.
  225.     Lines:     Bits indicating the active levels.    This can be used in a
  226.                call to getGraph or createGraph. It dicatates which
  227.                horizontal lines need to be drawn.
  228.     Flags:     Various flags for drawing (see ovXXXX flags).  Can be used
  229.                in a call to getGraph or createGraph.
  230. */
  231. TNode* TOutlineViewer::firstThat(
  232.         Boolean (*test)(TOutlineViewer*, TNode* ,int ,int ,long ,ushort ))
  233. {
  234.   return iterate(test, True);
  235. }
  236.  
  237. // Called whenever Node is receives focus
  238.  
  239. void TOutlineViewer::focused(int i)
  240. {
  241.   foc = i;
  242. }
  243.  
  244.  
  245. /*
  246.   Internal function used by both FirstThat and ForEach to do the
  247.   actual iteration over the data. See FirstThat for more details }
  248. */
  249.  
  250.  
  251. TNode* traverseTree(TOutlineViewer* outLine,
  252.         Boolean (*action)(TOutlineViewer*, TNode*, int, int, long, ushort),
  253.         int& position, Boolean& checkResult, TNode* cur, int level,
  254.         long lines, Boolean lastChild)
  255. {
  256.  
  257.   Boolean result;
  258.   int j, childCount;
  259.   TNode* ret;
  260.   ushort flags;
  261.   Boolean children;
  262.  
  263.   if (cur == 0)
  264.         return 0;
  265.  
  266.   children = outLine->hasChildren(cur);
  267.  
  268.   flags = 0;
  269.   if (lastChild)
  270.         flags |= ovLast;
  271.  
  272.   if (children && outLine->isExpanded(cur))
  273.         flags |=  ovChildren;
  274.  
  275.   if (! children || outLine->isExpanded(cur))
  276.         flags |= ovExpanded;
  277.  
  278.   position++;
  279.  
  280.   result = (*action)(outLine, cur, level, position, lines, flags);
  281.   if (checkResult && result)
  282.         return cur;
  283.  
  284.   if (children && outLine->isExpanded(cur))
  285.   {
  286.     childCount = outLine->getNumChildren(cur);
  287.  
  288.     if (! lastChild)
  289.         lines |=  1 << level;
  290.  
  291.     for (j = 0; j < childCount; j++)
  292.     {
  293.        ret = traverseTree(outLine, action, position, checkResult,
  294.                outLine->getChild(cur, j), level + 1, lines,
  295.                        Boolean(j == (childCount - 1)));
  296.        if (ret)
  297.         return ret;
  298.     }
  299.   }
  300.   return 0;
  301. }
  302.  
  303.  
  304. TNode* TOutlineViewer::iterate(
  305.         Boolean (*action)(TOutlineViewer*, TNode*, int, int, long, ushort),
  306.         Boolean checkResult)
  307. {
  308.   int position = -1;
  309.   return traverseTree(this, action, position, checkResult,
  310.                                                   getRoot(), 0, 0, True);
  311. }
  312.  
  313.  
  314. // Iterates over all the nodes.     See FirstThat for a more details
  315.  
  316. TNode* TOutlineViewer::forEach(
  317.         Boolean (*action)(TOutlineViewer*,TNode*,int,int,long,ushort))
  318. {
  319.   return iterate(action, False);
  320. }
  321.  
  322. // Returns the outline palette
  323.  
  324. TPalette& TOutlineViewer::getPalette() const
  325. {
  326.         static TPalette p(cpOutlineViewer, sizeof(cpOutlineViewer));
  327.         return p;
  328. }
  329.  
  330. /*
  331.   Called to retrieve the characters to display prior to the
  332.   text returned by GetText.     Can be overridden to return
  333.   change the appearance of the outline. My default calls
  334.   createGraph with the default.
  335. */
  336.  
  337. char* TOutlineViewer::getGraph(int level, long lines, ushort flags)
  338. {
  339.   static const int levelWidth = 3,    endWidth   = levelWidth;
  340. /*  static const char* graphChars = "\x20\xB3\xC3\xC0\xC4\xC4+\xC4";*/
  341.  
  342.   return createGraph(level, lines, flags, levelWidth, endWidth, graphChars);
  343. }
  344.  
  345. //static Boolean isNode(TOutlineViewer* outLine, TNode* node, int level, /* XXX */
  346. //                                int position, long lines, ushort flags) /* XXX */
  347. static Boolean isNode(TOutlineViewer* /* outLine */, TNode* /* node */, /* XXX */
  348.                 int /* level */ , int position, /* XXX */
  349.                 long /* lines */, ushort /* flags */) /* XXX */
  350. {
  351.     return Boolean(auxPos == position);
  352. }
  353.  
  354.  
  355. // Returns a pointer to the node that is to be shown on line i
  356.  
  357. TNode* TOutlineViewer::getNode(int i)
  358. {
  359.   auxPos = i;
  360.  
  361.   return firstThat(isNode);
  362. }
  363.  
  364. /*
  365.   Returns if Node is selected.    By default, returns true if Node is
  366.   Focused (i.e. single selection).    Can be overriden to handle
  367.   multiple selections. }
  368. */
  369. Boolean TOutlineViewer::isSelected(int i)
  370. {
  371.   return (foc == i) ? True:False;
  372. }
  373.  
  374. static long focLines;
  375. static ushort focFlags;
  376. static int focLevel;
  377.  
  378. //static Boolean isFocused(TOutlineViewer* focusCheck, TNode* cur, int level, /* XXX */
  379. //                int position, long lines, ushort flags) /* XXX */ 
  380. static Boolean isFocused(TOutlineViewer* focusCheck, TNode* /* cur */, int level, /* XXX */
  381.                 int position, long lines, ushort flags) /* XXX */ 
  382. {
  383.       if (position == focusCheck->foc)
  384.       {
  385.         focLevel = level;
  386.         focLines = lines;
  387.         focFlags = flags;
  388.  
  389.         return True;
  390.       }
  391.       else
  392.         return False;
  393. }
  394.  
  395. // Called to handle an event
  396.  
  397. void TOutlineViewer::handleEvent(TEvent& event)
  398. {
  399.   const int mouseAutoToSkip = 3;
  400.  
  401.   TPoint mouse;
  402.   TNode* cur;
  403. //  int newFocus;    /* XXX */
  404.   int newFocus = 0;    /* XXX */
  405.   int count;
  406.   char* graph;
  407.   uchar dragged;
  408.  
  409.   TScroller::handleEvent(event);
  410.   switch (event.what)
  411.   {
  412.   case evMouseDown:
  413.         count = 0;
  414.         dragged = 0;
  415.         do {
  416.           if (dragged < 2)
  417.                 dragged++;
  418.           mouse = makeLocal(event.mouse.where);
  419.           if (mouseInView(event.mouse.where))
  420.             newFocus = delta.y + mouse.y;
  421.           else
  422.           {
  423.             if (event.what == evMouseAuto)
  424.                 count++;
  425.             if (count == mouseAutoToSkip)
  426.             {
  427.               count = 0;
  428.               if (mouse.y < 0)
  429.                 newFocus--;
  430.               if (mouse.y >= size.y)
  431.                 newFocus++;
  432.             }
  433.           }
  434.           if (foc != newFocus)
  435.           {
  436.             adjustFocus(newFocus);
  437.             drawView();
  438.           }
  439. #ifndef __UNPATCHED
  440.         } while ( !(event.mouse.eventFlags & meDoubleClick) &&
  441.                   mouseEvent(event, evMouseMove + evMouseAuto));
  442. #else
  443.         } while ( mouseEvent(event, evMouseMove + evMouseAuto) );
  444. #endif
  445.  
  446.         if (event.mouse.eventFlags & meDoubleClick)
  447.                 selected(foc);
  448.         else
  449.         {
  450.           if (dragged < 2)
  451.           {
  452.             cur = firstThat(isFocused);
  453.             graph = getGraph(focLevel,focLines,focFlags);
  454. //            if (mouse.x < strlen(graph) )    /* XXX */
  455.             if (mouse.x < (int)strlen(graph) )    /* XXX */
  456.             {
  457.               adjust(cur, ! isExpanded(cur) ? True:False);
  458.               update();
  459.               drawView();
  460.             }
  461.             delete graph;
  462.           }
  463.         }
  464.  
  465.         break;
  466.  
  467.     case evKeyboard:
  468.  
  469.         newFocus = foc;
  470.         switch (ctrlToArrow(event.keyDown.keyCode))
  471.         {
  472.         case kbUp:
  473.         case kbLeft:
  474.                 newFocus--;
  475.                 break;
  476.         case kbDown:
  477.         case kbRight:
  478.                 newFocus++;
  479.                 break;
  480.         case kbPgDn:
  481.                 newFocus += size.y - 1;
  482.                 break;
  483.     case kbPgUp:
  484.                 newFocus -= size.y - 1;
  485.                 break;
  486.         case kbHome:
  487.                 newFocus = delta.y;
  488.                 break;
  489. #ifndef __UNPATCHED
  490.         case kbEnd:
  491. #else
  492.         casekbEnd:
  493. #endif
  494.                 newFocus = delta.y + size.y - 1;
  495.                 break;
  496.         case kbCtrlPgUp:
  497.                 newFocus = 0;
  498.                 break;
  499.         case kbCtrlPgDn:
  500.                 newFocus = limit.y - 1;
  501.                 break;
  502.         case kbCtrlEnter:
  503.         case kbEnter:
  504.                 selected(newFocus);
  505.                 break;
  506.         default:
  507.                 uchar code = event.keyDown.charScan.charCode;
  508.           switch ( code )
  509.           {
  510.           case '-':
  511.           case '+':
  512.                 adjust(getNode(newFocus), code == '+' ? True:False);
  513.                 break;
  514.           case '*':
  515.                 expandAll(getNode(newFocus));
  516.                 break;
  517.           default:
  518.                 return;
  519.           }
  520.           update();
  521.         }
  522.         clearEvent(event);
  523.         adjustFocus(newFocus);
  524.         drawView();
  525.   };
  526. };
  527.  
  528. /*
  529.   Called whenever Node is selected by the user either via keyboard
  530.   control or by the mouse. }
  531. */
  532. void TOutlineViewer::selected(int /* i */)    /* XXX */
  533. {
  534. }
  535.  
  536. // Redraws the outline if the outliner sfFocus state changes
  537.  
  538. void TOutlineViewer::setState(ushort aState, Boolean enable)
  539. {
  540.   TScroller::setState(aState, enable);
  541.   if ( (aState & sfFocused) != 0)
  542.         drawView();
  543. }
  544.  
  545. static int updateCount;
  546. static int updateMaxX;
  547.  
  548. //static Boolean countNode(TOutlineViewer* beingCounted, TNode* p, int level, /* XXX */
  549. //                                int position, long lines,  ushort flags) /* XXX */
  550. static Boolean countNode(TOutlineViewer* beingCounted, TNode* p, int level, /* XXX */
  551.                                 int /* position */, long lines,  ushort flags) /* XXX */
  552. {
  553.     int len;
  554.     char *graph;
  555.  
  556.     updateCount++;
  557.     graph = beingCounted->getGraph(level, lines, flags);
  558.     len = strlen(beingCounted->getText(p)) + strlen(graph);
  559.     if (updateMaxX < len)
  560.       updateMaxX = len;
  561.     delete graph;
  562.     return False;
  563. }
  564.  
  565. /*
  566.   Updates the limits of the outline viewer.     Should be called whenever
  567.   the data of the outline viewer changes.  This includes during
  568.   the initalization of base classes.  TOutlineViewer assumes that
  569.   the outline is empty.     If the outline becomes non-empty during the
  570.   initialization, Update must be called. Also, if during the operation
  571.   of the TOutlineViewer the data being displayed changes, Update
  572.   and DrawView must be called. }
  573. */
  574. void TOutlineViewer::update()
  575. {
  576.   updateCount = 0;
  577.   updateMaxX = 0;
  578.   firstThat(countNode);
  579.   setLimit(updateMaxX, updateCount);
  580.   adjustFocus(foc);
  581. }
  582.  
  583. void TOutlineViewer::disposeNode(TNode* node)
  584. {
  585.   if (node)
  586.   {
  587.       if (node->childList)
  588.         disposeNode(node->childList);
  589.       if (node->next)
  590.         disposeNode(node->next);
  591.       delete node;
  592.   }
  593. }
  594.  
  595. #if !defined(NO_STREAMABLE)
  596.  
  597. void* TOutlineViewer::read(ipstream& ip)
  598. {
  599.     TScroller::read(ip);
  600.     ip >> foc;
  601.     return this;
  602. }
  603.  
  604. void TOutlineViewer::write(opstream& op)
  605. {
  606.     TScroller::write(op);
  607.     op << foc;
  608. }
  609.  
  610. #endif
  611.  
  612. // TOutline
  613.  
  614. TOutline::TOutline(const TRect& bounds, TScrollBar* aHScrollBar,
  615.                         TScrollBar* aVScrollBar,  TNode* aRoot) :
  616.                 TOutlineViewer(bounds,    aHScrollBar, aVScrollBar)
  617. {
  618.   root = aRoot;
  619.   update();
  620. }
  621.  
  622. TOutline::~TOutline()
  623. {
  624.   disposeNode(root);
  625. }
  626.  
  627. void TOutline::adjust(TNode* node, Boolean expand)
  628. {
  629.   node->expanded = expand;
  630. }
  631.  
  632. TNode* TOutline::getRoot()
  633. {
  634.   return root;
  635. }
  636.  
  637. int TOutline::getNumChildren(TNode* node)
  638. {
  639.   int i;
  640.   TNode* p;
  641.  
  642.   p = node->childList;
  643.   i = 0;
  644.   while (p)
  645.   {
  646.     i++;
  647.     p = p->next;
  648.   }
  649.   return i;
  650. }
  651.  
  652. TNode* TOutline::getChild(TNode* node, int i)
  653. {
  654.   TNode *p;
  655.  
  656.   p = node->childList;
  657.   while ((i != 0) && (p != 0))
  658.   {
  659.     i--;
  660.     p = p->next;
  661.   }
  662.   return p;
  663. }
  664.  
  665. char* TOutline::getText(TNode* node)
  666. {
  667.   return node->text;
  668. }
  669.  
  670. Boolean TOutline::isExpanded(TNode* node)
  671. {
  672.   return node->expanded;
  673. }
  674.  
  675. Boolean TOutline::hasChildren(TNode* node)
  676. {
  677.   return Boolean(node->childList != 0);
  678. }
  679.  
  680. #if !defined(NO_STREAMABLE)
  681.  
  682. TNode* TOutline::readNode(ipstream& ip)
  683. {
  684.     int nChildren;
  685.     uchar more;
  686.     uchar expand;
  687.  
  688.     TNode* node = new TNode((char*)0);
  689.  
  690.     ip >> more;
  691.     ip >> expand;
  692.     ip >> nChildren;
  693.     node->text = ip.readString();
  694.     node->expanded = Boolean(expand);
  695.  
  696.     if (nChildren)
  697.         node->childList = readNode(ip);
  698.     else
  699.         node->childList = 0;
  700.  
  701.     if (more)
  702.         node->next = readNode(ip);
  703.     else
  704.         node->next = 0;
  705.  
  706.     return node;
  707. }
  708.  
  709. void TOutline::writeNode(TNode* node, opstream& op)
  710. {
  711.     uchar more = (node->next != 0) ? 1 : 0;
  712.     uchar expand = (node->expanded) ? 1 : 0;
  713.  
  714.     op << more;
  715.     op << expand;
  716.     op << getNumChildren(node);
  717.     op.writeString(node->text);
  718.  
  719.     if ( node->childList != 0 )
  720.         writeNode(node->childList, op);
  721.  
  722.     if (node->next != 0)
  723.         writeNode(node->next, op);
  724. }
  725.  
  726.  
  727. void* TOutline::read(ipstream& ip)
  728. {
  729.     TOutlineViewer::read(ip);
  730.  
  731.     root = readNode(ip);
  732.  
  733.     return this;
  734. }
  735.  
  736. void TOutline::write(opstream& op)
  737. {
  738.     TOutlineViewer::write(op);
  739.  
  740.     writeNode(root, op);
  741. }
  742.  
  743. TStreamable* TOutline::build()
  744. {
  745.     return new TOutline( streamableInit );
  746. }
  747.  
  748. #endif
  749.