//
// 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 <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Multi_Label.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Pixmap.H>
#include <FL/fl_message.H>

#include <stdlib.h>   // 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();
}
