// // "$Id: Fl_Tree_Item.cxx 7672 2010-07-10 09:44:45Z matt $" // #include #include #include #include #include #include ////////////////////// // Fl_Tree_Item.cxx ////////////////////// // // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK // Copyright (C) 2009 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Was the last event inside the specified xywh? static int event_inside(const int xywh[4]) { return(Fl::event_inside(xywh[0],xywh[1],xywh[2],xywh[3])); } /// Constructor. /// Makes a new instance of Fl_Tree_Item using defaults from 'prefs'. /// Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) { _label = 0; _labelfont = prefs.labelfont(); _labelsize = prefs.labelsize(); _labelfgcolor = prefs.fgcolor(); _labelbgcolor = prefs.bgcolor(); _widget = 0; _open = 1; _visible = 1; _active = 1; _selected = 0; _xywh[0] = 0; _xywh[1] = 0; _xywh[2] = 0; _xywh[3] = 0; _collapse_xywh[0] = 0; _collapse_xywh[1] = 0; _collapse_xywh[2] = 0; _collapse_xywh[3] = 0; _label_xywh[0] = 0; _label_xywh[1] = 0; _label_xywh[2] = 0; _label_xywh[3] = 0; _usericon = 0; _userdata = 0; _parent = 0; } // DTOR Fl_Tree_Item::~Fl_Tree_Item() { if ( _label ) { free((void*)_label); _label = 0; } _widget = 0; // Fl_Group will handle destruction _usericon = 0; // user handled allocation //_children.clear(); // array's destructor handles itself } /// Copy constructor. Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Item *o) { _label = o->label() ? strdup(o->label()) : 0; _labelfont = o->labelfont(); _labelsize = o->labelsize(); _labelfgcolor = o->labelfgcolor(); _labelbgcolor = o->labelbgcolor(); _widget = o->widget(); _open = o->_open; _visible = o->_visible; _active = o->_active; _selected = o->_selected; _xywh[0] = o->_xywh[0]; _xywh[1] = o->_xywh[1]; _xywh[2] = o->_xywh[2]; _xywh[3] = o->_xywh[3]; _collapse_xywh[0] = o->_collapse_xywh[0]; _collapse_xywh[1] = o->_collapse_xywh[1]; _collapse_xywh[2] = o->_collapse_xywh[2]; _collapse_xywh[3] = o->_collapse_xywh[3]; _label_xywh[0] = o->_label_xywh[0]; _label_xywh[1] = o->_label_xywh[1]; _label_xywh[2] = o->_label_xywh[2]; _label_xywh[3] = o->_label_xywh[3]; _usericon = o->usericon(); _userdata = o->user_data(); _parent = o->_parent; } /// Print the tree as 'ascii art' to stdout. /// Used mainly for debugging. /// void Fl_Tree_Item::show_self(const char *indent) const { if ( label() ) { printf("%s-%s (%d children, this=%p, parent=%p depth=%d)\n", indent,label(),children(),(void*)this, (void*)_parent, depth()); } if ( children() ) { char *i2 = (char*)malloc(strlen(indent) + 2); strcpy(i2, indent); strcat(i2, " |"); for ( int t=0; tshow_self(i2); } } fflush(stdout); } /// Set the label. Makes a copy of the name. void Fl_Tree_Item::label(const char *name) { if ( _label ) { free((void*)_label); _label = 0; } _label = name ? strdup(name) : 0; } /// Return the label. const char *Fl_Tree_Item::label() const { return(_label); } /// Return child item for the specified 'index'. const Fl_Tree_Item *Fl_Tree_Item::child(int index) const { return(_children[index]); } /// Clear all the children for this item. void Fl_Tree_Item::clear_children() { _children.clear(); } /// Return the index of the immediate child of this item that has the label 'name'. /// /// \returns index of found item, or -1 if not found. /// int Fl_Tree_Item::find_child(const char *name) { if ( name ) { for ( int t=0; tlabel() ) { if ( strcmp(child(t)->label(), name) == 0 ) { return(t); } } } } return(-1); } /// Find item by descending array of names. /// Only Fl_Tree should need this method. /// /// \returns item, or 0 if not found /// const Fl_Tree_Item *Fl_Tree_Item::find_item(char **arr) const { for ( int t=0; tlabel() ) { if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? if ( *(arr+1) ) { // more in arr? descend return(_children[t]->find_item(arr+1)); } else { // end of arr? done return(_children[t]); } } } } return(0); } /// Find item by by descending array of names. /// Only Fl_Tree should need this method. /// /// \returns item, or 0 if not found /// Fl_Tree_Item *Fl_Tree_Item::find_item(char **arr) { for ( int t=0; tlabel() ) { if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? if ( *(arr+1) ) { // more in arr? descend return(_children[t]->find_item(arr+1)); } else { // end of arr? done return(_children[t]); } } } } return(0); } /// Find the index number for the specified 'item' /// in the current item's list of children. /// /// \returns the index, or -1 if not found. /// int Fl_Tree_Item::find_child(Fl_Tree_Item *item) { for ( int t=0; tlabel(new_label); item->_parent = this; switch ( prefs.sortorder() ) { case FL_TREE_SORT_NONE: { _children.add(item); return(item); } case FL_TREE_SORT_ASCENDING: { for ( int t=0; t<_children.total(); t++ ) { Fl_Tree_Item *c = _children[t]; if ( c->label() && strcmp(c->label(), new_label) > 0 ) { _children.insert(t, item); return(item); } } _children.add(item); return(item); } case FL_TREE_SORT_DESCENDING: { for ( int t=0; t<_children.total(); t++ ) { Fl_Tree_Item *c = _children[t]; if ( c->label() && strcmp(c->label(), new_label) < 0 ) { _children.insert(t, item); return(item); } } _children.add(item); return(item); } } return(item); } /// Descend into the path specified by \p arr, and add a new child there. /// Should be used only by Fl_Tree's internals. /// Adds the item based on the value of prefs.sortorder(). /// \returns the item added. /// Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) { int t = find_child(*arr); Fl_Tree_Item *item; if ( t == -1 ) { item = (Fl_Tree_Item*)add(prefs, *arr); } else { item = (Fl_Tree_Item*)child(t); } if ( *(arr+1) ) { // descend? return(item->add(prefs, arr+1)); } else { return(item); // end? done } } /// Insert a new item into current item's children at a specified position. /// \returns the new item inserted. /// Fl_Tree_Item *Fl_Tree_Item::insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos) { Fl_Tree_Item *item = new Fl_Tree_Item(prefs); item->label(new_label); item->_parent = this; _children.insert(pos, item); return(item); } /// Insert a new item above this item. /// \returns the new item inserted, or 0 if an error occurred. /// Fl_Tree_Item *Fl_Tree_Item::insert_above(const Fl_Tree_Prefs &prefs, const char *new_label) { Fl_Tree_Item *p = _parent; if ( ! p ) return(0); // Walk our parent's children to find ourself for ( int t=0; tchildren(); t++ ) { Fl_Tree_Item *c = p->child(t); if ( this == c ) { return(p->insert(prefs, new_label, t)); } } return(0); } /// Remove child by item. /// \returns 0 if removed, -1 if item not an immediate child. /// int Fl_Tree_Item::remove_child(Fl_Tree_Item *item) { for ( int t=0; tclear_children(); _children.remove(t); return(0); } } return(-1); } /// Remove immediate child (and its children) by its label 'name'. /// \returns 0 if removed, -1 if not found. /// int Fl_Tree_Item::remove_child(const char *name) { for ( int t=0; tlabel() ) { if ( strcmp(child(t)->label(), name) == 0 ) { _children.remove(t); return(0); } } } return(-1); } /// Swap two of our children, given two child index values. /// Use this eg. for sorting. /// /// This method is FAST, and does not involve lookups. /// /// No range checking is done on either index value. /// /// \returns /// - 0 : OK /// - -1 : failed: 'a' or 'b' is not our immediate child /// void Fl_Tree_Item::swap_children(int ax, int bx) { _children.swap(ax, bx); } /// Swap two of our children, given item pointers. /// Use this eg. for sorting. /// /// This method is SLOW because it involves linear lookups. /// For speed, use swap_children(int,int) instead. /// /// \returns /// - 0 : OK /// - -1 : failed: 'a' or 'b' is not our immediate child /// int Fl_Tree_Item::swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b) { int ax = -1, bx = -1; for ( int t=0; tfind_clicked(prefs) ) != NULL) { // check child and its descendents return(item); // found? } } } return(0); } /// Non-const version of the above. /// Find the item that the last event was over. /// /// Returns the item if its visible, and mouse is over it. /// Works even if widget deactivated. /// Use event_on_collapse_icon() to determine if collapse button was pressed. /// /// \returns the visible item under the event if found, or 0 if none. /// Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs) { if ( ! _visible ) return(0); if ( is_root() && !prefs.showroot() ) { // skip event check if we're root but root not being shown } else { // See if event is over us if ( event_inside(_xywh) ) { // event within this item? return(this); // found } } if ( is_open() ) { // open? check children of this item for ( int t=0; tfind_clicked(prefs) ) != NULL ) { // check child and its descendents return(item); // found? } } } return(0); } /// Draw this item and its children. void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, const Fl_Tree_Prefs &prefs, int lastchild) { if ( ! _visible ) return; fl_font(_labelfont, _labelsize); int H = _labelsize; if(usericon() && H < usericon()->h()) H = usericon()->h(); H += prefs.linespacing() + fl_descent(); // adjust horizontally if we draw no connecting lines if ( is_root() && prefs.connectorstyle() == FL_TREE_CONNECTOR_NONE ) { X -= prefs.openicon()->w(); W += prefs.openicon()->w(); } // Colors, fonts Fl_Color fg = _selected ? prefs.bgcolor() : _labelfgcolor; Fl_Color bg = _selected ? prefs.selectcolor() : _labelbgcolor; if ( ! _active ) { fg = fl_inactive(fg); if ( _selected ) bg = fl_inactive(bg); } // Update the xywh of this item _xywh[0] = X; _xywh[1] = Y; _xywh[2] = W; _xywh[3] = H; // Text size int textw=0, texth=0; fl_measure(_label, textw, texth, 0); int textycenter = Y+(H/2); int &icon_w = _collapse_xywh[2] = prefs.openicon()->w(); int &icon_x = _collapse_xywh[0] = X + (icon_w + prefs.connectorwidth())/2 - 3; int &icon_y = _collapse_xywh[1] = textycenter - (prefs.openicon()->h()/2); _collapse_xywh[3] = prefs.openicon()->h(); // Horizontal connector values int hstartx = X+icon_w/2-1; int hendx = hstartx + prefs.connectorwidth(); int hcenterx = X + icon_w + ((hendx - (X + icon_w)) / 2); // See if we should draw this item // If this item is root, and showroot() is disabled, don't draw. // char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; if ( drawthis ) { // Draw connectors if ( prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { // Horiz connector between center of icon and text // if this is root, the connector should not dangle in thin air on the left if (is_root()) draw_horizontal_connector(hcenterx, hendx, textycenter, prefs); else draw_horizontal_connector(hstartx, hendx, textycenter, prefs); if ( has_children() && is_open() ) { // Small vertical line down to children draw_vertical_connector(hcenterx, textycenter, Y+H, prefs); } // Connectors for last child if ( ! is_root() ) { if ( lastchild ) { draw_vertical_connector(hstartx, Y, textycenter, prefs); } else { draw_vertical_connector(hstartx, Y, Y+H, prefs); } } } // Draw collapse icon if ( has_children() && prefs.showcollapse() ) { // Draw icon image if ( is_open() ) { prefs.closeicon()->draw(icon_x,icon_y); } else { prefs.openicon()->draw(icon_x,icon_y); } } // Background for this item int cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth(); int cwidth = cw1>cw2 ? cw1 : cw2; int &bx = _label_xywh[0] = X+(icon_w/2-1+cwidth); int &by = _label_xywh[1] = Y; int &bw = _label_xywh[2] = W-(icon_w/2-1+cwidth); int &bh = _label_xywh[3] = H; // 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(), bx, by, bw, bh, bg); } else { // Not Selected? use plain filled rectangle fl_color(bg); fl_rectf(bx, by, bw, bh); } } // Draw user icon (if any) int useroff = (icon_w/2-1+cwidth); if ( usericon() ) { // Item has user icon? Use it useroff += prefs.usericonmarginleft(); icon_y = textycenter - (usericon()->h() >> 1); usericon()->draw(X+useroff,icon_y); useroff += usericon()->w(); } else if ( prefs.usericon() ) { // Prefs has user icon? Use it useroff += prefs.usericonmarginleft(); icon_y = textycenter - (prefs.usericon()->h() >> 1); prefs.usericon()->draw(X+useroff,icon_y); useroff += prefs.usericon()->w(); } useroff += prefs.labelmarginleft(); // Draw label if ( widget() ) { // Widget? Draw it int lx = X+useroff; int ly = by; int lw = widget()->w(); int lh = bh; if ( widget()->x() != lx || widget()->y() != ly || widget()->w() != lw || widget()->h() != lh ) { widget()->resize(lx, ly, lw, lh); // fltk will handle drawing this } } else { // No label widget? Draw text label if ( _label ) { fl_color(fg); fl_draw(_label, X+useroff, Y+H-fl_descent()-1); } } Y += H; } // end drawthis // Draw children if ( has_children() && is_open() ) { int child_x = drawthis ? // offset children to right, (hcenterx - (icon_w/2) + 1) : X; // unless didn't drawthis int child_w = W - (child_x-X); int child_y_start = Y; for ( int t=0; tdraw(child_x, Y, child_w, tree, prefs, lastchild); } if ( has_children() && is_open() ) { Y += prefs.openchild_marginbottom(); // offset below open child tree } if ( ! lastchild ) { draw_vertical_connector(hstartx, child_y_start, Y, prefs); } } } /// Was the event on the 'collapse' button? /// int Fl_Tree_Item::event_on_collapse_icon(const Fl_Tree_Prefs &prefs) const { if ( _visible && _active && has_children() && prefs.showcollapse() ) { return(event_inside(_collapse_xywh) ? 1 : 0); } else { return(0); } } /// Was event on the label()? /// int Fl_Tree_Item::event_on_label(const Fl_Tree_Prefs &prefs) const { if ( _visible && _active ) { return(event_inside(_label_xywh) ? 1 : 0); } else { return(0); } } /// Internal: Show the FLTK widget() for this item and all children. /// Used by open() to re-show widgets that were hidden by a previous close() /// void Fl_Tree_Item::show_widgets() { if ( _widget ) _widget->show(); if ( is_open() ) { for ( int t=0; t<_children.total(); t++ ) { _children[t]->show_widgets(); } } } /// Internal: Hide the FLTK widget() for this item and all children. /// Used by close() to hide widgets. /// void Fl_Tree_Item::hide_widgets() { if ( _widget ) _widget->hide(); for ( int t=0; t<_children.total(); t++ ) { _children[t]->hide_widgets(); } } /// Open this item and all its children. void Fl_Tree_Item::open() { _open = 1; // Tell children to show() their widgets for ( int t=0; t<_children.total(); t++ ) { _children[t]->show_widgets(); } } /// Close this item and all its children. void Fl_Tree_Item::close() { _open = 0; // Tell children to hide() their widgets for ( int t=0; t<_children.total(); t++ ) { _children[t]->hide_widgets(); } } /// Returns how many levels deep this item is in the hierarchy. /// /// For instance; root has a depth of zero, and its immediate children /// would have a depth of 1, and so on. /// int Fl_Tree_Item::depth() const { int count = 0; const Fl_Tree_Item *item = parent(); while ( item ) { ++count; item = item->parent(); } return(count); } /// Return the next item in the tree. /// /// This method can be used to walk the tree forward. /// For an example of how to use this method, see Fl_Tree::first(). /// /// \returns the next item in the tree, or 0 if there's no more items. /// Fl_Tree_Item *Fl_Tree_Item::next() { Fl_Tree_Item *p, *c = this; if ( c->has_children() ) { return(c->child(0)); } while ( ( p = c->parent() ) != NULL ) { // loop upwards through parents int t = p->find_child(c); // find our position in parent's children[] array if ( ++t < p->children() ) // not last child? return(p->child(t)); // return next child c = p; // child becomes parent to move up generation } // loop: moves up to next parent return(0); // hit root? done } /// Return the previous item in the tree. /// /// This method can be used to walk the tree backwards. /// For an example of how to use this method, see Fl_Tree::last(). /// /// \returns the previous item in the tree, or 0 if there's no item above this one (hit the root). /// Fl_Tree_Item *Fl_Tree_Item::prev() { Fl_Tree_Item *p=parent(); // start with parent if ( ! p ) return(0); // hit root? done int t = p->find_child(this); // find our position in parent's children[] array if ( --t == -1 ) { // are we first child? return(p); // return immediate parent } p = p->child(t); // take parent's previous child while ( p->has_children() ) { // has children? p = p->child(p->children()-1); // take last child } return(p); } // // End of "$Id: Fl_Tree_Item.cxx 7672 2010-07-10 09:44:45Z matt $". //