// // Demonstrate Fl_Tree custom item draw callback. - erco 11/09/2013 // // Copyright 2013 Greg Ercolano. // Copyright 1998-2023 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // #include #include /* ctime.. */ #include #include #include #include #ifndef MAX #define MAX(a,b) ((a)>(b))?(a):(b) #endif // DERIVE CUSTOM CLASS FROM Fl_Tree_Item TO IMPLEMENT SHOWING THE TIME OF DAY // This demonstrates that item content can be dynamic and highly customized. // class MyTimeItem : public Fl_Tree_Item { const char *time_format; protected: // Remove trailing crlf const char* StripCrlf(char *s) { char *ss = strchr(s, '\n'); if (ss) *ss = 0; return s; } const struct tm* GetTimeStruct() { time_t t = time(NULL); if ( strcmp(time_format, "Local") == 0 ) return localtime(&t); if ( strcmp(time_format, "GMT" ) == 0 ) return gmtime(&t); return 0; } public: MyTimeItem(Fl_Tree *tree, const char *time_format) : Fl_Tree_Item(tree) { label(time_format); this->time_format = time_format; } // Handle custom drawing of the item // Fl_Tree has already handled drawing everything to the left // of the label area, including any 'user icon', collapse buttons, // connector lines, etc. // // All we're responsible for is drawing the 'label' area of the item // and it's background. Fl_Tree gives us a hint as to what the // foreground and background colors should be via the fg/bg parameters, // and whether we're supposed to render anything or not. // // The only other thing we must do is return the maximum X position // of scrollable content, i.e. the right most X position of content // that we want the user to be able to use the horizontal scrollbar // to reach. // int draw_item_content(int render) FL_OVERRIDE { 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 int dx = Fl::box_dx(prefs().selectbox()) + 1; int dw = Fl::box_dw(prefs().selectbox()) + 2; if ( render ) { fl_color(fg); if ( label() ) fl_draw(label(), X+dx, Y, W-dw, 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 fl_color(fg); const struct tm *tm = GetTimeStruct(); char s[80]; sprintf(s, "Date: %02d/%02d/%02d", tm->tm_mon+1, tm->tm_mday, tm->tm_year % 100); lw=0, lh=0; fl_measure(s, lw, lh); // get box around text (including white space) if ( render ) fl_draw(s, X,Y+4,W,H, FL_ALIGN_LEFT|FL_ALIGN_TOP); sprintf(s, "Time: %02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); if ( render ) fl_draw(s, X,Y+H/2,W,H/2, FL_ALIGN_LEFT|FL_ALIGN_TOP); int lw2=0, lh2=0; fl_measure(s, lw2, lh2); X += MAX(lw, lw2); return X; // return right most edge of what we've rendered } }; // TIMER TO HANDLE DYNAMIC CONTENT IN THE TREE void Timer_CB(void *data) { Fl_Tree *tree = (Fl_Tree*)data; tree->redraw(); // keeps time updated Fl::repeat_timeout(0.2, Timer_CB, data); } int main(int argc, char *argv[]) { Fl::scheme("gtk+"); // default scheme: can be overridden by commandline Fl_Tree *tree = 0; Fl_Double_Window *win = new Fl_Double_Window(350, 450, "Tree Custom Draw Items"); win->begin(); { // Create the tree tree = new Fl_Tree(0, 0, win->w(), win->h() - 50); tree->showroot(0); // don't show root of tree tree->selectmode(FL_TREE_SELECT_MULTI); // multiselect // Add some items tree->add("Flintstones/Fred"); tree->add("Flintstones/Wilma"); tree->add("Flintstones/Pebbles"); { MyTimeItem *myitem; myitem = new MyTimeItem(tree, "Local"); // create custom item myitem->labelsize(20); tree->add("Time Add Item/Local", myitem); myitem = new MyTimeItem(tree, "GMT"); // create custom item myitem->labelsize(20); tree->add("Time Add Item/GMT", myitem); } // 'Replace' approach { Fl_Tree_Item *item; MyTimeItem *myitem; item = tree->add("Time Replace Item/Local Time"); // Replace the 'Local' item with our own myitem = new MyTimeItem(tree, "Local"); // create custom item myitem->labelsize(20); item->replace(myitem); // replace normal item with custom item = tree->add("Time Replace Item/GMT Time"); // Replace the 'GMT' item with our own myitem = new MyTimeItem(tree, "GMT"); // create custom item myitem->labelsize(20); item->replace(myitem); // replace normal item with custom } tree->add("Superjail/Warden"); tree->add("Superjail/Jared"); tree->add("Superjail/Alice"); tree->add("Superjail/Jailbot"); tree->show_self(); // Start with some items closed tree->close("Superjail"); // Optional: set a "special" selection color (light blue) tree->selection_color(0xcceeff00); // Optional: set a "special" box type for the selection box tree->selectbox(FL_THIN_UP_BOX); // Create the scheme choice box new Fl_Scheme_Choice((win->w() - 130) / 2, win->h() - 40, 130, 30, "Scheme: "); // Set up a timer to keep time in tree updated Fl::add_timeout(0.2, Timer_CB, (void*)tree); } win->end(); win->resizable(tree); win->size_range(300, 350); win->show(argc, argv); return(Fl::run()); }