// // How to use Fl_Multi_Label to make menu items with images and labels. // // Copyright 2017 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 #include #include #include #include #include #include #include #include #include // free() // Document icon static const char *L_document_xpm[] = { "13 11 3 1", " c None", "x c #d8d8f8", "@ c #202060", " @@@@@@@@@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @xxxxxxx@ ", " @@@@@@@@@ "}; static Fl_Pixmap L_document_pixmap(L_document_xpm); // Folder icon static const char *L_folder_xpm[] = { "13 11 3 1", " c None", "x c #d8d833", "@ c #808011", " ", " @@@@ ", " @xxxx@ ", "@@@@@xxxx@@ ", "@xxxxxxxxx@ ", "@xxxxxxxxx@ ", "@xxxxxxxxx@ ", "@xxxxxxxxx@ ", "@xxxxxxxxx@ ", "@xxxxxxxxx@ ", "@@@@@@@@@@@ "}; static Fl_Pixmap L_folder_pixmap(L_folder_xpm); // Red "X" static const char *L_redx_xpm[] = { "13 11 5 1", " c None", "+ c #222222", "x c #555555", "- c #882222", "@ c #ffffff", " x+++x ", " ++---++ ", " ++-----++ ", "++-@@-@@-++ ", "++--@@@--++ ", "++---@---++ ", "++--@@@--++ ", "++-@@-@@-++ ", " ++-----++ ", " ++---++ ", " x+++x "}; static Fl_Pixmap L_redx_pixmap(L_redx_xpm); // Handle the different menu items.. void Menu_CB(Fl_Widget *w, void* data) { const char *itemname = (const char*)data; // "New", "Open", etc if ( strcmp(itemname, "Quit") == 0 ) { // handle Quit w->window()->hide(); } else { // just show a message for other items fl_message("'%s' would happen here", itemname); } } // Add an image in front of item's text int AddItemToMenu(Fl_Menu_ *menu, // menu to add item to const char *labeltext, // label text int shortcut, // shortcut (e.g. FL_COMMAND+'a') Fl_Callback *cb, // callback to invoke void *userdata, // userdata for callback Fl_Pixmap* pixmap, // image (if any) to add to item int flags=0) { // menu flags (e.g. FL_MENU_DIVIDER..) // Add a new menu item int i = menu->add(labeltext, shortcut, cb, userdata, flags); if ( !pixmap ) return i; Fl_Menu_Item *item = (Fl_Menu_Item*)&(menu->menu()[i]); // Create a multi label, assign it an image + text Fl_Multi_Label *ml = new Fl_Multi_Label; // Left side of label is image ml->typea = FL_IMAGE_LABEL; ml->labela = (const char*)pixmap; // Right side of label is text ml->typeb = FL_NORMAL_LABEL; ml->labelb = item->label(); // transfers "ownership" to ml // Assign multilabel to item. // There are three documented ways to achieve this. All of them are still // supported but two of them are deprecated (see docs and comments below). // The recommended way is to use Fl_Menu_Item::multi_label(Fl_Multi_Label *) // which was added in FLTK 1.4.0 and sets the correct labeltype(). // All of these statements overwrite the old label (pointer) whose // ownership has been "transferred" to the Fl_Multi_Label (correct). // The old label is not released (as documented). // ml->label(item); // deprecated (1.3.x) // item->label(FL_MULTI_LABEL, (const char *)ml); // deprecated (1.3.x) item->multi_label(ml); // new since 1.4.0 return i; } // Create Menu Items // This same technique works for Fl_Menu_ derived widgets, // e.g. Fl_Menu_Bar, Fl_Menu_Button, Fl_Choice.. // void CreateMenuItems(Fl_Menu_* menu) { // Add items with LABELS AND IMAGES using Fl_Multi_Label.. AddItemToMenu(menu, "File/New", FL_COMMAND+'n', Menu_CB, (void*)"New", &L_document_pixmap); AddItemToMenu(menu, "File/Open", FL_COMMAND+'o', Menu_CB, (void*)"Open", &L_folder_pixmap, FL_MENU_DIVIDER); AddItemToMenu(menu, "File/Quit", FL_COMMAND+'q', Menu_CB, (void*)"Quit", &L_redx_pixmap); // Create menu bar items with JUST LABELS menu->add("Edit/Copy", FL_COMMAND+'c', Menu_CB, (void*)"Copy"); menu->add("Edit/Paste", FL_COMMAND+'v', Menu_CB, (void*)"Paste"); // Create menu bar items with JUST IMAGES (no labels) // This shows why you need Fl_Multi_Label; the item->label() // gets clobbered by the item->image() setting. // // In the following lines 'menu->add(...)' assigns a duplicated label // string which must be released prior to assigning an image to the label // with 'item->image()'. // This is "not nice" but it prevents memory leaks (see GitHub Issue #875). int i; Fl_Menu_Item *item; i = menu->add("Images/One", 0, Menu_CB, (void*)"One"); item = (Fl_Menu_Item*)&(menu->menu()[i]); free((void*)item->label()); item->image(L_document_pixmap); i = menu->add("Images/Two", 0, Menu_CB, (void*)"Two"); item = (Fl_Menu_Item*)&(menu->menu()[i]); free((void*)item->label()); item->image(L_folder_pixmap); i = menu->add("Images/Three", 0, Menu_CB, (void*)"Three"); item = (Fl_Menu_Item*)&(menu->menu()[i]); free((void*)item->label()); item->image(L_redx_pixmap); } int main(int argc, char **argv) { Fl_Double_Window *win = new Fl_Double_Window(400, 400, "Menu items with images"); win->tooltip("Right click on window background\nfor popup menu"); // Help message Fl_Box *box = new Fl_Box(100,100,200,200); box->label(win->tooltip()); // no need to copy_label() because it's static box->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); // Menu bar Fl_Menu_Bar *menubar = new Fl_Menu_Bar(0,0,win->w(), 25); CreateMenuItems(menubar); // Right click context menu Fl_Menu_Button *menubutt = new Fl_Menu_Button(0,25,win->w(), win->h()-25); CreateMenuItems(menubutt); menubutt->type(Fl_Menu_Button::POPUP3); // Chooser menu Fl_Choice *choice = new Fl_Choice(140,50,200,25,"Choice"); CreateMenuItems(choice); choice->value(1); // TODO: Show complex labels with Fl_Multi_Label. From docs: // // "More complex labels can be constructed by setting labelb as another // Fl_Multi_Label and thus chaining up a series of label elements." win->end(); win->resizable(win); win->show(argc, argv); return Fl::run(); }