/********************************************************************************

   Fotocx - edit photos and manage collections

   Copyright 2007-2024 Michael Cornelison
   source code URL: https://kornelix.net
   contact: mkornelix@gmail.com

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version. See https://www.gnu.org/licenses

   This program 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 General Public License for more details.

*********************************************************************************

   Fotocx window and menu build functions

   build_widgets           build widgets and menus for F/G/M view modes
   m_viewmode              set current F/G/M view mode
   popup_menufunc          image/thumb right-click menu func
   image_Rclick_popup      popup menu for image right-click
   gallery_Lclick_func     thumbnail left-click function
   gallery_Rclick_popup    popup menu for thumbnail right-click
   m_custom_menu           build custom user menu
   m_edit_script           edit a script file calling multiple edit functions
   m_edit_script_addfunc   edit_done() hook to insert dialog settings into script
   m_run_script            run script like edit function for current image file
   m_batch_script          run script for batch of selected image files
   m_plugins               plugins menu function
   m_edit_plugins          add/revise/delete plugin menu functions
   m_run_plugin            run a plugin menu command and update image

*********************************************************************************/

#define EX extern                                                                //  enable extern declarations
#include "fotocx.h"                                                              //  (variables in fotocx.h are defined)

using namespace zfuncs;

/********************************************************************************/

GtkWidget   *mFile, *mGallery, *mMaps, *mMeta, *mSelect;
GtkWidget   *mEdit, *mRepair, *mRefine, *mEffects, *mWarp, *mComb;
GtkWidget   *mBatch, *mTools, *mHelp, *mDevmenu;
GtkWidget   *popmenu_image, *popmenu_raw, *popmenu_mpo, *popmenu_video;
GtkWidget   *popmenu_thumb, *popmenu_album;


//  initialize widgets and menus for F/G/M view modes
//  called from main() before gtk_main() loop is entered

void build_widgets()
{
   Mwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);                                   //  create main window
   gtk_window_set_title(MWIN,Frelease);

   MWhbox = gtk_box_new(HORIZONTAL,0);                                           //  main window top container
   gtk_container_add(GTK_CONTAINER(Mwin),MWhbox);

   MWmenu = gtk_box_new(VERTICAL,0);                                             //  container for main window menus
   gtk_box_pack_start(GTK_BOX(MWhbox),MWmenu,0,1,0);

   MWvbox = gtk_box_new(VERTICAL,0);                                             //  container for F/G/M views
   gtk_box_pack_start(GTK_BOX(MWhbox),MWvbox,1,1,0);

   G_SIGNAL(Mwin,"delete-event",delete_event,0);                                 //  connect signals to main window
   G_SIGNAL(Mwin,"destroy",destroy_event,0);
   G_SIGNAL(Mwin,"window-state-event",state_event,0);
   G_SIGNAL(Mwin,"key-press-event",KBpress,0);                                   //  connect KB events to main window
   G_SIGNAL(Mwin,"key-release-event",KBrelease,0);

   //  F view widgets - image file

   Fhbox = gtk_box_new(HORIZONTAL,0);                                            //  top container
   gtk_box_pack_start(GTK_BOX(MWvbox),Fhbox,1,1,0);
   Fvbox = gtk_box_new(VERTICAL,0);                                              //  vbox for image
   gtk_box_pack_start(GTK_BOX(Fhbox),Fvbox,1,1,0);
   Fpanel = gtk_box_new(HORIZONTAL,0);                                           //  panel over image
   gtk_box_pack_start(GTK_BOX(Fvbox),Fpanel,0,0,0);
   gtk_widget_set_size_request(Fpanel,0,20);
   Fpanlab = gtk_label_new("panel");
   gtk_box_pack_start(GTK_BOX(Fpanel),Fpanlab,0,0,0);
   Fpanelshow = 1;                                                               //  panel normally shows
   Fdrawin = gtk_drawing_area_new();                                             //  image drawing area
   gtk_box_pack_start(GTK_BOX(Fvbox),Fdrawin,1,1,0);
   gtk_widget_hide(Fhbox);

   gtk_widget_add_events(Fdrawin,GDK_BUTTON_PRESS_MASK);                         //  connect mouse events to image window
   gtk_widget_add_events(Fdrawin,GDK_BUTTON_RELEASE_MASK);
   gtk_widget_add_events(Fdrawin,GDK_BUTTON_MOTION_MASK);
   gtk_widget_add_events(Fdrawin,GDK_POINTER_MOTION_MASK);
   gtk_widget_add_events(Fdrawin,GDK_SCROLL_MASK);
   G_SIGNAL(Fdrawin,"button-press-event",mouse_event,0);                         //  connect signals
   G_SIGNAL(Fdrawin,"button-release-event",mouse_event,0);
   G_SIGNAL(Fdrawin,"motion-notify-event",mouse_event,0);
   G_SIGNAL(Fdrawin,"scroll-event",mouse_event,0);
   G_SIGNAL(Fdrawin,"draw",Fpaint,0);
   drag_drop_dest(Fdrawin,drop_event);                                           //  accept drag-drop file

   //  G view widgets - thumbnail gallery

   Ghbox = gtk_box_new(HORIZONTAL,0);                                            //  top container
   gtk_box_pack_start(GTK_BOX(MWvbox),Ghbox,1,1,0);
   Gvbox = gtk_box_new(VERTICAL,0);                                              //  vbox for gallery
   gtk_box_pack_start(GTK_BOX(Ghbox),Gvbox,1,1,0);
   Gpanel = gtk_box_new(HORIZONTAL,0);                                           //  top panel for [TOP] and navi buttons
   gtk_box_pack_start(GTK_BOX(Gvbox),Gpanel,0,0,2);
   Galbum = gtk_button_new_with_label("Album");                                  //  [Album] button in panel
   gtk_box_pack_start(GTK_BOX(Gpanel),Galbum,0,0,3);
   Gtop = gtk_button_new_with_label("TOP");                                      //  [TOP] button in panel
   gtk_box_pack_start(GTK_BOX(Gpanel),Gtop,0,0,3);
   Gsep = gtk_label_new(0);
   gtk_label_set_markup(GTK_LABEL(Gsep),"<span font=\"sans bold 12\" >@</span>");
   gtk_box_pack_start(GTK_BOX(Gpanel),Gsep,0,0,10);

   Gsep = gtk_separator_new(HORIZONTAL);                                         //  separator line
   gtk_box_pack_start(GTK_BOX(Gvbox),Gsep,0,0,3);
   Gscroll = gtk_scrolled_window_new(0,0);                                       //  scrolled window for gallery
   gtk_scrolled_window_set_policy(SCROLLWIN(Gscroll),NEVER,ALWAYS);
   Gadjust = gtk_scrolled_window_get_vadjustment(SCROLLWIN(Gscroll));
   gtk_box_pack_start(GTK_BOX(Gvbox),Gscroll,1,1,0);
   Gdrawin = gtk_drawing_area_new();                                             //  gallery drawing area
   gtk_container_add(GTK_CONTAINER(Gscroll),Gdrawin);
   gtk_widget_hide(Ghbox);

   gtk_widget_add_events(Gdrawin,GDK_BUTTON_PRESS_MASK);                         //  connect mouse events to gallery window
   gtk_widget_add_events(Gdrawin,GDK_BUTTON_RELEASE_MASK);
   gtk_widget_add_events(Gdrawin,GDK_POINTER_MOTION_MASK);
   G_SIGNAL(Gtop,"clicked",navi::newtop,0);
   G_SIGNAL(Galbum,"clicked",navi::newalbum,0);
   G_SIGNAL(Gdrawin,"button-press-event",navi::mouse_event,0);
   G_SIGNAL(Gdrawin,"button-release-event",navi::mouse_event,0);
   G_SIGNAL(Gdrawin,"motion-notify-event",navi::mouse_event,0);
   G_SIGNAL(Gdrawin,"draw",navi::gallery_paint,null);
   drag_drop_source(Gdrawin,navi::gallery_dragfile);                             //  start file drag-drop
   drag_drop_dest(Gdrawin,navi::gallery_dropfile);                               //  accept drag-drop file

   //  M view widgets - internet map

   Mhbox = gtk_box_new(HORIZONTAL,0);                                            //  top container
   gtk_box_pack_start(GTK_BOX(MWvbox),Mhbox,1,1,0);
   Mvbox = gtk_box_new(VERTICAL,0);                                              //  vbox for map window
   gtk_box_pack_start(GTK_BOX(Mhbox),Mvbox,1,1,0);
   gtk_widget_hide(Mhbox);

   //  menu popup text (tool tips) ----------------------------------------

   //  main menu buttons
   ch * File_tip = "File: new session, open, rename, copy/move, delete, print";
   ch * Gallery_tip = "Gallery: thumbnails, new/recent, sort, select, bookmarks, albums, slide show";
   ch * Maps_tip = "Maps: internet world map, images by map location";
   ch * custom_menu_tip = "Custom menu: add favorite or frequent functions here";
   ch * prev_next_tip = "Left/right click: previous/next image (also ←/→ keys)";
   ch * zoom_menu_tip = "Left/right click: zoom image/thumbnail in/out (also +/- keys)";
   ch * save_tip = "Save modified image as new file or new file version";
   ch * metadata_tip = "View/edit metadata, manage tags, images by location/date, search images";
   ch * select_tip = "Select image areas to edit separately, copy/paste selections";
   ch * undo_redo_tip = "Left/right click: undo/redo one edit step \n"
                           "  (with A-key: undo/redo all edits) \n"
                           " Middle click: go back to selected edit step";
   ch * edit_tip = "Edit: crop, rotate, retouch, rescale, markup, paint edits, copy areas, plugins";
   ch * repair_tip = "Repair: sharpen, blur, denoise, defog, red eyes, smart erase, adjust color";
   ch * refine_tip = "Refine: edit histogram, flatten histogram, local contrast, amplify contrast, retinex, saturation";
   ch * effects_tip = "Effects: cartoon, sketch, paint, emboss, dither, add texture, change colors, custom convolution";
   ch * warp_tip = "Warp: unbend, fix perspective, warp, unwarp closeup, flatten, image transforms";
   ch * combine_tip = "Combine: HDR, HDF, stack, panorama, mashup, image array";
   ch * batch_tip = "Batch: copy, move, convert, RAW convert, add/change metadata, custom scripts";
   ch * tools_tip = "Tools: image index, settings, shortcuts, magnify, find duplicates, grid lines";
   ch * help_tip = "Help: user guide, tutorial, change log, licenses, privacy, about";

   //  file menu
   ch * new_session_tip = "Open a parallel Fotocx session";
   ch * open_file_tip = "Select and view an image file";
   ch * cycle2files_tip = "Cycle 2 Prior Files";
   ch * cycle3files_tip = "Cycle 3 Prior Files";
   ch * view360_tip = "View 360 degree panorama image";
   ch * rename_tip = "Change file name";
   ch * permissions_tip = "View and change file permissions";
   ch * change_alpha_tip = "Add/Remove image file alpha channel (transparency)";
   ch * blank_image_tip = "Create a blank image";
   ch * copy_move_tip = "Copy or Move image file to new location";
   ch * copyto_desktop_tip = "Copy image file to the desktop";
   ch * copyto_clipboard_tip = "Copy image file to the clipboard";
   ch * deltrash_tip = "Delete or trash an image file";
   ch * convert_adobe_tip = "Convert Adobe document file into jpeg file(s)";
   ch * set_wallpaper_tip = "Set image file as desktop wallpaper (GNOME only)";
   ch * print_image_tip = "Print the current image file";
   ch * print_calibrated_tip = "Print current image file with adjusted colors";
   ch * calib_printer_tip = "Printer color calibration tool";
   ch * quit_tip = "Quit Fotocx";

   //  gallery menu
   ch * thumbview_tip = "Gallery view with thumbnails and file data";
   ch * metaview_tip = "Gallery view with thumbnails and basic metadata";
   ch * recentfiles_tip = "Gallery of recently viewed image files";
   ch * newest_files_tip = "Gallery of newest image files";
   ch * gallery_sort_tip = "Change gallery sort order";
   ch * gallery_screen_tip = "Select newest, oldest, matching pattern ...";
   ch * gallery_home_tip = "Jump to gallery beginning [home]";
   ch * gallery_end_tip = "Jump to gallery end [end]";
   ch * current_folder_tip = "Set gallery from current image file folder";
   ch * recent_folders_tip = "Set gallery to recently used folder";
   ch * folder_tree_tip = "Show folder tree, click any branch for gallery view";
   ch * select_files_tip = "Select input files for subsequent function";
   ch * rename_folder_tip = "Rename current folder (current gallery)";
   ch * add_subfolder_tip = "Add a new subfolder to current gallery";
   ch * bookmarks_tip = "Set and recall bookmarked image locations";
   ch * manage_albums_tip = "Organize images into albums";
   ch * update_albums_tip = "Update albums for new file versions";
   ch * album_mass_update_tip = "Mass update album files";
   ch * gallery2album_tip = "Save current gallery as album";
   ch * slideshow_tip = "Start a slide show";

   //  maps menu
   ch * map_regions_tip = "Add custom map region names, go to named region";
   ch * map_location_tip = "Input (partial) location name, find full name, go there";
   ch * set_map_markers_tip = "Show map markers for all images or current gallery";
   ch * show_on_map_tip = "Show current image location on map";

   //  metadata menu
   ch * meta_view_main_tip = "Show key metadata for current image or clicked thumbnail";
   ch * meta_view_all_tip = "List all metadata for current image or clicked thumbnail";
   ch * meta_edit_main_tip = "Edit key metadata: tags/geotags/caption/rating ...";
   ch * meta_edit_any_tip = "Edit any metadata item";
   ch * meta_delete_tip = "Remove selected image metadata";
   ch * meta_copy_tip = "Copy metadata from one image to another";
   ch * meta_fix_tip = "Repair malformed metadata preventing metadata edits";
   ch * meta_manage_tags_tip = "Define tags (keywords) used for searching images";
   ch * meta_choose_caps_tip = "Choose metadata shown on image (captions etc.)";
   ch * meta_toggle_caps_tip = "Toggle image metadata display ON/OFF";
   ch * meta_places_dates_tip = "Find all images for a location [date range]";
   ch * meta_timeline_tip = "Show image counts in year/month calendar, select, report";
   ch * search_images_tip = "Find images meeting multiple search criteria";

   //  select area menu
   ch * select_area_tip = "Select objects or areas for separate editing";
   ch * select_find_gap_tip = "Find a gap in an area outline";
   ch * select_blend_tip = "Blend area edits using the mouse";
   ch * select_show_tip = "Show (outline) selected areas";
   ch * select_hide_tip = "Hide selected areas";
   ch * select_enable_tip = "Enable areas for editing";
   ch * select_disable_tip = "Disable areas for editing";
   ch * select_invert_tip = "Invert selected areas (selected ↔ not selected)";
   ch * select_clear_tip = "Erase existing areas";
   ch * select_copy_tip = "Copy area for later pasting into image";
   ch * select_paste_tip = "Paste previously copied area into image";
   ch * select_load_tip = "Open a file and paste as selected area into image";
   ch * select_save_tip = "Save selected area to a file with transparency";

   //  edit menu
   ch * rotate_tip = "Rotate or level image, upright, mirror";
   ch * upright_tip = "Upright a rotated image with one button";
   ch * crop_tip = "Crop (trim) image margins";
   ch * retouch_tip = "Adjust brightness, contrast, color";
   ch * rescale_tip = "Change image pixel dimensions";
   ch * margins_tip = "Add margins around an image";
   ch * markup_tip = "Draw on image: text, line/arrow, box, circle, ellipse";
   ch * color_mode_tip = "Set black-white/color, negative/positive, sepia";
   ch * paint_edits_tip = "Paint current edit function gradually using the mouse";
   ch * paint_image_tip = "Paint on image using the mouse";
   ch * paint_transp_tip = "Paint image transparency using the mouse";
   ch * area_fill_tip = "Fill selected areas or transparent areas with a color";
   ch * copy_in_image_tip = "Copy pixels within an image using the mouse";
   ch * copy_from_image_tip = "Copy pixels from another image using the mouse";
   ch * copy_prior_edit_tip = "Copy pixels from prior edits using the mouse";

   //  repair menu
   ch * sharpen_tip = "Sharpen the image (various methods)";
   ch * blur_tip = "Blur the image (various methods)";
   ch * denoise_tip = "Suppress noise from low-light photos";
   ch * defog_tip = "Reduce fog/haze in an image or selected area";
   ch * redeyes_tip = "Fix red-eyes from flash photo";
   ch * smart_erase_tip = "Remove unwanted objects in an image";
   ch * remove_halo_tip = "Remove halos left by other edit functions";
   ch * jpeg_artifacts_tip = "Suppress JPEG compression artifacts";
   ch * anti_alias_tip = "Remove jaggies on image feature hard edges";
   ch * adjust_RGB_tip = "Adjust color using RGB or CMY colors";
   ch * adjust_HSL_tip = "Adjust color using HSL colors";
   ch * remove_dust_tip = "Remove dust spots from old scanned photos";
   ch * fringes_tip = "Fix color fringes (chromatic aberration)";

   //  refine menu
   ch * edit_hist_tip = "Edit brightness histogram directly";
   ch * flat_hist_tip = "Flatten brightness histogram to enhance details";
   ch * localcon_tip = "Rescale RGB levels to increase local contrast";
   ch * amplifycon_tip = "Amplify existing contrast to enhance visibility of details";
   ch * retinex_tip = "Rescale RGB - reduce color caste and fog/haze";
   ch * saturation_tip = "Adjust color saturation based on brightness";
   ch * soft_focus_tip = "Apply a soft focus effect to an image";
   ch * match_colors_tip = "Match image colors with those of another image";
   ch * brite_ramp_tip = "Add a brightness/color ramp across an image";
   ch * vignette_tip = "Add/remove brightness/color radially from chosen center";

   //  effects menu
   ch * outlines_tip = "Convert image into a line drawing";
   ch * sketch_tip = "Convert image into a simulated sketch";
   ch * cartoon_tip = "Convert image into a cartoon drawing";
   ch * emboss_tip = "Create an embossed or 3D appearance";
   ch * tiles1_tip = "Convert image into square tiles with 3D effect";
   ch * tiles2_tip = "Convert image into irregular tiles matching image";
   ch * painting_tip = "Convert image into a simulated painting";
   ch * texture_tip = "Add texture to an image";
   ch * pattern_tip = "Tile image with a repeating pattern";
   ch * dither_tip = "Convert image into dithered dots";
   ch * engrave_tip = "Convert image into black/white lines";
   ch * mosaic_tip = "Create a mosaic with tiles made from all images";
   ch * add_noise_tip = "Add random noise to an image";
   ch * set_colors_tip = "Set number of colors for R/G/B or total";
   ch * shift_colors_tip = "Shift/convert image colors into other colors";
   ch * alien_colors_tip = "Change image colors using an algorithm";
   ch * anykernel_tip = "Process an image using a custom convolution kernel";

   //  warp menu
   ch * unbend_tip = "Remove image curvature, especially in panoramas";
   ch * perspective_tip = "Fix perspective for object photographed from an angle";
   ch * warp_area_tip = "Distort image areas using the mouse";
   ch * warp_curved_tip = "Warp image (curved) using the mouse";
   ch * warp_linear_tip = "Warp image (linear) using the mouse";
   ch * warp_affine_tip = "Warp image (affine) using the mouse";
   ch * warp_radial_tip = "Add/remove barrel/pincushion distortion";
   ch * unwarp_closeup_tip = "Rectify closeup face photo (balloon face)";
   ch * makewaves_tip = "Warp an image with a wave pattern";
   ch * twist_tip = "Twist image centered at mouse position";
   ch * sphere_tip = "Make a spherical projection of an image";
   ch * inside_out_tip = "Turn an image inside-out";
   ch * tiny_planet_tip = "Convert an image into a Tiny Planet";
   ch * escher_spiral_tip = "Generate an inward spiraling repeating image";
   ch * flatphoto_tip = "Flatten photo of a curved image";

   //  combine menu
   ch * HDR_tip = "High Dynamic Range - combine bright/dark images for better detail";
   ch * HDF_tip = "High Depth of Field - combine near/far focus images for deeper focus";
   ch * stack_paint_tip = "Combine multiple photos to erase passing people, etc.";
   ch * stack_noise_tip = "Combine multiple noisy photos into a low-noise image";
   ch * stack_layers_tip = "Combine multiple images in layers, select and paint layers";
   ch * stack_split_tip = "Compare two images split by a sliding boundary";
   ch * panorama_tip = "Combine 2-6 images into a wide panorama";
   ch * show_cim_files_tip = "Show input files used in combine function";
   ch * mashup_tip = "Arrange images and text in a layout (montage)";
   ch * image_array_tip = "Combine many images into a array of images";

   //  batch menu
   ch * batch_convert_tip = "Rename/convert/rescale/move selected image files";
   ch * batch_copy_move_tip = "Copy/move selected image files to a new folder";
   ch * batch_upright_tip = "Upright rotated image files, selected or ALL";
   ch * batch_deltrash_tip = "Delete or Trash selected image files";
   ch * batch_RAW_tip = "Convert camera RAW files to tiff/png/jpeg/jxl";                                               //  jxl
   ch * batch_overlay_tip = "Add overlay image (e.g. copyright) to selected images";
   ch * batch_tags_tip = "Add/remove tags for selected image files";
   ch * batch_rename_tags_tip = "Revise tag names for all image files";
   ch * batch_photo_DT_tip = "Set or shift photo date/time for selected image files";
   ch * batch_change_meta_tip = "Add/change/delete metadata for selected image files";
   ch * batch_report_meta_tip = "Report selected metadata for selected image files";
   ch * batch_meta_mover_tip = "Move metadata between keys (e.g. caption -> description)";
   ch * batch_geotags_tip = "Add/revise location/geocoordinates for selected image files";
   ch * export_filelist_tip = "Create a file of selected image files";
   ch * export_files_tip = "Export selected image files to a folder";

   //  tools menu
   ch * index_tip = "Index new image files and make thumbnails";
   ch * quick_index_tip = "Quick incremental index update";
   ch * settings_tip = "User preferences and settings";
   ch * KB_shortcuts_tip = "Show or make Keyboard Shortcuts";
   ch * RGB_hist_tip = "RGB brightness histogram graph";
   ch * magnify_tip = "Magnify image around the mouse position";
   ch * measure_image_tip = "Measure distances within an image";
   ch * show_RGB_tip = "Show RGB colors at mouse position";
   ch * grid_settings_tip = "Set grid line counts or spacing";
   ch * toggle_grid_tip = "Toggle grid lines on and off";
   ch * line_color_tip = "Set foreground line color (crop, grid, select, warp)";
   ch * darkbrite_tip = "Highlight darkest and brightest pixels";
   ch * monitor_color_tip = "Chart to adjust monitor color";
   ch * duplicates_tip = "Search all image files and report duplicates";
   ch * resources_tip = "Show memory and CPU resources used";

   //  help menu
   ch * user_guide_tip = "Read or search the user guide";
   ch * video_tutorial_tip = "Fotocx video tutorial on YouTube";
   ch * outboard_programs_tip = "List outboard programs and functions";
   ch * logfile_tip = "View the log file (info and error messages)";
   ch * command_params_tip = "List Fotocx command line parameters";
   ch * changelog_tip = "Show Fotocx changes for recent releases";
   ch * copyright_tip = "Fotocx copyright notice";
   ch * privacy_tip = "Fotocx privacy policy";
   ch * about_tip = "Show Fotocx installed version and author contact";
   ch * homepage_tip = "Fotocx web site (information and downloads)";
   ch * uninstall_tip = "Remove Fotocx application and data files";


   //  build menu table ---------------------------------------------------------

   #define MENU(_topmenu, _text, _icon, _tip, _func, _arg)        \
      me = Nmenus++;                                              \
      if (me >= maxmenus) zappcrash("maxmenus exceeded");         \
      menutab[me].topmenu = _topmenu;                             \
      menutab[me].menu = _text;                                   \
      menutab[me].icon = _icon;                                   \
      menutab[me].desc = _tip;                                    \
      menutab[me].func = _func;                                   \
      if (_arg) menutab[me].arg = _arg;                           \
      else menutab[me].arg = _text;                               \

   int      me;
   Nmenus = 0;

   mFile = create_popmenu();
   MENU(mFile,    "New Session", 0,           new_session_tip,              m_new_session, 0 );
   MENU(mFile,    "Open Image File", 0,       open_file_tip,                m_open_file, 0 );
   MENU(mFile,    "Cycle 2 Files", 0,         cycle2files_tip,              m_cycle2files, 0 );
   MENU(mFile,    "Cycle 3 Files", 0,         cycle3files_tip,              m_cycle3files, 0 );
   MENU(mFile,    "View 360° Pano", 0,        view360_tip,                  m_view360, 0);
   MENU(mFile,    "Rename", 0,                rename_tip,                   m_rename, 0 );
   MENU(mFile,    "Permissions", 0,           permissions_tip,              m_permissions, 0 );
   MENU(mFile,    "Change Alpha", 0,          change_alpha_tip,             m_change_alpha, 0 );
   MENU(mFile,    "Blank Image", 0,           blank_image_tip,              m_blank_image, 0 );
   MENU(mFile,    "Copy/Move", 0,             copy_move_tip,                m_copy_move, 0 );
   MENU(mFile,    "Copy to Desktop", 0,       copyto_desktop_tip,           m_copyto_desktop, 0 );
   MENU(mFile,    "Copy to Clipboard", 0,     copyto_clipboard_tip,         m_copyto_clip, 0 );
   MENU(mFile,    "Delete/Trash", 0,          deltrash_tip,                 m_delete_trash, 0 );
   MENU(mFile,    "Convert Adobe", 0,         convert_adobe_tip,            m_convert_adobe, 0 );
   MENU(mFile,    "Set Wallpaper", 0,         set_wallpaper_tip,            m_wallpaper, 0 );
   MENU(mFile,    "Print Image", 0,           print_image_tip,              m_print_image, 0 );
   MENU(mFile,    "Print Calibrated", 0,      print_calibrated_tip,         m_print_calibrated, 0 );
   MENU(mFile,    "Calibrate Printer", 0,     calib_printer_tip,            m_calibrate_printer, 0 );
   MENU(mFile,    "Quit", 0,                  quit_tip,                     m_quit, 0 );

   mGallery = create_popmenu();
   MENU(mGallery,   "Thumb View", 0,          thumbview_tip,                m_thumbview, 0 );
   MENU(mGallery,   "Meta View", 0,           metaview_tip,                 m_metaview, 0 );
   MENU(mGallery,   "Recent Files", 0,        recentfiles_tip,              m_recentfiles, 0 );
   MENU(mGallery,   "Newest Files", 0,        newest_files_tip,             m_newest_files, 0 );
   MENU(mGallery,   "Gallery Sort", 0,        gallery_sort_tip,             m_gallery_sort, 0);
   MENU(mGallery,   "Gallery Screen", 0,      gallery_screen_tip,           m_gallery_screen, 0);
   MENU(mGallery,   "Gallery Home", 0,        gallery_home_tip,             navi::menufuncx, "Home" );
   MENU(mGallery,   "Gallery End", 0,         gallery_end_tip,              navi::menufuncx, "End" );
   MENU(mGallery,   "Current Folder", 0,      current_folder_tip,           m_current_folder, 0 );
   MENU(mGallery,   "Recent Folders", 0,      recent_folders_tip,           m_recent_folders, 0 );
   MENU(mGallery,   "Folder Tree", 0,         folder_tree_tip,              m_folder_tree, 0 );
   MENU(mGallery,   "Select Files", 0,        select_files_tip,             m_select_files, 0 );
   MENU(mGallery,   "Rename Folder", 0,       rename_folder_tip,            m_rename_folder, 0 );
   MENU(mGallery,   "Add Subfolder", 0,       add_subfolder_tip,            m_add_subfolder, 0 );
   MENU(mGallery,   "Bookmarks", 0,           bookmarks_tip,                m_bookmarks, 0 );
   MENU(mGallery,   "Manage Albums", 0,       manage_albums_tip,            m_manage_albums, 0 );
   MENU(mGallery,   "Update Albums", 0,       update_albums_tip,            m_update_albums, 0 );
   MENU(mGallery,   "Gallery to Album", 0,    gallery2album_tip,            m_gallery2album, 0 );
   MENU(mGallery,   "Album Mass Update", 0,   album_mass_update_tip,        m_album_mass_update, 0 );
   MENU(mGallery,   "Slide Show", 0,          slideshow_tip,                m_slideshow, 0 );

   mMaps = create_popmenu();
   MENU(mMaps,     "Map Regions", 0,          map_regions_tip,              m_map_regions, 0 );
   MENU(mMaps,     "Map Location", 0,         map_location_tip,             m_map_location, 0 );
   MENU(mMaps,     "Map Markers", 0,          set_map_markers_tip,          m_set_map_markers, 0 );
   MENU(mMaps,     "Show on Map", 0,          show_on_map_tip,              m_map_zoomin, 0 );

   mMeta = create_popmenu();
   MENU(mMeta,    "View Main Meta", 0,        meta_view_main_tip,           m_meta_view_short, 0 );
   MENU(mMeta,    "View All Meta", 0,         meta_view_all_tip,            m_meta_view_long, 0 );
   MENU(mMeta,    "Edit Main Meta", 0,        meta_edit_main_tip,           m_meta_edit_main, 0 );
   MENU(mMeta,    "Edit Any Meta", 0,         meta_edit_any_tip,            m_meta_edit_any, 0 );
   MENU(mMeta,    "Delete Meta", 0,           meta_delete_tip,              m_meta_delete, 0 );
   MENU(mMeta,    "Copy Meta", 0,             meta_copy_tip,                m_meta_copy, 0 );
   MENU(mMeta,    "Fix Meta", 0,              meta_fix_tip,                 m_meta_fix, 0 );
   MENU(mMeta,    "Manage Tags", 0,           meta_manage_tags_tip,         m_meta_manage_tags, 0 );
   MENU(mMeta,    "Choose Captions", 0,       meta_choose_caps_tip,         m_meta_choose_caps, 0 );
   MENU(mMeta,    "Toggle Captions", 0,       meta_toggle_caps_tip,         m_meta_toggle_caps, 0 );
   MENU(mMeta,    "Places/Dates", 0,          meta_places_dates_tip,        m_meta_places_dates, 0 );
   MENU(mMeta,    "Timeline", 0,              meta_timeline_tip,            m_meta_timeline, 0 );
   MENU(mMeta,    "Search", 0,                search_images_tip,            m_search_images, 0 );

   mSelect = create_popmenu();
   MENU(mSelect,     "Select Area", 0,        select_area_tip,              m_select_area, 0 );
   MENU(mSelect,     "Find Gap", 0,           select_find_gap_tip,          m_select_find_gap, 0 );
   MENU(mSelect,     "Area Blend", 0,         select_blend_tip,             m_select_blend, 0 );
   MENU(mSelect,     "Show", 0,               select_show_tip,              m_select_show, 0 );
   MENU(mSelect,     "Hide", 0,               select_hide_tip,              m_select_hide, 0 );
   MENU(mSelect,     "Enable", 0,             select_enable_tip,            m_select_enable, 0 );
   MENU(mSelect,     "Disable", 0,            select_disable_tip,           m_select_disable, 0 );
   MENU(mSelect,     "Invert", 0,             select_invert_tip,            m_select_invert, 0 );
   MENU(mSelect,     "Clear", 0,              select_clear_tip,             m_select_clear, 0 );
   MENU(mSelect,     "Copy", 0,               select_copy_tip,              m_select_copy, 0 );
   MENU(mSelect,     "Paste", 0,              select_paste_tip,             m_select_paste, 0 );
   MENU(mSelect,     "Load", 0,               select_load_tip,              m_select_load, 0 );
   MENU(mSelect,     "Save", 0,               select_save_tip,              m_select_save, 0 );

   mEdit = create_popmenu();
   MENU(mEdit,    "Rotate", 0,                rotate_tip,                   m_rotate, 0 );
   MENU(mEdit,    "Upright", 0,               upright_tip,                  m_upright, 0 );
   MENU(mEdit,    "Crop", 0,                  crop_tip,                     m_crop, 0 );
   MENU(mEdit,    "Retouch", 0,               retouch_tip,                  m_retouch, 0 );
   MENU(mEdit,    "Rescale", 0,               rescale_tip,                  m_rescale, 0 );
   MENU(mEdit,    "Margins", 0,               margins_tip,                  m_margins, 0 );
   MENU(mEdit,    "Markup", 0,                markup_tip,                   m_markup, 0 );
   MENU(mEdit,    "Color Mode", 0,            color_mode_tip,               m_color_mode, 0 );
   MENU(mEdit,    "Paint Edits", 0,           paint_edits_tip,              m_paint_edits, 0 );
   MENU(mEdit,    "Paint Image", 0,           paint_image_tip,              m_paint_image, 0 );
   MENU(mEdit,    "Paint Transp", 0,          paint_transp_tip,             m_paint_transp, 0 );
   MENU(mEdit,    "Area Fill", 0,             area_fill_tip,                m_area_fill, 0 );
   MENU(mEdit,    "Copy in Image", 0,         copy_in_image_tip,            m_copy_in_image, 0 );
   MENU(mEdit,    "Copy From Image", 0,       copy_from_image_tip,          m_copy_from_image, 0 );
   MENU(mEdit,    "Copy Prior Edit", 0,       copy_prior_edit_tip,          m_copy_prior_edit, 0 );

   mRepair = create_popmenu();
   MENU(mRepair,     "Sharpen", 0,            sharpen_tip,                  m_sharpen, 0 );
   MENU(mRepair,     "Blur", 0,               blur_tip,                     m_blur, 0 );
   MENU(mRepair,     "Denoise", 0,            denoise_tip,                  m_denoise, 0 );
   MENU(mRepair,     "Defog", 0,              defog_tip,                    m_defog, 0 );
   MENU(mRepair,     "Red Eyes", 0,           redeyes_tip,                  m_redeyes, 0 );
   MENU(mRepair,     "Smart Erase", 0,        smart_erase_tip,              m_smart_erase, 0 );
   MENU(mRepair,     "Remove Halo", 0,        remove_halo_tip,              m_remove_halo, 0 );
   MENU(mRepair,     "JPEG Artifacts", 0,     jpeg_artifacts_tip,           m_jpeg_artifacts, 0 );
   MENU(mRepair,     "Anti-Alias", 0,         anti_alias_tip,               m_anti_alias, 0 );
   MENU(mRepair,     "Adjust RGB", 0,         adjust_RGB_tip,               m_adjust_RGB, 0 );
   MENU(mRepair,     "Adjust HSL", 0,         adjust_HSL_tip,               m_adjust_HSL, 0 );
   MENU(mRepair,     "Remove Dust", 0,        remove_dust_tip,              m_remove_dust, 0 );
   MENU(mRepair,     "Color Fringes", 0,      fringes_tip,                  m_fringes, 0 );

   mRefine = create_popmenu();
   MENU(mRefine,     "Edit Histogram", 0,        edit_hist_tip,             m_edit_hist, 0 );
   MENU(mRefine,     "Flatten Histogram", 0,     flat_hist_tip,             m_flat_hist, 0 );
   MENU(mRefine,     "Local Contrast", 0,        localcon_tip,              m_localcon, 0 );
   MENU(mRefine,     "Amplify Contrast", 0,      amplifycon_tip,            m_amplifycon, 0 );
   MENU(mRefine,     "Global Retinex", 0,        retinex_tip,               m_gretinex, 0 );
   MENU(mRefine,     "Local Retinex", 0,         retinex_tip,               m_lretinex, 0 );
   MENU(mRefine,     "Saturation", 0,            saturation_tip,            m_saturation, 0 );
   MENU(mRefine,     "Soft Focus", 0,            soft_focus_tip,            m_soft_focus, 0 );
   MENU(mRefine,     "Match Colors", 0,          match_colors_tip,          m_match_colors, 0 );
   MENU(mRefine,     "Brightness Ramp", 0,       brite_ramp_tip,            m_brite_ramp, 0 );
   MENU(mRefine,     "Vignette", 0,              vignette_tip,              m_vignette, 0 );

   mEffects = create_popmenu();
   MENU(mEffects,    "Outlines", 0,              outlines_tip,              m_outlines, 0 );
   MENU(mEffects,    "Sketch", 0,                sketch_tip,                m_sketch, 0 );
   MENU(mEffects,    "Cartoon", 0,               cartoon_tip,               m_cartoon, 0 );
   MENU(mEffects,    "Emboss", 0,                emboss_tip,                m_emboss, 0 );
   MENU(mEffects,    "Tiles1", 0,                tiles1_tip,                m_tiles1, 0 );
   MENU(mEffects,    "Tiles2", 0,                tiles2_tip,                m_tiles2, 0 );
   MENU(mEffects,    "Painting", 0,              painting_tip,              m_painting, 0 );
   MENU(mEffects,    "Texture", 0,               texture_tip,               m_texture, 0 );
   MENU(mEffects,    "Pattern", 0,               pattern_tip,               m_pattern, 0 );
   MENU(mEffects,    "Dither", 0,                dither_tip,                m_dither, 0 );
   MENU(mEffects,    "Engrave", 0,               engrave_tip,               m_engrave, 0 );
   MENU(mEffects,    "Mosaic", 0,                mosaic_tip,                m_mosaic, 0 );
   MENU(mEffects,    "Add Noise", 0,             add_noise_tip,             m_add_noise, 0 );
   MENU(mEffects,    "Set Colors", 0,            set_colors_tip,            m_set_colors, 0 ); 
   MENU(mEffects,    "Shift Colors", 0,          shift_colors_tip,          m_shift_colors, 0 );
   MENU(mEffects,    "Alien Colors", 0,          alien_colors_tip,          m_alien_colors, 0 );
   MENU(mEffects,    "Custom Kernel", 0,         anykernel_tip,             m_anykernel, 0 );

   mWarp = create_popmenu();
   MENU(mWarp,    "Unbend", 0,                   unbend_tip,                m_unbend, 0 );
   MENU(mWarp,    "Perspective", 0,              perspective_tip,           m_perspective, 0 );
   MENU(mWarp,    "Warp Area", 0,                warp_area_tip,             m_warp_area, 0 );
   MENU(mWarp,    "Warp Curved", 0,              warp_curved_tip,           m_warp_curved, 0 );
   MENU(mWarp,    "Warp Linear", 0,              warp_linear_tip,           m_warp_linear, 0 );
   MENU(mWarp,    "Warp Affine", 0,              warp_affine_tip,           m_warp_affine, 0 );
   MENU(mWarp,    "Warp Radial", 0,              warp_radial_tip,           m_warp_radial, 0);
   MENU(mWarp,    "Unwarp Closeup", 0,           unwarp_closeup_tip,        m_unwarp_closeup, 0 );
   MENU(mWarp,    "Make Waves", 0,               makewaves_tip,             m_makewaves, 0);
   MENU(mWarp,    "Twist", 0,                    twist_tip,                 m_twist, 0);
   MENU(mWarp,    "Sphere", 0,                   sphere_tip,                m_sphere, 0);
   MENU(mWarp,    "Inside-out", 0,               inside_out_tip,            m_inside_out, 0);
   MENU(mWarp,    "Tiny Planet", 0,              tiny_planet_tip,           m_tiny_planet, 0);
   MENU(mWarp,    "Escher Spiral", 0,            escher_spiral_tip,         m_escher_spiral, 0);
   MENU(mWarp,    "Flatten Photo", 0,            flatphoto_tip,             m_flatphoto, 0 );

   mComb = create_popmenu();
   MENU(mComb,    "HDR", 0,                      HDR_tip,                   m_HDR, 0 );
   MENU(mComb,    "HDF", 0,                      HDF_tip,                   m_HDF, 0 );
   MENU(mComb,    "Stack/Paint", 0,              stack_paint_tip,           m_stack_paint, 0 );
   MENU(mComb,    "Stack/Noise", 0,              stack_noise_tip,           m_stack_noise, 0 );
   MENU(mComb,    "Stack/Layers", 0,             stack_layers_tip,          m_stack_layers, 0 );
   MENU(mComb,    "Stack/Split", 0,              stack_split_tip,           m_stack_split, 0 );
   MENU(mComb,    "Panorama", 0,                 panorama_tip,              m_panorama, 0 );
   MENU(mComb,    "Show CIM Files", 0,           show_cim_files_tip,        m_cim_show_files, 0 );
   MENU(mComb,    "Mashup", 0,                   mashup_tip,                m_mashup, 0 );
   MENU(mComb,    "Image Array", 0,              image_array_tip,           m_image_array, 0 );

   mBatch = create_popmenu();
   MENU(mBatch,   "Batch Convert", 0,            batch_convert_tip,         m_batch_convert, 0 );
   MENU(mBatch,   "Batch Copy/Move", 0,          batch_copy_move_tip,       m_batch_copy_move, 0 );
   MENU(mBatch,   "Batch Upright", 0,            batch_upright_tip,         m_batch_upright, 0 );
   MENU(mBatch,   "Batch Delete/Trash", 0,       batch_deltrash_tip,        m_batch_deltrash, 0 );
   MENU(mBatch,   "Batch RAW", 0,                batch_RAW_tip,             m_batch_RAW, 0 );
   MENU(mBatch,   "Batch Overlay", 0,            batch_overlay_tip,         m_batch_overlay, 0 );
   MENU(mBatch,   "Batch Tags", 0,               batch_tags_tip,            m_batch_tags, 0 );
   MENU(mBatch,   "Batch Rename Tags", 0,        batch_rename_tags_tip,     m_batch_rename_tags, 0 );
   MENU(mBatch,   "Batch Photo Date", 0,         batch_photo_DT_tip,        m_batch_photo_date_time, 0 );
   MENU(mBatch,   "Batch Change Meta", 0,        batch_change_meta_tip,     m_batch_change_meta, 0 );
   MENU(mBatch,   "Batch Report Meta", 0,        batch_report_meta_tip,     m_batch_report_meta, 0 );
   MENU(mBatch,   "Batch Move Meta", 0,          batch_meta_mover_tip,      m_batch_meta_mover, 0 );
   MENU(mBatch,   "Batch Geotags", 0,            batch_geotags_tip,         m_batch_geotags, 0 );
   MENU(mBatch,   "Export File List", 0,         export_filelist_tip,       m_export_filelist, 0 );
   MENU(mBatch,   "Export Files", 0,             export_files_tip,          m_export_files, 0 );

   mTools = create_popmenu();
   MENU(mTools,   "Index Files", 0,              index_tip,                 m_index, 0 );
   MENU(mTools,   "Quick Index", 0,              quick_index_tip,           m_quick_index, 0 );
   MENU(mTools,   "Settings", 0,                 settings_tip,              m_settings, 0 );
   MENU(mTools,   "KB Shortcuts", 0,             KB_shortcuts_tip,          m_KB_shortcuts, 0 );
   MENU(mTools,   "RGB Histogram", 0,            RGB_hist_tip,              m_RGB_hist, 0 );
   MENU(mTools,   "Magnify Image", 0,            magnify_tip,               m_magnify, 0 );
   MENU(mTools,   "Measure Image", 0,            measure_image_tip,         m_measure_image, 0 );
   MENU(mTools,   "Show RGB", 0,                 show_RGB_tip,              m_show_RGB, 0 );
   MENU(mTools,   "Grid Settings", 0,            grid_settings_tip,         m_grid_settings, 0 );
   MENU(mTools,   "Toggle Grid", 0,              toggle_grid_tip,           m_toggle_grid, 0 );
   MENU(mTools,   "Line Color", 0,               line_color_tip,            m_line_color, 0 );
   MENU(mTools,   "Dark/Bright Pixels", 0,       darkbrite_tip,             m_darkbrite, 0 );
   MENU(mTools,   "Monitor Color", 0,            monitor_color_tip,         m_monitor_color, 0 );
   MENU(mTools,   "Find Duplicates", 0,          duplicates_tip,            m_duplicates, 0 );
   MENU(mTools,   "Show Resources", 0,           resources_tip,             m_resources, 0 );

   mHelp = create_popmenu();
   MENU(mHelp,    "User Guide", 0,               user_guide_tip,            m_help, 0 );
   MENU(mHelp,    "Video Tutorial", 0,           video_tutorial_tip,        m_help, 0 );
   MENU(mHelp,    "Outboard Programs", 0,        outboard_programs_tip,     m_help, 0 );
   MENU(mHelp,    "Log File", 0,                 logfile_tip,               m_help, 0 );
   MENU(mHelp,    "Command Params", 0,           command_params_tip,        m_help, 0 );
   MENU(mHelp,    "Change Log", 0,               changelog_tip,             m_help, 0 );
   MENU(mHelp,    "Copyright", 0,                copyright_tip,             m_help, 0 );
   MENU(mHelp,    "Privacy", 0,                  privacy_tip,               m_help, 0 );
   MENU(mHelp,    "About", 0,                    about_tip,                 m_help, 0 );
   MENU(mHelp,    "Home Page", 0,                homepage_tip,              m_help, 0 );
   MENU(mHelp,    "Uninstall", 0,                uninstall_tip,             m_help, 0 );

   //  main menu buttons - must be last

   MENU(0,  "File",         "file.png",          File_tip,            (cbFunc *) popup_menu, (ch *) mFile);
   MENU(0,  "Gallery",      "gallery.png",       Gallery_tip,         (cbFunc *) popup_menu, (ch *) mGallery);
   MENU(0,  "Maps",         "maps.png",          Maps_tip,            (cbFunc *) popup_menu, (ch *) mMaps);
   MENU(0,  "Custom",       "custom.png",        custom_menu_tip,     m_custom_menu, 0 );
   MENU(0,  "Zoom",         "zoom.png",          zoom_menu_tip,       m_zoom_menu, 0 );
   MENU(0,  "Prev/Next",    "prev-next.png",     prev_next_tip,       m_prev_next, 0 );
   MENU(0,  "Save",         "save.png",          save_tip,            m_file_save, 0 );
   MENU(0,  "Metadata",     "metadata.png",      metadata_tip,        (cbFunc *) popup_menu, (ch *) mMeta);
   MENU(0,  "Select",       "select.png",        select_tip,          (cbFunc *) popup_menu, (ch *) mSelect);
   MENU(0,  "Undo/Redo",    "undo-redo.png",     undo_redo_tip,       m_undo_redo, 0 );
   MENU(0,  "Edit",         "edit.png",          edit_tip,            (cbFunc *) popup_menu, (ch *) mEdit);
   MENU(0,  "Repair",       "repair.png",        repair_tip,          (cbFunc *) popup_menu, (ch *) mRepair);
   MENU(0,  "Refine",       "refine.png",        refine_tip,          (cbFunc *) popup_menu, (ch *) mRefine);
   MENU(0,  "Effects",      "effects.png",       effects_tip,         (cbFunc *) popup_menu, (ch *) mEffects);
   MENU(0,  "Warp",         "warp.png",          warp_tip,            (cbFunc *) popup_menu, (ch *) mWarp);
   MENU(0,  "Combine",      "combine.png",       combine_tip,         (cbFunc *) popup_menu, (ch *) mComb);
   MENU(0,  "Batch",        "batch.png",         batch_tip,           (cbFunc *) popup_menu, (ch *) mBatch);
   MENU(0,  "Tools",        "tools.png",         tools_tip,           (cbFunc *) popup_menu, (ch *) mTools);
   MENU(0,  "Help",         "help.png",          help_tip,            (cbFunc *) popup_menu, (ch *) mHelp);

   //  show developer tools menu if required

   if (Fdevmenu)
   {
      mDevmenu = create_popmenu();
      MENU(mDevmenu, "zmalloc report",    0,       "show memory allocations",    m_zmalloc_report,    0);
      MENU(mDevmenu, "zmalloc growth",    0,       "show memory growth by tag",  m_zmalloc_growth,    0);
      MENU(mDevmenu, "mouse events",      0,       "toggle: show mouse events",  m_mouse_events,      0);
      MENU(mDevmenu, "Audit User Guide",  0,       "check all user guide links", m_audit_userguide,   0);
      MENU(mDevmenu, "zappcrash test",    0,       "backtrace dump",             m_zappcrash_test,    0 );
      MENU(mDevmenu, "Edit Template",     0,       "edit function template",     m_template,          0);
      MENU(0,        "Dev-Tools", "devmenu.png",   "developer tools",  (cbFunc *) popup_menu, (ch *) mDevmenu);
   }

   int   Vmenus = Nmenus;                                                        //  visible menus stop here

   //  internal functions not shown in menus

   MENU(mDevmenu, "Copy From Image Slave", 0, "slave process",  m_copy_from_image_slave, 0 );
   MENU(mDevmenu, "Autosearch",            0, "search results > stdout", m_autosearch, 0);
   MENU(mEffects, "dither0",               0, 0,          m_dither0, 0 );
   MENU(mEffects, "dither1",               0, 0,          m_dither1, 0 );
   MENU(mEffects, "dither2",               0, 0,          m_dither2, 0 );
   MENU(mEffects, "dither3",               0, 0,          m_dither3, 0 );
   MENU(mEdit,    "Plugins",               0, 0,          m_plugins, 0);
   MENU(mBatch,   "Edit Script",           0, 0,          m_edit_script, 0 );
   MENU(mBatch,   "Run Script",            0, 0,          m_run_script, 0);
   MENU(mBatch,   "Batch Script",          0, 0,          m_batch_script, 0);

   //  build the menu buttons for the main menu ---------------------------

   float    frgb[3], brgb[3];
   frgb[0] = MFrgb[0] / 256.0;                                                   //  menu font color
   frgb[1] = MFrgb[1] / 256.0;                                                   //  convert range to 0-1
   frgb[2] = MFrgb[2] / 256.0;
   brgb[0] = MBrgb[0] / 256.0;                                                   //  menu background color
   brgb[1] = MBrgb[1] / 256.0;                                                   //  convert range to 0-1
   brgb[2] = MBrgb[2] / 256.0;

   Vmenu *Xvm = Vmenu_new(MWmenu,frgb,brgb);                                     //  create main menu

   int   siz = iconsize;                                                         //  user settings parameter

   for (me = 0; me < Vmenus; me++)
   {
      if (menutab[me].topmenu)                                                   //  submenu within top menu
         add_popmenu_item(menutab[me].topmenu, menutab[me].menu,
                menutab[me].func, menutab[me].arg, menutab[me].desc);

      else                                                                       //  top menu (button)
      {
         if (strmatch(menu_style,"icons")) {                                     //  icons only
            if (menutab[me].icon)
               Vmenu_add(Xvm, 0, menutab[me].icon,siz,siz,menutab[me].desc,
                                 menutab[me].func, menutab[me].arg);
            else                                                                 //  no icon, use menu text
               Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc,
                                 menutab[me].func, menutab[me].arg);
         }

         else if (strmatch(menu_style,"text")) {                                 //  text only
            Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc,
                        menutab[me].func, menutab[me].arg);
         }

         else                                                                    //  icons + menu text
            Vmenu_add(Xvm, menutab[me].menu, menutab[me].icon, siz, siz,
                        menutab[me].desc, menutab[me].func, menutab[me].arg);
      }
   }

   Vmenu_add_setup(Xvm, 0, m_viewmode, "F");                                     //  add setup functions
   Vmenu_add_setup(Xvm, 1, m_viewmode, "G");                                     //    for top 3 menu buttons
   Vmenu_add_setup(Xvm, 2, m_viewmode, "M");                                     //      to set corresp. view mode

   Vmenu_add_RMfunc(Xvm, 0, m_viewmode, "F");                                    //  add right-mouse function
   Vmenu_add_RMfunc(Xvm, 1, m_viewmode, "G");                                    //    for top 3 menu buttons
   Vmenu_add_RMfunc(Xvm, 2, m_viewmode, "M");


   //  build table of eligible menus for KB shortcut assignment -----------------

   #define KBshort(_menu, _func, _arg)                               \
      me = Nkbsf++;                                                  \
      if (me >= maxkbsf) zappcrash("maxkbs exceeded");               \
      kbsftab[me].menu = _menu;                                      \
      kbsftab[me].func = _func;                                      \
      kbsftab[me].arg = _arg;

   Nkbsf = 0;

   //       menu                    called function           arg
   KBshort("Adjust HSL",            m_adjust_HSL,              0     );
   KBshort("Adjust RGB",            m_adjust_RGB,              0     );
   KBshort("Amplify Contrast",      m_amplifycon,              0     );
   KBshort("Blur",                  m_blur,                    0     );
   KBshort("Bookmarks",             m_bookmarks,               0     );
   KBshort("Choose Captions",       m_meta_choose_caps,        0     );
   KBshort("Color Mode",            m_color_mode,              0     );
   KBshort("Copy/Move",             m_copy_move,               0     );
   KBshort("Copy to Clipboard",     m_copyto_clip,             0     );
   KBshort("Copy to Desktop",       m_copyto_desktop,          0     );
   KBshort("Crop",                  m_crop,                    0     );
   KBshort("Current Album",         m_current_album,           0     );
   KBshort("Current Folder",        m_current_folder,          0     );
   KBshort("Cycle 2 Files",         m_cycle2files,             0     );
   KBshort("Cycle 3 Files",         m_cycle3files,             0     );
   KBshort("Delete Meta",           m_meta_delete,             0     );
   KBshort("Delete/Trash",          m_delete_trash,            0     );
   KBshort("Denoise",               m_denoise,                 0     );
   KBshort("Dither",                m_dither,                  0     );
   KBshort("Edit Any Meta",         m_meta_edit_any,           0     );
   KBshort("Edit Histogram",        m_edit_hist,               0     );
   KBshort("Edit Main Meta",        m_meta_edit_main,          0     );
   KBshort("File View",             m_viewmode,               "F"    );
   KBshort("Flatten Histogram",     m_flat_hist,               0     );
   KBshort("Folder Tree",           m_folder_tree,             0     );
   KBshort("Gallery View",          m_viewmode,               "G"    );
   KBshort("Global Retinex",        m_gretinex,                0     );
   KBshort("Grid Settings",         m_grid_settings,           0     );
   KBshort("KB Shortcuts",          m_KB_shortcuts,            0     );
   KBshort("Recent Folders",        m_recent_folders,          0     );
   KBshort("Line Color",            m_line_color,              0     );
   KBshort("Local Retinex",         m_lretinex,                0     );
   KBshort("Magnify Image",         m_magnify,                 0     );
   KBshort("Manage Albums",         m_manage_albums,           0     );
   KBshort("Map View",              m_viewmode,               "M"    );
   KBshort("Markup",                m_markup,                  0     );
   KBshort("Meta View",             m_metaview,                0     );
   KBshort("Newest",                m_newest_files,            0     );
   KBshort("New Session",           m_new_session,             0     );
   KBshort("New Version",           m_file_save_version,       0     );
   KBshort("Open Image File",       m_open_file,               0     );
   KBshort("Permissions",           m_permissions,             0     );
   KBshort("Places/Dates",          m_meta_places_dates,       0     );
   KBshort("Print Calibrated",      m_print_calibrated,        0     );
   KBshort("Print Image",           m_print_image,             0     );
   KBshort("Quit",                  m_quit,                    0     );
   KBshort("Recent",                m_recentfiles,             0     );
   KBshort("Red Eyes",              m_redeyes ,                0     );
   KBshort("Redo",                  m_redo,                    0     );
   KBshort("Rename",                m_rename,                  0     );
   KBshort("Replace",               m_file_save_replace,       0     );
   KBshort("Rescale",               m_rescale,                 0     );
   KBshort("Retouch",               m_retouch,                 0     );
   KBshort("RGB Histogram",         m_RGB_hist,                0     );
   KBshort("Rotate",                m_rotate,                  0     );
   KBshort("Save",                  m_file_save,               0     );
   KBshort("Search",                m_search_images,           0     );
   KBshort("Select Area",           m_select_area,             0     );
   KBshort("Select Files",          m_select_files,            0     );
   KBshort("Set Colors",            m_set_colors,              0     );
   KBshort("Settings",              m_settings,                0     );
   KBshort("Sharpen",               m_sharpen,                 0     );
   KBshort("Show Hidden",           m_show_hidden,             0     );
   KBshort("Show on Map",           m_map_zoomin,              0     );
   KBshort("Show Resources",        m_resources,               0     );
   KBshort("Timeline",              m_meta_timeline,           0     );
   KBshort("Toggle Captions",       m_meta_toggle_caps,        0     );
   KBshort("Toggle Grid",           m_toggle_grid,             0     );
   KBshort("Undo",                  m_undo,                    0     );
   KBshort("Upright",               m_upright,                 0     );
   KBshort("View 360° Pano",        m_view360,                 0     );
   KBshort("View Main Meta",        m_meta_view_short,         0     );
   KBshort("zmalloc by tag",        m_zmalloc_report,          0     );
   KBshort("zmalloc growth",        m_zmalloc_growth,          0     );
   KBshort("Zoom-in",               m_zoom,                   "in"   );
   KBshort("Zoom-out",              m_zoom,                   "out"  );
   //  zdialog completion buttons that can have KB shortcuts assigned
   KBshort("Apply",                 0,                         0     );
   KBshort("Cancel",                0,                         0     );
   KBshort("OK",                    0,                         0     );
   KBshort("Reset",                 0,                         0     );

   //  build right-click popup menus --------------------------------------------

   ch    *menumeta1 = "View Main Meta";
   ch    *menumeta1A = "View All Meta";
   ch    *menumeta2 = "Edit Main Meta";
   ch    *menurename = "Rename";
   ch    *menudeltrash = "Delete/Trash";
   ch    *menucopymove = "Copy/Move";
   ch    *menucopytodesktop = "Copy to Desktop";
   ch    *menucopytoclip = "Copy to Clipboard";
   ch    *menurotateimage = "Rotate";
   ch    *menucropimage = "Crop";
   ch    *menuretouch = "Retouch";
   ch    *menurescale = "Rescale";
   ch    *menusharpen = "Sharpen";
   ch    *menublur = "Blur";
   ch    *menudenoise = "Denoise";
   ch    *menuedithist = "Edit Histogram";
   ch    *menuflattenhist = "Flatten Histogram";
   ch    *menulocalcon = "Local Contrast";
   ch    *menuamplifycon = "Amplify Contrast";
   ch    *menusaturation = "Saturation";
   ch    *menushowonmap = "Show on Map";                                         //  24.60
   ch    *menuaddtoselection = "Add to Selection";                               //  24.20
   ch    *menuthumbframe = "Thumbnail Frame";
   ch    *menupopimage = "Popup Image";
   ch    *menualbumaddselfiles = "Add Selected Files Here";
   ch    *menualbumaddcurrfile = "Add Current File Here";
   ch    *menuremovefromalbum = "Remove from Album";

   popmenu_image = create_popmenu();                                             //  popup menu for image files
   add_popmenu_item(popmenu_image,menumeta1,popup_menufunc,"view main meta");
   add_popmenu_item(popmenu_image,menumeta1A,popup_menufunc,"view all meta");
   add_popmenu_item(popmenu_image,menumeta2,popup_menufunc,"edit main meta");
   add_popmenu_item(popmenu_image,menurename,popup_menufunc,"rename");
   add_popmenu_item(popmenu_image,menudeltrash,popup_menufunc,"delete/trash");
   add_popmenu_item(popmenu_image,menucopymove,popup_menufunc,"copymove");
   add_popmenu_item(popmenu_image,menucopytodesktop,popup_menufunc,"copytodesktop");
   add_popmenu_item(popmenu_image,menucopytoclip,popup_menufunc,"copytoclip");
   add_popmenu_item(popmenu_image,menurotateimage,popup_menufunc,"rotate");
   add_popmenu_item(popmenu_image,menucropimage,popup_menufunc,"crop");
   add_popmenu_item(popmenu_image,menuretouch,popup_menufunc,"retouch");
   add_popmenu_item(popmenu_image,menurescale,popup_menufunc,"rescale");
   add_popmenu_item(popmenu_image,menusharpen,popup_menufunc,"sharpen");
   add_popmenu_item(popmenu_image,menublur,popup_menufunc,"blur");
   add_popmenu_item(popmenu_image,menudenoise,popup_menufunc,"denoise");
   add_popmenu_item(popmenu_image,menuedithist,popup_menufunc,"edit hist");
   add_popmenu_item(popmenu_image,menuflattenhist,popup_menufunc,"flatten hist");
   add_popmenu_item(popmenu_image,menulocalcon,popup_menufunc,"local contrast");
   add_popmenu_item(popmenu_image,menuamplifycon,popup_menufunc,"amplify contrast");
   add_popmenu_item(popmenu_image,menusaturation,popup_menufunc,"saturation");
   add_popmenu_item(popmenu_image,menushowonmap,popup_menufunc,"show on map");
   add_popmenu_item(popmenu_image,menuaddtoselection,popup_menufunc,"add to selection");                               //  24.20

   popmenu_raw = create_popmenu();                                               //  popup menu for RAW files
   add_popmenu_item(popmenu_raw,menumeta1,popup_menufunc,"view main meta");
   add_popmenu_item(popmenu_raw,menumeta1A,popup_menufunc,"view all meta");
   add_popmenu_item(popmenu_raw,menumeta2,popup_menufunc,"edit main meta");
   add_popmenu_item(popmenu_raw,menurename,popup_menufunc,"rename");
   add_popmenu_item(popmenu_raw,menudeltrash,popup_menufunc,"delete/trash");
   add_popmenu_item(popmenu_raw,menucopymove,popup_menufunc,"copymove");

   popmenu_video = create_popmenu();                                             //  popup menu for VIDEO files
   add_popmenu_item(popmenu_video,menumeta1,popup_menufunc,"view main meta");
   add_popmenu_item(popmenu_video,menumeta1A,popup_menufunc,"view all meta");
   add_popmenu_item(popmenu_video,menumeta2,popup_menufunc,"edit main meta");
   add_popmenu_item(popmenu_video,menurename,popup_menufunc,"rename");
   add_popmenu_item(popmenu_video,menudeltrash,popup_menufunc,"delete/trash");
   add_popmenu_item(popmenu_video,menucopymove,popup_menufunc,"copymove");
   add_popmenu_item(popmenu_video,menucopytodesktop,popup_menufunc,"copytodesktop");
   add_popmenu_item(popmenu_video,menuthumbframe,popup_menufunc,"thumbnail_frame");

   popmenu_thumb = create_popmenu();                                             //  gallery thumbnail popup menu
   add_popmenu_item(popmenu_thumb,menupopimage,popup_menufunc,"popimage");
   add_popmenu_item(popmenu_thumb,menumeta1,popup_menufunc,"view main meta");
   add_popmenu_item(popmenu_thumb,menumeta1A,popup_menufunc,"view all meta");
   add_popmenu_item(popmenu_thumb,menumeta2,popup_menufunc,"edit main meta");
   add_popmenu_item(popmenu_thumb,menurename,popup_menufunc,"rename");
   add_popmenu_item(popmenu_thumb,menudeltrash,popup_menufunc,"delete/trash");
   add_popmenu_item(popmenu_thumb,menucopymove,popup_menufunc,"copymove");
   add_popmenu_item(popmenu_thumb,menucopytodesktop,popup_menufunc,"copytodesktop");
   add_popmenu_item(popmenu_thumb,menucopytoclip,popup_menufunc,"copytoclip");

   popmenu_album = create_popmenu();                                             //  album thumbnail popup menu
   add_popmenu_item(popmenu_album,menupopimage,popup_menufunc,"popimage");
   add_popmenu_item(popmenu_album,menumeta1,popup_menufunc,"view main meta");
   add_popmenu_item(popmenu_album,menumeta1A,popup_menufunc,"view all meta");
   add_popmenu_item(popmenu_album,menumeta2,popup_menufunc,"edit main meta");
   add_popmenu_item(popmenu_album,menucopytodesktop,popup_menufunc,"copytodesktop");
   add_popmenu_item(popmenu_album,menucopytoclip,popup_menufunc,"copytoclip");
   add_popmenu_item(popmenu_album,menualbumaddselfiles,popup_menufunc,"albumaddselfiles");
   add_popmenu_item(popmenu_album,menualbumaddcurrfile,popup_menufunc,"albumaddcurrfile");
   add_popmenu_item(popmenu_album,menuremovefromalbum,popup_menufunc,"removefromalbum");

   return;
}


//  right-click popup menu response function

void popup_menufunc(GtkWidget *, ch *menu)
{
   if (strmatch(menu,"view main meta")) meta_view(1);
   if (strmatch(menu,"view all meta")) meta_view(2);
   if (strmatch(menu,"edit main meta")) m_meta_edit_main(0,0);
   if (strmatch(menu,"rename")) m_rename(0,0);
   if (strmatch(menu,"delete/trash")) m_delete_trash(0,0);
   if (strmatch(menu,"copymove")) m_copy_move(0,0);
   if (strmatch(menu,"copytodesktop")) m_copyto_desktop(0,0);
   if (strmatch(menu,"copytoclip")) m_copyto_clip(0,0);
   if (strmatch(menu,"rotate")) m_rotate(0,0);
   if (strmatch(menu,"crop")) m_crop(0,0);
   if (strmatch(menu,"retouch")) m_retouch(0,0);
   if (strmatch(menu,"rescale")) m_rescale(0,0);
   if (strmatch(menu,"sharpen")) m_sharpen(0,0);
   if (strmatch(menu,"blur")) m_blur(0,0);
   if (strmatch(menu,"denoise")) m_denoise(0,0);
   if (strmatch(menu,"edit hist")) m_edit_hist(0,0);
   if (strmatch(menu,"flatten hist")) m_flat_hist(0,0);
   if (strmatch(menu,"local contrast")) m_localcon(0,0);
   if (strmatch(menu,"amplify contrast")) m_amplifycon(0,0);
   if (strmatch(menu,"saturation")) m_saturation(0,0);
   if (strmatch(menu,"show on map")) m_map_zoomin(0,0);
   if (strmatch(menu,"add to selection")) add_file_to_selection(curr_file);      //  24.20
   if (strmatch(menu,"thumbnail_frame")) m_thumbframe(0,0);
   if (strmatch(menu,"popimage")) gallery_popimage();
   if (strmatch(menu,"albumaddselfiles")) album_add_selfiles(2);
   if (strmatch(menu,"albumaddcurrfile")) album_add_currfile(2);
   if (strmatch(menu,"removefromalbum")) album_remove_file(clicked_posn);

   return;
}


//  main window mouse right-click popup menu

void image_Rclick_popup()
{
   int      ftype;

   if (! curr_file) return;

   ftype = image_file_type(curr_file);
   if (ftype == IMAGE) popup_menu(Mwin,popmenu_image);
   if (ftype == RAW) popup_menu(Mwin,popmenu_raw);
   if (ftype == VIDEO) popup_menu(Mwin,popmenu_video);
   return;
}


//  gallery thumbnail mouse left-click function
//  open the clicked file in view mode F

void gallery_Lclick_func(int Nth)
{
   ch       *file;
   int      err = 0;

   if (clicked_file) {                                                           //  free memory of clicked thumbnail
      zfree(clicked_file);
      clicked_file = 0;
   }

   file = gallery(0,"get",Nth);
   if (! file) return;

   err = f_open(file,Nth,0,1);                                                   //  clicked file >> current file
   if (! err) viewmode('F');
   zfree(file);
   return;
}


//  gallery thumbnail mouse right-click popup menu

void gallery_Rclick_popup(int Nth)
{
   FTYPE    ftype;

   clicked_posn = Nth;                                                           //  clicked gallery position (0 base)
   clicked_file = gallery(0,"get",Nth);                                          //  clicked_file is subject for zfree()
   if (! clicked_file) return;

   if (navi::gallerytype == ALBUM) {
      popup_menu(Mwin,popmenu_album);
      return;
   }

   ftype = image_file_type(clicked_file);
   if (ftype == IMAGE) popup_menu(Mwin,popmenu_thumb);
   if (ftype == RAW) popup_menu(Mwin,popmenu_raw);
   if (ftype == VIDEO) popup_menu(Mwin,popmenu_video);

   return;
}


/********************************************************************************/

//  set window view mode, F/G/M

void m_viewmode(GtkWidget *, ch *fgm)                                            //  menu function
{
   viewmode(fgm[0]);
   return;
}


void viewmode(ch fgm)                                                            //  callable
{
   ch     wintitle[100];

   Plog(1,"m_viewmode %c \n",fgm);

   if (! fgm) fgm = '0';                                                         //  null -> '0'

   if (fgm == FGM) {                                                             //  no view mode change
      zfuncs::vmenustop = 0;                                                     //  continue menu func
      return;
   }

   zfuncs::vmenustop = 1;                                                        //  view mode change, stop menu func

   if (fgm == '0')                                                               //  set no view mode (blank window)
   {
      gtk_widget_hide(Fhbox);
      gtk_widget_hide(Ghbox);
      gtk_widget_hide(Mhbox);
      FGM = '0';
      PFGM = '0';                                                                //  remember last F/G view

      Cstate = 0;                                                                //  no image drawing area
      Cdrawin = 0;
      gdkwin = 0;

      if (zd_deltrash) m_delete_trash(0,0);                                      //  set target file in active dialog
      if (zd_copymove) m_copy_move(0,0);
      if (zd_metaview) meta_view(0);
      if (zd_rename) m_rename(0,0);
      if (zd_permissions) m_permissions(0,0);
   }

   if (fgm == 'F')                                                               //  set F view mode for image file
   {      
      gtk_widget_hide(Ghbox);
      gtk_widget_hide(Mhbox);
      gtk_widget_show_all(Fhbox);
      if (! Fpanelshow) gtk_widget_hide(Fpanel);                                 //  no panel

      Cstate = &Fstate;                                                          //  set drawing area
      Cdrawin = Fdrawin;
      gdkwin = gtk_widget_get_window(Fdrawin);                                   //  GDK window

/***
      gdk_window_hide(gdkwin);                                                   //  *** gdk bug workaround *** remove     24.40
      zmainsleep(0.001);
      gdk_window_show_unraised(gdkwin);                                          //  sometimes fails to paint  U22.04
***/

      FGM = 'F';
      PFGM = 'F';                                                                //  remember last F/G view

      set_mwin_title();

      if (zd_deltrash) m_delete_trash(0,0);                                      //  set target file in active dialog
      if (zd_copymove) m_copy_move(0,0);
      if (zd_metaview) meta_view(0);
      if (zd_rename) m_rename(0,0);
      if (zd_permissions) m_permissions(0,0);
   }

   if (fgm == 'G')                                                               //  set G view mode for thumbnail gallery
   {
      gtk_widget_hide(Fhbox);
      gtk_widget_hide(Mhbox);
      gtk_widget_show_all(Ghbox);
      FGM = 'G';
      PFGM = 'G';                                                                //  remember last F/G view

      Cstate = 0;                                                                //  no image drawing area
      Cdrawin = 0;
      gdkwin = 0;

      if (curr_file) gallery(curr_file,"paint",0);                               //  set gallery posn. at curr. file
      else gallery(0,"paint",-1);                                                //  else leave unchanged
   }

   if (fgm == 'M')                                                               //  set M view mode for maps
   {
      if (Findexvalid == 0) {
         zmessageACK(Mwin,"image index disabled");                               //  no image index
         return;
      }

      if (CEF) return;                                                           //  don't interrupt edit func.

      gtk_widget_hide(Fhbox);
      gtk_widget_hide(Ghbox);
      gtk_widget_show_all(Mhbox);
      FGM = 'M';

      Cstate = 0;                                                                //  no image drawing area
      Cdrawin = 0;
      gdkwin = 0;

      m_worldmap(0,"init");                                                      //  load initial map
      snprintf(wintitle,100,"%s   Image Locations",Frelease);
      gtk_window_set_title(MWIN,wintitle);                                       //  window title
   }

   return;
}


/********************************************************************************/

//  generate custom user menu from custom menu file
//  menu entry options:
//     # comment or heading                  comment or heading for a group of menunames
//     menuname   # tooltip                  start a fotocx menu function
//     album  albumname                      open an album
//     file  filename                        open an image file


namespace custom_menu_names
{
   ch    **menulist = 0;
   int   Nm = 0;
}


//  menu function

void m_custom_menu(GtkWidget *, ch *)                                            //  24.20
{
   using namespace custom_menu_names;

   void custom_menu_process(GtkWidget *, ch *);
   void custom_menu_edit(GtkWidget *, ch *);

   GtkWidget   *popmenu;
   ch          *menu, *text, *pp;
   int         ii, nn;

   F1_help_topic = "custom menu";

   Plog(1,"m_custom_menu \n");

   if (menulist) zreadfile_free(menulist);                                       //  clear prior
   Nm = zreadfile(custom_menu_file,menulist);                                    //  (may be empty)

   popmenu = create_popmenu();                                                   //  create popup menu

   for (ii = 0; ii < Nm; ii++)                                                   //  parse:  menuname # text
   {
      menu = text = 0;

      pp = strchr(menulist[ii],'#');                                             //  get text alone after '#'
      if (pp) {
         *pp = 0;
         text = pp + 1;
         nn = strTrim2(text);                                                    //  remove blanks
         if (! nn) text = 0;
      }
      
      nn = strTrim2(menulist[ii]);                                               //  get menuname before '#'
      if (nn) menu = menulist[ii];                                               //  have menuname # text (tool tip) 
      else {
         *pp = '#';                                                              //  no menuname, restore '#'
         menu = pp;                                                              //  have only # text 
         text = 0;
      }  
      
      if (strmatchN(menu,"album",5)) text = "view album";

      add_popmenu_item(popmenu,menu,custom_menu_process,0,text);
   }

   add_popmenu_item(popmenu,"Edit Menu",custom_menu_edit,0,"edit this menu");    //  add menu edit function at end

   popup_menu(Mwin,popmenu);                                                     //  show popup menu

   return;
}


//  process custom menu entry:

void custom_menu_process(GtkWidget *, ch *text)                                  //  24.20
{
   using namespace custom_menu_names;

   ch       text2[300], albumfile[300];
   int      ii;

   Plog(1,"custom menu: %s \n",text);
   
   if (*text == '#') return;                                                     //  text = # comment - do nothing

   if (strchr(text,'~')) 
      repl_1str(text,text2,300,"~",getenv("HOME"));                              //  replace embedded "~" or "$HOME"
   else if (strstr(text,"$HOME"))                                                //    with "/home/<user>"
      repl_1str(text,text2,300,"$HOME",getenv("HOME"));
   else strncpy0(text2,text,300);
   
   if (strmatchcaseN(text2,"album",5))                                           //  text = album albumname
   {
      strTrim2(text2+5);
      snprintf(albumfile,300,"%s/%s",albums_folder,text2+5);                     //  add albums folder before name
      album_show(albumfile);                                                     //  show the album
      return;
   }
   
   if (strmatchcaseN(text2,"file",4))                                            //  text = file filename
   {
      strTrim2(text2+4);
      f_open(text2+4);                                                           //  show the file (image) 
      viewmode('F');
      return;
   }

   for (ii = 0; ii < Nmenus; ii++)                                               //  text = fotocx menu name
   {                                                                             //  find menu name in menu table
      if (! menutab[ii].menu) continue;                                          //  separator, null menu
      if (strmatchcase(text,menutab[ii].menu)) break;
   }

   if (ii < Nmenus) menutab[ii].func(0,0);                                       //  found, call menu function
   else zmessageACK(Mwin,text);                                                  //  menu not found, display it

   return;
}


void custom_menu_edit(GtkWidget *, ch *)                                         //  edit the custom menu file
{
   zdialog_edit_textfile(Mwin,custom_menu_file);                                 //  edit new menu                         24.30
   return;
}


/********************************************************************************

    Build a script file with one or more predefined edit functions
     that can be executed like a single edit function.

    fotocx.h:  ch       scriptfile[200];               //  current script file
               FILE     *script_fid;                   //  script file FID
               int      Fscriptbuild;                  //  flag, script build in progress

***/


//  menu function

void m_edit_script(GtkWidget *, ch *menu)
{
   int  edit_script_dialog_event(zdialog *zd, ch *event);

   zdialog  *zd;

   F1_help_topic = "script files";

   Plog(1,"m_edit_script \n");

/***
       _________________________________________
      |         Script Files                    |
      |                                         |
      |  [start]  begin making a script file    |
      |  [close]  finish making a script file   |
      |                                         |
      |                                     [X] |
      |_________________________________________|

***/

   zd = zdialog_new("Script Files",Mwin," X ",null);

   zdialog_add_widget(zd,"hbox","hb1","dialog");
   zdialog_add_widget(zd,"button","start","hb1","Start","space=5");
   zdialog_add_widget(zd,"label","labstart","hb1","begin making a script file");
   zdialog_add_widget(zd,"hbox","hb2","dialog");
   zdialog_add_widget(zd,"button","close","hb2","Close","space=5");
   zdialog_add_widget(zd,"label","labclose","hb2","finish making a script file");

   zdialog_run(zd,edit_script_dialog_event,"parent");
   return;
}


//  dialog event and completion function

int edit_script_dialog_event(zdialog *zd, ch *event)
{
   int  edit_script_start(void);
   int  edit_script_close(void);

   if (zd->zstat) {                                                              //  canceled
      zdialog_free(zd);
      if (script_fid) fclose(script_fid);
      script_fid = 0;
      Fscriptbuild = 0;
      return 1;
   }

   if (strmatch(event,"start"))                                                  //  start building script
      edit_script_start();

   if (strmatch(event,"close")) {                                                //  script if finished
      edit_script_close();
      zdialog_free(zd);
   }

   return 1;
}


//  start building a script file
//  if Fscriptbuild is active, edit_done() saves edit settings in script file

int edit_script_start()
{
   ch       *pp;

   if (Fscriptbuild) {
      zmessageACK(Mwin,"script already started");
      return 0;
   }

   pp = zgetfile("start a new script file",MWIN,"save",scripts_folder,1);
   if (! pp) return 0;

   strncpy0(scriptfile,pp,200);                                                  //  script file name from user
   zfree(pp);

   pp = strrchr(scriptfile,'/');                                                 //  script name = file base name
   if (! pp || strlen(pp) > 99) {
      zmessageACK(Mwin,"script file name too big");
      return 0;
   }

   script_fid = fopen(scriptfile,"w");                                           //  open script file
   if (! script_fid) {
      zmessageACK(Mwin,strerror(errno));
      return 0;
   }

   fprintf(script_fid,"script name: %s\n",pp+1);                                 //  write header record
   Fscriptbuild = 1;

   zmessageACK(Mwin,"perform edits to be included in the script file");
   return 1;
}


//  this function is called from edit_done() when Fscriptbuild is active
//  save all widget settings in the script file data

void edit_script_addfunc(editfunc *CEF)
{
   int      err;

   fprintf(script_fid,"menu: %s\n",CEF->menuname);                               //  write "menu: menu name"

   if (CEF->zd) {
      err = zdialog_save_widgets(CEF->zd,CEF->sd,CEF->menuname,script_fid);      //  write widget settings
      if (err) {
         zmessageACK(Mwin,"script file error");
         return;
      }
   }

   zmessageACK(Mwin,"%s added to script",CEF->menuname);
   return;
}


//  complete and save a script file under construction

int edit_script_close()
{
   if (! Fscriptbuild) {
      zmessageACK(Mwin,"no script file was started");
      return 0;
   }

   fprintf(script_fid,"menu: end");
   fclose(script_fid);
   script_fid = 0;
   Fscriptbuild = 0;
   zmessageACK(Mwin,"script file closed");
   return 1;
}


//  present a popup menu for user to select a script file
//  run the given function using the selected script file
//  called by  m_run_script()  and  m_batch_script()
//
//  'runfunc' arg: void runfunc(GtkWidget *, ch *scriptname);

void select_script(cbFunc *runfunc)
{
   static ch         **scriptnames = 0;
   static GtkWidget  *scriptmenu = 0;
   static int        ns = 0;

   ch          buff[200], *pp;
   int         ii, kk;
   FILE        *fid;

   if (scriptnames)
   {
      for (ii = 0; ii < ns; ii++)                                                //  free prior memory
         zfree(scriptnames[ii]);
      zfree(scriptnames);
      scriptnames = 0;
   }

   if (scriptmenu) gtk_widget_destroy(scriptmenu);
   scriptmenu = 0;

   ns = zreaddir(scripts_folder,scriptnames);                                    //  get all script names
   if (ns < 1) {                                                                 //    from script file folder
      zmessageACK(Mwin,"no script files found");
      return;
   }

   scriptmenu = create_popmenu();                                                //  create popup menu for script names
   add_popmenu_item(scriptmenu,"Cancel",0,0,0);

   for (ii = kk = 0; ii < ns; ii++)
   {
      snprintf(scriptfile,200,"%s/%s",scripts_folder,scriptnames[ii]);           //  read script file 1st record
      fid = fopen(scriptfile,"r");
      if (! fid) continue;
      pp = fgets_trim(buff,200,fid,1);                                           //  read "script name: scriptname"
      if (pp && strmatch(pp+13,scriptnames[ii])) {                               //    must match file name
         add_popmenu_item(scriptmenu,scriptnames[ii],runfunc,0,0);               //  add to popup menu
         kk++;
      }
      fclose(fid);
   }

   if (kk) popup_menu(0,scriptmenu);                                             //  present menu to select and run
   else zmessageACK(Mwin,"no script files found");

   return;
}


//  execute a script file using the current image file

void run_script(GtkWidget *, ch *scriptname)
{
   ch       buff[200];
   ch       *pp, menuname[40];
   int      ii, err;

   if (! curr_file) {
      zmessageACK(Mwin,"no current file");
      return;
   }

   if (script_fid) Plog(0,"*** run_script(): script_fid not 0 \n");

   Funcbusy(+1);                                                                 //  enable user kill

   snprintf(scriptfile,200,"%s/%s",scripts_folder,scriptname);                   //  script file from script name

   Plog(1,"start script: %s \n",scriptfile);
   script_fid = fopen(scriptfile,"r");                                           //  open script file
   if (! script_fid) {
      zmessageACK(Mwin,"script error: %s \n %s",scriptfile,strerror(errno));
      goto badfile;
   }

   pp = fgets_trim(buff,200,script_fid,1);                                       //  read "script name: scriptname"
   if (! pp) goto badfile;
   if (! strmatch(pp+13,scriptname)) goto badfile;                               //  must match script name

   while (true)                                                                  //  process script file
   {
      if (Fescape) break;                                                        //  user kill

      Fscriptrun = 1;                                                            //  script file active

      pp = fgets_trim(buff,200,script_fid,1);                                    //  read "menu: menuname"
      if (! pp) goto badfile;
      if (! strmatchN(pp,"menu: ",6)) goto badfile;
      strncpy0(menuname,pp+6,40);
      if (strmatch(menuname,"end")) break;                                       //  end of script

      for (ii = 0; menutab[ii].menu; ii++)                                       //  convert menu name to menu function
         if (strmatch(menuname,menutab[ii].menu)) break;

      if (! menutab[ii].menu) {
         zmessageACK(Mwin,"unknown edit function: %s",menuname);
         goto badfile;
      }

      Plog(1,"start edit: %s for file: %s \n",menuname,curr_file);

      menutab[ii].func(0,menutab[ii].arg);                                       //  call the menu function
      zmainsleep(0.1);

      if (CEF && CEF->zd) {
         err = zdialog_load_widgets(CEF->zd,CEF->sd,CEF->menuname,script_fid);   //  read and load dialog settings
         if (err) {
            zmessageACK(Mwin,"load widgets failed: %s",menuname);
            goto badfile;
         }
         zdialog_send_event(CEF->zd,"apply");                                    //  finish edit
         if (CEF && CEF->threadfunc) thread_wait();                              //  insure complete
         if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"done");
         Plog(1,"finish edit \n");
      }
   }

   fclose(script_fid);                                                           //  close script file
   script_fid = 0;
   Fscriptrun = 0;                                                               //  script file not active
   Fescape = 0;
   Funcbusy(-1);
   return;

badfile:
   zmessageACK(Mwin,"script file format error: %s",scriptname);
   if (script_fid) fclose(script_fid);
   script_fid = 0;
   Fscriptrun = 0;
   Fescape = 0;
   Funcbusy(-1);
   return;
}


//  execute a script file using pre-selected image files

void batch_script(GtkWidget *, ch *scriptname)
{
   ch       *imagefile;
   ch       *newfilevers;
   int      ii, err;

   if (! SFcount) {                                                              //  preselected file list
      zmessageACK(Mwin,"no files selected");
      return;
   }

   Funcbusy(+1);                                                                 //  enable user kill

   for (ii = 0; ii < SFcount; ii++)                                              //  loop image files to process
   {
      if (Fescape) break;

      imagefile = SelFiles[ii];
      err = f_open(imagefile);                                                   //  open, current image file
      if (err) {
         zmessageACK(Mwin,"open failure: %s \n %s",imagefile,strerror(errno));
         break;
      }

      run_script(0,scriptname);                                                  //  run script for image file

      newfilevers = file_new_version(curr_file);                                 //  get next avail. file version name
      if (! newfilevers) break;

      if (strmatch(curr_file_type,"RAW")) {                                      //  if RAW, substitute tif-16
         strcpy(curr_file_type,"tif");
         curr_file_bpc = 16;
      }
      err = f_save(newfilevers,curr_file_type,curr_file_bpc,0,1);                //  save file
      zfree(newfilevers);
   }

   Fescape = 0;
   Funcbusy(-1);

   zmessage_post(Mwin,"20/20",3,"script complete");
   return;
}


//  menu function
//  select and run a script file using the current image file

void m_run_script(GtkWidget *, ch *menu)
{
   F1_help_topic = "script files";
   Plog(1,"m_run_script \n");
   select_script(run_script);
   return;
}


//  menu function
//  select and run a script file using multiple selected files

void m_batch_script(GtkWidget *, ch *menu)
{
   int  batch_script_dialog_event(zdialog *zd, ch *event);

   zdialog  *zd;
   ch       text[100];

   F1_help_topic = "script files";

   Plog(1,"m_batch_script \n");

/***
       _________________________________________
      |         Batch Script                    |
      |                                         |
      |  [Select Files]  NN files selected      |
      |  [Select Script]  script file to run    |
      |                                         |
      |                                     [X] |
      |_________________________________________|

***/

   zd = zdialog_new("Batch Script",Mwin," X ",null);

   zdialog_add_widget(zd,"hbox","hb1","dialog");
   zdialog_add_widget(zd,"button","select-files","hb1","Select Files","space=5");
   zdialog_add_widget(zd,"label","fcount","hb1","no files selected");

   zdialog_add_widget(zd,"hbox","hb2","dialog");
   zdialog_add_widget(zd,"button","select-script","hb2","Select Script","space=5");
   zdialog_add_widget(zd,"label","labscript","hb2","script file to run");

   snprintf(text,100,"%d image files selected",SFcount);                         //  show selected files count
   zdialog_stuff(zd,"fcount",text);

   zdialog_run(zd,batch_script_dialog_event,"parent");
   return;
}


//  dialog event and completion function

int batch_script_dialog_event(zdialog *zd, ch *event)
{
   ch       countmess[80];

   F1_help_topic = "script files";

   if (zd->zstat) {                                                              //  canceled
      zdialog_free(zd);
      return 1;
   }

   if (strmatch(event,"select-files"))
   {
      select_files(0,1);                                                         //  get new file list                     24.20

      if (SFcount) {
         snprintf(countmess,80,"%d image files selected",SFcount);               //  update dialog file count
         zdialog_stuff(zd,"fcount",countmess);
      }
      else zdialog_stuff(zd,"fcount","no files selected");
   }

   if (strmatch(event,"select-script")) {                                        //  select and run a script file
      if (! SFcount) zmessageACK(Mwin,"no files selected");
      else {
         select_script(batch_script);
         zdialog_free(zd);
      }
   }

   return 1;
}


/********************************************************************************/

//  Plugin menu functions

namespace plugins_names
{
   int         Nplugins;                                                         //  plugin menu items
   ch          **plugins = 0;
   GtkWidget   *popup_plugmenu = 0;
}


//  choose and run a plugin function or edit the plugins menu

void m_plugins(GtkWidget *, ch *)
{
   using namespace plugins_names;

   void  m_edit_plugins(GtkWidget *, ch *);
   void  m_run_plugin(GtkWidget *, ch *);

   ch          *pp, *tt;
   FILE        *fid;
   int         ii;

   F1_help_topic = "plugins";

   Plog(1,"m_plugins \n");

   if (! regfile(plugins_file)) {
      fid = fopen(plugins_file,"w");                                             //  not found, create default             24.30
      fprintf(fid,"Gimp = gimp %%s # edit current image using Gimp \n");
      fprintf(fid,"auto-gamma = mogrify -auto-gamma %%s # apply auto gamma curve \n");
      fclose(fid);
   }

   if (plugins) zreadfile_free(plugins);                                         //  read plugins file each time
   Nplugins = zreadfile(plugins_file,plugins);
   if (Nplugins < 1) return;

   popup_plugmenu = create_popmenu();                                            //  create popup menu for plugins

   for (ii = 0; ii < Nplugins; ii++)                                             //  add the plugin menu functions
   {
      pp = strstr(plugins[ii]," = ");                                            //  get plugin record:
      if (! pp) {                                                                //    xxxx = xxxxx  # tooltip
         Plog(0,"plugin bad format: %s \n",plugins[ii]);                         //         |        |
         continue;                                                               //         pp       tt
      }
      
      pp++;
      *pp = 0;
      
      tt = strchr(pp+1,'#');                                                     //  locate tool tip                       24.30
      if (tt) {
         *tt = 0;
         tt++;
      }
      
      add_popmenu_item(popup_plugmenu, plugins[ii], m_run_plugin,0,tt);          //  create menu, menu name and tooltip

      *pp = '=';                                                                 //  restore '=' 
   }

   add_popmenu_item(popup_plugmenu, "Edit Plugins",                              //  last entry is Edit Plugins
                     m_edit_plugins, 0, "Edit plugins menu");

   popup_menu(Mwin,popup_plugmenu);                                              //  popup the menu

   return;
}


/********************************************************************************/

//  edit plugins menu
//  edit the plugins file in /home/<user>/.fotocx/plugins

void m_edit_plugins(GtkWidget *, ch *)
{
   F1_help_topic = "plugins";

   Plog(1,"m_edit_plugins \n");
   if (FGM != 'F' && FGM != 'G') return;
   zdialog_edit_textfile(Mwin,plugins_file);                                     //  24.30
   return;
}


/********************************************************************************/

//  process plugin menu selection
//  execute corresponding command using current image file

editfunc    EFplugin;

void m_run_plugin(GtkWidget *, ch *menu)
{
   using namespace plugins_names;

   int         ii, jj, err;
   ch          *pp = 0, *pp1, *pp2;
   ch          plugcommand[400], temp[400];
   ch          menuname[100];
   ch          *plugfile = 0, *outfile = 0;
   zdialog     *zd = 0;
   double      basetime;

   F1_help_topic = "plugins";

   Plog(1,"run plugin: %s \n",menu);

   for (ii = 0; ii < Nplugins; ii++)                                             //  search plugins for menu name
   {
      pp = strchr(plugins[ii],'=');                                              //  match menu name to plugin command
      if (! pp) continue;                                                        //  menu name = command "%s"
      *pp = 0;
      jj = strmatch(plugins[ii],menu);
      *pp = '=';
      if (jj) break;
   }

   if (ii == Nplugins) {
      zmessageACK(Mwin,"plugin menu not found %s",menu);
      return;
   }

   strncpy0(plugcommand,pp+1,400);                                               //  corresp. command
   strTrim2(plugcommand);

   pp = strstr(plugcommand,"%s");                                                //  no file placeholder in command
   if (! pp) {
      zd = zmessage_post(Mwin,"20/20",0,"Plugin working ...");
      err = zshell("log ack",plugcommand);                                       //  execute non-file plugin command
      zd = zmessage_post(Mwin,"20/20",3,"Plugin exited");
      return;
   }

   if (! curr_file) {
      zmessageACK(Mwin,"no current file");
      return;
   }

   plugfile = zstrdup(curr_file,"run_plugin",50);
   pp1 = strrchr(plugfile,'/');                                                  //  plugfile: /.../curr_file.ext
   pp2 = strrchr(curr_file,'.');                                                 //                |         |
                                                                                 //                pp1       pp2
   if (! pp1 || ! pp2) {
      zmessageACK(Mwin,"cannot parse current file");
      return;
   }

   strcpy(pp1,"/fotocx-plugin-file");                                            //  plugfile: /.../fotocx-plugin-file.ext
   strcat(pp1,pp2);

   err = cp_copy(curr_file,plugfile);
   if (err) {
      zmessageACK(Mwin,"cannot copy current file");
      return;
   }

   if (! strstr(plugcommand,"\"%s\"")) {                                         //  insure plugin command "%s"
      repl_1str(plugcommand,temp,400,"%s","\"%s\"");                             //     is bracketed with quotes
      strcpy(plugcommand,temp);
   }

   pp = zescape_quotes(plugfile);                                                //  escape quotes in file name
   repl_1str(plugcommand,command,CCC,"%s",pp);                                   //  command = plugcommand "plugfile"
   zfree(pp);

   zd = zmessage_post(Mwin,"20/20",0,"Plugin working ...");
   basetime = get_seconds();                                                     //  basetime = NOW
   err = zshell("log ack",command);                                              //  execute plugin command, return
   zdialog_free(zd);                                                             //  kill 'working' message

   if (err) {
      zmessageACK(Mwin,"plugin command failed");
      return;
   }

   snprintf(menuname,100,"Plugin %s",menu);                                      //  make fotocx edit function
   pp = strchr(menuname,'=');                                                    //  Plugin progname = ...
   if (pp) pp[-1] = 0;
   EFplugin.menuname = zstrdup(menuname,"run_plugin");                           //  Plugin progname
   EFplugin.menufunc = m_run_plugin;
   EFplugin.Farea = 2;                                                           //  select area usable
   if (! edit_setup(EFplugin)) return;

   outfile = find_outfile(plugfile,basetime);                                    //  search for plugin output file
   if (! outfile) {                                                              //    with mod time > basetime
      zmessageACK(Mwin,"no plugin output file found");
      edit_cancel(0);
      return;
   }

   PXM_free(E3pxm);                                                              //  discard E3 image from edit_setup
   E3pxm = PXM_load(outfile,1);                                                  //  new E3 from plugin output file
   if (! E3pxm) {
      zmessageACK(Mwin,"cannot read pluging output file");
      edit_cancel(0);
      return;
   }

   Eww = E3pxm->ww;                                                              //  new E3 size
   Ehh = E3pxm->hh;

   if (sa_stat == sa_stat_fini) {                                                //  if select area, restore unselected 
      if (Eww != E1pxm->ww || Ehh != E1pxm->hh)
         zmessageACK(Mwin,"output file size change, cannot use select area");
      else sa_postfix();
   }

   CEF->Fmods++;                                                                 //  E3 modified
   CEF->Fsaved = 0;

   edit_done(0);
   
   remove(outfile);                                                              //  24.30

   return;
}


/********************************************************************************/

//  Find the output file from a plugin or other function which processes
//    an input file and creates an output file.
//  Searches for output file with root name match and modtime > basetime.
//  Returned filename is subject for zfree().
//  Note: only files < 1 hour old are checked, so user must return from
//    the plugin app to Fotocx within 1 hour after making the output file.

ch * find_outfile(ch *infile, double basetime)
{
   ch       *findcommand = "find %s -type f -cmin -60 -print";                   //  get files < 1 hour old
   ch       buff[XFCC];
   ch       outfile[XFCC];
   ch       findpatt[XFCC];
   ch       basename[200];
   ch       *pp, *pp1, *pp2;
   int      bcc;
   FILE     *fid;
   STATB    statB;
   double   modtime = basetime;
   double   modtimex;

   *outfile = 0;

   if (! regfile(infile,&statB)) return 0;

   pp = strrchr(infile,'/');                                                     //  get input file base name
   if (! pp) return 0;
   strncpy0(basename,pp,200);
   pp = strrchr(basename,'.');
   if (pp) *pp = 0;
   bcc = strlen(basename);

   repl_Nstrs(infile,findpatt,XFCC," ","\\ ","\"","\\\"","'","\\'",0);           //  escapes for 'find' command
   pp = strrchr(findpatt,'/');                                                   //  search folder
   if (! pp) return 0;
   pp[1] = 0;

   snprintf(command,XFCC,findcommand,findpatt);                                  //  search input file folder
   Plog(2,"%s\n",command);                                                       //    for recent regular files

   fid = popen(command,"r");
   if (! fid) return 0;

   while ((pp1 = fgets_trim(buff,XFCC,fid))) {                                   //  loop files found
      if (image_file_type(pp1) != IMAGE) continue;                               //  not image file type
      pp2 = strrchr(buff,'/');                                                   //  get file base name
      if (! pp2 || ! strmatchN(pp2,basename,bcc)) continue;                      //  no match
      modtimex = get_modtime(pp1);                                               //  get file mod time
      if (modtimex < modtime) continue;                                          //  not latest match
      strncpy0(outfile,pp1,XFCC);                                                //  save latest file found
      modtime = modtimex;
   }
   pclose(fid);

   Plog(1,"plugin output file: %s \n",outfile);
   if (*outfile) return zstrdup(outfile,"find_outfile");
   else return 0;
}


