Browse Source

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
pull/49/head
Greg Ercolano 11 years ago
parent
commit
abdc834705
  1. 35
      FL/Fl_Tree.H
  2. 98
      FL/Fl_Tree_Item.H
  3. 1
      FL/Fl_Tree_Item_Array.H
  4. 163
      examples/tree-custom-draw-items.cxx
  5. 189
      src/Fl_Tree.cxx
  6. 551
      src/Fl_Tree_Item.cxx
  7. 23
      src/Fl_Tree_Item_Array.cxx

35
FL/Fl_Tree.H

@ -372,11 +372,18 @@ public:
/////////////////////// ///////////////////////
void root_label(const char *new_label); void root_label(const char *new_label);
Fl_Tree_Item* root(); Fl_Tree_Item* root();
void root(Fl_Tree_Item *newitem);
const Fl_Tree_Prefs& prefs() const { return _prefs; }
//////////////////////////////// ////////////////////////////////
// Item creation/removal methods // 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 *add(const char *path, Fl_Tree_Item *newitem);
#endif
Fl_Tree_Item* add(Fl_Tree_Item *parent_item, const char *name); 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_above(Fl_Tree_Item *above, const char *name);
Fl_Tree_Item* insert(Fl_Tree_Item *item, const char *name, int pos); 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(); // deprecated in ABI 10303
Fl_Tree_Item *last_visible_item(); Fl_Tree_Item *last_visible_item();
#if FLTK_ABI_VERSION >= 10303 #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 #endif
Fl_Tree_Item *first_selected_item(); Fl_Tree_Item *first_selected_item();
Fl_Tree_Item *last_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 deselect_all(Fl_Tree_Item *item=0, int docallback=1);
int select_only(Fl_Tree_Item *selitem, 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 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 #if FLTK_ABI_VERSION >= 10303
void extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to); int extend_selection(Fl_Tree_Item *from,
int extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int dir, int val, bool visible); Fl_Tree_Item *to,
int extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible); int val=1,
bool visible=false);
#else #else
// Adding overload if not at least one overload breaks ABI, so avoid
// See: http://www.ros.org/reps/rep-0009.html
private: private:
int extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, int dir, int val, bool visible); // Adding overload if not at least one overload breaks ABI, so avoid
int extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to, int val, bool visible); // 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: public:
#endif #endif
void set_item_focus(Fl_Tree_Item *item); void set_item_focus(Fl_Tree_Item *item);
@ -522,10 +537,6 @@ public:
void item_draw_mode(int mode); void item_draw_mode(int mode);
#endif #endif
#if FLTK_ABI_VERSION >= 10303 #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_dimensions();
void calc_tree(); void calc_tree();
#endif #endif

98
FL/Fl_Tree_Item.H

@ -52,6 +52,17 @@
/// When you make changes to items, you'll need to tell the tree to redraw() /// When you make changes to items, you'll need to tell the tree to redraw()
/// for the changes to show up. /// 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_Tree;
class FL_EXPORT Fl_Tree_Item { class FL_EXPORT Fl_Tree_Item {
#if FLTK_ABI_VERSION >= 10303 #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 *_prev_sibling; // previous sibling (same level)
Fl_Tree_Item *_next_sibling; // next sibling (same level) Fl_Tree_Item *_next_sibling; // next sibling (same level)
#endif /*FLTK_ABI_VERSION*/ #endif /*FLTK_ABI_VERSION*/
// Protected methods
protected: protected:
void _Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree); void _Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree);
void show_widgets(); 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_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 draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs);
void recalc_tree(); 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: public:
Fl_Tree_Item(const Fl_Tree_Prefs &prefs); // CTOR -- backwards compatible Fl_Tree_Item(const Fl_Tree_Prefs &prefs); // CTOR -- backwards compatible
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
@ -110,20 +128,35 @@ public:
#endif #endif
~Fl_Tree_Item(); // DTOR ~Fl_Tree_Item(); // DTOR
Fl_Tree_Item(const Fl_Tree_Item *o); // COPY CTOR 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]); } int x() const { return(_xywh[0]); }
/// The item's y position relative to the window
int y() const { return(_xywh[1]); } 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]); } int w() const { return(_xywh[2]); }
/// The item's height
int h() const { return(_xywh[3]); } 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]); } 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]); } 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]); } 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 label_h() const { return(_label_xywh[3]); }
int calc_item_height(const Fl_Tree_Prefs &prefs) const;
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
virtual int draw_item_content(int render);
void draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, 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 #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 #endif
void show_self(const char *indent = "") const; void show_self(const char *indent = "") const;
void label(const char *val); void label(const char *val);
@ -157,25 +190,27 @@ public:
void labelfgcolor(Fl_Color val) { void labelfgcolor(Fl_Color val) {
_labelfgcolor = 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. /// Return item's label foreground text color.
Fl_Color labelfgcolor() const { Fl_Color labelfgcolor() const {
return(_labelfgcolor); 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. /// 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) { void labelbgcolor(Fl_Color val) {
_labelbgcolor = val; _labelbgcolor = val;
} }
/// Return item's background text color. /// Return item's label background text color.
/// If the color is 0xffffffff, it is 'transparent'. /// 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 { Fl_Color labelbgcolor() const {
return(_labelbgcolor); return(_labelbgcolor);
} }
@ -209,15 +244,29 @@ public:
void clear_children(); void clear_children();
void swap_children(int ax, int bx); void swap_children(int ax, int bx);
int swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b); int swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b);
const Fl_Tree_Item *find_child_item(char **arr) const; // const const Fl_Tree_Item *find_child_item(const char *name) const;
Fl_Tree_Item *find_child_item(char **arr); // non-const Fl_Tree_Item *find_child_item(const char *name);
const Fl_Tree_Item *find_item(char **arr) const; // const const Fl_Tree_Item *find_child_item(char **arr) const;
Fl_Tree_Item *find_item(char **arr); // non-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 // Adding items
////////////////// //////////////////
Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, const char *new_label); Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs,
Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs, char **arr); 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(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); Fl_Tree_Item *insert_above(const Fl_Tree_Prefs &prefs, const char *new_label);
int depth() const; int depth() const;
@ -246,6 +295,7 @@ public:
_parent = val; _parent = val;
} }
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
const Fl_Tree_Prefs& prefs() const;
/// Return the tree for this item. /// Return the tree for this item.
const Fl_Tree *tree() const { const Fl_Tree *tree() const {
return(_tree); return(_tree);
@ -323,9 +373,8 @@ public:
/// Change the item's activation state to the optionally specified 'val'. /// Change the item's activation state to the optionally specified 'val'.
/// ///
/// When deactivated, the item will be 'grayed out'; the callback() /// When deactivated, the item will be 'grayed out'; the callback()
/// won't be invoked if the user clicks on the label. If the item /// won't be invoked if the user clicks on the label. If a widget()
/// has a widget() associated with the item, its activation state /// is associated with the item, its activation state will be changed as well.
/// will be changed as well.
/// ///
/// If 'val' is not specified, the item will be activated. /// If 'val' is not specified, the item will be activated.
/// ///
@ -350,7 +399,7 @@ public:
char is_activated() const { char is_activated() const {
return(is_flag(ACTIVE)); return(is_flag(ACTIVE));
} }
/// See if the item is activated. /// See if the item is activated. Alias for is_activated().
char is_active() const { char is_active() const {
return(is_activated()); return(is_activated());
} }
@ -391,6 +440,7 @@ public:
} }
// Protected methods // Protected methods
// TODO: move these to top 'protected:' section
protected: protected:
#if FLTK_ABI_VERSION >= 10301 #if FLTK_ABI_VERSION >= 10301
/// Set a flag to an on or off value. val is 0 or 1. /// Set a flag to an on or off value. val is 0 or 1.

1
FL/Fl_Tree_Item_Array.H

@ -87,6 +87,7 @@ public:
void clear(); void clear();
void add(Fl_Tree_Item *val); void add(Fl_Tree_Item *val);
void insert(int pos, Fl_Tree_Item *new_item); void insert(int pos, Fl_Tree_Item *new_item);
void replace(int pos, Fl_Tree_Item *new_item);
void remove(int index); void remove(int index);
int remove(Fl_Tree_Item *item); int remove(Fl_Tree_Item *item);
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303

163
examples/tree-custom-draw-items.cxx

@ -17,68 +17,159 @@
// http://www.fltk.org/str.php // http://www.fltk.org/str.php
// //
#include <stdio.h> #include <stdio.h>
#include <math.h> // sin(3) #include <time.h> /* ctime.. */
#include <FL/Fl.H> #include <FL/Fl.H>
#include <FL/Fl_Double_Window.H> #include <FL/Fl_Double_Window.H>
#include <FL/Fl_Tree.H> #include <FL/Fl_Tree.H>
#ifndef MAX
#define MAX(a,b) ((a)>(b))?(a):(b)
#endif
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
static void draw_item(Fl_Tree_Item *item, void *data) { // DERIVE CUSTOM CLASS FROM Fl_Tree_Item TO IMPLEMENT SHOWING THE TIME OF DAY
Fl_Tree *tree = (Fl_Tree*)data; // This demonstrates that item content can be dynamic and highly customized.
int X=item->label_x(), Y=item->label_y(), //
W=item->label_w(), H=item->label_h(); class MyTimeItem : public Fl_Tree_Item {
// Draw the background const char *time_format;
fl_color(item->is_selected() ? tree->selection_color() : item->labelbgcolor()); protected:
fl_rectf(X,Y,W,H); // Remove trailing crlf
// Draw some red/grn/blu boxes const char* StripCrlf(char *s)
int x = X + 5; { char *ss = strchr(s, '\n'); if (ss) *ss = 0; return s; }
fl_color(FL_RED); fl_rectf(x, Y+2, 10, H-4); x += 10; const struct tm* GetTimeStruct() {
fl_color(FL_GREEN); fl_rectf(x, Y+2, 10, H-4); x += 10; time_t t = time(NULL);
fl_color(FL_BLUE); fl_rectf(x, Y+2, 10, H-4); x += 10; if ( strcmp(time_format, "Local") == 0 ) return localtime(&t);
x += 5; if ( strcmp(time_format, "GMT" ) == 0 ) return gmtime(&t);
// Draw text return 0;
fl_font(item->labelfont(), item->labelsize()); }
fl_color(item->labelfgcolor()); public:
char s[80]; MyTimeItem(Fl_Tree *tree, const char *time_format) : Fl_Tree_Item(tree) {
sprintf(s, "Custom: '%s'", item->label()?item->label():"---"); label(time_format);
fl_draw(s, x+tree->labelmarginleft(),Y,W,H, FL_ALIGN_LEFT); this->time_format = time_format;
int fw=0,fh=0; }
fl_measure(s,fw,fh); // Handle custom drawing of the item
x += fw + 10; // Fl_Tree has already handled drawing everything to the left
// Draw a red sine wave past the text to end of xywh area // of the label area, including any 'user icon', collapse buttons,
fl_color(FL_RED); // connector lines, etc.
for ( float a=0.0; x<(X+W); x++,a+=.1) { //
int y = Y + sin(a) * ((H-2)/2) + (H/2); // All we're responsible for is drawing the 'label' area of the item
fl_point(x,y); // 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[]) { int main(int argc, char *argv[]) {
Fl::scheme("gtk+"); 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(); win->begin();
{ {
// Create the tree // Create the tree
Fl_Tree *tree = new Fl_Tree(0, 0, win->w(), win->h()); Fl_Tree *tree = new Fl_Tree(0, 0, win->w(), win->h());
tree->showroot(0); // don't show root of tree tree->showroot(0); // don't show root of tree
tree->item_draw_callback(draw_item, (void*)tree); // setup a callback for the tree tree->selectmode(FL_TREE_SELECT_MULTI); // multiselect
// Add some items // Add some items
tree->add("Flintstones/Fred"); tree->add("Flintstones/Fred");
tree->add("Flintstones/Wilma"); tree->add("Flintstones/Wilma");
tree->add("Flintstones/Pebbles"); tree->add("Flintstones/Pebbles");
tree->add("Simpsons/Homer"); {
tree->add("Simpsons/Marge"); MyTimeItem *myitem;
tree->add("Simpsons/Bart"); myitem = new MyTimeItem(tree, "Local"); // create custom item
tree->add("Simpsons/Lisa"); 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/Warden");
tree->add("Superjail/Jared"); tree->add("Superjail/Jared");
tree->add("Superjail/Alice"); tree->add("Superjail/Alice");
tree->add("Superjail/Jailbot"); tree->add("Superjail/Jailbot");
tree->show_self();
// Start with some items closed // Start with some items closed
tree->close("Simpsons");
tree->close("Superjail"); 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->end();
win->resizable(win); win->resizable(win);

189
src/Fl_Tree.cxx

@ -58,7 +58,7 @@ static char **parse_path(const char *path) {
static void free_path(char **arr) { static void free_path(char **arr) {
if ( arr ) { if ( arr ) {
if ( arr[0] ) { delete[] arr[0]; } // deletes cp in parse_path 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'. /// depending on direction \p 'dir', \p 'val', and \p 'visible'.
/// ///
/// Efficient: does not walk entire tree; starts with \p 'from' and stops /// 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 /// at \p 'to' while moving in direction \p 'dir'. Dir must be specified though.
/// though; when not available (such as during SHIFT-click operations), #if FLTK_ABI_VERSION >= 10303
/// the other method extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,bool) ///
/// should be used. Handles calling redraw() if anything changed. /// 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] from Starting item
/// \param[in] to Ending item /// \param[in] to Ending item
@ -140,15 +145,10 @@ Fl_Tree::~Fl_Tree() {
/// \param[in] visible true=affect only open(), visible items,<br> /// \param[in] visible true=affect only open(), visible items,<br>
/// false=affect open or closed items (default) /// false=affect open or closed items (default)
/// \returns The number of items whose selection states were changed, if any. /// \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_dir(Fl_Tree_Item *from, Fl_Tree_Item *to,
int Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to, int dir, int val, bool visible ) {
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 changed = 0; int changed = 0;
for (Fl_Tree_Item *item=from; item; item = next_item(item, dir, visible) ) { for (Fl_Tree_Item *item=from; item; item = next_item(item, dir, visible) ) {
switch (val) { 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'. /// Extend a selection between \p 'from' and \p 'to' depending on \p 'visible'.
/// ///
/// Similar to the more efficient /// Similar to the more efficient
/// extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,int,bool) method, /// extend_selection_dir(Fl_Tree_Item*,Fl_Tree_Item*,int dir,int val,bool vis)
/// but direction (up or down) doesn't need to be known.<br> /// method, but direction (up or down) doesn't need to be known.<br>
/// We're less efficient because we search the tree for to/from, then operate /// 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", /// on items in between. The more efficient method avoids the "search",
/// but necessitates a direction to be specified to find \p 'to'.<br> /// but necessitates a direction to be specified to find \p 'to'.<br>
@ -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,<br> /// \param[in] visible true=affect only open(), visible items,<br>
/// false=affect open or closed items (default) /// false=affect open or closed items (default)
/// \returns The number of items whose selection states were changed, if any. /// \returns The number of items whose selection states were changed, if any.
///
#if FLTK_ABI_VERSION >= 10303 #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 Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to,
int val, bool visible) { int val, bool visible) {
#else #else
/// \notes Made public in 1.3.3 ABI
// Adding overload if not at least one overload breaks ABI, so avoid // 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 Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to,
int val, bool visible) { int val, bool visible) {
#endif #endif
@ -237,13 +239,17 @@ int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to,
} }
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
// nothing // not needed, above overload handles this
#else #else
/// Extend a selection between \p 'from' and \p 'to'. /// 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) { void Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to) {
const int val = 1; const int val = 1; // 0=clr, 1=set, 2=toggle
const bool visible = false; const bool visible = false; // true=only 'open' items, false='open' or 'closed'
extend_selection__(from, to, val, visible); extend_selection__(from, to, val, visible); // use private method until we can release it
} }
#endif #endif
@ -521,11 +527,7 @@ int Fl_Tree::handle(int e) {
Fl_Tree_Item *to = item; Fl_Tree_Item *to = item;
int val = is_ctrl ? 2 : 1; // toggle_select() or just select()? int val = is_ctrl ? 2 : 1; // toggle_select() or just select()?
bool visible = true; bool visible = true;
#if FLTK_ABI_VERSION >= 10303 extend_selection_dir(from, to, dir, val, visible);
extend_selection(from, to, dir, val, visible);
#else
extend_selection__(from, to, dir, val, visible);
#endif
break; break;
} }
} }
@ -850,6 +852,7 @@ int Fl_Tree::draw_tree() {
/// Print the tree as 'ascii art' to stdout. /// Print the tree as 'ascii art' to stdout.
/// Used mainly for debugging. /// Used mainly for debugging.
/// \todo should be const /// \todo should be const
/// \version 1.3.0
/// ///
void Fl_Tree::show_self() { void Fl_Tree::show_self() {
if ( ! _root ) return; if ( ! _root ) return;
@ -870,9 +873,28 @@ Fl_Tree_Item* Fl_Tree::root() {
return(_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'. /// Adds a new item, given a menu style \p 'path'.
/// Any parent nodes that don't already exist are created automatically. /// Any parent nodes that don't already exist are created automatically.
/// Adds the item based on the value of sortorder(). /// 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 '\') /// To specify items or submenus that contain slashes ('/' or '\')
/// use an escape character to protect them, e.g. /// 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" /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp"
/// \endcode /// \endcode
/// \param[in] path The path to the item, e.g. "Flintsone/Fred". /// \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. /// \returns The new item added, or 0 on error.
/// \version 1.3.3
/// ///
Fl_Tree_Item* Fl_Tree::add(const char *path) { Fl_Tree_Item* Fl_Tree::add(const char *path, Fl_Tree_Item *item) {
if ( ! _root ) { // Create root if none // Tree has no root? make one
if ( ! _root ) {
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
_root = new Fl_Tree_Item(this); _root = new Fl_Tree_Item(this);
#else #else
@ -892,19 +919,36 @@ Fl_Tree_Item* Fl_Tree::add(const char *path) {
#endif #endif
_root->parent(0); _root->parent(0);
_root->label("ROOT"); _root->label("ROOT");
} }
// Find parent item via path
char **arr = parse_path(path); char **arr = parse_path(path);
Fl_Tree_Item *item = _root->add(_prefs, arr); item = _root->add(_prefs, arr, item);
free_path(arr); free_path(arr);
return(item); 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'. /// 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. /// \param[in] parent_item The parent item the new child item will be added to.
/// Must not be NULL. /// Must not be NULL.
/// \param[in] name The label for the new item /// \param[in] name The label for the new item
/// \returns The new item added. /// \returns The new item added.
/// \version 1.3.0 release
/// ///
Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *parent_item, const char *name) { Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *parent_item, const char *name) {
return(parent_item->add(_prefs, name)); return(parent_item->add(_prefs, name));
@ -949,7 +993,7 @@ int Fl_Tree::remove(Fl_Tree_Item *item) {
return(0); return(0);
} }
/// Clear all children from the tree. /// Clear the entire tree's children, including the root.
/// The tree will be left completely empty. /// The tree will be left completely empty.
/// ///
void Fl_Tree::clear() { void Fl_Tree::clear() {
@ -987,7 +1031,7 @@ void Fl_Tree::clear_children(Fl_Tree_Item *item) {
/// \see item_pathname() /// \see item_pathname()
/// ///
Fl_Tree_Item *Fl_Tree::find_item(const char *path) { 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<Fl_Tree_Item*>( return(const_cast<Fl_Tree_Item*>(
static_cast<const Fl_Tree&>(*this).find_item(path))); static_cast<const Fl_Tree&>(*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] 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. /// \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. /// \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) { Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) {
return next_item(item, dir, true); 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 the first open(), visible item in the tree, or 0 if none.
/// \returns First visible item in 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() /// \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* Fl_Tree::first_visible_item() {
Fl_Tree_Item *i = showroot() ? first() : next(first()); 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. /// 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() { Fl_Tree_Item* Fl_Tree::last_visible() {
return(last_visible_item()); 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 the last open(), visible item in the tree.
/// \returns Last visible item in the tree, or 0 if none. /// \returns Last visible item in the tree, or 0 if none.
/// \see first_visible_item(), last_visible_item(), next_visible_item() /// \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* Fl_Tree::last_visible_item() {
Fl_Tree_Item *item = last(); 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. /// \returns The last selected item, or 0 if none.
/// \see first_selected_item(), last_selected_item(), next_selected_item() /// \see first_selected_item(), last_selected_item(), next_selected_item()
/// \version 1.3.3
/// ///
Fl_Tree_Item *Fl_Tree::last_selected_item() { Fl_Tree_Item *Fl_Tree::last_selected_item() {
return(next_selected_item(0, FL_Up)); 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 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 'visible' is false, even items whose parents are close()ed will be returned.
/// ///
/// If \p item is 0, the return value will be: /// If \p item is 0, the return value will be the result of this truth table:
/// <pre> /// <PRE>
/// last_visible_item() - If \p visible=true and \p dir=FL_Up<br> /// visible=true visible=false
/// first_visible_item() - If \p visible=true and \p dir=FL_Down<br> /// ------------------- -------------
/// last() - If \p visible=false and \p dir=FL_Up<br> /// dir=Fl_Up: last_visible_item() last()
/// first() - If \p visible=false and \p dir=FL_Down /// dir=Fl_Down: first_visible_item() first()
/// </pre> /// </PRE>
/// ///
/// \par Example use: /// \par Example use:
/// \code /// \code
@ -1361,7 +1409,8 @@ Fl_Tree_Item *Fl_Tree::last_selected_item() {
/// \see first(), last(), next(),<BR> /// \see first(), last(), next(),<BR>
/// first_visible_item(), last_visible_item(), next_visible_item(),<BR> /// first_visible_item(), last_visible_item(), next_visible_item(),<BR>
/// first_selected_item(), last_selected_item(), next_selected_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) { Fl_Tree_Item *Fl_Tree::next_item(Fl_Tree_Item *item, int dir, bool visible) {
if ( ! item ) { // no start item? if ( ! item ) { // no start item?
if ( visible ) { 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) /// FL_Down for down the tree (default)
/// \returns The next selected item, or 0 if there are no more selected items. /// \returns The next selected item, or 0 if there are no more selected items.
/// \see first_selected_item(), last_selected_item(), next_selected_item() /// \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) { Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item, int dir) {
switch (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. /// \param[out] ret_items The returned array of selected items.
/// \returns The number of items in the returned array. /// \returns The number of items in the returned array.
/// \see first_selected_item(), next_selected_item() /// \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) { int Fl_Tree::get_selected_items(Fl_Tree_Item_Array &ret_items) {
ret_items.clear(); ret_items.clear();
@ -2350,7 +2401,7 @@ void Fl_Tree::item_reselect_mode(Fl_Tree_Item_Reselect_Mode mode) {
} }
#endif #endif
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10301
/// Get the 'item draw mode' used for the tree /// Get the 'item draw mode' used for the tree
Fl_Tree_Item_Draw_Mode Fl_Tree::item_draw_mode() const { Fl_Tree_Item_Draw_Mode Fl_Tree::item_draw_mode() const {
return(_prefs.item_draw_mode()); 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) { void Fl_Tree::item_draw_mode(int mode) {
_prefs.item_draw_mode(Fl_Tree_Item_Draw_Mode(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 #endif
/// See if \p 'item' is currently displayed on-screen (visible within the widget). /// 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 /// Normally you should not need this method, and should use the global
/// Fl::scrollbar_size(int) instead to manage the size of ALL /// Fl::scrollbar_size(int) instead to manage the size of ALL
/// your widgets' scrollbars. This ensures your application /// your widgets' scrollbars. This ensures your application
/// has a consistent UI, is the default behavior, and is normally /// has a consistent UI, and is the default behavior. Normally
/// what you want. /// this is what you want.
/// ///
/// Only use THIS method if you really need to override just this /// Only use this method if you really need to override just THIS
/// widget instance's scrollbar size. (The need for this should be rare.) /// 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 /// Setting \p size to the special value of 0 causes the widget to
/// track the global Fl::scrollbar_size(), which is the default. /// track the global Fl::scrollbar_size(), which is the default.

551
src/Fl_Tree_Item.cxx

@ -36,7 +36,9 @@ static int event_inside(const int xywh[4]) {
/// Constructor. /// Constructor.
/// Makes a new instance of Fl_Tree_Item using defaults from \p 'prefs'. /// 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) { Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) {
_Init(prefs, 0); _Init(prefs, 0);
@ -91,6 +93,11 @@ void Fl_Tree_Item::_Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree) {
/// Constructor. /// Constructor.
/// Makes a new instance of Fl_Tree_Item for \p 'tree'. /// 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) { Fl_Tree_Item::Fl_Tree_Item(Fl_Tree *tree) {
_Init(tree->_prefs, tree); _Init(tree->_prefs, tree);
} }
@ -186,7 +193,7 @@ const char *Fl_Tree_Item::label() const {
return(_label); 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 { const Fl_Tree_Item *Fl_Tree_Item::child(int index) const {
return(_children[index]); return(_children[index]);
} }
@ -201,25 +208,51 @@ void Fl_Tree_Item::clear_children() {
/// that has the label \p 'name'. /// that has the label \p 'name'.
/// ///
/// \returns index of found item, or -1 if not found. /// \returns index of found item, or -1 if not found.
/// \version 1.3.0 release
/// ///
int Fl_Tree_Item::find_child(const char *name) { int Fl_Tree_Item::find_child(const char *name) {
if ( name ) { if ( name ) {
for ( int t=0; t<children(); t++ ) { for ( int t=0; t<children(); t++ )
if ( child(t)->label() ) { if ( child(t)->label() )
if ( strcmp(child(t)->label(), name) == 0 ) { if ( strcmp(child(t)->label(), name) == 0 )
return(t); return(t);
}
}
}
} }
return(-1); 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; t<children(); t++ )
if ( child(t)->label() )
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<Fl_Tree_Item*>(
static_cast<const Fl_Tree_Item &>(*this).find_child_item(name)));
}
/// Find child item by descending array \p 'arr' of names. /// Find child item by descending array \p 'arr' of names.
/// Does not include self in search. /// Does not include self in search.
/// Only Fl_Tree should need this method. /// Only Fl_Tree should need this method.
/// ///
/// \returns item, or 0 if not found /// \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 { const Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) const {
for ( int t=0; t<children(); t++ ) { for ( int t=0; t<children(); t++ ) {
@ -241,9 +274,10 @@ const Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) const {
/// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead.
/// ///
/// \returns item, or 0 if not found /// \returns item, or 0 if not found
/// \version 1.3.0 release
/// ///
Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) { Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) {
// I evoke "Effective C++, 3rd Ed", p.23. Sola fide, Amen. // "Effective C++, 3rd Ed", p.23. Sola fide, Amen.
return(const_cast<Fl_Tree_Item*>( return(const_cast<Fl_Tree_Item*>(
static_cast<const Fl_Tree_Item &>(*this).find_child_item(arr))); static_cast<const Fl_Tree_Item &>(*this).find_child_item(arr)));
} }
@ -252,9 +286,10 @@ Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) {
/// Includes self in search. /// Includes self in search.
/// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. /// 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 { const Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) const {
if ( ! *names ) return(0);
if ( label() && strcmp(label(), *names) == 0 ) { // match self? if ( label() && strcmp(label(), *names) == 0 ) { // match self?
++names; // skip self ++names; // skip self
if ( *names == 0 ) return(this); // end of names, found ourself 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 /// \returns item, or 0 if not found
/// ///
Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) { 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<Fl_Tree_Item*>( return(const_cast<Fl_Tree_Item*>(
static_cast<const Fl_Tree_Item &>(*this).find_item(names))); static_cast<const Fl_Tree_Item &>(*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. /// \returns the index, or -1 if not found.
/// ///
int Fl_Tree_Item::find_child(Fl_Tree_Item *item) { int Fl_Tree_Item::find_child(Fl_Tree_Item *item) {
for ( int t=0; t<children(); t++ ) { for ( int t=0; t<children(); t++ )
if ( item == child(t) ) { if ( item == child(t) )
return(t); return(t);
}
}
return(-1); return(-1);
} }
@ -295,15 +328,33 @@ int Fl_Tree_Item::find_child(Fl_Tree_Item *item) {
/// and defaults from \p 'prefs'. /// and defaults from \p 'prefs'.
/// An internally managed copy is made of the label string. /// An internally managed copy is made of the label string.
/// Adds the item based on the value of prefs.sortorder(). /// 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,
const char *new_label) {
return(add(prefs, new_label, (Fl_Tree_Item*)0));
}
/// Add \p 'item' as immediate child with \p 'new_label'
/// and defaults from \p 'prefs'.
/// If \p 'item' is NULL, a new item is created.
/// An internally managed copy is made of the label string.
/// Adds the item based on the value of prefs.sortorder().
/// \returns the item added
/// \version 1.3.3
/// ///
Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, const char *new_label) { Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs,
const char *new_label,
Fl_Tree_Item *item) {
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303
Fl_Tree_Item *item = new Fl_Tree_Item(_tree); if ( !item )
{ item = new Fl_Tree_Item(_tree); item->label(new_label); }
#else #else
Fl_Tree_Item *item = new Fl_Tree_Item(prefs); if ( !item )
{ item = new Fl_Tree_Item(prefs); item->label(new_label); }
#endif #endif
recalc_tree(); // may change tree geometry recalc_tree(); // may change tree geometry
item->label(new_label);
item->_parent = this; item->_parent = this;
switch ( prefs.sortorder() ) { switch ( prefs.sortorder() ) {
case FL_TREE_SORT_NONE: { 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. /// Should be used only by Fl_Tree's internals.
/// Adds the item based on the value of prefs.sortorder(). /// Adds the item based on the value of prefs.sortorder().
/// \returns the item added. /// \returns the item added.
/// \version 1.3.0 release
/// ///
Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) { Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) {
int t = (*arr && *(arr+1)) ? find_child(*arr) : -1; return add(prefs, arr, 0);
Fl_Tree_Item *item = 0; }
if ( t == -1 ) {
item = (Fl_Tree_Item*)add(prefs, *arr); /// Descend into path specified by \p 'arr' and add \p 'newitem' there.
} else { /// Should be used only by Fl_Tree's internals.
item = (Fl_Tree_Item*)child(t); /// If item is NULL, a new item is created.
} /// Adds the item based on the value of prefs.sortorder().
recalc_tree(); // may change tree geometry /// \returns the item added.
if ( *(arr+1) ) { // descend? /// \version 1.3.3 ABI feature
return(item->add(prefs, arr+1)); ///
} else { Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs,
return(item); // end? done 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 /// 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); 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. /// Remove \p 'item' from the current item's children.
/// \returns 0 if removed, -1 if item not an immediate child. /// \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'. /// 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. /// \returns 0 if removed, -1 if not found.
/// \version 1.3.3
/// ///
int Fl_Tree_Item::remove_child(const char *name) { int Fl_Tree_Item::remove_child(const char *name) {
for ( int t=0; t<children(); t++ ) { for ( int t=0; t<children(); t++ ) {
@ -425,6 +566,7 @@ int Fl_Tree_Item::remove_child(const char *name) {
/// Use e.g. for sorting.<br> /// Use e.g. for sorting.<br>
/// This method is FAST, and does not involve lookups.<br> /// This method is FAST, and does not involve lookups.<br>
/// No range checking is done on either index value. /// 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) { void Fl_Tree_Item::swap_children(int ax, int bx) {
_children.swap(ax, 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.<br> /// This method is SLOW because it involves linear lookups.<br>
/// For speed, use swap_children(int,int) instead. /// 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 /// \returns
/// - 0 : OK /// - 0 : OK
/// - -1 : failed: item \p 'a' or \p 'b' is not our child. /// - -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. /// 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) { void Fl_Tree_Item::draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs) {
fl_color(prefs.connectorcolor()); fl_color(prefs.connectorcolor());
switch ( prefs.connectorstyle() ) { 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. /// 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) { void Fl_Tree_Item::draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs) {
fl_color(prefs.connectorcolor()); fl_color(prefs.connectorcolor());
switch ( prefs.connectorstyle() ) { 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. /// \param[in] yonly -- 0: check both event's X and Y values.
/// -- 1: only check event's Y value, don't care about X. /// -- 1: only check event's Y value, don't care about X.
/// \returns pointer to clicked item, or NULL if none found /// \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) { Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs, int yonly) {
// "Effective C++, 3rd Ed", p.23. Sola fide, Amen. // "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 #endif
} }
/// Return the item's 'visible' height. /// Return the item's 'visible' height. Takes into account the item's:
/// Doesn't include linespacing(); prevents affecting e.g. height of widget(). /// - 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 { int Fl_Tree_Item::calc_item_height(const Fl_Tree_Prefs &prefs) const {
if ( ! is_visible() ) return(0); 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 #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. /// Draw this item and its children.
/// ///
/// \param[in] X Horizontal position for item being drawn /// \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,
/// \param[in] W Recommended width of item /// returns new position for next item
/// \param[in] itemfocus The tree's current focus item (if any) /// \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). /// \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. /// Mainly used by parent tree when render==0 to
/// \param[in] lastchild Is this item the last child in a subtree? /// calculate tree's max width.
/// \param[in] render Whether or not to render the item: /// \param[in] lastchild Is this item the last child in a subtree?
/// - 0 -- no rendering, just calculate size. /// \param[in] render Whether or not to render the item:
/// (used to calculate size of tree without doing drawing) /// 0: no rendering, just calculate size w/out drawing.
/// - 1 -- render the item as well as doing size calculations /// 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, void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus,
int &tree_item_xmax, int lastchild, int render) { int &tree_item_xmax, int lastchild, int render) {
Fl_Tree_Prefs &prefs = _tree->_prefs; Fl_Tree_Prefs &prefs = _tree->_prefs;
if ( !is_visible() ) return; if ( !is_visible() ) return;
int tree_top = _tree->_tiy; int tree_top = tree()->_tiy;
int tree_bot = tree_top + _tree->_tih; int tree_bot = tree_top + tree()->_tih;
int H = calc_item_height(prefs); // height of item int H = calc_item_height(prefs); // height of item
int H2 = H + prefs.linespacing(); // height of item with line spacing 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 cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth();
int conn_w = cw1>cw2 ? cw1 : cw2; 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 // Usericon position
int uicon_x = bg_x + ( (usericon() || prefs.usericon()) ? prefs.usericonmarginleft() : 0); int uicon_x = X+(icon_w/2-1+conn_w) + ( (usericon() || prefs.usericon())
int uicon_w = usericon() ? usericon()->w() : prefs.usericon() ? prefs.usericon()->w() : 0; ? prefs.usericonmarginleft() : 0);
int uicon_w = usericon() ? usericon()->w()
: prefs.usericon() ? prefs.usericon()->w() : 0;
// Label position // Label xywh
int label_x = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); _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.. // Begin calc of this item's max width..
// It might not even be visible, so start at zero. // It might not even be visible, so start at zero.
// //
int ixmax = 0; int xmax = 0;
// Recalc widget position // Recalc widget position
// Do this whether clipped or not, so that when scrolled, // 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) // (so that they don't get mouse events, etc)
// //
if ( widget() ) { if ( widget() ) {
int wx = label_x; int wx = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0);
int wy = bg_y; int wy = label_y();
int ww = widget()->w(); // use widget's width int ww = widget()->w(); // use widget's width
int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET)
? widget()->h() : H; ? 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 if (!render) clipped = 0; // NOT rendering? Then don't clip, so we calc unclipped items
char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1;
if ( !clipped ) { if ( !clipped ) {
Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, _tree->selection_color()) Fl_Color fg = drawfgcolor();
: is_active() ? _labelfgcolor Fl_Color bg = drawbgcolor();
: 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;
// See if we should draw this item // See if we should draw this item
// If this item is root, and showroot() is disabled, don't draw. // If this item is root, and showroot() is disabled, don't draw.
// 'clipped' is an optimization to prevent drawing anything offscreen. // 'clipped' is an optimization to prevent drawing anything offscreen.
// //
if ( drawthis ) { // draw this item at all? 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 // Draw connectors
if ( render && prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { if ( render && prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) {
// Horiz connector between center of icon and text // 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); 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) // Draw user icon (if any)
if ( render && usericon() ) { if ( render && usericon() ) {
// Item has user icon? Use it // 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); int uicon_y = item_y_center - (prefs.usericon()->h() >> 1);
prefs.usericon()->draw(uicon_x,uicon_y); prefs.usericon()->draw(uicon_x,uicon_y);
} }
// Draw label // Draw item's content
if ( _label && xmax = draw_item_content(render);
( !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
}
} // end non-child damage } // end non-child damage
// Draw child FLTK widget? // Draw child FLTK widget?
if ( widget() ) { if ( widget() ) {
if (render) if (render)
_tree->draw_child(*widget()); // let group handle drawing child tree()->draw_child(*widget()); // let group handle drawing child
if ( widget()->label() && render ) if ( widget()->label() && render )
_tree->draw_outside_label(*widget()); // label too tree()->draw_outside_label(*widget());// label too
ixmax = widget()->x() + widget()->w(); // update max width of widget xmax = widget()->x() + widget()->w(); // update max width of widget
} }
// Draw focus box around item's bg last // Draw focus box around item's bg last
if ( render && if ( render &&
this == itemfocus && this == itemfocus &&
Fl::visible_focus() && Fl::visible_focus() &&
Fl::focus() == _tree && Fl::focus() == tree() &&
prefs.selectmode() != FL_TREE_SELECT_NONE ) { 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 drawthis
} // end clipped } // end clipped
if ( drawthis ) Y += H2; // adjust Y (even if clipped) if ( drawthis ) Y += H2; // adjust Y (even if clipped)
// Manage tree_item_xmax // Manage tree_item_xmax
if ( ixmax > tree_item_xmax ) if ( xmax > tree_item_xmax )
tree_item_xmax = ixmax; tree_item_xmax = xmax;
// Draw child items (if any) // Draw child items (if any)
if ( has_children() && is_open() ) { if ( has_children() && is_open() ) {
int child_x = drawthis ? (hconn_x_center - (icon_w/2) + 1) // offset children to right, 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. /// Draw this item and its children.
/// ///
/// \param[in] X Horizontal position for item being drawn /// \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] W Recommended width of item
/// \param[in] tree The parent tree /// \param[in] tree The parent tree
/// \param[in] itemfocus The tree's current focus item (if any) /// \param[in] itemfocus The tree's current focus item (if any)
/// \param[in] prefs The tree's preferences /// \param[in] prefs The tree's preferences
/// \param[in] lastchild Is this item the last child in a subtree? /// \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, void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree,
Fl_Tree_Item *itemfocus, Fl_Tree_Item *itemfocus,
const Fl_Tree_Prefs &prefs, int lastchild) { 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 clipped = ((Y+H) < tree_top) || (Y>tree_bot) ? 1 : 0;
char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1;
if ( !clipped ) { if ( !clipped ) {
const Fl_Color unspecified = 0xffffffff;
Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, tree->selection_color()) Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, tree->selection_color())
: is_active() ? _labelfgcolor : is_active() ? _labelfgcolor
: fl_inactive(_labelfgcolor); : fl_inactive(_labelfgcolor);
Fl_Color bg = is_selected() ? is_active() ? tree->selection_color() Fl_Color bg = is_selected() ? is_active() ? tree->selection_color()
: fl_inactive(tree->selection_color()) : fl_inactive(tree->selection_color())
: _labelbgcolor == 0xffffffff ? tree->color() // transparent bg? : _labelbgcolor == unspecified ? tree->color()
: _labelbgcolor; : _labelbgcolor;
// See if we should draw this item // See if we should draw this item
// If this item is root, and showroot() is disabled, don't draw. // If this item is root, and showroot() is disabled, don't draw.
// 'clipped' is an optimization to prevent drawing anything offscreen. // '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); prefs.openicon()->draw(icon_x,icon_y);
} }
} }
// Draw the item // Draw background for the item.. only if different from tree's bg color
#if FLTK_ABI_VERSION >= 10303 if ( bg != tree->color() || is_selected() ) {
if ( !widget() && prefs.item_draw_callback() ) { if ( is_selected() ) { // Selected? Use selectbox() style
// Draw item using user supplied custom item draw callback fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg);
prefs.do_item_draw_callback(this); } else { // Not Selected? use plain filled rectangle
} fl_color(bg);
else fl_rectf(bg_x,bg_y,bg_w,bg_h);
#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 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 FLTK_ABI_VERSION >= 10301
if ( _label && if ( _label &&
( !widget() || ( !widget() ||
(prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) )
#else /*FLTK_ABI_VERSION*/ #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*/ #endif /*FLTK_ABI_VERSION*/
{ {
fl_color(fg); fl_color(fg);
fl_font(_labelfont, _labelsize); fl_font(_labelfont, _labelsize);
int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2;
fl_draw(_label, label_x, label_y); fl_draw(_label, label_x, label_y);
} }
} // end non-custom draw
} // end non-child damage } // end non-child damage
// Draw child FLTK widget? // Draw child FLTK widget?
if ( 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 /// 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. /// 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) { void Fl_Tree_Item::update_prev_next(int index) {
#if FLTK_ABI_VERSION >= 10301 #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, /// \returns the next open() visible() item below us,
/// or 0 if there's no more items. /// 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 *Fl_Tree_Item::next_visible(Fl_Tree_Prefs &prefs) {
Fl_Tree_Item *item = this; 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(). /// 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) { Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) {
return next_visible(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(). /// 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) { Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) {
return prev_visible(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) /// Call this when our geometry is changed. (Font size, label contents, etc)
/// Schedules tree to recalculate itself, as changes to us may affect tree /// Schedules tree to recalculate itself, as changes to us may affect tree
/// widget's scrollbar visibility and tab sizes. /// widget's scrollbar visibility and tab sizes.
/// \version 1.3.3 ABI
/// ///
void Fl_Tree_Item::recalc_tree() { void Fl_Tree_Item::recalc_tree() {
#if FLTK_ABI_VERSION >= 10303 #if FLTK_ABI_VERSION >= 10303

23
src/Fl_Tree_Item_Array.cxx

@ -150,6 +150,29 @@ void Fl_Tree_Item_Array::add(Fl_Tree_Item *val) {
insert(_total, 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. /// 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. /// The item will be delete'd (if non-NULL), so its destructor will be called.

Loading…
Cancel
Save