diff options
| author | Greg Ercolano <erco@seriss.com> | 2011-09-29 20:08:54 +0000 |
|---|---|---|
| committer | Greg Ercolano <erco@seriss.com> | 2011-09-29 20:08:54 +0000 |
| commit | 7d44e7f378fcd803c24747bf6e7ecebc2d796a19 (patch) | |
| tree | 7b944aa80c48f5fb2a70c7d95d1053c319f861ea /src/Fl_Tree.cxx | |
| parent | d9cda5c3aa48da9ef3238d993b34cde7047175d8 (diff) | |
Solve Fl_Tree issues w/ STR#2714 (shadow variable issue for apps)
by moving code implementations from Fl_Tree.H -> Fl_Tree.cxx.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9085 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Tree.cxx')
| -rw-r--r-- | src/Fl_Tree.cxx | 1493 |
1 files changed, 1142 insertions, 351 deletions
diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx index e95e183af..5548bba68 100644 --- a/src/Fl_Tree.cxx +++ b/src/Fl_Tree.cxx @@ -114,6 +114,299 @@ Fl_Tree::~Fl_Tree() { if ( _root ) { delete _root; _root = 0; } } +/// Standard FLTK event handler for this widget. +int Fl_Tree::handle(int e) { + int ret = 0; + // Developer note: Fl_Browser_::handle() used for reference here.. + // #include <FL/names.h> // for event debugging + // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e); + if (e == FL_ENTER || e == FL_LEAVE) return(1); + switch (e) { + case FL_FOCUS: { + // FLTK tests if we want focus. + // If a nav key was used to give us focus, and we've got no saved + // focus widget, determine which item gets focus depending on nav key. + // + if ( ! _item_focus ) { // no focus established yet? + switch (Fl::event_key()) { // determine if focus was navigated.. + case FL_Tab: { // received focus via TAB? + if ( Fl::event_state(FL_SHIFT) ) { // SHIFT-TAB similar to FL_Up + set_item_focus(next_visible_item(0, FL_Up)); + } else { // TAB similar to FL_Down + set_item_focus(next_visible_item(0, FL_Down)); + } + break; + } + case FL_Left: // received focus via LEFT or UP? + case FL_Up: { // XK_ISO_Left_Tab + set_item_focus(next_visible_item(0, FL_Up)); + break; + } + case FL_Right: // received focus via RIGHT or DOWN? + case FL_Down: + default: { + set_item_focus(next_visible_item(0, FL_Down)); + break; + } + } + } + if ( visible_focus() ) redraw(); // draw focus change + return(1); + } + case FL_UNFOCUS: { // FLTK telling us some other widget took focus. + if ( visible_focus() ) redraw(); // draw focus change + return(1); + } + case FL_KEYBOARD: { // keyboard shortcut + // Do shortcuts first or scrollbar will get them... + if (_prefs.selectmode() > FL_TREE_SELECT_NONE ) { + if ( !_item_focus ) { + set_item_focus(first()); + } + if ( _item_focus ) { + int ekey = Fl::event_key(); + switch (ekey) { + case FL_Enter: // ENTER: selects current item only + case FL_KP_Enter: + if ( when() & ~FL_WHEN_ENTER_KEY) { + select_only(_item_focus); + show_item(_item_focus); // STR #2426 + return(1); + } + break; + case ' ': // toggle selection state + switch ( _prefs.selectmode() ) { + case FL_TREE_SELECT_NONE: + break; + case FL_TREE_SELECT_SINGLE: + if ( ! _item_focus->is_selected() ) // not selected? + select_only(_item_focus); // select only this + else + deselect_all(); // select nothing + break; + case FL_TREE_SELECT_MULTI: + select_toggle(_item_focus); + break; + } + break; + case FL_Right: // open children (if any) + case FL_Left: { // close children (if any) + if ( _item_focus ) { + if ( ekey == FL_Right && _item_focus->is_close() ) { + // Open closed item + open(_item_focus); + redraw(); + ret = 1; + } else if ( ekey == FL_Left && _item_focus->is_open() ) { + // Close open item + close(_item_focus); + redraw(); + ret = 1; + } + return(1); + } + break; + } + case FL_Up: // next item up + case FL_Down: { // next item down + set_item_focus(next_visible_item(_item_focus, ekey)); // next item up|dn + if ( _item_focus ) { // item in focus? + // Autoscroll + int itemtop = _item_focus->y(); + int itembot = _item_focus->y()+_item_focus->h(); + if ( itemtop < y() ) { show_item_top(_item_focus); } + if ( itembot > y()+h() ) { show_item_bottom(_item_focus); } + // Extend selection + if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI && // multiselect on? + (Fl::event_state() & FL_SHIFT) && // shift key? + ! _item_focus->is_selected() ) { // not already selected? + select(_item_focus); // extend selection.. + } + return(1); + } + break; + } + } + } + } + break; + } + } + + // Let Fl_Group take a shot at handling the event + if (Fl_Group::handle(e)) { + return(1); // handled? don't continue below + } + + // Handle events the child FLTK widgets didn't need + + static Fl_Tree_Item *lastselect = 0; + // fprintf(stderr, "ERCODEBUG: Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING + if ( ! _root ) return(ret); + switch ( e ) { + case FL_PUSH: { // clicked on a tree item? + if (Fl::visible_focus() && handle(FL_FOCUS)) { + Fl::focus(this); + } + lastselect = 0; + Fl_Tree_Item *o = _root->find_clicked(_prefs); + if ( ! o ) break; + set_item_focus(o); // becomes new focus widget + redraw(); + ret |= 1; // handled + if ( Fl::event_button() == FL_LEFT_MOUSE ) { + if ( o->event_on_collapse_icon(_prefs) ) { // collapse icon clicked? + open_toggle(o); + } else if ( o->event_on_label(_prefs) && // label clicked? + (!o->widget() || !Fl::event_inside(o->widget())) && // not inside widget + (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) { // not on scroller + switch ( _prefs.selectmode() ) { + case FL_TREE_SELECT_NONE: + break; + case FL_TREE_SELECT_SINGLE: + select_only(o); + break; + case FL_TREE_SELECT_MULTI: { + if ( Fl::event_state() & FL_SHIFT ) { // SHIFT+PUSH? + select(o); // add to selection + } else if ( Fl::event_state() & FL_CTRL ) { // CTRL+PUSH? + select_toggle(o); // toggle selection state + lastselect = o; // save toggled item (prevent oscillation) + } else { + select_only(o); + } + break; + } + } + } + } + break; + } + case FL_DRAG: { + // do the scrolling first: + int my = Fl::event_y(); + if ( my < y() ) { // above top? + int p = vposition()-(y()-my); + if ( p < 0 ) p = 0; + vposition(p); + } else if ( my > (y()+h()) ) { // below bottom? + int p = vposition()+(my-y()-h()); + if ( p > (int)_vscroll->maximum() ) p = (int)_vscroll->maximum(); + vposition(p); + } + if ( Fl::event_button() != FL_LEFT_MOUSE ) break; + Fl_Tree_Item *o = _root->find_clicked(_prefs); + if ( ! o ) break; + set_item_focus(o); // becomes new focus widget + redraw(); + ret |= 1; + // Item's label clicked? + if ( o->event_on_label(_prefs) && + (!o->widget() || !Fl::event_inside(o->widget())) && + (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) { + // Handle selection behavior + switch ( _prefs.selectmode() ) { + case FL_TREE_SELECT_NONE: break; // no selection changes + case FL_TREE_SELECT_SINGLE: + select_only(o); + break; + case FL_TREE_SELECT_MULTI: + if ( Fl::event_state() & FL_CTRL && // CTRL-DRAG: toggle? + lastselect != o ) { // not already toggled from last microdrag? + select_toggle(o); // toggle selection + lastselect = o; // save we toggled it (prevents oscillation) + } else { + select(o); // select this + } + break; + } + } + break; + } + } + return(ret); +} + +/// Standard FLTK draw() method, handles draws the tree widget. +void Fl_Tree::draw() { + // Let group draw box+label but *NOT* children. + // We handle drawing children ourselves by calling each item's draw() + // + // Handle group's bg + Fl_Group::draw_box(); + Fl_Group::draw_label(); + // Handle tree + if ( ! _root ) return; + int cx = x() + Fl::box_dx(box()); + int cy = y() + Fl::box_dy(box()); + int cw = w() - Fl::box_dw(box()); + int ch = h() - Fl::box_dh(box()); + // These values are changed during drawing + // 'Y' will be the lowest point on the tree + int X = cx + _prefs.marginleft(); + int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0); + int W = cw - _prefs.marginleft(); // - _prefs.marginright(); + int Ysave = Y; + fl_push_clip(cx,cy,cw,ch); + { + fl_font(_prefs.labelfont(), _prefs.labelsize()); + _root->draw(X, Y, W, this, + (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus + _prefs); + } + fl_pop_clip(); + + // Show vertical scrollbar? + int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree + int ytoofar = (cy+ch) - Y; // ytoofar -- scrolled beyond bottom (e.g. stow) + + //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n", + //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value())); + + if ( ytoofar > 0 ) ydiff += ytoofar; + if ( Ysave<cy || ydiff > ch || int(_vscroll->value()) > 1 ) { + _vscroll->visible(); + + int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); + int sx = x()+w()-Fl::box_dx(box())-scrollsize; + int sy = y()+Fl::box_dy(box()); + int sw = scrollsize; + int sh = h()-Fl::box_dh(box()); + _vscroll->show(); + _vscroll->range(0.0,ydiff-ch); + _vscroll->resize(sx,sy,sw,sh); + _vscroll->slider_size(float(ch)/float(ydiff)); + } else { + _vscroll->Fl_Slider::value(0); + _vscroll->hide(); + } + fl_push_clip(cx,cy,cw,ch); + Fl_Group::draw_children(); // draws any FLTK children set via Fl_Tree::widget() + fl_pop_clip(); +} + +/// Print the tree as 'ascii art' to stdout. +/// Used mainly for debugging. +/// +void Fl_Tree::show_self() { + if ( ! _root ) return; + _root->show_self(); +} + +/// Set the label for the root item. +/// +/// Makes an internally managed copy of 'new_label'. +/// +void Fl_Tree::root_label(const char *new_label) { + if ( ! _root ) return; + _root->label(new_label); +} + +/// Returns the root item. +Fl_Tree_Item* Fl_Tree::root() { + return(_root); +} + /// Adds a new item, given a 'menu style' path, eg: "/Parent/Child/item". /// Any parent nodes that don't already exist are created automatically. /// Adds the item based on the value of sortorder(). @@ -140,6 +433,16 @@ Fl_Tree_Item* Fl_Tree::add(const char *path) { return(item); } +/// Add a new child to a tree-item. +/// +/// \param[in] item The existing item to add new child to. Must not be NULL. +/// \param[in] name The label for the new item +/// \returns the item that was added. +/// +Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) { + return(item->add(_prefs, name)); +} + /// Inserts a new item above the specified Fl_Tree_Item, with the label set to 'name'. /// \param[in] above -- the item above which to insert the new item. Must not be NULL. /// \param[in] name -- the name of the new item @@ -160,15 +463,39 @@ Fl_Tree_Item* Fl_Tree::insert(Fl_Tree_Item *item, const char *name, int pos) { return(item->insert(_prefs, name, pos)); } -/// Add a new child to a tree-item. -/// -/// \param[in] item The existing item to add new child to. Must not be NULL. -/// \param[in] name The label for the new item -/// \returns the item that was added. +/// Remove the specified \p item from the tree. +/// \p item may not be NULL. +/// If it has children, all those are removed too. +/// \returns 0 if done, -1 if 'item' not found. +/// +int Fl_Tree::remove(Fl_Tree_Item *item) { + if ( item == _root ) { + clear(); + } else { + Fl_Tree_Item *parent = item->parent(); // find item's parent + if ( ! parent ) return(-1); + parent->remove_child(item); // remove child + children + } + return(0); +} + +/// Clear all children from the tree. +/// The tree will be left completely empty. /// -Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) { - return(item->add(_prefs, name)); -} +void Fl_Tree::clear() { + if ( ! _root ) return; + _root->clear_children(); + delete _root; _root = 0; +} +/// Clear all the children of a particular node in the tree specified by \p item. +/// Item may not be NULL. +/// +void Fl_Tree::clear_children(Fl_Tree_Item *item) { + if ( item->has_children() ) { + item->clear_children(); + redraw(); // redraw only if there were children to clear + } +} /// Find the item, given a menu style path, eg: "/Parent/Child/item". /// There is both a const and non-const version of this method. @@ -257,96 +584,6 @@ int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item * return(0); } -/// Standard FLTK draw() method, handles draws the tree widget. -void Fl_Tree::draw() { - // Let group draw box+label but *NOT* children. - // We handle drawing children ourselves by calling each item's draw() - // - // Handle group's bg - Fl_Group::draw_box(); - Fl_Group::draw_label(); - // Handle tree - if ( ! _root ) return; - int cx = x() + Fl::box_dx(box()); - int cy = y() + Fl::box_dy(box()); - int cw = w() - Fl::box_dw(box()); - int ch = h() - Fl::box_dh(box()); - // These values are changed during drawing - // 'Y' will be the lowest point on the tree - int X = cx + _prefs.marginleft(); - int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0); - int W = cw - _prefs.marginleft(); // - _prefs.marginright(); - int Ysave = Y; - fl_push_clip(cx,cy,cw,ch); - { - fl_font(_prefs.labelfont(), _prefs.labelsize()); - _root->draw(X, Y, W, this, - (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus - _prefs); - } - fl_pop_clip(); - - // Show vertical scrollbar? - int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree - int ytoofar = (cy+ch) - Y; // ytoofar -- scrolled beyond bottom (e.g. stow) - - //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n", - //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value())); - - if ( ytoofar > 0 ) ydiff += ytoofar; - if ( Ysave<cy || ydiff > ch || int(_vscroll->value()) > 1 ) { - _vscroll->visible(); - - int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); - int sx = x()+w()-Fl::box_dx(box())-scrollsize; - int sy = y()+Fl::box_dy(box()); - int sw = scrollsize; - int sh = h()-Fl::box_dh(box()); - _vscroll->show(); - _vscroll->range(0.0,ydiff-ch); - _vscroll->resize(sx,sy,sw,sh); - _vscroll->slider_size(float(ch)/float(ydiff)); - } else { - _vscroll->Fl_Slider::value(0); - _vscroll->hide(); - } - fl_push_clip(cx,cy,cw,ch); - Fl_Group::draw_children(); // draws any FLTK children set via Fl_Tree::widget() - fl_pop_clip(); -} - -/// Returns next visible item above (dir==Fl_Up) or below (dir==Fl_Down) the specified \p item. -/// If \p item is 0, returns first() if \p dir is Fl_Up, or last() if \p dir is FL_Down. -/// -/// \param[in] item The item above/below which we'll find the next visible item -/// \param[in] dir The direction to search. Can be FL_Up or FL_Down. -/// \returns The item found, or 0 if there's no visible items above/below the specified \p item. -/// -Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) { - if ( ! item ) { // no start item? - item = ( dir == FL_Up ) ? last() : first(); // start at top or bottom - if ( ! item ) return(0); - if ( item->visible_r() ) return(item); // return first/last visible item - } - switch ( dir ) { - case FL_Up: return(item->prev_displayed(_prefs)); - case FL_Down: return(item->next_displayed(_prefs)); - default: return(item->next_displayed(_prefs)); - } -} - -/// Set the item that currently should have keyboard focus. -/// Handles calling redraw() to update the focus box (if it is visible). -/// -/// \param[in] item The item that should take focus. If NULL, none will have focus. -/// -void Fl_Tree::set_item_focus(Fl_Tree_Item *item) { - if ( _item_focus != item ) { // changed? - _item_focus = item; // update - if ( visible_focus() ) redraw(); // redraw to update focus box - } -} - /// Find the item that was clicked. /// You should use callback_item() instead, which is fast, /// and is meant to be used within a callback to determine the item clicked. @@ -374,6 +611,39 @@ void Fl_Tree::item_clicked(Fl_Tree_Item* val) { _callback_item = val; } +/// Return the item that was last clicked. +/// +/// Valid only from within the callback(). +/// +/// Deprecated: use callback_item() instead. +/// +/// \returns the item clicked, or 0 if none. +/// 0 may also be used to indicate several items were clicked/changed. +/// +Fl_Tree_Item* Fl_Tree::item_clicked() { + return(_callback_item); +} + +/// Returns next visible item above (dir==Fl_Up) or below (dir==Fl_Down) the specified \p item. +/// If \p item is 0, returns first() if \p dir is Fl_Up, or last() if \p dir is FL_Down. +/// +/// \param[in] item The item above/below which we'll find the next visible item +/// \param[in] dir The direction to search. Can be FL_Up or FL_Down. +/// \returns The item found, or 0 if there's no visible items above/below the specified \p item. +/// +Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) { + if ( ! item ) { // no start item? + item = ( dir == FL_Up ) ? last() : first(); // start at top or bottom + if ( ! item ) return(0); + if ( item->visible_r() ) return(item); // return first/last visible item + } + switch ( dir ) { + case FL_Up: return(item->prev_displayed(_prefs)); + case FL_Down: return(item->next_displayed(_prefs)); + default: return(item->next_displayed(_prefs)); + } +} + /// Returns the first item in the tree. /// /// Use this to walk the tree in the forward direction, eg: @@ -492,217 +762,336 @@ Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) { return(0); } -/// Standard FLTK event handler for this widget. -int Fl_Tree::handle(int e) { - int ret = 0; - // Developer note: Fl_Browser_::handle() used for reference here.. - // #include <FL/names.h> // for event debugging - // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e); - if (e == FL_ENTER || e == FL_LEAVE) return(1); - switch (e) { - case FL_FOCUS: { - // FLTK tests if we want focus. - // If a nav key was used to give us focus, and we've got no saved - // focus widget, determine which item gets focus depending on nav key. - // - if ( ! _item_focus ) { // no focus established yet? - switch (Fl::event_key()) { // determine if focus was navigated.. - case FL_Tab: { // received focus via TAB? - if ( Fl::event_state(FL_SHIFT) ) { // SHIFT-TAB similar to FL_Up - set_item_focus(next_visible_item(0, FL_Up)); - } else { // TAB similar to FL_Down - set_item_focus(next_visible_item(0, FL_Down)); - } - break; - } - case FL_Left: // received focus via LEFT or UP? - case FL_Up: { // XK_ISO_Left_Tab - set_item_focus(next_visible_item(0, FL_Up)); - break; - } - case FL_Right: // received focus via RIGHT or DOWN? - case FL_Down: - default: { - set_item_focus(next_visible_item(0, FL_Down)); - break; - } - } - } - if ( visible_focus() ) redraw(); // draw focus change - return(1); - } - case FL_UNFOCUS: { // FLTK telling us some other widget took focus. - if ( visible_focus() ) redraw(); // draw focus change - return(1); - } - case FL_KEYBOARD: { // keyboard shortcut - // Do shortcuts first or scrollbar will get them... - if (_prefs.selectmode() > FL_TREE_SELECT_NONE ) { - if ( !_item_focus ) { - set_item_focus(first()); - } - if ( _item_focus ) { - int ekey = Fl::event_key(); - switch (ekey) { - case FL_Enter: // ENTER: selects current item only - case FL_KP_Enter: - if ( when() & ~FL_WHEN_ENTER_KEY) { - select_only(_item_focus); - show_item(_item_focus); // STR #2426 - return(1); - } - break; - case ' ': // toggle selection state - switch ( _prefs.selectmode() ) { - case FL_TREE_SELECT_NONE: - break; - case FL_TREE_SELECT_SINGLE: - if ( ! _item_focus->is_selected() ) // not selected? - select_only(_item_focus); // select only this - else - deselect_all(); // select nothing - break; - case FL_TREE_SELECT_MULTI: - select_toggle(_item_focus); - break; - } - break; - case FL_Right: // open children (if any) - case FL_Left: { // close children (if any) - if ( _item_focus ) { - if ( ekey == FL_Right && _item_focus->is_close() ) { - // Open closed item - open(_item_focus); - redraw(); - ret = 1; - } else if ( ekey == FL_Left && _item_focus->is_open() ) { - // Close open item - close(_item_focus); - redraw(); - ret = 1; - } - return(1); - } - break; - } - case FL_Up: // next item up - case FL_Down: { // next item down - set_item_focus(next_visible_item(_item_focus, ekey)); // next item up|dn - if ( _item_focus ) { // item in focus? - // Autoscroll - int itemtop = _item_focus->y(); - int itembot = _item_focus->y()+_item_focus->h(); - if ( itemtop < y() ) { show_item_top(_item_focus); } - if ( itembot > y()+h() ) { show_item_bottom(_item_focus); } - // Extend selection - if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI && // multiselect on? - (Fl::event_state() & FL_SHIFT) && // shift key? - ! _item_focus->is_selected() ) { // not already selected? - select(_item_focus); // extend selection.. - } - return(1); - } - break; - } - } - } - } - break; - } +/// Open the specified 'item'. +/// This causes the item's children (if any) to be shown. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be opened. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked if item changed, +/// callback_reason() will be FL_TREE_REASON_OPENED +/// \returns +/// - 1 -- item was opened +/// - 0 -- item was already open, no change +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::open(Fl_Tree_Item *item, int docallback) { + if ( item->is_open() ) return(0); + item->open(); + redraw(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_OPENED); } + return(1); +} - // Let Fl_Group take a shot at handling the event - if (Fl_Group::handle(e)) { - return(1); // handled? don't continue below +/// Opens the item specified by \p path (eg: "Parent/child/item"). +/// This causes the item's children (if any) to be shown. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. open("Holidays/12\\/25\//2010"). +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked if item changed, +/// callback_reason() will be FL_TREE_REASON_OPENED +/// \returns +/// - 1 -- OK: item opened +/// - 0 -- OK: item was already open, no change +/// - -1 -- ERROR: item was not found +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::open(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(open(item, docallback)); +} + +/// Toggle the open state of \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item whose open state is to be toggled. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked, callback_reason() will be either +/// FL_TREE_REASON_OPENED or FL_TREE_REASON_CLOSED +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +void Fl_Tree::open_toggle(Fl_Tree_Item *item, int docallback) { + if ( item->is_open() ) { + close(item, docallback); + } else { + open(item, docallback); } +} - // Handle events the child FLTK widgets didn't need +/// Closes the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be closed. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked if item changed, +/// callback_reason() will be FL_TREE_REASON_CLOSED +/// \returns +/// - 1 -- item was closed +/// - 0 -- item was already closed, no change +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::close(Fl_Tree_Item *item, int docallback) { + if ( item->is_close() ) return(0); + item->close(); + redraw(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_CLOSED); + } + return(1); +} - static Fl_Tree_Item *lastselect = 0; - // fprintf(stderr, "ERCODEBUG: Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING - if ( ! _root ) return(ret); - switch ( e ) { - case FL_PUSH: { // clicked on a tree item? - if (Fl::visible_focus() && handle(FL_FOCUS)) { - Fl::focus(this); - } - lastselect = 0; - Fl_Tree_Item *o = _root->find_clicked(_prefs); - if ( ! o ) break; - set_item_focus(o); // becomes new focus widget - redraw(); - ret |= 1; // handled - if ( Fl::event_button() == FL_LEFT_MOUSE ) { - if ( o->event_on_collapse_icon(_prefs) ) { // collapse icon clicked? - open_toggle(o); - } else if ( o->event_on_label(_prefs) && // label clicked? - (!o->widget() || !Fl::event_inside(o->widget())) && // not inside widget - (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) { // not on scroller - switch ( _prefs.selectmode() ) { - case FL_TREE_SELECT_NONE: - break; - case FL_TREE_SELECT_SINGLE: - select_only(o); - break; - case FL_TREE_SELECT_MULTI: { - if ( Fl::event_state() & FL_SHIFT ) { // SHIFT+PUSH? - select(o); // add to selection - } else if ( Fl::event_state() & FL_CTRL ) { // CTRL+PUSH? - select_toggle(o); // toggle selection state - lastselect = o; // save toggled item (prevent oscillation) - } else { - select_only(o); - } - break; - } - } - } - } - break; +/// Closes the item specified by \p path, eg: "Parent/child/item". +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. close("Holidays/12\\/25\//2010"). +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked if item changed, +/// callback_reason() will be FL_TREE_REASON_CLOSED +/// \returns +/// - 1 -- OK: item closed +/// - 0 -- OK: item was already closed, no change +/// - -1 -- ERROR: item was not found +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::close(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(close(item, docallback)); +} + +/// See if \p item is open. +/// +/// Items that are 'open' are themselves not necessarily visible; +/// one of the item's parents might be closed. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// \returns +/// - 1 : item is open +/// - 0 : item is closed +/// +int Fl_Tree::is_open(Fl_Tree_Item *item) const { + return(item->is_open()?1:0); +} + +/// See if item specified by \p path (eg: "Parent/child/item") is open. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. is_open("Holidays/12\\/25\//2010"). +/// +/// Items that are 'open' are themselves not necessarily visible; +/// one of the item's parents might be closed. +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \returns +/// - 1 - OK: item is open +/// - 0 - OK: item is closed +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::is_open(const char *path) const { + const Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(item->is_open()?1:0); +} + +/// See if the specified \p item is closed. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// \returns +/// - 1 : item is open +/// - 0 : item is closed +/// +int Fl_Tree::is_close(Fl_Tree_Item *item) const { + return(item->is_close()); +} + +/// See if item specified by \p path (eg: "Parent/child/item") is closed. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. is_close("Holidays/12\\/25\//2010"). +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \returns +/// - 1 - OK: item is closed +/// - 0 - OK: item is open +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::is_close(const char *path) const { + const Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(item->is_close()?1:0); +} + +/// Select the specified \p item. Use 'deselect()' to de-select it. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked if item changed state, +/// callback_reason() will be FL_TREE_REASON_SELECTED +/// \returns +/// - 1 - item's state was changed +/// - 0 - item was already selected, no change was made +/// +int Fl_Tree::select(Fl_Tree_Item *item, int docallback) { + if ( ! item->is_selected() ) { + item->select(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_SELECTED); } - case FL_DRAG: { - // do the scrolling first: - int my = Fl::event_y(); - if ( my < y() ) { // above top? - int p = vposition()-(y()-my); - if ( p < 0 ) p = 0; - vposition(p); - } else if ( my > (y()+h()) ) { // below bottom? - int p = vposition()+(my-y()-h()); - if ( p > (int)_vscroll->maximum() ) p = (int)_vscroll->maximum(); - vposition(p); - } - if ( Fl::event_button() != FL_LEFT_MOUSE ) break; - Fl_Tree_Item *o = _root->find_clicked(_prefs); - if ( ! o ) break; - set_item_focus(o); // becomes new focus widget - redraw(); - ret |= 1; - // Item's label clicked? - if ( o->event_on_label(_prefs) && - (!o->widget() || !Fl::event_inside(o->widget())) && - (!_vscroll->visible() || !Fl::event_inside(_vscroll)) ) { - // Handle selection behavior - switch ( _prefs.selectmode() ) { - case FL_TREE_SELECT_NONE: break; // no selection changes - case FL_TREE_SELECT_SINGLE: - select_only(o); - break; - case FL_TREE_SELECT_MULTI: - if ( Fl::event_state() & FL_CTRL && // CTRL-DRAG: toggle? - lastselect != o ) { // not already toggled from last microdrag? - select_toggle(o); // toggle selection - lastselect = o; // save we toggled it (prevents oscillation) - } else { - select(o); // select this - } - break; - } - } - break; + redraw(); + return(1); + } + return(0); +} + +/// Select the item specified by \p path (eg: "Parent/child/item"). +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. select("Holidays/12\\/25\//2010"). +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked if item changed state, +/// callback_reason() will be FL_TREE_REASON_SELECTED +/// \returns +/// - 1 : OK: item's state was changed +/// - 0 : OK: item was already selected, no change was made +/// - -1 : ERROR: item was not found +/// +int Fl_Tree::select(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(select(item, docallback)); +} + +/// Toggle the select state of the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked, callback_reason() will be +/// either FL_TREE_REASON_SELECTED or FL_TREE_REASON_DESELECTED +/// +void Fl_Tree::select_toggle(Fl_Tree_Item *item, int docallback) { + item->select_toggle(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, item->is_selected() ? FL_TREE_REASON_SELECTED + : FL_TREE_REASON_DESELECTED); + } + redraw(); +} + +/// De-select the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked if item changed state, +/// callback_reason() will be FL_TREE_REASON_DESELECTED +/// \returns +/// - 0 - item was already deselected, no change was made +/// - 1 - item's state was changed +/// +int Fl_Tree::deselect(Fl_Tree_Item *item, int docallback) { + if ( item->is_selected() ) { + item->deselect(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_DESELECTED); } + redraw(); + return(1); } - return(ret); + return(0); +} + +/// Deselect an item specified by \p path (eg: "Parent/child/item"). +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. deselect("Holidays/12\\/25\//2010"). +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked if item changed state, +/// callback_reason() will be FL_TREE_REASON_DESELECTED +/// \returns +/// - 1 - OK: item's state was changed +/// - 0 - OK: item was already deselected, no change was made +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::deselect(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(deselect(item, docallback)); } /// Deselect \p item and all its children. @@ -737,6 +1126,41 @@ int Fl_Tree::deselect_all(Fl_Tree_Item *item, int docallback) { return(count); } +/// Select only the specified \p item, deselecting all others that might be selected. +/// If item is 0, first() is used. +/// Handles calling redraw() if anything was changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] selitem The item to be selected. If NULL, first() is used. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - the callback() is not invoked +/// - 1 - the callback() is invoked for each item that changed state, +/// callback_reason() will be either FL_TREE_REASON_SELECTED or +/// FL_TREE_REASON_DESELECTED +/// \returns the number of items whose selection states were changed, if any. +/// +int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) { + selitem = selitem ? selitem : first(); // NULL? use first() + if ( ! selitem ) return(0); + int changed = 0; + for ( Fl_Tree_Item *item = first(); item; item = item->next() ) { + if ( item == selitem ) { + if ( item->is_selected() ) continue; // don't count if already selected + select(item, docallback); + ++changed; + } else { + if ( item->is_selected() ) { + deselect(item, docallback); + ++changed; + } + } + } + return(changed); +} + /// Select \p item and all its children. /// If item is NULL, first() is used. /// Handles calling redraw() if anything was changed. @@ -768,39 +1192,319 @@ int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) { return(count); } -/// Select only the specified \p item, deselecting all others that might be selected. -/// If item is 0, first() is used. -/// Handles calling redraw() if anything was changed. -/// Invokes the callback depending on the value of optional parameter \p docallback. -/// -/// The callback can use callback_item() and callback_reason() respectively to determine -/// the item changed and the reason the callback was called. +/// Set the item that currently should have keyboard focus. +/// Handles calling redraw() to update the focus box (if it is visible). /// -/// \param[in] selitem The item to be selected. If NULL, first() is used. -/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: -/// - 0 - the callback() is not invoked -/// - 1 - the callback() is invoked for each item that changed state, -/// callback_reason() will be either FL_TREE_REASON_SELECTED or -/// FL_TREE_REASON_DESELECTED -/// \returns the number of items whose selection states were changed, if any. +/// \param[in] item The item that should take focus. If NULL, none will have focus. /// -int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) { - selitem = selitem ? selitem : first(); // NULL? use first() - if ( ! selitem ) return(0); - int changed = 0; - for ( Fl_Tree_Item *item = first(); item; item = item->next() ) { - if ( item == selitem ) { - if ( item->is_selected() ) continue; // don't count if already selected - select(item, docallback); - ++changed; - } else { - if ( item->is_selected() ) { - deselect(item, docallback); - ++changed; - } - } +void Fl_Tree::set_item_focus(Fl_Tree_Item *item) { + if ( _item_focus != item ) { // changed? + _item_focus = item; // update + if ( visible_focus() ) redraw(); // redraw to update focus box } - return(changed); +} + +/// See if the specified \p item is selected. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// +/// \return +/// - 1 : item selected +/// - 0 : item deselected +/// +int Fl_Tree::is_selected(Fl_Tree_Item *item) const { + return(item->is_selected()?1:0); +} + +/// See if item specified by \p path (eg: "Parent/child/item") is selected. +/// +/// Items or submenus that themselves contain slashes ('/' or '\') +/// should be escaped, e.g. is_selected("Holidays/12\\/25\//2010"). +/// +/// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") +/// \returns +/// - 1 : item selected +/// - 0 : item deselected +/// - -1 : item was not found +/// +int Fl_Tree::is_selected(const char *path) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(is_selected(item)); +} + +/// Get the default label fontsize used for creating new items. +Fl_Fontsize Fl_Tree::item_labelsize() const { + return(_prefs.labelsize()); +} + +/// Set the default label font size used for creating new items. +/// To change the font size on a per-item basis, use Fl_Tree_Item::labelsize(Fl_Fontsize) +/// +void Fl_Tree::item_labelsize(Fl_Fontsize val) { + _prefs.labelsize(val); +} + +/// Get the default font face used for creating new items. +Fl_Font Fl_Tree::item_labelfont() const { + return(_prefs.labelfont()); +} + +/// Set the default font face used for creating new items. +/// To change the font face on a per-item basis, use Fl_Tree_Item::labelfont(Fl_Font) +/// +void Fl_Tree::item_labelfont(Fl_Font val) { + _prefs.labelfont(val); +} + +/// Get the default label foreground color used for creating new items. +Fl_Color Fl_Tree::item_labelfgcolor(void) const { + return(_prefs.labelfgcolor()); +} + +/// Set the default label foreground color used for creating new items. +/// To change the foreground color on a per-item basis, use Fl_Tree_Item::labelfgcolor(Fl_Color) +/// +void Fl_Tree::item_labelfgcolor(Fl_Color val) { + _prefs.labelfgcolor(val); +} + +/// Get the default label background color used for creating new items. +Fl_Color Fl_Tree::item_labelbgcolor(void) const { + return(_prefs.labelbgcolor()); +} + +/// Set the default label background color used for creating new items. +/// To change the background color on a per-item basis, use Fl_Tree_Item::labelbgcolor(Fl_Color) +/// +void Fl_Tree::item_labelbgcolor(Fl_Color val) { + _prefs.labelbgcolor(val); +} + +/// Get the connector color used for tree connection lines. +Fl_Color Fl_Tree::connectorcolor() const { + return(_prefs.connectorcolor()); +} + +/// Set the connector color used for tree connection lines. +void Fl_Tree::connectorcolor(Fl_Color val) { + _prefs.connectorcolor(val); +} + +/// Get the amount of white space (in pixels) that should appear +/// between the widget's left border and the tree's contents. +/// +int Fl_Tree::marginleft() const { + return(_prefs.marginleft()); +} + +/// Set the amount of white space (in pixels) that should appear +/// between the widget's left border and the left side of the tree's contents. +/// +void Fl_Tree::marginleft(int val) { + _prefs.marginleft(val); + redraw(); +} + +/// Get the amount of white space (in pixels) that should appear +/// between the widget's top border and the top of the tree's contents. +/// +int Fl_Tree::margintop() const { + return(_prefs.margintop()); +} + +/// Sets the amount of white space (in pixels) that should appear +/// between the widget's top border and the top of the tree's contents. +/// +void Fl_Tree::margintop(int val) { + _prefs.margintop(val); + redraw(); +} + +/// Get the amount of white space (in pixels) that should appear +/// below an open child tree's contents. +/// +int Fl_Tree::openchild_marginbottom() const { + return(_prefs.openchild_marginbottom()); +} + +/// Set the amount of white space (in pixels) that should appear +/// below an open child tree's contents. +/// +void Fl_Tree::openchild_marginbottom(int val) { + _prefs.openchild_marginbottom(val); + redraw(); +} + +/// Gets the width of the horizontal connection lines (in pixels) +/// that appear to the left of each tree item's label. +/// +int Fl_Tree::connectorwidth() const { + return(_prefs.connectorwidth()); +} + +/// Sets the width of the horizontal connection lines (in pixels) +/// that appear to the left of each tree item's label. +/// +void Fl_Tree::connectorwidth(int val) { + _prefs.connectorwidth(val); + redraw(); +} + +/// Returns the Fl_Image being used as the default user icon for all +/// newly created items. +/// Returns zero if no icon has been set, which is the default. +/// +Fl_Image* Fl_Tree::usericon() const { + return(_prefs.usericon()); +} + +/// Sets the Fl_Image to be used as the default user icon for all +/// newly created items. +/// +/// If you want to specify user icons on a per-item basis, +/// use Fl_Tree_Item::usericon() instead. +/// +/// \param[in] val -- The new image to be used, or +/// zero to disable user icons. +/// +void Fl_Tree::usericon(Fl_Image *val) { + _prefs.usericon(val); + redraw(); +} + +/// Returns the icon to be used as the 'open' icon. +/// If none was set, the internal default is returned, +/// a simple '[+]' icon. +/// +Fl_Image* Fl_Tree::openicon() const { + return(_prefs.openicon()); +} + +/// Sets the icon to be used as the 'open' icon. +/// This overrides the built in default '[+]' icon. +/// +/// \param[in] val -- The new image, or zero to use the default [+] icon. +/// +void Fl_Tree::openicon(Fl_Image *val) { + _prefs.openicon(val); + redraw(); +} + +/// Returns the icon to be used as the 'close' icon. +/// If none was set, the internal default is returned, +/// a simple '[-]' icon. +/// +Fl_Image* Fl_Tree::closeicon() const { + return(_prefs.closeicon()); +} + +/// Sets the icon to be used as the 'close' icon. +/// This overrides the built in default '[-]' icon. +/// +/// \param[in] val -- The new image, or zero to use the default [-] icon. +/// +void Fl_Tree::closeicon(Fl_Image *val) { + _prefs.closeicon(val); + redraw(); +} + +/// Returns 1 if the collapse icon is enabled, 0 if not. +int Fl_Tree::showcollapse() const { + return(_prefs.showcollapse()); +} + +/// Set if we should show the collapse icon or not. +/// If collapse icons are disabled, the user will not be able +/// to interactively collapse items in the tree, unless the application +/// provides some other means via open() and close(). +/// +/// \param[in] val 1: shows collapse icons (default),\n +/// 0: hides collapse icons. +/// +void Fl_Tree::showcollapse(int val) { + _prefs.showcollapse(val); + redraw(); +} + +/// Returns 1 if the root item is to be shown, or 0 if not. +int Fl_Tree::showroot() const { + return(_prefs.showroot()); +} + +/// Set if the root item should be shown or not. +/// \param[in] val 1 -- show the root item (default)\n +/// 0 -- hide the root item. +/// +void Fl_Tree::showroot(int val) { + _prefs.showroot(val); + redraw(); +} + +/// Returns the line drawing style for inter-connecting items. +Fl_Tree_Connector Fl_Tree::connectorstyle() const { + return(_prefs.connectorstyle()); +} + +/// Sets the line drawing style for inter-connecting items. +void Fl_Tree::connectorstyle(Fl_Tree_Connector val) { + _prefs.connectorstyle(val); + redraw(); +} + +/// Set the default sort order used when items are added to the tree. +/// See Fl_Tree_Sort for possible values. +/// +Fl_Tree_Sort Fl_Tree::sortorder() const { + return(_prefs.sortorder()); +} + +/// Gets the sort order used to add items to the tree. +void Fl_Tree::sortorder(Fl_Tree_Sort val) { + _prefs.sortorder(val); + // no redraw().. only affects new add()itions +} + +/// Sets the style of box used to draw selected items. +/// This is an fltk Fl_Boxtype. +/// The default is influenced by FLTK's current Fl::scheme() +/// +Fl_Boxtype Fl_Tree::selectbox() const { + return(_prefs.selectbox()); +} + +/// Gets the style of box used to draw selected items. +/// This is an fltk Fl_Boxtype. +/// The default is influenced by FLTK's current Fl::scheme() +/// +void Fl_Tree::selectbox(Fl_Boxtype val) { + _prefs.selectbox(val); + redraw(); +} + +/// Gets the tree's current selection mode. +Fl_Tree_Select Fl_Tree::selectmode() const { + return(_prefs.selectmode()); +} + +/// Sets the tree's selection mode. +void Fl_Tree::selectmode(Fl_Tree_Select val) { + _prefs.selectmode(val); +} + +/// See if \p item is currently displayed on-screen (visible within the widget). +/// This can be used to detect if the item is scrolled off-screen. +/// Checks to see if the item's vertical position is within the top and bottom +/// edges of the display window. This does NOT take into account the hide()/show() +/// or open()/close() status of the item. +/// +/// \param[in] item The item to be checked. If NULL, first() is used. +/// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree. +/// +int Fl_Tree::displayed(Fl_Tree_Item *item) { + item = item ? item : first(); + if (!item) return(0); + return( (item->y() >= y()) && (item->y() <= (y()+h()-item->h())) ? 1 : 0); } /// Adjust the vertical scroll bar so that \p item is visible @@ -827,21 +1531,6 @@ void Fl_Tree::show_item(Fl_Tree_Item *item, int yoff) { redraw(); } -/// See if \p item is currently displayed on-screen (visible within the widget). -/// This can be used to detect if the item is scrolled off-screen. -/// Checks to see if the item's vertical position is within the top and bottom -/// edges of the display window. This does NOT take into account the hide()/show() -/// or open()/close() status of the item. -/// -/// \param[in] item The item to be checked. If NULL, first() is used. -/// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree. -/// -int Fl_Tree::displayed(Fl_Tree_Item *item) { - item = item ? item : first(); - if (!item) return(0); - return( (item->y() >= y()) && (item->y() <= (y()+h()-item->h())) ? 1 : 0); -} - /// Adjust the vertical scroll bar to show \p item at the top /// of the display IF it is currently off-screen (e.g. show_item_top()). /// If it is already on-screen, no change is made. @@ -884,6 +1573,14 @@ void Fl_Tree::show_item_bottom(Fl_Tree_Item *item) { if (item) show_item(item, h()-item->h()); } +/// Displays \p item, scrolling the tree as necessary. +/// \param[in] item The item to be displayed. If NULL, first() is used. +/// +void Fl_Tree::display(Fl_Tree_Item *item) { + item = item ? item : first(); + if (item) show_item_middle(item); +} + /// Returns the vertical scroll position as a pixel offset. /// The position returned is how many pixels of the tree are scrolled off the top edge /// of the screen. Example: A position of '3' indicates the top 3 pixels of @@ -908,12 +1605,106 @@ void Fl_Tree::vposition(int pos) { redraw(); } -/// Displays \p item, scrolling the tree as necessary. -/// \param[in] item The item to be displayed. If NULL, first() is used. +/// See if widget \p w is one of the Fl_Tree widget's scrollbars. +/// Use this to skip over the scrollbars when walking the child() array. Example: +/// \code +/// for ( int i=0; i<tree->children(); i++ ) { // walk children +/// Fl_Widget *w= tree->child(i); +/// if ( brow->is_scrollbar(w) ) continue; // skip scrollbars +/// ..do work here.. +/// } +/// \endcode +/// \param[in] w Widget to test +/// \returns 1 if \p w is a scrollbar, 0 if not. /// -void Fl_Tree::display(Fl_Tree_Item *item) { - item = item ? item : first(); - if (item) show_item_middle(item); +int Fl_Tree::is_scrollbar(Fl_Widget *w) { + return( ( w == _vscroll ) ? 1 : 0 ); +} + +/// Gets the current size of the scrollbars' troughs, in pixels. +/// +/// If this value is zero (default), this widget will use the global +/// Fl::scrollbar_size() value as the scrollbar's width. +/// +/// \returns Scrollbar size in pixels, or 0 if the global Fl::scrollsize() is being used. +/// \see Fl::scrollbar_size(int) +/// +int Fl_Tree::scrollbar_size() const { + return(_scrollbar_size); +} + +/// Sets the pixel size of the scrollbars' troughs to the \p size, in pixels. +/// +/// Normally you should not need this method, and should use the global +/// Fl::scrollbar_size(int) instead to manage the size of ALL +/// your widgets' scrollbars. This ensures your application +/// has a consistent UI, is the default behavior, and is normally +/// what you want. +/// +/// Only use THIS method if you really need to override the global +/// scrollbar size. The need for this should be rare. +/// +/// Setting \p size to the special value of 0 causes the widget to +/// track the global Fl::scrollbar_size(), which is the default. +/// +/// \param[in] size Sets the scrollbar size in pixels.\n +/// If 0 (default), scrollbar size tracks the global Fl::scrollbar_size() +/// \see Fl::scrollbar_size() +/// +void Fl_Tree::scrollbar_size(int size) { + _scrollbar_size = size; + int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); + if ( _vscroll->w() != scrollsize ) { + _vscroll->resize(x()+w()-scrollsize, h(), scrollsize, _vscroll->h()); + } +} + +/// Do the callback for the item, setting the item and reason +void Fl_Tree::do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason) { + callback_reason(reason); + callback_item(item); + do_callback((Fl_Widget*)this, user_data()); +} + +/// Sets the item that was changed for this callback. +/// Used internally to pass the item that invoked the callback. +/// +void Fl_Tree::callback_item(Fl_Tree_Item* item) { + _callback_item = item; +} + +/// Gets the item that caused the callback. +/// The callback() can use this value to see which item changed. +/// +Fl_Tree_Item* Fl_Tree::callback_item() { + return(_callback_item); +} + +/// Sets the reason for this callback. +/// Used internally to pass the reason the callback was invoked. +/// +void Fl_Tree::callback_reason(Fl_Tree_Reason reason) { + _callback_reason = reason; +} + +/// Gets the reason for this callback. +/// +/// The callback() can use this value to see why it was called. Example: +/// \code +/// void MyTreeCallback(Fl_Widget *w, void *userdata) { +/// Fl_Tree *tree = (Fl_Tree*)w; +/// Fl_Tree_Item *item = tree->callback_item(); // the item changed (can be NULL if more than one item was changed!) +/// switch ( tree->callback_reason() ) { // reason callback was invoked +/// case FL_TREE_REASON_OPENED: ..item was opened.. +/// case FL_TREE_REASON_CLOSED: ..item was closed.. +/// case FL_TREE_REASON_SELECTED: ..item was selected.. +/// case FL_TREE_REASON_DESELECTED: ..item was deselected.. +/// } +/// } +/// \endcode +/// +Fl_Tree_Reason Fl_Tree::callback_reason() const { + return(_callback_reason); } /** |
