Sophie

Sophie

distrib > Fedora > 18 > x86_64 > by-pkgid > b3a1f4d91c26f535919e39e25606614a > files > 2294

wt-doc-3.2.3-1.fc18.noarch.rpm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<title>Wt: Treelist example</title>

<link href="tabs.css" rel="stylesheet" type="text/css"/>
<link href="doxygen.css" rel="stylesheet" type="text/css" />

<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
  $(document).ready(function() { searchBox.OnSelectItem(0); });
</script>

</head>
<body>
<div id="top"><!-- do not remove this div! -->


<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  
  
  <td style="padding-left: 0.5em;">
   <div id="projectname">Wt
   &#160;<span id="projectnumber">3.2.3</span>
   </div>
   
  </td>
  
  
  
 </tr>
 </tbody>
</table>
</div>

<!-- Generated by Doxygen 1.7.5.1 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
  <div id="navrow1" class="tabs">
    <ul class="tablist">
      <li><a href="index.html"><span>Main&#160;Page</span></a></li>
      <li class="current"><a href="pages.html"><span>Related&#160;Pages</span></a></li>
      <li><a href="modules.html"><span>Modules</span></a></li>
      <li><a href="namespaces.html"><span>Namespaces</span></a></li>
      <li><a href="annotated.html"><span>Classes</span></a></li>
      <li><a href="files.html"><span>Files</span></a></li>
      <li>
        <div id="MSearchBox" class="MSearchBoxInactive">
        <span class="left">
          <img id="MSearchSelect" src="search/mag_sel.png"
               onmouseover="return searchBox.OnSearchSelectShow()"
               onmouseout="return searchBox.OnSearchSelectHide()"
               alt=""/>
          <input type="text" id="MSearchField" value="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)" 
               onblur="searchBox.OnSearchFieldFocus(false)" 
               onkeyup="searchBox.OnSearchFieldChange(event)"/>
          </span><span class="right">
            <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
          </span>
        </div>
      </li>
    </ul>
  </div>
</div>
<div class="header">
  <div class="headertitle">
<div class="title">Treelist example </div>  </div>
</div>
<div class="contents">
<div class="textblock"><p>In this example we will step through the code of the <a href="http://wipkip.irule.be/wt/examples/treelist/demotreelist.wt">Tree List example</a>. The source code of the entire example is available as leafs of the tree. Note that Wt offers a Tree List widget as part of the library (see <a class="el" href="classWt_1_1WTreeNode.html" title="A single node in a tree.">WTreeNode</a>), of which this example is a down-stripped version.</p>
<p>The example in particular demonstrates the use of stateless slot learning to simultaneously implement client-side and server-side event handling in C++.</p>
<p>The tree constructed as hierarchy of tree nodes. A single tree node is implemented in the class TreeNode. TreeNode uses the helper class IconPair for rendering icons that have a state (such as the expand/collapse icons). We start with a walk-over of this class.</p>
<h2><a class="anchor" id="stateicon_sec"></a>
IconPair: a pair of icons that reflects state.</h2>
<p>For the implementation of the tree list expand/collapse icons, as well as the label icons (such as the folder icon), we use class IconPair. It takes a pair of icons and shows only one at a time. Passing clickIsSwitch = true to the constructor will make the icon react to click events to switch the current icon.</p>
<p>This is the class definition of IconPair:</p>
 <div class="fragment"><pre class="fragment">
<span class="keyword">class </span>IconPair : <span class="keyword">public</span> Wt::WCompositeWidget
{
<span class="keyword">public</span>:
  IconPair(<span class="keyword">const</span> std::string icon1URI, <span class="keyword">const</span> std::string icon2URI,
           <span class="keywordtype">bool</span> clickIsSwitch = <span class="keyword">true</span>, <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a> *parent = 0);

  <span class="keywordtype">void</span> setState(<span class="keywordtype">int</span> num);

  <span class="keywordtype">int</span> state() <span class="keyword">const</span>;

  <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a> *icon1()<span class="keyword"> const </span>{ <span class="keywordflow">return</span> icon1_; }
  
  <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a> *icon2()<span class="keyword"> const </span>{ <span class="keywordflow">return</span> icon2_; }

  <span class="keywordtype">void</span> showIcon1();

  <span class="keywordtype">void</span> showIcon2();

<span class="keyword">private</span>:
  <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a> *impl_;

  <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a> *icon1_;

  <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a> *icon2_;

<span class="keyword">public</span>:
  <a class="code" href="classWt_1_1EventSignal.html" title="A signal that conveys user-interface events.">Wt::EventSignal&lt;Wt::WMouseEvent&gt;</a>&amp; icon1Clicked;

  <a class="code" href="classWt_1_1EventSignal.html" title="A signal that conveys user-interface events.">Wt::EventSignal&lt;Wt::WMouseEvent&gt;</a>&amp; icon2Clicked;

<span class="keyword">private</span>:
  <span class="keywordtype">int</span> previousState_;

  <span class="keywordtype">void</span> undoShowIcon1();

  <span class="keywordtype">void</span> undoShowIcon2();
};
</pre></div></p>
<p>IconPair is a composite widget, implemented as a <a class="el" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">WContainerWidget</a> which contains two <a class="el" href="classWt_1_1WImage.html" title="A widget that displays an image.">WImage</a> objects. The class defines two slots: IconPair::showIcon1() and IconPair::showIcon2(), which show the respective icon, while hiding the other icon.</p>
<p>Although Wt is a C++ (server-side) library, it can also generate client-side JavaScript code for instant visual response. This example will use this capability to implement all of the tree navigation at the client-side for those clients that support JavaScript -- as if it were implemented as a JavaScript library. But since everything is still plain C++ code, it works whatever technology is available or lacking at the client side. Think of a stateless slot implementation as creating a forked implementation, with JavaScript in the client for visual response -- when JavaScript is available, and C++ at the server. When no JavaScript is available, everything happens at the server.</p>
<p>The key concept behind Wt's capability to implement things at the client-side is stateless slot implementations. A stateless slot is, besides a normal C++ function that may be connected to a signal, a C++ function that promises to always have the same behaviour (until it is reset, as we will see later).</p>
<p>This applies to the two functions showIcon1() and showIcon2(), as they simply set the corresponding icon, irrespective of any application state. The library offers two methods for stateless slot implementations: AutoLearned and PreLearned. An AutoLearned stateless slot will only "become client-side" after the first invocation. Applied to our tree widget, this would mean that the first click on any icon would require a round-trip to the server the first time only. An AutoLearned stateless slot simply requires an indication that the particular slot confirms to the contract of being stateless. A PreLearned stateless slot, on the other hand, is "client-side" from the first invocation. To implement a PreLearned stateless however, we need to do some extra work by providing methods that exactly undo the effect of the slot. We provide here two such undo methods: undoShowIcon1() and undoShowIcon2().</p>
<p>Enough talk! Let's look at the implementation, starting with the constructor.</p>
 <div class="fragment"><pre class="fragment">IconPair::IconPair(<span class="keyword">const</span> std::string icon1URI, <span class="keyword">const</span> std::string icon2URI,
                   <span class="keywordtype">bool</span> clickIsSwitch, <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a> *parent)
  : Wt::WCompositeWidget(parent),
    impl_(new Wt::WContainerWidget()),
    icon1_(new Wt::WImage(icon1URI, impl_)),
    icon2_(new Wt::WImage(icon2URI, impl_)),
    icon1Clicked(icon1_-&gt;clicked()),
    icon2Clicked(icon2_-&gt;clicked())
{
</pre></div></p>
<p>IconPair inherits from <a class="el" href="classWt_1_1WCompositeWidget.html" title="A widget that hides the implementation of composite widgets.">WCompositeWidget</a>. A composite widget is a widget which is composed from other widgets, in a way not exposed in its API. In this way, you may later change the implementation without any problem.</p>
<p>Notice how we constructed three widgets that are used in the implementation: two images (icon1_ and icon2_), and a container (impl_) to hold them. The images are added to the container by passing the container as the last argument in their constructor.</p>
<p><a class="el" href="classWt_1_1WCompositeWidget.html" title="A widget that hides the implementation of composite widgets.">WCompositeWidget</a> requires to set the implementation widget, which is in our case a <a class="el" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">WContainerWidget</a>:</p>
<p><div class="fragment"><pre class="fragment">  setImplementation(impl_);
</pre></div></p>
<p>We declare the slots showIcon1() and showIcon2() as stateless slots, allowing for client-side optimisation, and offer an undo function which facilitates a PreLearned client-side implementation.</p>
<p>The calls to <a class="el" href="classWt_1_1WObject.html#adaa163b9e92933f3b2ff4ec58e2734c6" title="Declares a slot to be stateless and learn client-side behaviour on first invocation.">WObject::implementStateless()</a> state that the slots showIcon1() and showIcon2() are stateless slots, and their visual effect may be learned in advance. The effect of these statements is merely an optimization. Any non-visual effects of these slots are still propagated and executed, as expected.</p>
<p><div class="fragment"><pre class="fragment">
  implementStateless(&amp;IconPair::showIcon1, &amp;IconPair::undoShowIcon1);
  implementStateless(&amp;IconPair::showIcon2, &amp;IconPair::undoShowIcon2);
</pre></div></p>
<p>Next, we declare the widget to be an inline widget. An inline widget will be layed out following the natural flow of text (left to right). This does not really matter for our example, since TreeNode will do the layout with a <a class="el" href="classWt_1_1WTable.html" title="A container widget which provides layout of children in a table grid.">WTable</a>, but we do so to provide consistency with a <a class="el" href="classWt_1_1WImage.html" title="A widget that displays an image.">WImage</a> which is also inline by default.</p>
<p><div class="fragment"><pre class="fragment">  setInline(<span class="keyword">true</span>);
</pre></div></p>
<p>The initial state is to show the first icon:</p>
<p><div class="fragment"><pre class="fragment">  icon2_-&gt;hide();
</pre></div></p>
<p>To react to click events, we connect signals with slots:</p>
<p><div class="fragment"><pre class="fragment">
  <span class="keywordflow">if</span> (clickIsSwitch) {
    icon1_-&gt;clicked().connect(icon1_, &amp;<a class="code" href="classWt_1_1WWidget.html#a0825c3ccbd4999afc1a88fafa6aa6fc7" title="Hides the widget.">Wt::WImage::hide</a>);
    icon1_-&gt;clicked().connect(icon2_, &amp;<a class="code" href="classWt_1_1WWidget.html#a52dcef5a385ddfa0a8c3e6c20000f181" title="Shows the widget.">Wt::WImage::show</a>);

    icon2_-&gt;clicked().connect(icon2_, &amp;<a class="code" href="classWt_1_1WWidget.html#a0825c3ccbd4999afc1a88fafa6aa6fc7" title="Hides the widget.">Wt::WImage::hide</a>);
    icon2_-&gt;clicked().connect(icon1_, &amp;<a class="code" href="classWt_1_1WWidget.html#a52dcef5a385ddfa0a8c3e6c20000f181" title="Shows the widget.">Wt::WImage::show</a>); <span class="comment">//</span>
</pre></div></p>
<p>We change the cursor to a pointer to hint that clicking these icons may do something useful.</p>
<p><div class="fragment"><pre class="fragment">
    decorationStyle().setCursor(<a class="code" href="namespaceWt.html#a6dc0346a6ae888d6d4ab44f022e61eb6af860d39b9a2ba8f7d7359453bf15a37c" title="Pointing hand, CSS &#39;pointer&#39; cursor.">Wt::PointingHandCursor</a>);
  }
} <span class="comment">//</span>
</pre></div></p>
<p>We also change the cursor to a pointer to hint that clicking these icons will in fact perform an action.</p>
<p>The rest of the class definition is:</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> IconPair::setState(<span class="keywordtype">int</span> num)
{
  <span class="keywordflow">if</span> (num == 0) {
    icon1_-&gt;show();
    icon2_-&gt;hide();
  } <span class="keywordflow">else</span> {
    icon1_-&gt;hide();
    icon2_-&gt;show();
  }
}

<span class="keywordtype">int</span> IconPair::state()<span class="keyword"> const</span>
<span class="keyword"></span>{
  <span class="keywordflow">return</span> (icon1_-&gt;isHidden() ? 1 : 0);
}

<span class="keywordtype">void</span> IconPair::showIcon1()
{
  previousState_ = (icon1_-&gt;isHidden() ? 1 : 0);
  setState(0);
}

<span class="keywordtype">void</span> IconPair::showIcon2()
{
  previousState_ = (icon1_-&gt;isHidden() ? 1 : 0);
  setState(1);
}

<span class="keywordtype">void</span> IconPair::undoShowIcon1()
{
  setState(previousState_);
}

<span class="keywordtype">void</span> IconPair::undoShowIcon2()
{
  setState(previousState_);
} <span class="comment">//</span>
</pre></div></p>
<p>Note the implementations of undoShowIcon1() and undoShowIcon2(): they simply, but accurately, reset the state to what it was before the respective showIcon1() and showIcon2() calls.</p>
<h2><a class="anchor" id="treenode_sec"></a>
TreeNode: an expandable tree node.</h2>
<p>TreeNode contains the implementation of the tree, as a hierarchy of tree nodes. The layout of a single node is done using a 2x2 <a class="el" href="classWt_1_1WTable.html" title="A container widget which provides layout of children in a table grid.">WTable</a>:</p>
<div class="fragment"><pre class="fragment">
   |-----------------------|
   | +/- | label           |
   |------------------------
   |     | child1          |
   |     | child2          |
   |     | child3          |
   |     |       ...       |
   |-----------------------| </pre></div><p>The TreeNode manages a list of child nodes in a <a class="el" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">WContainerWidget</a> which will be hidden and shown when the node is expanded or collapsed, and children are collapsed when the node is expanded.</p>
<p>This is the TreeNode class definition:</p>
 <div class="fragment"><pre class="fragment"><span class="keyword">class </span>TreeNode : <span class="keyword">public</span> Wt::WCompositeWidget
{
<span class="keyword">public</span>:
  TreeNode(<span class="keyword">const</span> std::string labelText,
           <a class="code" href="namespaceWt.html#a140dea437d52d3d7c438ea3bd16a1480" title="Enumeration that indicates the text format.">Wt::TextFormat</a> labelFormat,
           IconPair *labelIcon, <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a> *parent = 0);

  <span class="keywordtype">void</span> addChildNode(TreeNode *node);

  <span class="keywordtype">void</span> removeChildNode(TreeNode *node);

  <span class="keyword">const</span> std::vector&lt;TreeNode *&gt;&amp; childNodes()<span class="keyword"> const </span>{ <span class="keywordflow">return</span> childNodes_; }

  <span class="keywordtype">void</span> collapse();

  <span class="keywordtype">void</span> expand();

<span class="keyword">private</span>:
  std::vector&lt;TreeNode *&gt; childNodes_;

  TreeNode                 *parentNode_;

  <a class="code" href="classWt_1_1WTable.html" title="A container widget which provides layout of children in a table grid.">Wt::WTable</a>               *layout_;

  IconPair                 *expandIcon_;

  <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a>               *noExpandIcon_;

  IconPair                 *labelIcon_;

  <a class="code" href="classWt_1_1WText.html" title="A widget that renders (XHTML) text.">Wt::WText</a>                *labelText_;

  <a class="code" href="classWt_1_1WText.html" title="A widget that renders (XHTML) text.">Wt::WText</a>                *childCountLabel_;

  <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a>     *expandedContent_;

  <span class="keywordtype">void</span> adjustExpandIcon();

  <span class="keywordtype">bool</span> isLastChildNode() <span class="keyword">const</span>;

  <span class="keywordtype">void</span> childNodesChanged();

  <span class="keywordtype">bool</span> wasCollapsed_;

  <span class="keywordtype">void</span> undoCollapse();

  <span class="keywordtype">void</span> undoExpand();

  <span class="keyword">enum</span> ImageIndex { Middle = 0, Last = 1 };

  <span class="keyword">static</span> std::string imageLine_[];
  <span class="keyword">static</span> std::string imagePlus_[];
  <span class="keyword">static</span> std::string imageMin_[];
}; <span class="comment">//</span>
</pre></div></p>
<p>The public interface of the TreeNode provides methods to manage its children, and two public slots to expand or collapse the node. Remember, a slot is nothing more than a method (and the public slots: does not actually mean anything, except providing a hint to the user of this class that these methods are made to be connected to signals).</p>
<p>We start with the implementation of the constructor:</p>
 <div class="fragment"><pre class="fragment">TreeNode::TreeNode(<span class="keyword">const</span> std::string labelText,
                   <a class="code" href="namespaceWt.html#a140dea437d52d3d7c438ea3bd16a1480" title="Enumeration that indicates the text format.">Wt::TextFormat</a> labelFormat,
                   IconPair *labelIcon,
                   <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a> *parent)
  : Wt::WCompositeWidget(parent),
    parentNode_(0),
    labelIcon_(labelIcon)
{
</pre></div></p>
<p>We start with declaring stateless implementations for the slots. It is good practice to do this first, since it must be done before any connections are made to the slots.</p>
<p><div class="fragment"><pre class="fragment">  <span class="comment">// pre-learned stateless implementations ...</span>
  implementStateless(&amp;TreeNode::expand, &amp;TreeNode::undoExpand);
  implementStateless(&amp;TreeNode::collapse, &amp;TreeNode::undoCollapse);
</pre></div></p>
<p>We will implement the treenode as 2 by 2 table.</p>
<p><div class="fragment"><pre class="fragment">  setImplementation(layout_ = <span class="keyword">new</span> <a class="code" href="classWt_1_1WTable.html" title="A container widget which provides layout of children in a table grid.">Wt::WTable</a>());
</pre></div></p>
<p>We create all icons. Since currently the node is empty, we only show the no-expand version (which is simply a horizontal line).</p>
<p><div class="fragment"><pre class="fragment">
  expandIcon_ = <span class="keyword">new</span> IconPair(imagePlus_[Last], imageMin_[Last]);
  expandIcon_-&gt;hide();
  noExpandIcon_ = <span class="keyword">new</span> <a class="code" href="classWt_1_1WImage.html" title="A widget that displays an image.">Wt::WImage</a>(imageLine_[Last]);
</pre></div></p>
<p>The expanded content is a <a class="el" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">WContainerWidget</a>.</p>
<p><div class="fragment"><pre class="fragment">
  expandedContent_ = <span class="keyword">new</span> <a class="code" href="classWt_1_1WContainerWidget.html" title="A widget that holds and manages child widgets.">Wt::WContainerWidget</a>();
  expandedContent_-&gt;hide();
</pre></div></p>
<p>We create the label and child count text widgets:</p>
<p><div class="fragment"><pre class="fragment">
  labelText_ = <span class="keyword">new</span> <a class="code" href="classWt_1_1WText.html" title="A widget that renders (XHTML) text.">Wt::WText</a>(labelText);
  labelText_-&gt;setTextFormat(labelFormat);
  labelText_-&gt;setStyleClass(<span class="stringliteral">&quot;treenodelabel&quot;</span>);
  childCountLabel_ = <span class="keyword">new</span> <a class="code" href="classWt_1_1WText.html" title="A widget that renders (XHTML) text.">Wt::WText</a>();
  childCountLabel_-&gt;setMargin(7, <a class="code" href="namespaceWt.html#a5a6f4636bcc6ab3c075165d249b3a5a3a6568fecac7c7d7223afaed240bcfdd9e" title="Left side.">Wt::Left</a>);
  childCountLabel_-&gt;setStyleClass(<span class="stringliteral">&quot;treenodechildcount&quot;</span>);
</pre></div></p>
<p>Now we add all widgets in the proper table cell, and set the correct alignment.</p>
<p><div class="fragment"><pre class="fragment">
  layout_-&gt;elementAt(0, 0)-&gt;addWidget(expandIcon_);
  layout_-&gt;elementAt(0, 0)-&gt;addWidget(noExpandIcon_);

  <span class="keywordflow">if</span> (labelIcon_) {
    layout_-&gt;elementAt(0, 1)-&gt;addWidget(labelIcon_);
    labelIcon_-&gt;setVerticalAlignment(<a class="code" href="namespaceWt.html#ab8f772c69bc8180c31f9e4f4593b143fa2ed90f01dbf74049f428d33769d48e3f" title="Align vertically the middle to the middle of the parent widget.">Wt::AlignMiddle</a>);
  }
  layout_-&gt;elementAt(0, 1)-&gt;addWidget(labelText_);
  layout_-&gt;elementAt(0, 1)-&gt;addWidget(childCountLabel_);

  layout_-&gt;elementAt(1, 1)-&gt;addWidget(expandedContent_);

  layout_-&gt;elementAt(0, 0)-&gt;setContentAlignment(<a class="code" href="namespaceWt.html#ab8f772c69bc8180c31f9e4f4593b143fa58fe5182bd266132c59718c6d30945a9" title="Align top of widget with top of tallest sibling widget.">Wt::AlignTop</a>);
  layout_-&gt;elementAt(0, 1)-&gt;setContentAlignment(<a class="code" href="namespaceWt.html#ab8f772c69bc8180c31f9e4f4593b143fa2ed90f01dbf74049f428d33769d48e3f" title="Align vertically the middle to the middle of the parent widget.">Wt::AlignMiddle</a>);
</pre></div></p>
<p>Finally, we connect the click events of the expandIcon to the expand and collapse slots.</p>
<p><div class="fragment"><pre class="fragment">
  expandIcon_-&gt;icon1Clicked.connect(<span class="keyword">this</span>, &amp;TreeNode::expand);
  expandIcon_-&gt;icon2Clicked.connect(<span class="keyword">this</span>, &amp;TreeNode::collapse);
} <span class="comment">//</span>
</pre></div></p>
<p><a class="el" href="classWt_1_1WTable.html#ac4b59af9a44e185edb14e7f1f439d6c4" title="Accesses the table element at the given row and column.">WTable::elementAt(int row, int column)</a> is used repeatedly to add or modify contents of the table cells, expanding the table geometry as needed. Finally, we make connections from the expand and collapse icons to the slots we define in the TreeNode class.</p>
<p>Again, we optimize the visual effect of expand() and collaps() in client-side JavaScript, which is possible since they both have an effect independent of application state. Typically, one will start with a default dynamic slot implementation, and indicate stateless implementations where desired and possible, using one of the two mechanisms of stateless slot learning.</p>
<p>The "business logic" of the TreeNode is simply to manage its children. Whenever a child is added or removed, adjustments to its look are updated by calling childNodesChanged().</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">bool</span> TreeNode::isLastChildNode()<span class="keyword"> const</span>
<span class="keyword"></span>{
  <span class="keywordflow">if</span> (parentNode_) {
    <span class="keywordflow">return</span> parentNode_-&gt;childNodes_.back() == <span class="keyword">this</span>;
  } <span class="keywordflow">else</span>
    <span class="keywordflow">return</span> <span class="keyword">true</span>;
}

<span class="keywordtype">void</span> TreeNode::addChildNode(TreeNode *node)
{
  childNodes_.push_back(node);
  node-&gt;parentNode_ = <span class="keyword">this</span>;

  expandedContent_-&gt;addWidget(node);

  childNodesChanged();
}

<span class="keywordtype">void</span> TreeNode::removeChildNode(TreeNode *node)
{
  childNodes_.erase(std::find(childNodes_.begin(), childNodes_.end(), node));

  node-&gt;parentNode_ = 0;

  expandedContent_-&gt;removeWidget(node);

  childNodesChanged();
} <span class="comment">//</span>
</pre></div></p>
<p>The expand icon of the last child is rendered differently, as it needs to terminate the vertical guide line. To keep the implementation simple, we simply let every child reset its proper look by calling adjustExpandIcon().</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> TreeNode::childNodesChanged()
{
  <span class="keywordflow">for</span> (<span class="keywordtype">unsigned</span> i = 0; i &lt; childNodes_.size(); ++i)
    childNodes_[i]-&gt;adjustExpandIcon();
</pre></div></p>
<p>When getting a first child, or losing the last child, the expand icon changes too.</p>
<p><div class="fragment"><pre class="fragment">
  adjustExpandIcon();
</pre></div></p>
<p>We also update the childCount label.</p>
<p><div class="fragment"><pre class="fragment">
  <span class="keywordflow">if</span> (childNodes_.size())
    childCountLabel_
      -&gt;setText(<span class="stringliteral">&quot;(&quot;</span> + boost::lexical_cast&lt;std::string&gt;(childNodes_.size())
                + <span class="stringliteral">&quot;)&quot;</span>);
  <span class="keywordflow">else</span>
    childCountLabel_-&gt;setText(<span class="stringliteral">&quot;&quot;</span>);
</pre></div></p>
<p>Finally, we call <a class="el" href="classWt_1_1WObject.html#a92d8b00edc79b7a60d41d7c088f50436" title="Resets learned stateless slot implementations.">WObject::resetLearnedSlots()</a>. Because the expand() slot depends on the number of children, because it needs to collapse all children -- this slot is not entirely stateless, breaking the contract for a stateless slot. However, we can get away with still implementing as a stateless slot, by indicating when the state has changed.</p>
<p><div class="fragment"><pre class="fragment">
  resetLearnedSlots();
} <span class="comment">//</span>
</pre></div></p>
<p>The implementation of the collapse slot is as follows:</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> TreeNode::collapse()
{
</pre></div></p>
<p>First we record the current state, so the undo method can exactly undo what happened.</p>
<p><div class="fragment"><pre class="fragment">  wasCollapsed_ = expandedContent_-&gt;isHidden();
</pre></div></p>
<p>Next, we implement the actual collapse logic:</p>
<p><div class="fragment"><pre class="fragment">
  expandIcon_-&gt;setState(0);
  expandedContent_-&gt;hide();
  <span class="keywordflow">if</span> (labelIcon_)
    labelIcon_-&gt;setState(0);
} <span class="comment">//</span>
</pre></div></p>
<p>Similarly, the implementation of the expand slot. However, in this case we need to collapse all children as well.</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> TreeNode::expand()
{
  wasCollapsed_ = expandedContent_-&gt;isHidden();

  expandIcon_-&gt;setState(1);
  expandedContent_-&gt;show();
  <span class="keywordflow">if</span> (labelIcon_)
    labelIcon_-&gt;setState(1);

  <span class="comment">/*</span>
<span class="comment">   * collapse all children</span>
<span class="comment">   */</span>
  <span class="keywordflow">for</span> (<span class="keywordtype">unsigned</span> i = 0; i &lt; childNodes_.size(); ++i)
    childNodes_[i]-&gt;collapse();
} <span class="comment">//</span>
</pre></div></p>
<p>Since we implement these slots as prelearned stateless slots, we also need to define the undo functions. Note that Because expand() also collapses all child nodes, the undo function of expand() is not simply collapse() and vice-versa.</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> TreeNode::undoCollapse()
{
  <span class="keywordflow">if</span> (!wasCollapsed_) {
    <span class="comment">// re-expand</span>
    expandIcon_-&gt;setState(1);
    expandedContent_-&gt;show();
    <span class="keywordflow">if</span> (labelIcon_)
      labelIcon_-&gt;setState(1);    
  }
}

<span class="keywordtype">void</span> TreeNode::undoExpand()
{
  <span class="keywordflow">if</span> (wasCollapsed_) {
    <span class="comment">// re-collapse</span>
    expandIcon_-&gt;setState(0);
    expandedContent_-&gt;hide();
    <span class="keywordflow">if</span> (labelIcon_)
      labelIcon_-&gt;setState(0);
  }

  <span class="comment">/*</span>
<span class="comment">   * undo collapse of children</span>
<span class="comment">   */</span>
  <span class="keywordflow">for</span> (<span class="keywordtype">unsigned</span> i = 0; i &lt; childNodes_.size(); ++i)
    childNodes_[i]-&gt;undoCollapse();  
} <span class="comment">//</span>
</pre></div></p>
<p>Finally, the adjustExpandIcon() function sets the correct images, which depends on how the node relates to its siblings. The last node looks a bit different.</p>
<p><div class="fragment"><pre class="fragment">
<span class="keywordtype">void</span> TreeNode::adjustExpandIcon()
{
</pre></div></p>
<p>We set the expand icon images:</p>
<p><div class="fragment"><pre class="fragment">  ImageIndex index = isLastChildNode() ? Last : Middle;

  <span class="keywordflow">if</span> (expandIcon_-&gt;icon1()-&gt;imageLink().url() != imagePlus_[index])
    expandIcon_-&gt;icon1()-&gt;setImageLink(imagePlus_[index]);
  <span class="keywordflow">if</span> (expandIcon_-&gt;icon2()-&gt;imageLink().url() != imageMin_[index])
    expandIcon_-&gt;icon2()-&gt;setImageLink(imageMin_[index]);
  <span class="keywordflow">if</span> (noExpandIcon_-&gt;imageLink().url() != imageLine_[index])
    noExpandIcon_-&gt;setImageLink(imageLine_[index]);
</pre></div></p>
<p>Then, we set the vertical guide line if not the last child, and nothing if the last child:</p>
<p><div class="fragment"><pre class="fragment">
  <span class="keywordflow">if</span> (index == Last) {
    layout_-&gt;elementAt(0, 0)
      -&gt;decorationStyle().setBackgroundImage(<span class="stringliteral">&quot;&quot;</span>);
    layout_-&gt;elementAt(1, 0)
      -&gt;decorationStyle().setBackgroundImage(<span class="stringliteral">&quot;&quot;</span>);
  } <span class="keywordflow">else</span> {
    layout_-&gt;elementAt(0, 0)
      -&gt;decorationStyle().setBackgroundImage(<span class="stringliteral">&quot;icons/line-trunk.gif&quot;</span>,
                                             <a class="code" href="classWt_1_1WCssDecorationStyle.html#a6110934e7bf757aa4b2235137027b23aa0aeb307a9966be9867e8cfb44dadeff6" title="Repeat vertically.">Wt::WCssDecorationStyle::RepeatY</a>);
    layout_-&gt;elementAt(1, 0)
      -&gt;decorationStyle().setBackgroundImage(<span class="stringliteral">&quot;icons/line-trunk.gif&quot;</span>,
                                             <a class="code" href="classWt_1_1WCssDecorationStyle.html#a6110934e7bf757aa4b2235137027b23aa0aeb307a9966be9867e8cfb44dadeff6" title="Repeat vertically.">Wt::WCssDecorationStyle::RepeatY</a>);
  } <span class="comment">//</span>
</pre></div></p>
<p>Finally, we select the correct icon, depending on whether the node has children:</p>
<p><div class="fragment"><pre class="fragment">
  <span class="keywordflow">if</span> (childNodes_.empty()) {
    <span class="keywordflow">if</span> (noExpandIcon_-&gt;isHidden()) {
      noExpandIcon_-&gt;show();
      expandIcon_-&gt;hide();
    }
  } <span class="keywordflow">else</span> {
    <span class="keywordflow">if</span> (expandIcon_-&gt;isHidden()) {
      noExpandIcon_-&gt;hide();
      expandIcon_-&gt;show();
    }
  }
} <span class="comment">//</span>
</pre></div></p>
<p>And that's it. By using the TreeNode class in a hierarchy, we can create a tree widget. The tree widget will be implemented entirely in JavaScript, if available, and otherwise as plain HTML. In any case, client-side and server-side state are completely synchronized, and identical by definition since they are derived from the same C++ code. </p>
</div></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
<a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(0)"><span class="SelectionMark">&#160;</span>All</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(1)"><span class="SelectionMark">&#160;</span>Classes</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(2)"><span class="SelectionMark">&#160;</span>Namespaces</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(3)"><span class="SelectionMark">&#160;</span>Files</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(4)"><span class="SelectionMark">&#160;</span>Functions</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(5)"><span class="SelectionMark">&#160;</span>Variables</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(6)"><span class="SelectionMark">&#160;</span>Typedefs</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(7)"><span class="SelectionMark">&#160;</span>Enumerations</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(8)"><span class="SelectionMark">&#160;</span>Enumerator</a></div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<hr size="1"><address style="text-align: right; margin: 3px"><small>
Generated on Thu Nov 1 2012 for <a href="http://www.webtoolkit.eu/wt">the
C++ Web Toolkit (Wt)</a> by&nbsp;<a
href="http://www.doxygen.org/index.html"><img src="doxygen.png"
alt="doxygen" border="0" style="vertical-align: middle; display:
inline-block; height: 2em"></a> 1.7.5.1</small></address>
</body>
</html>