From abdc83470530c5fa8e15370a2032093cd91dace7 Mon Sep 17 00:00:00 2001 From: Greg Ercolano Date: Mon, 20 Jan 2014 21:23:24 +0000 Subject: o Added draw_item_content() to Fl_Tree_Item, a volatile method that can be overridden by subclasses to take drawing control of tree item's content. This replaces the old "item_draw_callback()" technique added a few months ago as an ABI feature; turned out the new technique is a better way to go. o The examples/tree-custom-draw-items.cxx demo adjusted accordingly. o Added missing docs for some methods that had none, including label_[xywh](). o Added related methods needed to implement this, including: Fl_Tree_Item_Array::replace() Fl_Tree_Item::replace() Fl_Tree::root(item) Fl_Tree::add() variations Fl_Tree_Item::drawbgcolor()/drawfgcolor() o Carefully worked the FLTK_ABI_VERSION macros so as to be ABI compatible with 1.3.0. o Verified 1.3.0 ABI compatibility with ABI Compliance Checker 1.99.8.5: http://ispras.linuxbase.org/index.php/ABI_compliance_checker git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10071 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- FL/Fl_Tree.H | 35 ++- FL/Fl_Tree_Item.H | 98 +++++-- FL/Fl_Tree_Item_Array.H | 1 + examples/tree-custom-draw-items.cxx | 163 ++++++++--- src/Fl_Tree.cxx | 189 +++++++------ src/Fl_Tree_Item.cxx | 551 ++++++++++++++++++++++++++---------- src/Fl_Tree_Item_Array.cxx | 23 ++ 7 files changed, 747 insertions(+), 313 deletions(-) diff --git a/FL/Fl_Tree.H b/FL/Fl_Tree.H index ab637b947..c4a516dba 100644 --- a/FL/Fl_Tree.H +++ b/FL/Fl_Tree.H @@ -372,11 +372,18 @@ public: /////////////////////// void root_label(const char *new_label); Fl_Tree_Item* root(); + void root(Fl_Tree_Item *newitem); + const Fl_Tree_Prefs& prefs() const { return _prefs; } //////////////////////////////// // Item creation/removal methods //////////////////////////////// +#if FLTK_ABI_VERSION >= 10303 + Fl_Tree_Item *add(const char *path, Fl_Tree_Item *newitem=0); +#else Fl_Tree_Item *add(const char *path); + Fl_Tree_Item *add(const char *path, Fl_Tree_Item *newitem); +#endif Fl_Tree_Item* add(Fl_Tree_Item *parent_item, const char *name); Fl_Tree_Item *insert_above(Fl_Tree_Item *above, const char *name); Fl_Tree_Item* insert(Fl_Tree_Item *item, const char *name, int pos); @@ -407,7 +414,7 @@ public: Fl_Tree_Item *last_visible(); // deprecated in ABI 10303 Fl_Tree_Item *last_visible_item(); #if FLTK_ABI_VERSION >= 10303 - Fl_Tree_Item *next_visible_item(Fl_Tree_Item *start, int dir); + Fl_Tree_Item *next_visible_item(Fl_Tree_Item *start, int dir); // made public in 1.3.3 ABI #endif Fl_Tree_Item *first_selected_item(); Fl_Tree_Item *last_selected_item(); @@ -444,16 +451,24 @@ public: int deselect_all(Fl_Tree_Item *item=0, int docallback=1); int select_only(Fl_Tree_Item *selitem, int docallback=1); int select_all(Fl_Tree_Item *item=0, int docallback=1); + int extend_selection_dir(Fl_Tree_Item *from, + Fl_Tree_Item *to, + int dir, + int val, + bool visible); #if FLTK_ABI_VERSION >= 10303 - void extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to); - int extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int dir, int val, bool visible); - int extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible); + int extend_selection(Fl_Tree_Item *from, + Fl_Tree_Item *to, + int val=1, + bool visible=false); #else - // Adding overload if not at least one overload breaks ABI, so avoid - // See: http://www.ros.org/reps/rep-0009.html private: - int extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, int dir, int val, bool visible); - int extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible); + // Adding overload if not at least one overload breaks ABI, so avoid + // by keeping private until we can break ABI. ref: http://www.ros.org/reps/rep-0009.html + int extend_selection__(Fl_Tree_Item *from, + Fl_Tree_Item *to, + int val, + bool visible); public: #endif void set_item_focus(Fl_Tree_Item *item); @@ -522,10 +537,6 @@ public: void item_draw_mode(int mode); #endif #if FLTK_ABI_VERSION >= 10303 - void item_draw_callback(Fl_Tree_Item_Draw_Callback *cb, void *data=0); - Fl_Tree_Item_Draw_Callback* item_draw_callback() const; - void* item_draw_user_data() const; - void do_item_draw_callback(Fl_Tree_Item *o) const; void calc_dimensions(); void calc_tree(); #endif diff --git a/FL/Fl_Tree_Item.H b/FL/Fl_Tree_Item.H index 8493b7e44..23ca54c87 100644 --- a/FL/Fl_Tree_Item.H +++ b/FL/Fl_Tree_Item.H @@ -52,6 +52,17 @@ /// When you make changes to items, you'll need to tell the tree to redraw() /// for the changes to show up. /// +/// New 1.3.3 ABI feature: +/// You can define custom items by either adding a custom widget to the item +/// with Fl_Tree_Item::widget(), or override the draw_item_content() method +/// if you want to just redefine how the label is drawn. +/// +/// The following shows the Fl_Tree_Item's dimensions, useful when overriding +/// the draw_item_content() method: +/// +/// \image html Fl_Tree_Item-dimensions.png "Fl_Tree_Item's internal dimensions." width=6cm +/// \image latex Fl_Tree_Item-dimensions.png "Fl_Tree_Item's internal dimensions." width=6cm +/// class Fl_Tree; class FL_EXPORT Fl_Tree_Item { #if FLTK_ABI_VERSION >= 10303 @@ -96,6 +107,7 @@ class FL_EXPORT Fl_Tree_Item { Fl_Tree_Item *_prev_sibling; // previous sibling (same level) Fl_Tree_Item *_next_sibling; // next sibling (same level) #endif /*FLTK_ABI_VERSION*/ + // Protected methods protected: void _Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree); void show_widgets(); @@ -103,6 +115,12 @@ protected: void draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs); void draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs); void recalc_tree(); + int calc_item_height(const Fl_Tree_Prefs &prefs) const; +#if FLTK_ABI_VERSION >= 10303 + Fl_Color drawfgcolor() const; + Fl_Color drawbgcolor() const; +#endif + public: Fl_Tree_Item(const Fl_Tree_Prefs &prefs); // CTOR -- backwards compatible #if FLTK_ABI_VERSION >= 10303 @@ -110,20 +128,35 @@ public: #endif ~Fl_Tree_Item(); // DTOR Fl_Tree_Item(const Fl_Tree_Item *o); // COPY CTOR + /// The item's x position relative to the window int x() const { return(_xywh[0]); } + /// The item's y position relative to the window int y() const { return(_xywh[1]); } + /// The entire item's width to right edge of Fl_Tree's inner width + /// within scrollbars. int w() const { return(_xywh[2]); } + /// The item's height int h() const { return(_xywh[3]); } + /// The item's label x position relative to the window + /// \version 1.3.3 int label_x() const { return(_label_xywh[0]); } + /// The item's label y position relative to the window + /// \version 1.3.3 int label_y() const { return(_label_xywh[1]); } + /// The item's maximum label width to right edge of Fl_Tree's inner width + /// within scrollbars. + /// \version 1.3.3 int label_w() const { return(_label_xywh[2]); } + /// The item's label height + /// \version 1.3.3 int label_h() const { return(_label_xywh[3]); } - int calc_item_height(const Fl_Tree_Prefs &prefs) const; #if FLTK_ABI_VERSION >= 10303 + virtual int draw_item_content(int render); void draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, - int &tree_item_xmax, int lastchild=1, int render=1); + int &tree_item_xmax, int lastchild=1, int render=1); #else - void draw(int X, int &Y, int W, Fl_Widget *tree, Fl_Tree_Item *itemfocus, const Fl_Tree_Prefs &prefs, int lastchild=1); + void draw(int X, int &Y, int W, Fl_Widget *tree, + Fl_Tree_Item *itemfocus, const Fl_Tree_Prefs &prefs, int lastchild=1); #endif void show_self(const char *indent = "") const; void label(const char *val); @@ -157,25 +190,27 @@ public: void labelfgcolor(Fl_Color val) { _labelfgcolor = val; } - /// Set item's label text color. - void labelcolor(Fl_Color val) { - _labelfgcolor = val; - } - /// Return item's label text color. - Fl_Color labelcolor() const { - return(_labelfgcolor); - } /// Return item's label foreground text color. Fl_Color labelfgcolor() const { return(_labelfgcolor); } + /// Set item's label text color. Alias for labelfgcolor(Fl_Color)). + void labelcolor(Fl_Color val) { + labelfgcolor(val); + } + /// Return item's label text color. Alias for labelfgcolor() const). + Fl_Color labelcolor() const { + return labelfgcolor(); + } /// Set item's label background color. - /// A special case is made for color 0xffffffff which is treated as 'transparent'. + /// A special case is made for color 0xffffffff which uses the parent tree's bg color. void labelbgcolor(Fl_Color val) { _labelbgcolor = val; } - /// Return item's background text color. - /// If the color is 0xffffffff, it is 'transparent'. + /// Return item's label background text color. + /// If the color is 0xffffffff, the default behavior is the parent tree's + /// bg color will be used. (An overloaded draw_item_content() can override + /// this behavior.) Fl_Color labelbgcolor() const { return(_labelbgcolor); } @@ -209,15 +244,29 @@ public: void clear_children(); void swap_children(int ax, int bx); int swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b); - const Fl_Tree_Item *find_child_item(char **arr) const; // const - Fl_Tree_Item *find_child_item(char **arr); // non-const - const Fl_Tree_Item *find_item(char **arr) const; // const - Fl_Tree_Item *find_item(char **arr); // non-const + const Fl_Tree_Item *find_child_item(const char *name) const; + Fl_Tree_Item *find_child_item(const char *name); + const Fl_Tree_Item *find_child_item(char **arr) const; + Fl_Tree_Item *find_child_item(char **arr); + const Fl_Tree_Item *find_item(char **arr) const; + Fl_Tree_Item *find_item(char **arr); ////////////////// // Adding items ////////////////// - Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, const char *new_label); - Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, char **arr); + Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, + const char *new_label, + Fl_Tree_Item *newitem); + Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, + const char *new_label); + Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, + char **arr, + Fl_Tree_Item *newitem); + Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, + char **arr); +#if FLTK_ABI_VERSION >= 10303 + Fl_Tree_Item *replace(Fl_Tree_Item *new_item); + Fl_Tree_Item *replace_child(Fl_Tree_Item *olditem, Fl_Tree_Item *newitem); +#endif Fl_Tree_Item *insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos=0); Fl_Tree_Item *insert_above(const Fl_Tree_Prefs &prefs, const char *new_label); int depth() const; @@ -246,6 +295,7 @@ public: _parent = val; } #if FLTK_ABI_VERSION >= 10303 + const Fl_Tree_Prefs& prefs() const; /// Return the tree for this item. const Fl_Tree *tree() const { return(_tree); @@ -323,9 +373,8 @@ public: /// Change the item's activation state to the optionally specified 'val'. /// /// When deactivated, the item will be 'grayed out'; the callback() - /// won't be invoked if the user clicks on the label. If the item - /// has a widget() associated with the item, its activation state - /// will be changed as well. + /// won't be invoked if the user clicks on the label. If a widget() + /// is associated with the item, its activation state will be changed as well. /// /// If 'val' is not specified, the item will be activated. /// @@ -350,7 +399,7 @@ public: char is_activated() const { return(is_flag(ACTIVE)); } - /// See if the item is activated. + /// See if the item is activated. Alias for is_activated(). char is_active() const { return(is_activated()); } @@ -391,6 +440,7 @@ public: } // Protected methods + // TODO: move these to top 'protected:' section protected: #if FLTK_ABI_VERSION >= 10301 /// Set a flag to an on or off value. val is 0 or 1. diff --git a/FL/Fl_Tree_Item_Array.H b/FL/Fl_Tree_Item_Array.H index 802381f7f..ff3eb0ea5 100644 --- a/FL/Fl_Tree_Item_Array.H +++ b/FL/Fl_Tree_Item_Array.H @@ -87,6 +87,7 @@ public: void clear(); void add(Fl_Tree_Item *val); void insert(int pos, Fl_Tree_Item *new_item); + void replace(int pos, Fl_Tree_Item *new_item); void remove(int index); int remove(Fl_Tree_Item *item); #if FLTK_ABI_VERSION >= 10303 diff --git a/examples/tree-custom-draw-items.cxx b/examples/tree-custom-draw-items.cxx index 57ac85c31..fbee62700 100644 --- a/examples/tree-custom-draw-items.cxx +++ b/examples/tree-custom-draw-items.cxx @@ -17,68 +17,159 @@ // http://www.fltk.org/str.php // #include -#include // sin(3) +#include /* ctime.. */ #include #include #include +#ifndef MAX +#define MAX(a,b) ((a)>(b))?(a):(b) +#endif + #if FLTK_ABI_VERSION >= 10303 -static void draw_item(Fl_Tree_Item *item, void *data) { - Fl_Tree *tree = (Fl_Tree*)data; - int X=item->label_x(), Y=item->label_y(), - W=item->label_w(), H=item->label_h(); - // Draw the background - fl_color(item->is_selected() ? tree->selection_color() : item->labelbgcolor()); - fl_rectf(X,Y,W,H); - // Draw some red/grn/blu boxes - int x = X + 5; - fl_color(FL_RED); fl_rectf(x, Y+2, 10, H-4); x += 10; - fl_color(FL_GREEN); fl_rectf(x, Y+2, 10, H-4); x += 10; - fl_color(FL_BLUE); fl_rectf(x, Y+2, 10, H-4); x += 10; - x += 5; - // Draw text - fl_font(item->labelfont(), item->labelsize()); - fl_color(item->labelfgcolor()); - char s[80]; - sprintf(s, "Custom: '%s'", item->label()?item->label():"---"); - fl_draw(s, x+tree->labelmarginleft(),Y,W,H, FL_ALIGN_LEFT); - int fw=0,fh=0; - fl_measure(s,fw,fh); - x += fw + 10; - // Draw a red sine wave past the text to end of xywh area - fl_color(FL_RED); - for ( float a=0.0; x<(X+W); x++,a+=.1) { - int y = Y + sin(a) * ((H-2)/2) + (H/2); - fl_point(x,y); +// DERIVE CUSTOM CLASS FROM Fl_Tree_Item TO IMPLEMENT SHOWING THE TIME OF DAY +// This demonstrates that item content can be dynamic and highly customized. +// +class MyTimeItem : public Fl_Tree_Item { + const char *time_format; +protected: + // Remove trailing crlf + const char* StripCrlf(char *s) + { char *ss = strchr(s, '\n'); if (ss) *ss = 0; return s; } + const struct tm* GetTimeStruct() { + time_t t = time(NULL); + if ( strcmp(time_format, "Local") == 0 ) return localtime(&t); + if ( strcmp(time_format, "GMT" ) == 0 ) return gmtime(&t); + return 0; + } +public: + MyTimeItem(Fl_Tree *tree, const char *time_format) : Fl_Tree_Item(tree) { + label(time_format); + this->time_format = time_format; + } + // Handle custom drawing of the item + // Fl_Tree has already handled drawing everything to the left + // of the label area, including any 'user icon', collapse buttons, + // connector lines, etc. + // + // All we're responsible for is drawing the 'label' area of the item + // and it's background. Fl_Tree gives us a hint as to what the + // foreground and background colors should be via the fg/bg parameters, + // and whether we're supposed to render anything or not. + // + // The only other thing we must do is return the maximum X position + // of scrollable content, i.e. the right most X position of content + // that we want the user to be able to use the horizontal scrollbar + // to reach. + // + int draw_item_content(int render) { + Fl_Color fg = drawfgcolor(); + Fl_Color bg = drawbgcolor(); + // Show the date and time as two small strings + // one on top of the other in a single item. + // + // Our item's label dimensions + int X = label_x(), Y = label_y(), + W = label_w(), H = label_h(); + // Render background + if ( render ) { + if ( is_selected() ) { // Selected? Use selectbox() style + fl_draw_box(prefs().selectbox(),X,Y,W,H,bg); + } else { // Not Selected? use plain filled rectangle + fl_color(bg); fl_rectf(X,Y,W,H); + } + } + // Render the label + if ( render ) { + fl_color(fg); + if ( label() ) fl_draw(label(), X,Y,W,H, FL_ALIGN_LEFT); + } + int lw=0, lh=0; + if ( label() ) { + lw=0; lh=0; fl_measure(label(), lw, lh); + } + X += lw + 8; + // Draw some red/grn/blu boxes + if ( render ) { + fl_color(FL_RED); fl_rectf(X+0, Y+2, 10, H-4); + fl_color(FL_GREEN); fl_rectf(X+10, Y+2, 10, H-4); + fl_color(FL_BLUE); fl_rectf(X+20, Y+2, 10, H-4); + } + X += 35; + // Render the date and time, one over the other + fl_font(labelfont(), 8); // small font + const struct tm *tm = GetTimeStruct(); + char s[80]; + sprintf(s, "Date: %02d/%02d/%02d", tm->tm_mon+1, tm->tm_mday, tm->tm_year % 100); + lw=0, lh=0; fl_measure(s, lw, lh); // get box around text (including white space) + if ( render ) fl_draw(s, X,Y+4,W,H, FL_ALIGN_LEFT|FL_ALIGN_TOP); + sprintf(s, "Time: %02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + if ( render ) fl_draw(s, X,Y+H/2,W,H/2, FL_ALIGN_LEFT|FL_ALIGN_TOP); + int lw2=0, lh2=0; fl_measure(s, lw2, lh2); + X += MAX(lw, lw2); + return X; // return right most edge of what we've rendered } +}; + +// TIMER TO HANDLE DYNAMIC CONTENT IN THE TREE +void Timer_CB(void *data) { + Fl_Tree *tree = (Fl_Tree*)data; + tree->redraw(); // keeps time updated + Fl::repeat_timeout(0.2, Timer_CB, data); } int main(int argc, char *argv[]) { Fl::scheme("gtk+"); - Fl_Double_Window *win = new Fl_Double_Window(250, 400, "Simple Tree"); + Fl_Double_Window *win = new Fl_Double_Window(350, 400, "Simple Tree"); win->begin(); { // Create the tree Fl_Tree *tree = new Fl_Tree(0, 0, win->w(), win->h()); - tree->showroot(0); // don't show root of tree - tree->item_draw_callback(draw_item, (void*)tree); // setup a callback for the tree + tree->showroot(0); // don't show root of tree + tree->selectmode(FL_TREE_SELECT_MULTI); // multiselect // Add some items tree->add("Flintstones/Fred"); tree->add("Flintstones/Wilma"); tree->add("Flintstones/Pebbles"); - tree->add("Simpsons/Homer"); - tree->add("Simpsons/Marge"); - tree->add("Simpsons/Bart"); - tree->add("Simpsons/Lisa"); + { + MyTimeItem *myitem; + myitem = new MyTimeItem(tree, "Local"); // create custom item + myitem->labelsize(20); + tree->add("Time Add Item/Local", myitem); + + myitem = new MyTimeItem(tree, "GMT"); // create custom item + myitem->labelsize(20); + tree->add("Time Add Item/GMT", myitem); + } + // 'Replace' approach + { + Fl_Tree_Item *item; + MyTimeItem *myitem; + item = tree->add("Time Replace Item/Local Time"); + // Replace the 'Local' item with our own + myitem = new MyTimeItem(tree, "Local"); // create custom item + myitem->labelsize(20); + item->replace(myitem); // replace normal item with custom + + item = tree->add("Time Replace Item/GMT Time"); + // Replace the 'GMT' item with our own + myitem = new MyTimeItem(tree, "GMT"); // create custom item + myitem->labelsize(20); + item->replace(myitem); // replace normal item with custom + } tree->add("Superjail/Warden"); tree->add("Superjail/Jared"); tree->add("Superjail/Alice"); tree->add("Superjail/Jailbot"); + tree->show_self(); + // Start with some items closed - tree->close("Simpsons"); tree->close("Superjail"); + + // Set up a timer to keep time in tree updated + Fl::add_timeout(0.2, Timer_CB, (void*)tree); } win->end(); win->resizable(win); diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx index b5f427a0c..23d4a3c75 100644 --- a/src/Fl_Tree.cxx +++ b/src/Fl_Tree.cxx @@ -58,7 +58,7 @@ static char **parse_path(const char *path) { static void free_path(char **arr) { if ( arr ) { if ( arr[0] ) { delete[] arr[0]; } // deletes cp in parse_path - delete[] arr; // deletes ptr array + delete[] arr; // deletes ptr array } } @@ -128,10 +128,15 @@ Fl_Tree::~Fl_Tree() { /// depending on direction \p 'dir', \p 'val', and \p 'visible'. /// /// Efficient: does not walk entire tree; starts with \p 'from' and stops -/// at \p 'to' while moving in direction \p 'dir'. Dir must be specified -/// though; when not available (such as during SHIFT-click operations), -/// the other method extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,bool) -/// should be used. Handles calling redraw() if anything changed. +/// at \p 'to' while moving in direction \p 'dir'. Dir must be specified though. +#if FLTK_ABI_VERSION >= 10303 +/// +/// If dir cannot be known in advance, such as during SHIFT-click operations, +/// the method extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,bool) +/// should be used. +#endif +/// +/// Handles calling redraw() if anything changed. /// /// \param[in] from Starting item /// \param[in] to Ending item @@ -140,15 +145,10 @@ Fl_Tree::~Fl_Tree() { /// \param[in] visible true=affect only open(), visible items,
/// false=affect open or closed items (default) /// \returns The number of items whose selection states were changed, if any. +/// \version 1.3.3 /// -#if FLTK_ABI_VERSION >= 10303 -int Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, - int dir, int val, bool visible ) { -#else -// Adding overload if not at least one overload breaks ABI, so avoid -int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, - int dir, int val, bool visible ) { -#endif +int Fl_Tree::extend_selection_dir(Fl_Tree_Item *from, Fl_Tree_Item *to, + int dir, int val, bool visible ) { int changed = 0; for (Fl_Tree_Item *item=from; item; item = next_item(item, dir, visible) ) { switch (val) { @@ -171,8 +171,8 @@ int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, /// Extend a selection between \p 'from' and \p 'to' depending on \p 'visible'. /// /// Similar to the more efficient -/// extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,int,bool) method, -/// but direction (up or down) doesn't need to be known.
+/// extend_selection_dir(Fl_Tree_Item*,Fl_Tree_Item*,int dir,int val,bool vis) +/// method, but direction (up or down) doesn't need to be known.
/// We're less efficient because we search the tree for to/from, then operate /// on items in between. The more efficient method avoids the "search", /// but necessitates a direction to be specified to find \p 'to'.
@@ -185,12 +185,14 @@ int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, /// \param[in] visible true=affect only open(), visible items,
/// false=affect open or closed items (default) /// \returns The number of items whose selection states were changed, if any. -/// #if FLTK_ABI_VERSION >= 10303 +/// \version 1.3.3 ABI feature int Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible) { #else +/// \notes Made public in 1.3.3 ABI // Adding overload if not at least one overload breaks ABI, so avoid +// by making a private function until ABI can change.. int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible) { #endif @@ -237,13 +239,17 @@ int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, } #if FLTK_ABI_VERSION >= 10303 -// nothing +// not needed, above overload handles this #else /// Extend a selection between \p 'from' and \p 'to'. +/// Extends selection for items and all children, visible ('open') or not. +/// Walks entire tree from top to bottom looking for \p 'from' and \p 'to'. +/// \version 1.3.0 +/// void Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to) { - const int val = 1; - const bool visible = false; - extend_selection__(from, to, val, visible); + const int val = 1; // 0=clr, 1=set, 2=toggle + const bool visible = false; // true=only 'open' items, false='open' or 'closed' + extend_selection__(from, to, val, visible); // use private method until we can release it } #endif @@ -521,11 +527,7 @@ int Fl_Tree::handle(int e) { Fl_Tree_Item *to = item; int val = is_ctrl ? 2 : 1; // toggle_select() or just select()? bool visible = true; -#if FLTK_ABI_VERSION >= 10303 - extend_selection(from, to, dir, val, visible); -#else - extend_selection__(from, to, dir, val, visible); -#endif + extend_selection_dir(from, to, dir, val, visible); break; } } @@ -850,6 +852,7 @@ int Fl_Tree::draw_tree() { /// Print the tree as 'ascii art' to stdout. /// Used mainly for debugging. /// \todo should be const +/// \version 1.3.0 /// void Fl_Tree::show_self() { if ( ! _root ) return; @@ -870,9 +873,28 @@ Fl_Tree_Item* Fl_Tree::root() { return(_root); } +/// Sets the root item to \p 'newitem'. +/// +/// If a root item already exists, clear() is first to clear it +/// before replacing it with newitem. +/// +#if FLTK_ABI_VERSION >= 10303 +/// Use this to install a custom item (derived from Fl_Tree_Item) as the root +/// of the tree. This allows the derived class to implement custom drawing +/// by overriding Fl_Tree_Item::draw_item_content(). +/// +#endif +/// \version 1.3.3 +/// +void Fl_Tree::root(Fl_Tree_Item *newitem) { + if ( _root ) clear(); + _root = newitem; +} + /// Adds a new item, given a menu style \p 'path'. /// Any parent nodes that don't already exist are created automatically. /// Adds the item based on the value of sortorder(). +/// If \p 'item' is NULL, a new item is created. /// /// To specify items or submenus that contain slashes ('/' or '\') /// use an escape character to protect them, e.g. @@ -881,10 +903,15 @@ Fl_Tree_Item* Fl_Tree::root() { /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp" /// \endcode /// \param[in] path The path to the item, e.g. "Flintsone/Fred". +/// \param[in] item The new item to be added. +/// If NULL, a new item is created with +/// a name that is the last element in \p 'path'. /// \returns The new item added, or 0 on error. +/// \version 1.3.3 /// -Fl_Tree_Item* Fl_Tree::add(const char *path) { - if ( ! _root ) { // Create root if none +Fl_Tree_Item* Fl_Tree::add(const char *path, Fl_Tree_Item *item) { + // Tree has no root? make one + if ( ! _root ) { #if FLTK_ABI_VERSION >= 10303 _root = new Fl_Tree_Item(this); #else @@ -892,19 +919,36 @@ Fl_Tree_Item* Fl_Tree::add(const char *path) { #endif _root->parent(0); _root->label("ROOT"); - } + } + // Find parent item via path char **arr = parse_path(path); - Fl_Tree_Item *item = _root->add(_prefs, arr); + item = _root->add(_prefs, arr, item); free_path(arr); return(item); } +#if FLTK_ABI_VERSION >= 10303 +// do nothing here: add(path,item) where item defaults to 0 takes its place +#else +/// Adds a new item given a menu style \p 'path'. +/// Same as calling add(path, NULL); +/// \param[in] path The path to the item to be created, e.g. "Flintsone/Fred". +/// \returns The new item added, or 0 on error. +/// \see add(const char*,Fl_Tree_Item*) +/// \version 1.3.0 release +/// +Fl_Tree_Item* Fl_Tree::add(const char *path) { + return add(path, 0); +} +#endif + /// Add a new child item labeled \p 'name' to the specified \p 'parent_item'. /// /// \param[in] parent_item The parent item the new child item will be added to. /// Must not be NULL. /// \param[in] name The label for the new item /// \returns The new item added. +/// \version 1.3.0 release /// Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *parent_item, const char *name) { return(parent_item->add(_prefs, name)); @@ -949,7 +993,7 @@ int Fl_Tree::remove(Fl_Tree_Item *item) { return(0); } -/// Clear all children from the tree. +/// Clear the entire tree's children, including the root. /// The tree will be left completely empty. /// void Fl_Tree::clear() { @@ -987,7 +1031,7 @@ void Fl_Tree::clear_children(Fl_Tree_Item *item) { /// \see item_pathname() /// Fl_Tree_Item *Fl_Tree::find_item(const char *path) { - // I evoke "Effective C++, 3rd Ed", p.23. Sola fide, Amen. + // "Effective C++, 3rd Ed", p.23. Sola fide, Amen. return(const_cast( static_cast(*this).find_item(path))); } @@ -1136,6 +1180,7 @@ Fl_Tree_Item* Fl_Tree::item_clicked() { /// \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. +/// \version 1.3.3 /// Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) { return next_item(item, dir, true); @@ -1166,6 +1211,7 @@ Fl_Tree_Item* Fl_Tree::first_visible() { /// Returns the first open(), visible item in the tree, or 0 if none. /// \returns First visible item in tree, or 0 if none. /// \see first_visible_item(), last_visible_item(), next_visible_item() +/// \version 1.3.3 /// Fl_Tree_Item* Fl_Tree::first_visible_item() { Fl_Tree_Item *i = showroot() ? first() : next(first()); @@ -1234,7 +1280,7 @@ Fl_Tree_Item* Fl_Tree::last() { } /// Returns the last open(), visible item in the tree. -/// \deprecated in 1.3.3 ABI -- use last_visible_item() instead. +/// \deprecated in 1.3.3 -- use last_visible_item() instead. /// Fl_Tree_Item* Fl_Tree::last_visible() { return(last_visible_item()); @@ -1243,6 +1289,7 @@ Fl_Tree_Item* Fl_Tree::last_visible() { /// Returns the last open(), visible item in the tree. /// \returns Last visible item in the tree, or 0 if none. /// \see first_visible_item(), last_visible_item(), next_visible_item() +/// \version 1.3.3 /// Fl_Tree_Item* Fl_Tree::last_visible_item() { Fl_Tree_Item *item = last(); @@ -1313,6 +1360,7 @@ Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) { /// /// \returns The last selected item, or 0 if none. /// \see first_selected_item(), last_selected_item(), next_selected_item() +/// \version 1.3.3 /// Fl_Tree_Item *Fl_Tree::last_selected_item() { return(next_selected_item(0, FL_Up)); @@ -1325,13 +1373,13 @@ Fl_Tree_Item *Fl_Tree::last_selected_item() { /// If \p 'visible' is true, only items whose parents are open() will be returned. /// If \p 'visible' is false, even items whose parents are close()ed will be returned. /// -/// If \p item is 0, the return value will be: -///
-///      last_visible_item()   - If \p visible=true and \p dir=FL_Up
-/// first_visible_item() - If \p visible=true and \p dir=FL_Down
-/// last() - If \p visible=false and \p dir=FL_Up
-/// first() - If \p visible=false and \p dir=FL_Down -///
+/// If \p item is 0, the return value will be the result of this truth table: +///
+///                        visible=true           visible=false
+///                        -------------------    -------------
+///          dir=Fl_Up:    last_visible_item()    last()
+///        dir=Fl_Down:    first_visible_item()   first()
+/// 
/// /// \par Example use: /// \code @@ -1361,7 +1409,8 @@ Fl_Tree_Item *Fl_Tree::last_selected_item() { /// \see first(), last(), next(),
/// first_visible_item(), last_visible_item(), next_visible_item(),
/// first_selected_item(), last_selected_item(), next_selected_item() -/// +/// \version 1.3.3 +/// Fl_Tree_Item *Fl_Tree::next_item(Fl_Tree_Item *item, int dir, bool visible) { if ( ! item ) { // no start item? if ( visible ) { @@ -1408,6 +1457,7 @@ Fl_Tree_Item *Fl_Tree::next_item(Fl_Tree_Item *item, int dir, bool visible) { /// FL_Down for down the tree (default) /// \returns The next selected item, or 0 if there are no more selected items. /// \see first_selected_item(), last_selected_item(), next_selected_item() +/// \version 1.3.3 /// Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item, int dir) { switch (dir) { @@ -1451,6 +1501,7 @@ Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item, int dir) { /// \param[out] ret_items The returned array of selected items. /// \returns The number of items in the returned array. /// \see first_selected_item(), next_selected_item() +/// \version 1.3.3 ABI feature /// int Fl_Tree::get_selected_items(Fl_Tree_Item_Array &ret_items) { ret_items.clear(); @@ -2350,7 +2401,7 @@ void Fl_Tree::item_reselect_mode(Fl_Tree_Item_Reselect_Mode mode) { } #endif -#if FLTK_ABI_VERSION >= 10303 +#if FLTK_ABI_VERSION >= 10301 /// Get the 'item draw mode' used for the tree Fl_Tree_Item_Draw_Mode Fl_Tree::item_draw_mode() const { return(_prefs.item_draw_mode()); @@ -2373,54 +2424,6 @@ void Fl_Tree::item_draw_mode(Fl_Tree_Item_Draw_Mode mode) { void Fl_Tree::item_draw_mode(int mode) { _prefs.item_draw_mode(Fl_Tree_Item_Draw_Mode(mode)); } - -/// Set a callback to be invoked to handle drawing the Fl_Tree_Item -/// instead of the default label drawing behavior. Lets one define -/// custom drawing behavior for Fl_Tree_Item's. e.g. -/// \code -/// static void draw_item(Fl_Tree_Item *item, void *data) { -/// Fl_Tree *tree = (Fl_Tree*)data; -/// int X=item->label_x(), Y=item->label_y(), -/// W=item->label_w(), H=item->label_h(); -/// // Draw the background -/// fl_color(item->is_selected() ? tree->selection_color() : item->labelbgcolor()); -/// fl_rectf(X,Y,W,H); -/// // Draw text -/// fl_font(item->labelfont(), item->labelsize()); -/// fl_color(item->labelfgcolor()); -/// fl_draw("Some text", X+tree->labelmarginleft(),Y,W,H, FL_ALIGN_LEFT); -/// } -/// .. -/// int main() { -/// Fl_Tree *tree = new Fl_Tree(0,0,100,100); -/// tree->item_draw_callback(draw_item, (void*)tree); -/// [..] -/// \endcode -/// \param[in] cb The callback to use -/// \param[in] data Optional item_draw_user_data() (default=NULL) -/// \note This only affects the drawing of item's labels, -/// it does not affect the drawing of widgets assigned with -/// Fl_Tree_Item::widget(). -/// -void Fl_Tree::item_draw_callback(Fl_Tree_Item_Draw_Callback *cb, void *data) { - _prefs.item_draw_callback(cb,data); // no recalc_tree() -- changes don't affect item geometry -} - -/// Get the current item draw callback. Returns 0 if none. -Fl_Tree_Item_Draw_Callback* Fl_Tree::item_draw_callback() const { - return(_prefs.item_draw_callback()); -} - -/// Get the current item draw callback's user data. -void* Fl_Tree::item_draw_user_data() const { - return(_prefs.item_draw_user_data()); -} - -/// Invoke the configured item_draw_callback(). -/// Do NOT call this if no item_draw_callback() was configured. -void Fl_Tree::do_item_draw_callback(Fl_Tree_Item *o) const { - _prefs.do_item_draw_callback(o); -} #endif /// See if \p 'item' is currently displayed on-screen (visible within the widget). @@ -2613,11 +2616,11 @@ int Fl_Tree::scrollbar_size() const { /// 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. +/// has a consistent UI, and is the default behavior. Normally +/// this is what you want. /// -/// Only use THIS method if you really need to override just this -/// widget instance's scrollbar size. (The need for this should be rare.) +/// Only use this method if you really need to override just THIS +/// instance of the widget's scrollbar size. (This need 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. diff --git a/src/Fl_Tree_Item.cxx b/src/Fl_Tree_Item.cxx index ca0fdfcb3..202ad09a4 100644 --- a/src/Fl_Tree_Item.cxx +++ b/src/Fl_Tree_Item.cxx @@ -36,7 +36,9 @@ static int event_inside(const int xywh[4]) { /// Constructor. /// Makes a new instance of Fl_Tree_Item using defaults from \p 'prefs'. -/// \deprecated in 1.3.3 ABI -- use Fl_Tree_Item(Fl_Tree*) instead. +#if FLTK_ABI_VERSION >= 10303 +/// \deprecated in 1.3.3 ABI -- you must use Fl_Tree_Item(Fl_Tree*) for proper horizontal scrollbar behavior. +#endif /// Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) { _Init(prefs, 0); @@ -91,6 +93,11 @@ void Fl_Tree_Item::_Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree) { /// Constructor. /// Makes a new instance of Fl_Tree_Item for \p 'tree'. /// +/// This must be used instead of the older, deprecated Fl_Tree_Item(Fl_Tree_Prefs) +/// constructor for proper horizontal scrollbar calculation. +/// +/// \version 1.3.3 ABI feature +/// Fl_Tree_Item::Fl_Tree_Item(Fl_Tree *tree) { _Init(tree->_prefs, tree); } @@ -186,7 +193,7 @@ const char *Fl_Tree_Item::label() const { return(_label); } -/// Return child item for the specified 'index'. +/// Return const child item for the specified 'index'. const Fl_Tree_Item *Fl_Tree_Item::child(int index) const { return(_children[index]); } @@ -201,25 +208,51 @@ void Fl_Tree_Item::clear_children() { /// that has the label \p 'name'. /// /// \returns index of found item, or -1 if not found. +/// \version 1.3.0 release /// int Fl_Tree_Item::find_child(const char *name) { if ( name ) { - for ( int t=0; tlabel() ) { - if ( strcmp(child(t)->label(), name) == 0 ) { + for ( int t=0; tlabel() ) + if ( strcmp(child(t)->label(), name) == 0 ) return(t); - } - } - } } return(-1); } +/// Return the /immediate/ child of current item +/// that has the label \p 'name'. +/// +/// \returns const found item, or 0 if not found. +/// \version 1.3.3 +/// +const Fl_Tree_Item* Fl_Tree_Item::find_child_item(const char *name) const { + if ( name ) + for ( int t=0; tlabel() ) + if ( strcmp(child(t)->label(), name) == 0 ) + return(child(t)); + return(0); +} + +/// Return the /immediate/ child of current item +/// that has the label \p 'name'. +/// +/// \returns found item, or 0 if not found. +/// \version 1.3.3 +/// +Fl_Tree_Item* Fl_Tree_Item::find_child_item(const char *name) { + // "Effective C++, 3rd Ed", p.23. Sola fide, Amen. + return(const_cast( + static_cast(*this).find_child_item(name))); +} + /// Find child item by descending array \p 'arr' of names. /// Does not include self in search. /// Only Fl_Tree should need this method. /// /// \returns item, or 0 if not found +/// \version 1.3.0 release /// const Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) const { for ( int t=0; t( static_cast(*this).find_child_item(arr))); } @@ -252,9 +286,10 @@ Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) { /// Includes self in search. /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. /// -/// \returns item, or 0 if not found +/// \returns const item, or 0 if not found /// const Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) const { + if ( ! *names ) return(0); if ( label() && strcmp(label(), *names) == 0 ) { // match self? ++names; // skip self if ( *names == 0 ) return(this); // end of names, found ourself @@ -272,7 +307,7 @@ const Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) const { /// \returns item, or 0 if not found /// Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) { - // I evoke "Effective C++, 3rd Ed", p.23. Sola fide, Amen. + // "Effective C++, 3rd Ed", p.23. Sola fide, Amen. return(const_cast( static_cast(*this).find_item(names))); } @@ -283,11 +318,9 @@ Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) { /// \returns the index, or -1 if not found. /// int Fl_Tree_Item::find_child(Fl_Tree_Item *item) { - for ( int t=0; t= 10303 - Fl_Tree_Item *item = new Fl_Tree_Item(_tree); + if ( !item ) + { item = new Fl_Tree_Item(_tree); item->label(new_label); } #else - Fl_Tree_Item *item = new Fl_Tree_Item(prefs); + if ( !item ) + { item = new Fl_Tree_Item(prefs); item->label(new_label); } #endif recalc_tree(); // may change tree geometry - item->label(new_label); item->_parent = this; switch ( prefs.sortorder() ) { case FL_TREE_SORT_NONE: { @@ -340,21 +391,49 @@ Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, const char *new_labe /// Should be used only by Fl_Tree's internals. /// Adds the item based on the value of prefs.sortorder(). /// \returns the item added. +/// \version 1.3.0 release /// Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) { - int t = (*arr && *(arr+1)) ? find_child(*arr) : -1; - Fl_Tree_Item *item = 0; - if ( t == -1 ) { - item = (Fl_Tree_Item*)add(prefs, *arr); - } else { - item = (Fl_Tree_Item*)child(t); - } - recalc_tree(); // may change tree geometry - if ( *(arr+1) ) { // descend? - return(item->add(prefs, arr+1)); - } else { - return(item); // end? done + return add(prefs, arr, 0); +} + +/// Descend into path specified by \p 'arr' and add \p 'newitem' there. +/// Should be used only by Fl_Tree's internals. +/// If item is NULL, a new item is created. +/// Adds the item based on the value of prefs.sortorder(). +/// \returns the item added. +/// \version 1.3.3 ABI feature +/// +Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, + char **arr, + Fl_Tree_Item *newitem) { + if ( !*arr ) return 0; + // See if we can find an existing child with name requested. + Fl_Tree_Item *child = find_child_item(*arr); + if ( child ) { // Child found? + if ( *(arr+1) == 0 ) { // ..and at end of path? + if ( !newitem ) { // ..and no item specified? + return 0; // ..error: child exists already + } else { + // Child found, end of path, item specified + return child->add(prefs, newitem->label(), newitem); + } + } + // Child found: more path elements to go or item specified? + // Descend into child to handle add.. + return child->add(prefs, arr+1, newitem); // recurse } + // No child found, see if we reached end of path. + // If so, add as an immediate child, done + if ( *(arr+1) == 0 ) // end of path? + return add(prefs, *arr, newitem); // add as immediate child + + // No child found, but more to path? + // If so, create new child to handle add() + Fl_Tree_Item *newchild; + return (newchild=add(prefs, *arr)) // create new immediate child + ? newchild->add(prefs,arr+1,newitem) // it worked? recurse to add + : 0; // failed? error } /// Insert a new item named \p 'new_label' into current item's @@ -390,6 +469,64 @@ Fl_Tree_Item *Fl_Tree_Item::insert_above(const Fl_Tree_Prefs &prefs, const char return(0); } +#if FLTK_ABI_VERSION >= 10303 +/// Return the parent tree's prefs. +/// \returns a reference to the parent tree's Fl_Tree_Prefs +/// \version 1.3.3 ABI feature +/// +const Fl_Tree_Prefs& Fl_Tree_Item::prefs() const { + return(_tree->_prefs); +} + +/// Replace the current item with a new item. +/// +/// The current item is destroyed if successful. +/// No checks are made to see if an item with the same name exists. +/// +/// This method can be used to, for example, install 'custom' items +/// into the tree derived from Fl_Tree_Item; see draw_item_content(). +/// +/// \param[in] newitem The new item to replace the current item +/// \returns newitem on success, NULL if could not be replaced. +/// \see Fl_Tree_Item::draw_item_content(), Fl_Tree::root(Fl_Tree_Item*) +/// \version 1.3.3 ABI feature +/// +Fl_Tree_Item *Fl_Tree_Item::replace(Fl_Tree_Item *newitem) { + Fl_Tree_Item *p = parent(); + if ( !p ) { // no parent? then we're the tree's root.. + _tree->root(newitem); // ..tell tree to replace root + return newitem; + } + // has parent? ask parent to replace us + return p->replace_child(this, newitem); +} + +/// Replace existing child \p 'olditem' with \p 'newitem'. +/// +/// The \p 'olditem' is destroyed if successful. +/// Can be used to put custom items (derived from Fl_Tree_Item) into the tree. +/// No checks are made to see if an item with the same name exists. +/// +/// \param[in] olditem The item to be found and replaced +/// \param[in] newitem The new item to take the place of \p 'olditem' +/// \returns newitem on success and \p 'olditem' is destroyed. +/// NULL on error if \p 'olditem' was not found +/// as an immediate child. +/// \see replace(), Fl_Tree_Item::draw() +/// \version 1.3.3 ABI feature +/// +Fl_Tree_Item *Fl_Tree_Item::replace_child(Fl_Tree_Item *olditem, + Fl_Tree_Item *newitem) { + int pos = find_child(olditem); // find our index for olditem + if ( pos == -1 ) return(NULL); + newitem->_parent = this; + // replace in array (handles stitching neighboring items) + _children.replace(pos, newitem); + recalc_tree(); // newitem may have changed tree geometry + return newitem; +} +#endif + /// Remove \p 'item' from the current item's children. /// \returns 0 if removed, -1 if item not an immediate child. /// @@ -406,7 +543,11 @@ int Fl_Tree_Item::remove_child(Fl_Tree_Item *item) { } /// Remove immediate child (and its children) by its label \p 'name'. +/// If more than one item matches \p 'name', only the first +/// matching item is removed. +/// \param[in] name The label name of the immediate child to remove /// \returns 0 if removed, -1 if not found. +/// \version 1.3.3 /// int Fl_Tree_Item::remove_child(const char *name) { for ( int t=0; t /// This method is FAST, and does not involve lookups.
/// No range checking is done on either index value. +/// \param[in] ax,bx the index of the items to swap /// void Fl_Tree_Item::swap_children(int ax, int bx) { _children.swap(ax, bx); @@ -435,6 +577,9 @@ void Fl_Tree_Item::swap_children(int ax, int bx) { /// /// This method is SLOW because it involves linear lookups.
/// For speed, use swap_children(int,int) instead. +/// +/// \param[in] a,b The item ptrs of the two items to swap. +/// Both must be immediate children of the current item. /// \returns /// - 0 : OK /// - -1 : failed: item \p 'a' or \p 'b' is not our child. @@ -451,6 +596,11 @@ int Fl_Tree_Item::swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b) { } /// Internal: Horizontal connector line based on preference settings. +/// \param[in] x1 The left hand X position of the horizontal connector +/// \param[in] x2 The right hand X position of the horizontal connector +/// \param[in] y The vertical position of the horizontal connector +/// \param[in] prefs The Fl_Tree prefs +/// void Fl_Tree_Item::draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs) { fl_color(prefs.connectorcolor()); switch ( prefs.connectorstyle() ) { @@ -472,6 +622,11 @@ void Fl_Tree_Item::draw_horizontal_connector(int x1, int x2, int y, const Fl_Tre } /// Internal: Vertical connector line based on preference settings. +/// \param[in] x The x position of the vertical connector +/// \param[in] y1 The top of the vertical connector +/// \param[in] y2 The bottom of the vertical connector +/// \param[in] prefs The Fl_Tree prefs +/// void Fl_Tree_Item::draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs) { fl_color(prefs.connectorcolor()); switch ( prefs.connectorstyle() ) { @@ -535,7 +690,7 @@ const Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs, int y /// \param[in] yonly -- 0: check both event's X and Y values. /// -- 1: only check event's Y value, don't care about X. /// \returns pointer to clicked item, or NULL if none found -/// \version 1.3.3 ABI +/// \version 1.3.3 ABI feature /// Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs, int yonly) { // "Effective C++, 3rd Ed", p.23. Sola fide, Amen. @@ -620,8 +775,14 @@ static void draw_item_focus(Fl_Boxtype B, Fl_Color fg, Fl_Color bg, int X, int Y #endif } -/// Return the item's 'visible' height. -/// Doesn't include linespacing(); prevents affecting e.g. height of widget(). +/// Return the item's 'visible' height. Takes into account the item's: +/// - visibility (if !is_visible(), returns 0) +/// - labelfont() height: if label() != NULL +/// - widget() height: if widget() != NULL +/// - openicon() height (if not NULL) +/// - usericon() height (if not NULL) +/// Does NOT include Fl_Tree::linespacing(); +/// \returns maximum pixel height /// int Fl_Tree_Item::calc_item_height(const Fl_Tree_Prefs &prefs) const { if ( ! is_visible() ) return(0); @@ -645,26 +806,154 @@ int Fl_Tree_Item::calc_item_height(const Fl_Tree_Prefs &prefs) const { } #if FLTK_ABI_VERSION >= 10303 +// These methods held for 1.3.3 ABI: all need 'tree()' back-reference. + +/// Returns the recommended foreground color used for drawing this item. +/// \see draw_item_content() +/// \version 1.3.3 ABI ABI +/// +Fl_Color Fl_Tree_Item::drawfgcolor() const { + return is_selected() ? fl_contrast(_labelfgcolor, tree()->selection_color()) + : is_active() ? _labelfgcolor + : fl_inactive(_labelfgcolor); +} + +/// Returns the recommended background color used for drawing this item. +/// \see draw_item_content() +/// \version 1.3.3 ABI +/// +Fl_Color Fl_Tree_Item::drawbgcolor() const { + const Fl_Color unspecified = 0xffffffff; + return is_selected() ? is_active() ? tree()->selection_color() + : fl_inactive(tree()->selection_color()) + : _labelbgcolor == unspecified ? tree()->color() + : _labelbgcolor; +} + +/// Draw the item content +/// +/// This method can be overridden to implement custom drawing +/// by filling the label_[xywh]() area with content. +/// +/// A minimal example of how to override draw_item_content() +/// and draw just a normal item's background and label ourselves: +/// +/// \code +/// class MyTreeItem : public Fl_Tree_Item { +/// public: +/// MyTreeItem() { } +/// ~MyTreeItem() { } +/// // DRAW OUR CUSTOM CONTENT FOR THE ITEM +/// int draw_item_content(int render) { +/// // Our item's dimensions + text content +/// int X=label_x(), Y=label_y(), W=label_w(), H=label_h(); +/// const char *text = label() ? label() : ""; +/// // Rendering? Do any drawing that's needed +/// if ( render ) { +/// // Draw bg -- a filled rectangle +/// fl_color(drawbgcolor()); fl_rectf(X,Y,W,H); +/// // Draw label +/// fl_font(labelfont(), labelsize()); // use item's label font/size +/// fl_color(drawfgcolor()); // use recommended fg color +/// fl_draw(text, X,Y,W,H, FL_ALIGN_LEFT); // draw the item's label +/// } +/// // Rendered or not, we must calculate content's max X position +/// int lw=0, lh=0; +/// fl_measure(text, lw, lh); // get width of label text +/// return X + lw; // return X + label width +/// } +/// }; +/// \endcode +/// +/// You can draw anything you want inside draw_item_content() +/// using any of the fl_draw.H functions, as long as it's +/// within the label's xywh area. +/// +/// To add instances of your custom item to the tree, you can use: +/// +/// \code +/// // Example #1: using add() +/// MyTreeItem *bart = new MyTreeItem(..); // class derived from Fl_Tree_Item +/// tree->add("/Simpsons/Bart", bart); // Add item as /Simpsons/Bart +/// \endcode +/// +/// ..or you can insert or replace existing items: +/// +/// \code +/// // Example #2: using replace() +/// MyTreeItem *marge = new MyTreeItem(..); // class derived from Fl_Tree_Item +/// item = tree->add("/Simpsons/Marge"); // create item +/// item->replace(mi); // replace it with our own +/// \endcode +/// +/// \param[in] render Whether we should render content (1), or just tally +/// the geometry (0). Fl_Tree may want only to find the widest +/// item in the tree for scrollbar calculations. +/// +/// \returns the right-most X coordinate, or 'xmax' of content we drew, +/// i.e. the "scrollable" content. +/// The tree uses the largest xmax to determine the maximum +/// width of the tree's content (needed for e.g. computing the +/// horizontal scrollbar's size). +/// \version 1.3.3 ABI feature +/// +int Fl_Tree_Item::draw_item_content(int render) { + Fl_Color fg = drawfgcolor(); + Fl_Color bg = drawbgcolor(); + const Fl_Tree_Prefs &prefs = tree()->prefs(); + int xmax = label_x(); + // Background for this item, only if different from tree's bg + if ( render && (bg != tree()->color() || is_selected()) ) { + if ( is_selected() ) { // Selected? Use selectbox() style + fl_draw_box(prefs.selectbox(), + label_x(), label_y(), label_w(), label_h(), bg); + } else { // Not Selected? use plain filled rectangle + fl_color(bg); + fl_rectf(label_x(), label_y(), label_w(), label_h()); + } + if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it + } + // Draw label + if ( _label && + ( !widget() || + (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) { + if ( render ) { + fl_color(fg); + fl_font(_labelfont, _labelsize); + } + int lx = label_x()+(_label ? prefs.labelmarginleft() : 0); + int ly = label_y()+(label_h()/2)+(_labelsize/2)-fl_descent()/2; + int lw=0, lh=0; + fl_measure(_label, lw, lh); // get box around text (including white space) + if ( render ) fl_draw(_label, lx, ly); + xmax = lx + lw; // update max width of drawn item + } + return xmax; +} + /// Draw this item and its children. /// -/// \param[in] X Horizontal position for item being drawn -/// \param[in,out] Y Vertical position for item being drawn, returns new position for next item -/// \param[in] W Recommended width of item -/// \param[in] itemfocus The tree's current focus item (if any) +/// \param[in] X Horizontal position for item being drawn +/// \param[in,out] Y Vertical position for item being drawn, +/// returns new position for next item +/// \param[in] W Recommended width for item +/// \param[in] itemfocus The tree's current focus item (if any) /// \param[in,out] tree_item_xmax The tree's running xmax (right-most edge so far). -/// Mainly used by parent tree when render==0 to calculate tree's max width. -/// \param[in] lastchild Is this item the last child in a subtree? -/// \param[in] render Whether or not to render the item: -/// - 0 -- no rendering, just calculate size. -/// (used to calculate size of tree without doing drawing) -/// - 1 -- render the item as well as doing size calculations +/// Mainly used by parent tree when render==0 to +/// calculate tree's max width. +/// \param[in] lastchild Is this item the last child in a subtree? +/// \param[in] render Whether or not to render the item: +/// 0: no rendering, just calculate size w/out drawing. +/// 1: render item as well as size calc +/// +/// \version 1.3.3 ABI feature: modified parameters /// void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, int &tree_item_xmax, int lastchild, int render) { Fl_Tree_Prefs &prefs = _tree->_prefs; if ( !is_visible() ) return; - int tree_top = _tree->_tiy; - int tree_bot = tree_top + _tree->_tih; + int tree_top = tree()->_tiy; + int tree_bot = tree_top + tree()->_tih; int H = calc_item_height(prefs); // height of item int H2 = H + prefs.linespacing(); // height of item with line spacing @@ -697,23 +986,22 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, int cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth(); int conn_w = cw1>cw2 ? cw1 : cw2; - // Background xywh - int &bg_x = _label_xywh[0] = X+(icon_w/2-1+conn_w); - int &bg_y = _label_xywh[1] = Y; - int &bg_w = _label_xywh[2] = _tree->_tix + _tree->_tiw - bg_x; - int &bg_h = _label_xywh[3] = H; - // Usericon position - int uicon_x = bg_x + ( (usericon() || prefs.usericon()) ? prefs.usericonmarginleft() : 0); - int uicon_w = usericon() ? usericon()->w() : prefs.usericon() ? prefs.usericon()->w() : 0; + int uicon_x = X+(icon_w/2-1+conn_w) + ( (usericon() || prefs.usericon()) + ? prefs.usericonmarginleft() : 0); + int uicon_w = usericon() ? usericon()->w() + : prefs.usericon() ? prefs.usericon()->w() : 0; - // Label position - int label_x = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); + // Label xywh + _label_xywh[0] = uicon_x + uicon_w + prefs.labelmarginleft(); + _label_xywh[1] = Y; + _label_xywh[2] = tree()->_tix + tree()->_tiw - _label_xywh[0]; + _label_xywh[3] = H; // Begin calc of this item's max width.. // It might not even be visible, so start at zero. // - int ixmax = 0; + int xmax = 0; // Recalc widget position // Do this whether clipped or not, so that when scrolled, @@ -721,8 +1009,8 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, // (so that they don't get mouse events, etc) // if ( widget() ) { - int wx = label_x; - int wy = bg_y; + int wx = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); + int wy = label_y(); int ww = widget()->w(); // use widget's width int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) ? widget()->h() : H; @@ -742,19 +1030,14 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, if (!render) clipped = 0; // NOT rendering? Then don't clip, so we calc unclipped items char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; if ( !clipped ) { - Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, _tree->selection_color()) - : is_active() ? _labelfgcolor - : fl_inactive(_labelfgcolor); - Fl_Color bg = is_selected() ? is_active() ? _tree->selection_color() - : fl_inactive(_tree->selection_color()) - : _labelbgcolor == 0xffffffff ? _tree->color() // transparent bg? - : _labelbgcolor; + Fl_Color fg = drawfgcolor(); + Fl_Color bg = drawbgcolor(); // See if we should draw this item // If this item is root, and showroot() is disabled, don't draw. // 'clipped' is an optimization to prevent drawing anything offscreen. // if ( drawthis ) { // draw this item at all? - if ( (_tree->damage() & ~FL_DAMAGE_CHILD) || !render ) { // non-child damage? + if ( (tree()->damage() & ~FL_DAMAGE_CHILD) || !render ) { // non-child damage? // Draw connectors if ( render && prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { // Horiz connector between center of icon and text @@ -779,17 +1062,6 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, prefs.openicon()->draw(icon_x,icon_y); } } - // Background for this item - // Draw bg only if different from tree's bg - if ( render && (bg != _tree->color() || is_selected()) ) { - if ( is_selected() ) { // Selected? Use selectbox() style - fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); - } else { // Not Selected? use plain filled rectangle - fl_color(bg); - fl_rectf(bg_x,bg_y,bg_w,bg_h); - } - if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it - } // Draw user icon (if any) if ( render && usericon() ) { // Item has user icon? Use it @@ -800,44 +1072,31 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); prefs.usericon()->draw(uicon_x,uicon_y); } - // Draw label - if ( _label && - ( !widget() || - (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) { - if ( render ) { - fl_color(fg); - fl_font(_labelfont, _labelsize); - } - int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; - - int lw=0, lh=0; - fl_measure(_label, lw, lh); // get box around text (including white space) - if ( render ) fl_draw(_label, label_x, label_y); - ixmax = label_x + lw; // update max width of drawn item - } + // Draw item's content + xmax = draw_item_content(render); } // end non-child damage // Draw child FLTK widget? if ( widget() ) { if (render) - _tree->draw_child(*widget()); // let group handle drawing child + tree()->draw_child(*widget()); // let group handle drawing child if ( widget()->label() && render ) - _tree->draw_outside_label(*widget()); // label too - ixmax = widget()->x() + widget()->w(); // update max width of widget + tree()->draw_outside_label(*widget());// label too + xmax = widget()->x() + widget()->w(); // update max width of widget } // Draw focus box around item's bg last if ( render && this == itemfocus && Fl::visible_focus() && - Fl::focus() == _tree && + Fl::focus() == tree() && prefs.selectmode() != FL_TREE_SELECT_NONE ) { - draw_item_focus(FL_NO_BOX,fg,bg,bg_x+1,bg_y+1,bg_w-1,bg_h-1); + draw_item_focus(FL_NO_BOX,fg,bg,label_x()+1,label_y()+1,label_w()-1,label_h()-1); } } // end drawthis } // end clipped if ( drawthis ) Y += H2; // adjust Y (even if clipped) // Manage tree_item_xmax - if ( ixmax > tree_item_xmax ) - tree_item_xmax = ixmax; + if ( xmax > tree_item_xmax ) + tree_item_xmax = xmax; // Draw child items (if any) if ( has_children() && is_open() ) { int child_x = drawthis ? (hconn_x_center - (icon_w/2) + 1) // offset children to right, @@ -866,13 +1125,16 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, /// Draw this item and its children. /// /// \param[in] X Horizontal position for item being drawn -/// \param[in,out] Y Vertical position for item being drawn, returns new position for next item +/// \param[in,out] Y Vertical position for item being drawn, +/// returns new position for next item /// \param[in] W Recommended width of item /// \param[in] tree The parent tree /// \param[in] itemfocus The tree's current focus item (if any) /// \param[in] prefs The tree's preferences /// \param[in] lastchild Is this item the last child in a subtree? /// +/// \version 1.3.0 release, removed 1.3.3 ABI +/// void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, Fl_Tree_Item *itemfocus, const Fl_Tree_Prefs &prefs, int lastchild) { @@ -961,13 +1223,14 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, char clipped = ((Y+H) < tree_top) || (Y>tree_bot) ? 1 : 0; char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; if ( !clipped ) { + const Fl_Color unspecified = 0xffffffff; Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, tree->selection_color()) - : is_active() ? _labelfgcolor + : is_active() ? _labelfgcolor : fl_inactive(_labelfgcolor); Fl_Color bg = is_selected() ? is_active() ? tree->selection_color() : fl_inactive(tree->selection_color()) - : _labelbgcolor == 0xffffffff ? tree->color() // transparent bg? - : _labelbgcolor; + : _labelbgcolor == unspecified ? tree->color() + : _labelbgcolor; // See if we should draw this item // If this item is root, and showroot() is disabled, don't draw. // 'clipped' is an optimization to prevent drawing anything offscreen. @@ -998,51 +1261,40 @@ void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, prefs.openicon()->draw(icon_x,icon_y); } } - // Draw the item -#if FLTK_ABI_VERSION >= 10303 - if ( !widget() && prefs.item_draw_callback() ) { - // Draw item using user supplied custom item draw callback - prefs.do_item_draw_callback(this); - } - else -#endif - { - // Background for this item - // Draw bg only if different from tree's bg - if ( bg != tree->color() || is_selected() ) { - if ( is_selected() ) { // Selected? Use selectbox() style - fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); - } else { // Not Selected? use plain filled rectangle - fl_color(bg); - fl_rectf(bg_x,bg_y,bg_w,bg_h); - } - if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it - } - // Draw user icon (if any) - if ( usericon() ) { - // Item has user icon? Use it - int uicon_y = item_y_center - (usericon()->h() >> 1); - usericon()->draw(uicon_x,uicon_y); - } else if ( prefs.usericon() ) { - // Prefs has user icon? Use it - int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); - prefs.usericon()->draw(uicon_x,uicon_y); + // Draw background for the item.. only if different from tree's bg color + if ( bg != tree->color() || is_selected() ) { + if ( is_selected() ) { // Selected? Use selectbox() style + fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); + } else { // Not Selected? use plain filled rectangle + fl_color(bg); + fl_rectf(bg_x,bg_y,bg_w,bg_h); } - // Draw label + if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it + } + // Draw user icon (if any) + if ( usericon() ) { + // Item has user icon? Use it + int uicon_y = item_y_center - (usericon()->h() >> 1); + usericon()->draw(uicon_x,uicon_y); + } else if ( prefs.usericon() ) { + // Prefs has user icon? Use it + int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); + prefs.usericon()->draw(uicon_x,uicon_y); + } + // Draw label #if FLTK_ABI_VERSION >= 10301 - if ( _label && - ( !widget() || - (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) + if ( _label && + ( !widget() || + (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) #else /*FLTK_ABI_VERSION*/ - if ( _label && !widget() ) // back compat: don't draw label if widget() present + if ( _label && !widget() ) // back compat: don't draw label if widget() present #endif /*FLTK_ABI_VERSION*/ - { - fl_color(fg); - fl_font(_labelfont, _labelsize); - int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; - fl_draw(_label, label_x, label_y); - } - } // end non-custom draw + { + fl_color(fg); + fl_font(_labelfont, _labelsize); + int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; + fl_draw(_label, label_x, label_y); + } } // end non-child damage // Draw child FLTK widget? if ( widget() ) { @@ -1292,7 +1544,8 @@ Fl_Tree_Item *Fl_Tree_Item::prev_sibling() { /// Update our _prev_sibling and _next_sibling pointers to point to neighbors /// given \p index as being our current position in the parent's item array. -/// Call this whenever items in the array are added/removed/moved/swapped. +/// Call this whenever items in the array are added/removed/moved/swapped/etc. +/// \param[in] index Our index# in the parent /// void Fl_Tree_Item::update_prev_next(int index) { #if FLTK_ABI_VERSION >= 10301 @@ -1323,6 +1576,7 @@ void Fl_Tree_Item::update_prev_next(int index) { /// /// \returns the next open() visible() item below us, /// or 0 if there's no more items. +/// \version 1.3.3 /// Fl_Tree_Item *Fl_Tree_Item::next_visible(Fl_Tree_Prefs &prefs) { Fl_Tree_Item *item = this; @@ -1335,7 +1589,7 @@ Fl_Tree_Item *Fl_Tree_Item::next_visible(Fl_Tree_Prefs &prefs) { } /// Same as next_visible(). -/// \deprecated in 1.3.3 ABI for confusing name, use next_visible() +/// \deprecated in 1.3.3 for confusing name, use next_visible() instead Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) { return next_visible(prefs); } @@ -1370,7 +1624,7 @@ Fl_Tree_Item *Fl_Tree_Item::prev_visible(Fl_Tree_Prefs &prefs) { } /// Same as prev_visible(). -/// \deprecated in 1.3.3 ABI for confusing name, use prev_visible() +/// \deprecated in 1.3.3 for confusing name, use prev_visible() /// Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) { return prev_visible(prefs); @@ -1391,6 +1645,7 @@ int Fl_Tree_Item::visible_r() const { /// Call this when our geometry is changed. (Font size, label contents, etc) /// Schedules tree to recalculate itself, as changes to us may affect tree /// widget's scrollbar visibility and tab sizes. +/// \version 1.3.3 ABI /// void Fl_Tree_Item::recalc_tree() { #if FLTK_ABI_VERSION >= 10303 diff --git a/src/Fl_Tree_Item_Array.cxx b/src/Fl_Tree_Item_Array.cxx index 2519cf226..d83b0be12 100644 --- a/src/Fl_Tree_Item_Array.cxx +++ b/src/Fl_Tree_Item_Array.cxx @@ -150,6 +150,29 @@ void Fl_Tree_Item_Array::add(Fl_Tree_Item *val) { insert(_total, val); } +/// Replace the item at \p index with \p newitem. +/// +/// Old item at index position will be destroyed, +/// and the new item will take it's place, and stitched into the linked list. +/// +void Fl_Tree_Item_Array::replace(int index, Fl_Tree_Item *newitem) { + if ( _items[index] ) { // delete if non-zero +#if FLTK_ABI_VERSION >= 10303 + if ( _flags & MANAGE_ITEM ) +#endif + // Destroy old item + delete _items[index]; + } + _items[index] = newitem; // install new item +#if FLTK_ABI_VERSION >= 10303 + if ( _flags & MANAGE_ITEM ) +#endif + { + // Restitch into linked list + _items[index]->update_prev_next(index); + } +} + /// Remove the item at \param[in] index from the array. /// /// The item will be delete'd (if non-NULL), so its destructor will be called. -- cgit v1.2.3