Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
15947 commits behind the upstream repository.
menuWindow.cpp 89.38 KiB
// Gmsh - Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.

#include <string.h>
#include <stdio.h>
#include <time.h>
#include <FL/Fl_Box.H>
#include <FL/fl_ask.H>
#include "GUI.h"
#include "Draw.h"
#include "menuWindow.h"
#include "shortcutWindow.h"
#include "graphicWindow.h"
#include "optionWindow.h"
#include "statisticsWindow.h"
#include "messageWindow.h"
#include "contextWindow.h"
#include "visibilityWindow.h"
#include "clippingWindow.h"
#include "manipWindow.h"
#include "fieldWindow.h"
#include "pluginWindow.h"
#include "solverWindow.h"
#include "aboutWindow.h"
#include "fileDialogs.h"
#include "partitionDialog.h"
#include "projectionEditor.h"
#include "classificationEditor.h"
#include "Options.h"
#include "Solvers.h"
#include "GmshMessage.h"
#include "CommandLine.h"
#include "Generator.h"
#include "HighOrder.h"
#include "GModel.h"
#include "PView.h"
#include "PViewData.h"
#include "PViewOptions.h"
#include "Field.h"
#include "OS.h"
#include "StringUtils.h"
#include "OpenFile.h"
#include "CreateFile.h"
#include "findLinks.h"
#include "GeoStringInterface.h"
#include "Options.h"
#include "Context.h"

extern Context_T CTX;

static void file_new_cb(Fl_Widget *w, void *data)
{
 test:
  if(file_chooser(0, 1, "New", "*")) {
    std::string name = file_chooser_get_name(1);
    if(!StatFile(name.c_str())){
      if(fl_choice("File '%s' already exists.\n\nDo you want to erase it?",
                   "Cancel", "Erase", NULL, name.c_str()))
        UnlinkFile(name.c_str());
      else
        goto test;
    }
    FILE *fp = fopen(name.c_str(), "w");
    if(!fp){
      Msg::Error("Unable to open file '%s'", name.c_str());
      return;
    }
    time_t now;
    time(&now);
    fprintf(fp, "// Gmsh project created on %s", ctime(&now));
    fclose(fp);
    OpenProject(name.c_str());
    Draw();
  }
}

#if defined(HAVE_NATIVE_FILE_CHOOSER)
#  define TT "\t"
#  define NN "\n"
#else
#  define TT " ("
#  define NN ")\t"
#endif

static const char *input_formats =
  "All files" TT "*" NN
  "Gmsh geometry" TT "*.geo" NN
  "Gmsh mesh" TT "*.msh" NN
  "Gmsh post-processing view" TT "*.pos" NN
#if defined(HAVE_OCC)
  "STEP model" TT "*.{stp,step}" NN
  "IGES model" TT "*.{igs,iges}" NN
  "BRep model" TT "*.brep" NN
#endif
  "I-deas universal mesh" TT "*.unv" NN
  "Diffpack 3D mesh" TT "*.diff" NN
  "VTK mesh" TT "*.vtk" NN
#if defined(HAVE_MED)
  "MED file" TT "*.{med,mmed,rmed}" NN
#endif
  "Medit mesh" TT "*.mesh" NN
  "Nastran bulk data file" TT "*.{bdf,nas}" NN
  "Plot3D structured mesh" TT "*.p3d" NN
  "STL surface mesh" TT "*.stl" NN
  "VRML surface mesh" TT "*.{wrl,vrml}" NN
#if defined(HAVE_LIBJPEG)
  "JPEG" TT "*.{jpg,jpeg}" NN
#endif
#if defined(HAVE_LIBPNG)
  "PNG" TT "*.png" NN
#endif
  "BMP" TT "*.bmp" NN
  "PPM" TT "*.ppm" NN
  "PGM" TT "*.pgm" NN
  "PBM" TT "*.pbm" NN
  "PNM" TT "*.pnm" NN;

static void file_open_cb(Fl_Widget *w, void *data)
{
  int n = PView::list.size();
  if(file_chooser(0, 0, "Open", input_formats)) {
    OpenProject(file_chooser_get_name(1).c_str());
    Draw();
  }
  if(n != (int)PView::list.size())
    GUI::instance()->menu->setContext(menu_post, 0);
}

static void file_merge_cb(Fl_Widget *w, void *data)
{
  int n = PView::list.size();
  int f = file_chooser(1, 0, "Merge", input_formats);
  if(f) {
    for(int i = 1; i <= f; i++)
      MergeFile(file_chooser_get_name(i).c_str());
    Draw();
  }
  if(n != (int)PView::list.size())
    GUI::instance()->menu->setContext(menu_post, 0);
}

static int _save_msh(const char *name){ return msh_dialog(name); }
static int _save_pos(const char *name){ return pos_dialog(name); }
static int _save_options(const char *name){ return options_dialog(name); }
static int _save_geo(const char *name){ return geo_dialog(name); }
static int _save_cgns(const char *name){ return cgns_write_dialog(name); }
static int _save_unv(const char *name){ return unv_dialog(name); }
static int _save_vtk(const char *name){ return generic_mesh_dialog
    (name, "VTK Options", FORMAT_VTK, true); }
static int _save_diff(const char *name){ return generic_mesh_dialog
    (name, "Diffpack Options", FORMAT_DIFF, true); }
static int _save_med(const char *name){ return generic_mesh_dialog
    (name, "MED Options", FORMAT_MED, false); }
static int _save_mesh(const char *name){ return generic_mesh_dialog
    (name, "MESH Options", FORMAT_MESH, false); }
static int _save_bdf(const char *name){ return bdf_dialog(name); }
static int _save_p3d(const char *name){ return generic_mesh_dialog
    (name, "P3D Options", FORMAT_P3D, false); }
static int _save_stl(const char *name){ return generic_mesh_dialog
    (name, "STL Options", FORMAT_STL, true); }
static int _save_vrml(const char *name){ return generic_mesh_dialog
    (name, "VRML Options", FORMAT_VRML, false); }
static int _save_eps(const char *name){ return gl2ps_dialog
    (name, "EPS Options", FORMAT_EPS); }
static int _save_gif(const char *name){ return gif_dialog(name); }
static int _save_jpeg(const char *name){ return jpeg_dialog(name); }
static int _save_tex(const char *name){ return latex_dialog(name); }
static int _save_pdf(const char *name){ return gl2ps_dialog
    (name, "PDF Options", FORMAT_PDF); }
static int _save_png(const char *name){ return generic_bitmap_dialog
    (name, "PNG Options", FORMAT_PNG); }
static int _save_ps(const char *name){ return gl2ps_dialog
    (name, "PS Options", FORMAT_PS); }
static int _save_ppm(const char *name){ return generic_bitmap_dialog
    (name, "PPM Options", FORMAT_PPM); }
static int _save_svg(const char *name){ return gl2ps_dialog
    (name, "SVG Options", FORMAT_SVG); }
static int _save_yuv(const char *name){ return generic_bitmap_dialog
    (name, "YUV Options", FORMAT_YUV); }

static int _save_auto(const char *name)
{
  switch(GuessFileFormatFromFileName(name)){
  case FORMAT_MSH  : return _save_msh(name);
  case FORMAT_POS  : return _save_pos(name);
  case FORMAT_OPT  : return _save_options(name);
  case FORMAT_GEO  : return _save_geo(name);
  case FORMAT_CGNS : return _save_cgns(name);
  case FORMAT_UNV  : return _save_unv(name);
  case FORMAT_VTK  : return _save_vtk(name);
  case FORMAT_MED  : return _save_med(name);
  case FORMAT_MESH : return _save_mesh(name);
  case FORMAT_BDF  : return _save_bdf(name);
  case FORMAT_DIFF : return _save_diff(name);
  case FORMAT_P3D  : return _save_p3d(name);
  case FORMAT_STL  : return _save_stl(name);
  case FORMAT_VRML : return _save_vrml(name);
  case FORMAT_EPS  : return _save_eps(name);
  case FORMAT_GIF  : return _save_gif(name);
  case FORMAT_JPEG : return _save_jpeg(name);
  case FORMAT_TEX  : return _save_tex(name);
  case FORMAT_PDF  : return _save_pdf(name);
  case FORMAT_PNG  : return _save_png(name);
  case FORMAT_PS   : return _save_ps(name);
  case FORMAT_PPM  : return _save_ppm(name);
  case FORMAT_SVG  : return _save_svg(name);
  case FORMAT_YUV  : return _save_yuv(name);
  default :
    CreateOutputFile(name, FORMAT_AUTO); 
    return 1;
  }
}

typedef struct{
  const char *pat;
  int (*func) (const char *name);
} patXfunc;

static void file_save_as_cb(Fl_Widget *w, void *data)
{
  static patXfunc formats[] = {
    {"Guess from extension" TT "*.*", _save_auto},
    {"Gmsh mesh" TT "*.msh", _save_msh},
    {"Gmsh mesh statistics" TT "*.pos", _save_pos},
    {"Gmsh options" TT "*.opt", _save_options},
    {"Gmsh unrolled geometry" TT "*.geo", _save_geo},
#if defined(HAVE_LIBCGNS)
    {"CGNS" TT "*.cgns", _save_cgns},
#endif
    {"I-deas universal mesh" TT "*.unv", _save_unv},
    {"Diffpack 3D mesh" TT "*.diff", _save_diff},
    {"VTK mesh" TT "*.vtk", _save_vtk},
#if defined(HAVE_MED)
    {"MED file" TT "*.med", _save_med},
#endif
    {"Medit mesh" TT "*.mesh", _save_mesh},
    {"Nastran bulk data file" TT "*.bdf", _save_bdf},
    {"Plot3D structured mesh" TT "*.p3d", _save_p3d},
    {"STL surface mesh" TT "*.stl", _save_stl},
    {"VRML surface mesh" TT "*.wrl", _save_vrml},
    {"Encapsulated PostScript" TT "*.eps", _save_eps},
    {"GIF" TT "*.gif", _save_gif},
#if defined(HAVE_LIBJPEG)
    {"JPEG" TT "*.jpg", _save_jpeg},
#endif
    {"LaTeX" TT "*.tex", _save_tex},
    {"PDF" TT "*.pdf", _save_pdf},
#if defined(HAVE_LIBPNG)
    {"PNG" TT "*.png", _save_png},
#endif
    {"PostScript" TT "*.ps", _save_ps},
    {"PPM" TT "*.ppm", _save_ppm},
    {"SVG" TT "*.svg", _save_svg},
    {"YUV" TT "*.yuv", _save_yuv},
  };
  int nbformats = sizeof(formats) / sizeof(formats[0]);
  static char *pat = 0;
  if(!pat) {
    pat = new char[nbformats * 256];
    strcpy(pat, formats[0].pat);
    for(int i = 1; i < nbformats; i++) {
      strcat(pat, NN);
      strcat(pat, formats[i].pat);
    }
  }

 test:
  if(file_chooser(0, 1, "Save As", pat)) {
    std::string name = file_chooser_get_name(1);
    if(CTX.confirm_overwrite) {
      if(!StatFile(name.c_str()))
        if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?", 
                      "Cancel", "Replace", NULL, name.c_str()))
          goto test;
    }
    int i = file_chooser_get_filter();
    if(i >= 0 && i < nbformats){
      if(!formats[i].func(name.c_str())) goto test;
    }
    else{ // handle any additional automatic fltk filter
      if(!_save_auto(name.c_str())) goto test;
    }
  }
}

#undef TT
#undef NN

static void file_rename_cb(Fl_Widget *w, void *data)
{
 test:
  if(file_chooser(0, 1, "Rename", "*", CTX.filename)) {
    std::string name = file_chooser_get_name(1);
    if(CTX.confirm_overwrite) {
      if(!StatFile(name.c_str()))
        if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?", 
                      "Cancel", "Replace", NULL, name.c_str()))
          goto test;
    }
    rename(CTX.filename, name.c_str());
    OpenProject(name.c_str());
    Draw();
  }
}

void file_quit_cb(Fl_Widget *w, void *data)
{
  Msg::Exit(0);
}

#if defined(__APPLE__)
#  define CC(str) "Cmd+" str " "
#else
#  define CC(str) "Ctrl+" str
#endif

static void help_short_cb(Fl_Widget *w, void *data)
{
  Msg::Direct(" ");
  Msg::Direct("Keyboard shortcuts:");
  Msg::Direct(" ");
  Msg::Direct("  Left arrow    Go to previous time step"); 
  Msg::Direct("  Right arrow   Go to next time step"); 
  Msg::Direct("  Up arrow      Make previous view visible"); 
  Msg::Direct("  Down arrow    Make next view visible"); 
  Msg::Direct(" ");
  Msg::Direct("  <             Go back to previous context");
  Msg::Direct("  >             Go forward to next context");
  Msg::Direct("  0             Reload project file");
  Msg::Direct("  1 or F1       Mesh lines");
  Msg::Direct("  2 or F2       Mesh surfaces");
  Msg::Direct("  3 or F3       Mesh volumes");
  Msg::Direct("  Escape        Cancel lasso zoom/selection, toggle mouse selection ON/OFF");
  Msg::Direct(" ");
  Msg::Direct("  g             Go to geometry module");
  Msg::Direct("  m             Go to mesh module");
  Msg::Direct("  p             Go to post-processing module");
  Msg::Direct("  s             Go to solver module");
  Msg::Direct(" ");
  Msg::Direct("  Shift+a       Bring all windows to front");
  Msg::Direct("  Shift+g       Show geometry options");
  Msg::Direct("  Shift+m       Show mesh options");
  Msg::Direct("  Shift+o       Show general options"); 
  Msg::Direct("  Shift+p       Show post-processing options");
  Msg::Direct("  Shift+s       Show solver options"); 
  Msg::Direct("  Shift+u       Show post-processing view plugins");
  Msg::Direct("  Shift+w       Show post-processing view options");
  Msg::Direct("  Shift+Escape  Enable full mouse selection");
  Msg::Direct(" ");
  Msg::Direct("  " CC("i") "        Show statistics window"); 
  Msg::Direct("  " CC("l") "        Show message console");
#if defined(__APPLE__)
  Msg::Direct("  " CC("m") "        Minimize window"); 
#endif
  Msg::Direct("  " CC("n") "        Create new project file"); 
  Msg::Direct("  " CC("o") "        Open project file"); 
  Msg::Direct("  " CC("q") "        Quit");
  Msg::Direct("  " CC("r") "        Rename project file");
  Msg::Direct("  " CC("s") "        Save file as");
  Msg::Direct(" ");
  Msg::Direct("  Shift+" CC("c") "  Show clipping plane window");
  Msg::Direct("  Shift+" CC("m") "  Show manipulator window"); 
  Msg::Direct("  Shift+" CC("n") "  Show option window"); 
  Msg::Direct("  Shift+" CC("o") "  Merge file(s)"); 
  Msg::Direct("  Shift+" CC("s") "  Save mesh in default format");
  Msg::Direct("  Shift+" CC("u") "  Show plugin window");
  Msg::Direct("  Shift+" CC("v") "  Show visibility window");
  Msg::Direct(" ");
  Msg::Direct("  Alt+a         Loop through axes modes"); 
  Msg::Direct("  Alt+b         Hide/show bounding boxes");
  Msg::Direct("  Alt+c         Loop through predefined color schemes");
  Msg::Direct("  Alt+e         Hide/Show element outlines for visible post-pro views");
  Msg::Direct("  Alt+f         Change redraw mode (fast/full)"); 
  Msg::Direct("  Alt+h         Hide/show all post-processing views"); 
  Msg::Direct("  Alt+i         Hide/show all post-processing view scales");
  Msg::Direct("  Alt+l         Hide/show geometry lines");
  Msg::Direct("  Alt+m         Toggle visibility of all mesh entities");
  Msg::Direct("  Alt+n         Hide/show all post-processing view annotations");
  Msg::Direct("  Alt+o         Change projection mode (orthographic/perspective)");
  Msg::Direct("  Alt+p         Hide/show geometry points");
  Msg::Direct("  Alt+r         Loop through range modes for visible post-pro views"); 
  Msg::Direct("  Alt+s         Hide/show geometry surfaces");
  Msg::Direct("  Alt+t         Loop through interval modes for visible post-pro views"); 
  Msg::Direct("  Alt+v         Hide/show geometry volumes");
  Msg::Direct("  Alt+w         Enable/disable all lighting");
  Msg::Direct("  Alt+x         Set X view"); 
  Msg::Direct("  Alt+y         Set Y view"); 
  Msg::Direct("  Alt+z         Set Z view"); 
  Msg::Direct(" ");
  Msg::Direct("  Alt+Shift+a   Hide/show small axes"); 
  Msg::Direct("  Alt+Shift+b   Hide/show mesh volume faces");
  Msg::Direct("  Alt+Shift+d   Hide/show mesh surface faces");
  Msg::Direct("  Alt+Shift+l   Hide/show mesh lines");
  Msg::Direct("  Alt+Shift+o   Adjust projection parameters");
  Msg::Direct("  Alt+Shift+p   Hide/show mesh points");
  Msg::Direct("  Alt+Shift+s   Hide/show mesh surface edges");
  Msg::Direct("  Alt+Shift+v   Hide/show mesh volume edges");
  Msg::Direct("  Alt+Shift+w   Reverse all mesh normals");
  Msg::Direct("  Alt+Shift+x   Set -X view"); 
  Msg::Direct("  Alt+Shift+y   Set -Y view"); 
  Msg::Direct("  Alt+Shift+z   Set -Z view"); 
  Msg::Direct(" ");
  GUI::instance()->messages->show();
}

#undef CC

static void help_mouse_cb(Fl_Widget *w, void *data)
{
  Msg::Direct(" ");
  Msg::Direct("Mouse actions:");
  Msg::Direct(" ");
  Msg::Direct("  Move                - Highlight the entity under the mouse pointer");
  Msg::Direct("                        and display its properties in the status bar");
  Msg::Direct("                      - Resize a lasso zoom or a lasso (un)selection");
  Msg::Direct("  Left button         - Rotate");
  Msg::Direct("                      - Select an entity");
  Msg::Direct("                      - Accept a lasso zoom or a lasso selection"); 
  Msg::Direct("  Ctrl+Left button    Start a lasso zoom or a lasso (un)selection"); 
  Msg::Direct("  Middle button       - Zoom");
  Msg::Direct("                      - Unselect an entity");
  Msg::Direct("                      - Accept a lasso zoom or a lasso unselection");
  Msg::Direct("  Ctrl+Middle button  Orthogonalize display"); 
  Msg::Direct("  Right button        - Pan");
  Msg::Direct("                      - Cancel a lasso zoom or a lasso (un)selection");
  Msg::Direct("                      - Pop-up menu on post-processing view button");
  Msg::Direct("  Ctrl+Right button   Reset to default viewpoint");   
  Msg::Direct(" ");   
  Msg::Direct("  For a 2 button mouse, Middle button = Shift+Left button");
  Msg::Direct("  For a 1 button mouse, Middle button = Shift+Left button, "
              "Right button = Alt+Left button");
  Msg::Direct(" ");
  GUI::instance()->messages->show();
}

static void help_command_line_cb(Fl_Widget *w, void *data)
{
  Msg::Direct(" ");
  Print_Usage("gmsh");
  GUI::instance()->messages->show();
}

static void help_online_cb(Fl_Widget *w, void *data)
{
  std::string prog = FixWindowsPath(CTX.web_browser);
  char cmd[1024];
  ReplaceMultiFormat(prog.c_str(), "http://geuz.org/gmsh/doc/texinfo/", cmd);
  SystemCall(cmd);
}

static void help_about_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->about->win->show();
}

void mod_geometry_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_geometry, 0);
}

void mod_mesh_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_mesh, 0);
}

void mod_solver_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_solver, 0);
}

void mod_post_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_post, 0);
}

void mod_back_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(NULL, -1);
}

void mod_forward_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(NULL, 1);
}

static void geometry_elementary_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_geometry_elementary, 0);
}

static void geometry_physical_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_geometry_physical, 0);
}

static void geometry_edit_cb(Fl_Widget *w, void *data)
{
  std::string prog = FixWindowsPath(CTX.editor);
  std::string file = FixWindowsPath(CTX.filename);
  char cmd[1024];
  ReplaceMultiFormat(prog.c_str(), file.c_str(), cmd);
  SystemCall(cmd);
}

void geometry_reload_cb(Fl_Widget *w, void *data)
{
  OpenProject(CTX.filename);
  Draw();
}

static void geometry_elementary_add_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_geometry_elementary_add, 0);
}

static void add_new_point()
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  Draw();

  GUI::instance()->geoContext->show(1);

  while(1) {
    for(unsigned int i = 0; i < GUI::instance()->graph.size(); i++)
      GUI::instance()->graph[i]->gl->addPointMode = true;
    Msg::StatusBar(3, false, "Move mouse and/or enter coordinates\n"
                   "[Press 'Shift' to hold position, 'e' to add point "
                   "or 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_NONE);
    if(ib == 'e'){
      add_point(CTX.filename,
                GUI::instance()->geoContext->input[2]->value(),
                GUI::instance()->geoContext->input[3]->value(),
                GUI::instance()->geoContext->input[4]->value(),
                GUI::instance()->geoContext->input[5]->value());
      GUI::instance()->resetVisibility();
      Draw();
    }
    if(ib == 'q'){
      for(unsigned int i = 0; i < GUI::instance()->graph.size(); i++)
        GUI::instance()->graph[i]->gl->addPointMode = false;
      break;
    }
  }

  Msg::StatusBar(3, false, "");
}

static void add_new_multiline(std::string type)
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  Draw();

  std::vector<int> p;
  while(1) {
    if(p.empty())
      Msg::StatusBar(3, false, "Select control points\n"
                     "[Press 'e' to end selection or 'q' to abort]");
    else
      Msg::StatusBar(3, false, "Select control points\n"
                     "[Press 'e' to end selection, 'u' to undo last selection "
                     "or 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_POINT);
    if(ib == 'l') {
      for(unsigned int i = 0; i < GUI::instance()->selectedVertices.size(); i++){
        GUI::instance()->selectedVertices[i]->setSelection(1);
        p.push_back(GUI::instance()->selectedVertices[i]->tag());
      }
      Draw();
    }
    if(ib == 'r') {
      Msg::Warning("Entity de-selection not supported yet during multi-line creation");
    }
    if(ib == 'e') {
      if(p.size() >= 2)
	add_multline(type, p, CTX.filename);
      GUI::instance()->resetVisibility();
      GModel::current()->setSelection(0);
      Draw();
      p.clear();
    }
    if(ib == 'u') {
      if(p.size()){
        GVertex *gv = GModel::current()->getVertexByTag(p.back());
        if(gv) gv->setSelection(0);
        Draw();
        p.pop_back();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
  }

  Msg::StatusBar(3, false, "");
}

static void add_new_line()
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  Draw();

  std::vector<int> p;
  while(1) {
    if(p.empty())
      Msg::StatusBar(3, false, "Select start point\n"
                     "[Press 'q' to abort]");
    if(p.size() == 1)
      Msg::StatusBar(3, false, "Select end point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_POINT);
    if(ib == 'l') {
      GUI::instance()->selectedVertices[0]->setSelection(1);
      Draw();
      p.push_back(GUI::instance()->selectedVertices[0]->tag());
    }
    if(ib == 'r') {
      Msg::Warning("Entity de-selection not supported yet during line creation");
    }
    if(ib == 'u') {
      if(p.size()){
        GVertex *gv = GModel::current()->getVertexByTag(p.back());
        if(gv) gv->setSelection(0);
        Draw();
        p.pop_back();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
    if(p.size() == 2) {
      add_multline("Line", p, CTX.filename);
      GUI::instance()->resetVisibility();
      GModel::current()->setSelection(0);
      Draw();
      p.clear();
    }
  }

  Msg::StatusBar(3, false, "");
}

static void add_new_circle()
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  Draw();

  std::vector<int> p;
  while(1) {
    if(p.empty())
      Msg::StatusBar(3, false, "Select start point\n"
                     "[Press 'q' to abort]");
    if(p.size() == 1)
      Msg::StatusBar(3, false, "Select center point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    if(p.size() == 2)
      Msg::StatusBar(3, false, "Select end point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_POINT);
    if(ib == 'l') {
      GUI::instance()->selectedVertices[0]->setSelection(1);
      Draw();
      p.push_back(GUI::instance()->selectedVertices[0]->tag());
    }
    if(ib == 'r') {
      Msg::Warning("Entity de-selection not supported yet during circle creation");
    }
    if(ib == 'u') {
      if(p.size()){
        GVertex *gv = GModel::current()->getVertexByTag(p.back());
        if(gv) gv->setSelection(0);
        Draw();
        p.pop_back();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
    if(p.size() == 3) {
      add_circ(p[0], p[1], p[2], CTX.filename); // begin, center, end
      GUI::instance()->resetVisibility();
      GModel::current()->setSelection(0);
      Draw();
      p.clear();
    }
  }

  Msg::StatusBar(3, false, "");
}

static void add_new_ellipse()
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  Draw();

  std::vector<int> p;
  while(1) {
    if(p.empty())
      Msg::StatusBar(3, false, "Select start point\n"
                     "[Press 'q' to abort]");
    if(p.size() == 1)
      Msg::StatusBar(3, false, "Select center point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    if(p.size() == 2)
      Msg::StatusBar(3, false, "Select major axis point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    if(p.size() == 3)
      Msg::StatusBar(3, false, "Select end point\n"
                     "[Press 'u' to undo last selection or 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_POINT);
    if(ib == 'l') {
      GUI::instance()->selectedVertices[0]->setSelection(1);
      Draw();
      p.push_back(GUI::instance()->selectedVertices[0]->tag());
    }
    if(ib == 'r') {
      Msg::Warning("Entity de-selection not supported yet during ellipse creation");
    }
    if(ib == 'u') {
      if(p.size()){
        GVertex *gv = GModel::current()->getVertexByTag(p.back());
        if(gv) gv->setSelection(0);
        Draw();
        p.pop_back();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
    if(p.size() == 4) {
      add_ell(p[0], p[1], p[2], p[3], CTX.filename);
      GUI::instance()->resetVisibility();
      GModel::current()->setSelection(0);
      Draw();
      p.clear();
    }
  }

  Msg::StatusBar(3, false, "");
}

static int select_contour(int type, int num, List_T * List)
{
  int k = 0;

  switch (type) {
  case ENT_LINE:
    k = allEdgesLinked(num, List);
    for(int i = 0; i < List_Nbr(List); i++) {
      int ip;
      List_Read(List, i, &ip);
      GEdge *ge = GModel::current()->getEdgeByTag(abs(ip));
      if(ge) ge->setSelection(1);
    }
    break;
  case ENT_SURFACE:
    k = allFacesLinked(num, List);
    for(int i = 0; i < List_Nbr(List); i++) {
      int ip;
      List_Read(List, i, &ip);
      GFace *gf = GModel::current()->getFaceByTag(abs(ip));
      if(gf) gf->setSelection(1);
    }
    break;
  }

  Draw();
  return k;
}

static void add_new_surface_volume(int mode)
{
  List_T *List1 = List_Create(10, 10, sizeof(int));
  List_T *List2 = List_Create(10, 10, sizeof(int));
  int type;
  if(mode == 2) {
    type = ENT_SURFACE;
    opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
    opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
  }
  else {
    type = ENT_LINE;
    opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
    opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
  }
  Draw();

  while(1) {
    List_Reset(List1);
    List_Reset(List2);

    while(1) {
      if(type == ENT_LINE){
        if(!List_Nbr(List1))
          Msg::StatusBar(3, false, "Select surface boundary\n"
                         "[Press 'q' to abort]");
        else
          Msg::StatusBar(3, false, "Select surface boundary\n"
                         "[Press 'u' to undo last selection or 'q' to abort]");
      }
      else{
        if(!List_Nbr(List1))
          Msg::StatusBar(3, false, "Select volume boundary\n"
                         "[Press 'q' to abort]");
        else
          Msg::StatusBar(3, false, "Select volume boundary\n"
                         "[Press 'u' to undo last selection or 'q' to abort]");
      }
      
      char ib = GUI::instance()->selectEntity(type);
      if(ib == 'q') {
        GModel::current()->setSelection(0);
        Draw();
        goto stopall;
      }
      if(ib == 'u') {
        if(List_Nbr(List1) > 0){
          int num;
          List_Read(List1, List_Nbr(List1)-1, &num);
          if(type == ENT_LINE){
            GEdge *ge = GModel::current()->getEdgeByTag(abs(num));
            if(ge) ge->setSelection(0);
          }
          else{
            GFace *gf = GModel::current()->getFaceByTag(abs(num));
            if(gf) gf->setSelection(0);
          }
          List_Pop(List1);
          Draw();
        }
      }
      if(ib == 'r') {
        Msg::Warning("Entity de-selection not supported yet during "
                     "surface/volume creation");
      }
      if(ib == 'l') {
        int num = (type == ENT_LINE) ?
          GUI::instance()->selectedEdges[0]->tag() :
          GUI::instance()->selectedFaces[0]->tag();
        if(select_contour(type, num, List1)) {
          if(type == ENT_LINE)
            add_lineloop(List1, CTX.filename, &num);
          else
            add_surfloop(List1, CTX.filename, &num);
          List_Reset(List1);
          List_Add(List2, &num);
          while(1) {
            if(!List_Nbr(List1))
              Msg::StatusBar
                (3, false, "Select hole boundaries (if none, press 'e')\n"
                 "[Press 'e' to end selection or 'q' to abort]");
            else
              Msg::StatusBar
                (3, false, "Select hole boundaries\n"
                 "[Press 'e' to end selection, 'u' to undo last selection "
                 "or 'q' to abort]");
            ib = GUI::instance()->selectEntity(type);
            if(ib == 'q') {
              GModel::current()->setSelection(0);
              Draw();
              goto stopall;
            }
            if(ib == 'e') {
              GModel::current()->setSelection(0);
              Draw();
              List_Reset(List1);
              break;
            }
            if(ib == 'u') {
              if(List_Nbr(List1) > 0){
                int num;
                List_Read(List1, List_Nbr(List1)-1, &num);
                if(type == ENT_LINE){
                  GEdge *ge = GModel::current()->getEdgeByTag(abs(num));
                  if(ge) ge->setSelection(0);
                }
                else{
                  GFace *gf = GModel::current()->getFaceByTag(abs(num));
                  if(gf) gf->setSelection(0);
                }
                List_Pop(List1);
                Draw();
              }
            }
            if(ib == 'l') {
              int num = (type == ENT_LINE) ? 
                GUI::instance()->selectedEdges[0]->tag() :
                GUI::instance()->selectedFaces[0]->tag();
              if(select_contour(type, num, List1)) {
                if(type == ENT_LINE)
                  add_lineloop(List1, CTX.filename, &num);
                else
                  add_surfloop(List1, CTX.filename, &num);
                List_Reset(List1);
                List_Add(List2, &num);
              }
            }
            if(ib == 'r') {
              Msg::Warning("Entity de-selection not supported yet during "
                           "surface/volume creation");
            }
          }
          if(List_Nbr(List2)) {
            switch (mode) {
            case 0: add_surf("Plane Surface", List2, CTX.filename); break;
            case 1: add_surf("Ruled Surface", List2, CTX.filename); break;
            case 2: add_vol(List2, CTX.filename); break;
            }
            GUI::instance()->resetVisibility();
            GModel::current()->setSelection(0);
            Draw();
            break;
          }
        } // if select_contour
      }
    }
  }

 stopall:
  List_Delete(List1);
  List_Delete(List2);

  Msg::StatusBar(3, false, "");
}

static void geometry_elementary_add_new_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_add_new, 0);
    return;
  }

  std::string str((const char*)data);
  if(str == "Parameter")
    GUI::instance()->geoContext->show(0);
  else if(str == "Point")
    add_new_point();
  else if(str == "Line")
    add_new_line();
  else if(str == "Spline")
    add_new_multiline(str);
  else if(str == "BSpline")
    add_new_multiline(str);
  else if(str == "Circle")
    add_new_circle();
  else if(str == "Ellipse")
    add_new_ellipse();
  else if(str == "Plane Surface")
    add_new_surface_volume(0);
  else if(str == "Ruled Surface")
    add_new_surface_volume(1);
  else if(str == "Volume")
    add_new_surface_volume(2);
  else
    Msg::Error("Unknown entity to create: %s", str.c_str());
}

static void split_selection()
{
  opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  Draw();
  Msg::StatusBar(3, false, "Select a line to split\n"
                 "[Press 'q' to abort]");
  GEdge* edge_to_split = 0;
  while(1){
    char ib = GUI::instance()->selectEntity(ENT_LINE);
    if(ib == 'q')
      break;
    if(!GUI::instance()->selectedEdges.empty()){
      edge_to_split = GUI::instance()->selectedEdges[0];
      edge_to_split->setSelection(1);
      break;
    }
  }
  Msg::StatusBar(3, false, "");
  if(GUI::instance()->selectedEdges.empty()) return;
  List_T *List1 = List_Create(5, 5, sizeof(int));
  Msg::StatusBar(3, false, "Select break points\n"
                 "[Press 'e' to end selection or 'q' to abort]");
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  Draw();
  while(1){
    char ib = GUI::instance()->selectEntity(ENT_POINT);
    if(ib == 'q')
      break;
    if(ib == 'e'){
      split_edge(edge_to_split->tag(), List1, CTX.filename);
      break;
    }
    for(unsigned int i = 0; i < GUI::instance()->selectedVertices.size(); i++){
      int tag = GUI::instance()->selectedVertices[i]->tag();
      int index = List_ISearchSeq(List1, &tag, fcmp_int); 
      if(index < 0) List_Add(List1, &tag);
      GUI::instance()->selectedVertices[i]->setSelection(1);
    }
  }
  Msg::StatusBar(3, false, "");
  GUI::instance()->resetVisibility();
  GModel::current()->setSelection(0);
  Draw();
}

static void action_point_line_surface_volume(int action, int mode, const char *what)
{
  int type;
  const char *str;

  if(!strcmp(what, "Point")) {
    type = ENT_POINT;
    str = "points";
    opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  }
  else if(!strcmp(what, "Line")) {
    type = ENT_LINE;
    str = "lines";
    opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1);
  }
  else if(!strcmp(what, "Surface")) {
    type = ENT_SURFACE;
    str = "surfaces";
    opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
  }
  else if(!strcmp(what, "Volume")) {
    type = ENT_VOLUME;
    str = "volumes";
    opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
  }
  else{
    Msg::Error("Unknown entity to select");
    return;
  }

  if(action == 8){
    GUI::instance()->meshContext->show(0);
  }
  Draw();
    
  List_T *List1 = List_Create(5, 5, sizeof(int));
  while(1) {
    if(!List_Nbr(List1))
      Msg::StatusBar(3, false, "Select %s\n"
                     "[Press 'e' to end selection or 'q' to abort]", str);
    else
      Msg::StatusBar(3, false, "Select %s\n"
                     "[Press 'e' to end selection, 'u' to undo last selection "
                     "or 'q' to abort]", str);
    
    char ib = GUI::instance()->selectEntity(type);
    if(ib == 'l') {
      // we don't use List_Insert in order to keep the original
      // ordering (this is slower, but this way undo works as
      // expected)
      int tag;
      switch (type) {
      case ENT_POINT: 
        for(unsigned int i = 0; i < GUI::instance()->selectedVertices.size(); i++){
          GUI::instance()->selectedVertices[i]->setSelection(1);
          tag = GUI::instance()->selectedVertices[i]->tag();
          if(List_ISearchSeq(List1, &tag, fcmp_int) < 0)
            List_Add(List1, &tag);
        }
        break;
      case ENT_LINE:
        for(unsigned int i = 0; i < GUI::instance()->selectedEdges.size(); i++){
          GUI::instance()->selectedEdges[i]->setSelection(1);
          tag = GUI::instance()->selectedEdges[i]->tag();
          if(List_ISearchSeq(List1, &tag, fcmp_int) < 0)
            List_Add(List1, &tag);
        }
        break;
      case ENT_SURFACE:
        for(unsigned int i = 0; i < GUI::instance()->selectedFaces.size(); i++){
          GUI::instance()->selectedFaces[i]->setSelection(1);
          tag = GUI::instance()->selectedFaces[i]->tag();
          if(List_ISearchSeq(List1, &tag, fcmp_int) < 0)
            List_Add(List1, &tag);
        }
        break;
      case ENT_VOLUME:
        for(unsigned int i = 0; i < GUI::instance()->selectedRegions.size(); i++){
          GUI::instance()->selectedRegions[i]->setSelection(1);
          tag = GUI::instance()->selectedRegions[i]->tag();
          if(List_ISearchSeq(List1, &tag, fcmp_int) < 0)
            List_Add(List1, &tag);
        }
        break;
      }
      Draw();
    }
    if(ib == 'r') {
      // we don't use List_Suppress in order to keep the original
      // ordering (this is slower, but this way undo works as
      // expected)
      int index, tag;
      switch (type) {
      case ENT_POINT:
        for(unsigned int i = 0; i < GUI::instance()->selectedVertices.size(); i++){
          tag = GUI::instance()->selectedVertices[i]->tag();
          index = List_ISearchSeq(List1, &tag, fcmp_int); 
          if(index >= 0) List_PSuppress(List1, index);
          GUI::instance()->selectedVertices[i]->setSelection(0);
        }
        break;
      case ENT_LINE:
        for(unsigned int i = 0; i < GUI::instance()->selectedEdges.size(); i++){
          tag = GUI::instance()->selectedEdges[i]->tag();
          index = List_ISearchSeq(List1, &tag, fcmp_int); 
          if(index >= 0) List_PSuppress(List1, index);
          GUI::instance()->selectedEdges[i]->setSelection(0);
        }
        break;
      case ENT_SURFACE:
        for(unsigned int i = 0; i < GUI::instance()->selectedFaces.size(); i++){
          tag = GUI::instance()->selectedFaces[i]->tag();
          index = List_ISearchSeq(List1, &tag, fcmp_int); 
          if(index >= 0) List_PSuppress(List1, index);
          GUI::instance()->selectedFaces[i]->setSelection(0);
        }
        break;
      case ENT_VOLUME:
        for(unsigned int i = 0; i < GUI::instance()->selectedRegions.size(); i++){
          tag = GUI::instance()->selectedRegions[i]->tag();
          index = List_ISearchSeq(List1, &tag, fcmp_int); 
          if(index >= 0) List_PSuppress(List1, index);
          GUI::instance()->selectedRegions[i]->setSelection(0);
        }
        break;
      }
      Draw();
    }
    if(ib == 'u') {
      if(List_Nbr(List1)) {
        int num;
        List_Read(List1, List_Nbr(List1) - 1, &num);
        if(type == ENT_POINT){
          GVertex *gv = GModel::current()->getVertexByTag(num);
          if(gv) gv->setSelection(0);
        }
        else if(type == ENT_LINE){
          GEdge *ge = GModel::current()->getEdgeByTag(num);
          if(ge) ge->setSelection(0);
        }
        else if(type == ENT_SURFACE){
          GFace *gf = GModel::current()->getFaceByTag(num);
          if(gf) gf->setSelection(0);
        }
        else if(type == ENT_VOLUME){
          GRegion *gr = GModel::current()->getRegionByTag(num);
          if(gr) gr->setSelection(0);
        }
        Draw();
        List_Pop(List1);
      }
    }
    if(ib == 'i') {
      Msg::Error("Inverting selection!");
    }
    if(ib == 'e') {
      if(List_Nbr(List1)){
        switch (action) {
        case 0:
          translate(mode, List1, CTX.filename, what,
                    GUI::instance()->geoContext->input[6]->value(),
                    GUI::instance()->geoContext->input[7]->value(),
                    GUI::instance()->geoContext->input[8]->value());
          break;
        case 1:
          rotate(mode, List1, CTX.filename, what,
                 GUI::instance()->geoContext->input[12]->value(),
                 GUI::instance()->geoContext->input[13]->value(),
                 GUI::instance()->geoContext->input[14]->value(),
                 GUI::instance()->geoContext->input[9]->value(),
                 GUI::instance()->geoContext->input[10]->value(),
                 GUI::instance()->geoContext->input[11]->value(),
                 GUI::instance()->geoContext->input[15]->value());
          break;
        case 2:
          dilate(mode, List1, CTX.filename, what,
                 GUI::instance()->geoContext->input[16]->value(),
                 GUI::instance()->geoContext->input[17]->value(),
                 GUI::instance()->geoContext->input[18]->value(),
                 GUI::instance()->geoContext->input[19]->value());
          break;
        case 3:
          symmetry(mode, List1, CTX.filename, what,
                   GUI::instance()->geoContext->input[20]->value(),
                   GUI::instance()->geoContext->input[21]->value(),
                   GUI::instance()->geoContext->input[22]->value(),
                   GUI::instance()->geoContext->input[23]->value());
          break;
        case 4:
          extrude(List1, CTX.filename, what,
                  GUI::instance()->geoContext->input[6]->value(),
                  GUI::instance()->geoContext->input[7]->value(),
                  GUI::instance()->geoContext->input[8]->value());
          break;
        case 5:
          protude(List1, CTX.filename, what,
                  GUI::instance()->geoContext->input[12]->value(),
                  GUI::instance()->geoContext->input[13]->value(),
                  GUI::instance()->geoContext->input[14]->value(),
                  GUI::instance()->geoContext->input[9]->value(),
                  GUI::instance()->geoContext->input[10]->value(),
                  GUI::instance()->geoContext->input[11]->value(),
                  GUI::instance()->geoContext->input[15]->value());
          break;
        case 6:
          delet(List1, CTX.filename, what);
          break;
        case 7:
          add_physical(what, List1, CTX.filename);
          break;
        case 8:
          add_charlength(List1, CTX.filename, 
                         GUI::instance()->meshContext->input[0]->value());
          break;
        case 9:
          add_recosurf(List1, CTX.filename);
          break;

        default:
          Msg::Error("Unknown action on selected entities");
          break;
        }
        List_Reset(List1);
        GUI::instance()->resetVisibility();
        GModel::current()->setSelection(0);
        if(action <= 6) SetBoundingBox();
        Draw();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
  }
  List_Delete(List1);

  Msg::StatusBar(3, false, "");
}
  
static void geometry_elementary_add_translate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_add_translate, 0);
    return;
  }
  GUI::instance()->geoContext->show(2);
  action_point_line_surface_volume(0, 1, (const char*)data);
}

static void geometry_elementary_add_rotate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_add_rotate, 0);
    return;
  }
  GUI::instance()->geoContext->show(3);
  action_point_line_surface_volume(1, 1, (const char*)data);
}

static void geometry_elementary_add_scale_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_add_scale, 0);
    return;
  }
  GUI::instance()->geoContext->show(4);
  action_point_line_surface_volume(2, 1, (const char*)data);
}

static void geometry_elementary_add_symmetry_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_add_symmetry, 0);
    return;
  }
  GUI::instance()->geoContext->show(5);
  action_point_line_surface_volume(3, 1, (const char*)data);
}

static void geometry_elementary_translate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_translate, 0);
    return;
  }
  GUI::instance()->geoContext->show(2);
  action_point_line_surface_volume(0, 0, (const char*)data);
}

static void geometry_elementary_rotate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_rotate, 0);
    return;
  }
  GUI::instance()->geoContext->show(3);
  action_point_line_surface_volume(1, 0, (const char*)data);
}

static void geometry_elementary_scale_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_scale, 0);
    return;
  }
  GUI::instance()->geoContext->show(4);
  action_point_line_surface_volume(2, 0, (const char*)data);
}

static void geometry_elementary_symmetry_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_symmetry, 0);
    return;
  }
  GUI::instance()->geoContext->show(5);
  action_point_line_surface_volume(3, 0, (const char*)data);
}

static void geometry_elementary_extrude_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_geometry_elementary_extrude, 0);
}

static void geometry_elementary_extrude_translate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_extrude_translate, 0);
    return;
  }
  GUI::instance()->geoContext->show(2);
  action_point_line_surface_volume(4, 0, (const char*)data);
}

static void geometry_elementary_extrude_rotate_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_extrude_rotate, 0);
    return;
  }
  GUI::instance()->geoContext->show(3);
  action_point_line_surface_volume(5, 0, (const char*)data);
}

static void geometry_elementary_delete_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_delete, 0);
    return;
  }
  action_point_line_surface_volume(6, 0, (const char*)data);
}

static void geometry_elementary_split_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_elementary_split, 0);
    return;
  }
  split_selection();
}

static void geometry_elementary_coherence_cb(Fl_Widget *w, void *data)
{
  coherence(CTX.filename);
}

static void geometry_physical_add_cb(Fl_Widget *w, void *data)
{
  if(!data){
    GUI::instance()->menu->setContext(menu_geometry_physical_add, 0);
    return;
  }
  std::string str((const char*)data);
  if(str == "Point")
    GUI::instance()->callForSolverPlugin(0);
  else if(str == "Line")
    GUI::instance()->callForSolverPlugin(1);

  action_point_line_surface_volume(7, 0, str.c_str());
}
static void mesh_save_cb(Fl_Widget *w, void *data)
{
  char name[256];
  if(CTX.output_filename)
    strcpy(name, CTX.output_filename);
  else
    GetDefaultFileName(CTX.mesh.format, name);
  if(CTX.confirm_overwrite) {
    if(!StatFile(name))
      if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?",
                    "Cancel", "Replace", NULL, name))
        return;
  }
  CreateOutputFile(name, CTX.mesh.format);
}

static void mesh_define_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_mesh_define, 0);
}

void mesh_1d_cb(Fl_Widget *w, void *data)
{
  GModel::current()->mesh(1);
  Draw();
  Msg::StatusBar(2, false, " ");
}

void mesh_2d_cb(Fl_Widget *w, void *data)
{
  GModel::current()->mesh(2);
  Draw();
  Msg::StatusBar(2, false, " ");
}

void mesh_3d_cb(Fl_Widget *w, void *data)
{
  GModel::current()->mesh(3);
  Draw();
  Msg::StatusBar(2, false, " ");
}

static void mesh_delete_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_mesh_delete, 0);
}

static void mesh_delete_parts_cb(Fl_Widget *w, void *data)
{
  const char *str = (const char*)data;
  int what;

  if(!strcmp(str, "elements")){
    CTX.pick_elements = 1;
    what = ENT_ALL;
  }
  else if(!strcmp(str, "lines")){
    CTX.pick_elements = 0;
    what = ENT_LINE;
  }
  else if(!strcmp(str, "surfaces")){
    CTX.pick_elements = 0;
    what = ENT_SURFACE;
  }
  else if(!strcmp(str, "volumes")){
    CTX.pick_elements = 0;
    what = ENT_VOLUME;
  }
  else
    return;

  std::vector<MElement*> ele;
  std::vector<GEntity*> ent;

  while(1) {
    CTX.mesh.changed = ENT_ALL;
    Draw();

    if(ele.size() || ent.size())
      Msg::StatusBar(3, false, "Select %s\n"
                     "[Press 'e' to end selection, 'u' to undo last selection or "
                     "'q' to abort]", str);
    else
      Msg::StatusBar(3, false, "Select %s\n"
                     "[Press 'e' to end selection or 'q' to abort]", str);

    char ib = GUI::instance()->selectEntity(what);
    if(ib == 'l') {
      if(CTX.pick_elements){
        for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++){
          if(GUI::instance()->selectedElements[i]->getVisibility() != 2){
            GUI::instance()->selectedElements[i]->setVisibility(2); 
            ele.push_back(GUI::instance()->selectedElements[i]);
          }
        }
      }
      else{
        for(unsigned int i = 0; i < GUI::instance()->selectedEdges.size(); i++){
          if(GUI::instance()->selectedEdges[i]->getSelection() != 1){
            GUI::instance()->selectedEdges[i]->setSelection(1); 
            ent.push_back(GUI::instance()->selectedEdges[i]);
          }
        }
        for(unsigned int i = 0; i < GUI::instance()->selectedFaces.size(); i++){
          if(GUI::instance()->selectedFaces[i]->getSelection() != 1){
            GUI::instance()->selectedFaces[i]->setSelection(1); 
            ent.push_back(GUI::instance()->selectedFaces[i]);
          }
        }
        for(unsigned int i = 0; i < GUI::instance()->selectedRegions.size(); i++){
          if(GUI::instance()->selectedRegions[i]->getSelection() != 1){
            GUI::instance()->selectedRegions[i]->setSelection(1);
            ent.push_back(GUI::instance()->selectedRegions[i]);
          }
        }
      }
    }
    if(ib == 'r') {
      if(CTX.pick_elements){
        for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++)
          GUI::instance()->selectedElements[i]->setVisibility(1);
      }
      else{
        for(unsigned int i = 0; i < GUI::instance()->selectedEdges.size(); i++)
          GUI::instance()->selectedEdges[i]->setSelection(0);
        for(unsigned int i = 0; i < GUI::instance()->selectedFaces.size(); i++)
          GUI::instance()->selectedFaces[i]->setSelection(0);
        for(unsigned int i = 0; i < GUI::instance()->selectedRegions.size(); i++)
          GUI::instance()->selectedRegions[i]->setSelection(0);
      }
    }
    if(ib == 'u') {
      if(CTX.pick_elements){
        if(ele.size()){
          ele[ele.size() - 1]->setVisibility(1);
          ele.pop_back();
        }
      }
      else{
        if(ent.size()){
          ent[ent.size() - 1]->setSelection(0);
          ent.pop_back();
        }
      }
    }
    if(ib == 'e') {
      if(CTX.pick_elements){
        for(unsigned int i = 0; i < ele.size(); i++)
          if(ele[i]->getVisibility() == 2) ele[i]->setVisibility(0);
      }
      else{
        for(unsigned int i = 0; i < ent.size(); i++)
          if(ent[i]->getSelection() == 1) ent[i]->setVisibility(0);
      }
      GModel::current()->removeInvisibleElements();
      ele.clear();
      ent.clear();
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      break;
    }
  }

  CTX.mesh.changed = ENT_ALL;
  CTX.pick_elements = 0;
  Draw();  
  Msg::StatusBar(3, false, "");
}

static void mesh_inspect_cb(Fl_Widget *w, void *data)
{
  CTX.pick_elements = 1;
  CTX.mesh.changed = ENT_ALL;
  Draw();

  while(1) {
    Msg::StatusBar(3, false, "Select element\n[Press 'q' to abort]");
    char ib = GUI::instance()->selectEntity(ENT_ALL);
    if(ib == 'l') {
      if(GUI::instance()->selectedElements.size()){
        MElement *ele = GUI::instance()->selectedElements[0];
        GModel::current()->setSelection(0);
        ele->setVisibility(2);
        Msg::Direct("Element %d:", ele->getNum());
	int type = ele->getTypeForMSH();
	const char *name;
	MElement::getInfoMSH(type, &name);
        Msg::Direct("  Type: %d ('%s')", type, name); 
        Msg::Direct("  Dimension: %d", ele->getDim());
        Msg::Direct("  Order: %d", ele->getPolynomialOrder()); 
        Msg::Direct("  Partition: %d", ele->getPartition()); 
        char tmp1[32], tmp2[512];
        sprintf(tmp2, "  Vertices:");
        for(int i = 0; i < ele->getNumVertices(); i++){
          sprintf(tmp1, " %d", ele->getVertex(i)->getNum());
          strcat(tmp2, tmp1);
        }
        Msg::Direct(tmp2);
        SPoint3 pt = ele->barycenter();
        Msg::Direct("  Barycenter: (%g,%g,%g)", pt[0], pt[1], pt[2]);
        Msg::Direct("  Rho: %g", ele->rhoShapeMeasure());
        Msg::Direct("  Gamma: %g", ele->gammaShapeMeasure());
        Msg::Direct("  Eta: %g", ele->etaShapeMeasure());
        Msg::Direct("  Disto: %g", ele->distoShapeMeasure());
        CTX.mesh.changed = ENT_ALL;
        Draw();
        GUI::instance()->messages->show();
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      break;
    }
  }

  CTX.pick_elements = 0;
  CTX.mesh.changed = ENT_ALL;
  Draw();
  Msg::StatusBar(3, false, "");
}

static void mesh_degree_cb(Fl_Widget *w, void *data)
{
  if((long)data == 2)
    SetOrderN(GModel::current(), 2, CTX.mesh.second_order_linear, 
              CTX.mesh.second_order_incomplete);
  else
    SetOrder1(GModel::current());
  CTX.mesh.changed |= (ENT_LINE | ENT_SURFACE | ENT_VOLUME);
  Draw();
  Msg::StatusBar(2, false, " ");
}

static void mesh_optimize_cb(Fl_Widget *w, void *data)
{
  if(CTX.threads_lock) {
    Msg::Info("I'm busy! Ask me that later...");
    return;
  }
  CTX.threads_lock = 1;
  OptimizeMesh(GModel::current());
  CTX.threads_lock = 0;
  CTX.mesh.changed |= (ENT_LINE | ENT_SURFACE | ENT_VOLUME);
  Draw();
  Msg::StatusBar(2, false, " ");
}

static void mesh_refine_cb(Fl_Widget *w, void *data)
{
  RefineMesh(GModel::current(), CTX.mesh.second_order_linear);
  CTX.mesh.changed |= (ENT_LINE | ENT_SURFACE | ENT_VOLUME);
  Draw();
  Msg::StatusBar(2, false, " ");
}

static void mesh_optimize_netgen_cb(Fl_Widget *w, void *data)
{
  if(CTX.threads_lock) {
    Msg::Info("I'm busy! Ask me that later...");
    return;
  }
  CTX.threads_lock = 1;
  OptimizeMeshNetgen(GModel::current());
  CTX.threads_lock = 0;
  CTX.mesh.changed |= (ENT_LINE | ENT_SURFACE | ENT_VOLUME);
  Draw();
  Msg::StatusBar(2, false, " ");
}

static void mesh_partition_cb(Fl_Widget *w, void *data)
{
  partition_dialog();
}

static void mesh_define_length_cb(Fl_Widget *w, void *data)
{
  action_point_line_surface_volume(8, 0, "Point");
}
static void mesh_define_recombine_cb(Fl_Widget *w, void *data)
{
  action_point_line_surface_volume(9, 0, "Surface");
}

static void mesh_define_transfinite_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->menu->setContext(menu_mesh_define_transfinite, 0);
}

static void add_transfinite(int dim)
{
  opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
  switch (dim) {
  case 1: opt_geometry_lines(0, GMSH_SET | GMSH_GUI, 1); break;
  case 2: opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1); break;
  case 3: opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1); break;
  }
  Draw();

  std::vector<int> p;
  char ib;
  while(1) {
    switch (dim) {
    case 1:
      if(p.empty())
        Msg::StatusBar(3, false, "Select lines\n"
                       "[Press 'e' to end selection or 'q' to abort]");
      else
        Msg::StatusBar(3, false, "Select lines\n"
                       "[Press 'e' to end selection, 'u' to undo last selection "
                       "or 'q' to abort]");
      ib = GUI::instance()->selectEntity(ENT_LINE);
      break;
    case 2:
      Msg::StatusBar(3, false, "Select surface\n[Press 'q' to abort]");
      ib = GUI::instance()->selectEntity(ENT_SURFACE);
      break;
    case 3:
      Msg::StatusBar(3, false, "Select volume\n[Press 'q' to abort]");
      ib = GUI::instance()->selectEntity(ENT_VOLUME);
      break;
    default:
      ib = 'l';
      break;
    }

    if(ib == 'e') {
      if(dim == 1) {
        if(p.size())
          add_trsfline(p, CTX.filename,
                       GUI::instance()->meshContext->choice[0]->text(),
                       GUI::instance()->meshContext->input[2]->value(),
                       GUI::instance()->meshContext->input[1]->value());
      }
      GModel::current()->setSelection(0);
      Draw();
      p.clear();
    }
    if(ib == 'u') {
      if(dim == 1) {
        if(p.size()){
          GEdge *ge = GModel::current()->getEdgeByTag(p.back());
          if(ge) ge->setSelection(0);
          Draw();
          p.pop_back();
        }
      }
    }
    if(ib == 'q') {
      GModel::current()->setSelection(0);
      Draw();
      break;
    }
    if(ib == 'r') {
      Msg::Warning("Entity de-selection not supported yet during "
                   "transfinite definition");
    }
    if(ib == 'l') {
      switch (dim) {
      case 1:
        for(unsigned int i = 0; i < GUI::instance()->selectedEdges.size(); i++){
          GUI::instance()->selectedEdges[i]->setSelection(1);
          p.push_back(GUI::instance()->selectedEdges[i]->tag());
        }
        Draw();
        break;
      case 2:
      case 3:
        if(dim == 2){
          GUI::instance()->selectedFaces[0]->setSelection(1);
          Draw();
          p.push_back(GUI::instance()->selectedFaces[0]->tag());
        }
        else{
          GUI::instance()->selectedRegions[0]->setSelection(1);
          Draw();
          p.push_back(GUI::instance()->selectedRegions[0]->tag());
        }
        while(1) {
          if(p.size() == 1)
            Msg::StatusBar(3, false, "Select (ordered) boundary points\n"
                           "[Press 'e' to end selection or 'q' to abort]");
          else
            Msg::StatusBar(3, false, "Select (ordered) boundary points\n"
                           "[Press 'e' to end selection, 'u' to undo last selection "
                           "or 'q' to abort]");
          ib = GUI::instance()->selectEntity(ENT_POINT);
          if(ib == 'l') {
            GUI::instance()->selectedVertices[0]->setSelection(1);
            Draw();
            p.push_back(GUI::instance()->selectedVertices[0]->tag());
          }
          if(ib == 'u') {
            if(p.size() > 1){
              GVertex *gv = GModel::current()->getVertexByTag(p.back());
              if(gv) gv->setSelection(0);
              Draw();
              p.pop_back();
            }
          }
          if(ib == 'r') {
            Msg::Warning("Entity de-selection not supported yet during "
                         "transfinite definition");
          }
          if(ib == 'e') {
            switch (dim) {
            case 2:
              if(p.size() == 0 + 1 || p.size() == 3 + 1 || p.size() == 4 + 1)
                add_trsfsurf(p, CTX.filename,
                             GUI::instance()->meshContext->choice[1]->text());
              else
                Msg::Error("Wrong number of points for transfinite surface");
              break;
            case 3:
              if(p.size() == 6 + 1 || p.size() == 8 + 1)
                add_trsfvol(p, CTX.filename);
              else
                Msg::Error("Wrong number of points for transfinite volume");
              break;
            }
            GModel::current()->setSelection(0);
            Draw();
            p.clear();
            break;
          }
          if(ib == 'q') {
            GModel::current()->setSelection(0);
            Draw();
            goto stopall;
          }
        }
        break;
      }
    }
  }

 stopall:
  Msg::StatusBar(3, false, "");
}

static void mesh_define_transfinite_line_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->meshContext->show(1);
  add_transfinite(1);
}

static void mesh_define_transfinite_surface_cb(Fl_Widget *w, void *data)
{
  GUI::instance()->meshContext->show(2);
  add_transfinite(2);
}

static void mesh_define_transfinite_volume_cb(Fl_Widget *w, void *data)
{
  add_transfinite(3);
}

static void view_toggle_cb(Fl_Widget *w, void *data)
{
  int num = (int)(long)data;
  opt_view_visible(num, GMSH_SET,
                   GUI::instance()->menu->toggle[num]->value());
  Draw();
}

static void view_reload(int index)
{
  if(index >= 0 && index < (int)PView::list.size()){
    PView *p = PView::list[index];

    if(StatFile(p->getData()->getFileName().c_str())){
      Msg::Error("File '%s' does not exist", p->getData()->getFileName().c_str());
      return;
    }

    int n = PView::list.size();

    // FIXME: use fileIndex
    MergeFile(p->getData()->getFileName().c_str());

    if((int)PView::list.size() > n){ // we loaded a new view
      // delete old data and replace with new
      delete p->getData();
      p->setData(PView::list.back()->getData());
      PView::list.back()->setData(0);
      // delete new view
      delete PView::list.back();
      // in case the reloaded view has a different number of time steps
      if(p->getOptions()->TimeStep > p->getData()->getNumTimeSteps() - 1)
        p->getOptions()->TimeStep = 0;
      p->setChanged(true);
      GUI::instance()->updateViews();
    }
  }
}

static void view_reload_cb(Fl_Widget *w, void *data)
{
  view_reload((int)(long)data);
  Draw();
}

static void view_reload_all_cb(Fl_Widget *w, void *data)
{
  for(unsigned int i = 0; i < PView::list.size(); i++)
    view_reload(i);
  Draw();
}

static void view_reload_visible_cb(Fl_Widget *w, void *data)
{
  for(unsigned int i = 0; i < PView::list.size(); i++)
    if(opt_view_visible(i, GMSH_GET, 0))
      view_reload(i);
  Draw();
}

static void view_remove_other_cb(Fl_Widget *w, void *data)
{
  if(PView::list.empty()) return;
  for(int i = PView::list.size() - 1; i >= 0; i--)
    if(i != (long)data) delete PView::list[i];
  GUI::instance()->updateViews();
  Draw();
}

static void view_remove_all_cb(Fl_Widget *w, void *data)
{
  if(PView::list.empty()) return;
  while(PView::list.size()) delete PView::list[0];
  GUI::instance()->updateViews();
  Draw();
}

static void view_remove_visible_cb(Fl_Widget *w, void *data)
{
  if(PView::list.empty()) return;
  for(int i = PView::list.size() - 1; i >= 0; i--)
    if(opt_view_visible(i, GMSH_GET, 0)) delete PView::list[i];
  GUI::instance()->updateViews();
  Draw();
}

static void view_remove_invisible_cb(Fl_Widget *w, void *data)
{
  if(PView::list.empty()) return;
  for(int i = PView::list.size() - 1; i >= 0; i--)
    if(!opt_view_visible(i, GMSH_GET, 0)) delete PView::list[i];
  GUI::instance()->updateViews();
  Draw();
}

static void view_remove_empty_cb(Fl_Widget *w, void *data)
{
  if(PView::list.empty()) return;
  for(int i = PView::list.size() - 1; i >= 0; i--)
    if(PView::list[i]->getData()->empty()) delete PView::list[i];
  GUI::instance()->updateViews();
  Draw();
}

static void view_remove_cb(Fl_Widget *w, void *data)
{
  delete PView::list[(int)(long)data];
  GUI::instance()->updateViews();
  Draw();
}

static void view_save_as(int index, const char *title, int format)
{
  PView *view = PView::list[index];
  
 test:
  if(file_chooser(0, 1, title, "*", view->getData()->getFileName().c_str())){
    std::string name = file_chooser_get_name(1);
    if(CTX.confirm_overwrite) {
      if(!StatFile(name.c_str()))
        if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?",
                      "Cancel", "Replace", NULL, name.c_str()))
          goto test;
    }
    view->write(name.c_str(), format);
  }
}

static void view_save_ascii_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As ASCII View", 0);
}

static void view_save_binary_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As Binary View", 1);
}

static void view_save_parsed_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As Parsed View", 2);
}

static void view_save_stl_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As STL Triangulation", 3);
}

static void view_save_txt_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As Raw Text", 4);
}

static void view_save_msh_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As Gmsh Mesh", 5);
}

static void view_save_med_cb(Fl_Widget *w, void *data)
{
  view_save_as((int)(long)data, "Save As MED file", 6);
}

static void view_alias_cb(Fl_Widget *w, void *data)
{
  new PView(PView::list[(int)(long)data], false);
  GUI::instance()->updateViews();
  Draw();
}

static void view_alias_with_options_cb(Fl_Widget *w, void *data)
{
  new PView(PView::list[(int)(long)data], true);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_space_all_cb(Fl_Widget *w, void *data)
{
  PView::combine(false, 1, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_space_visible_cb(Fl_Widget *w, void *data)
{
  PView::combine(false, 0, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_space_by_name_cb(Fl_Widget *w, void *data)
{
  PView::combine(false, 2, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_time_all_cb(Fl_Widget *w, void *data)
{
  PView::combine(true, 1, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_time_visible_cb(Fl_Widget *w, void *data)
{
  PView::combine(true, 0, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_combine_time_by_name_cb(Fl_Widget *w, void *data)
{
  PView::combine(true, 2, CTX.post.combine_remove_orig);
  GUI::instance()->updateViews();
  Draw();
}

static void view_all_visible_cb(Fl_Widget *w, void *data)
{
  for(unsigned int i = 0; i < PView::list.size(); i++)
    opt_view_visible(i, GMSH_SET | GMSH_GUI, 
                     (long)data < 0 ? !opt_view_visible(i, GMSH_GET, 0) :
                     (long)data > 0 ? 1 : 0);
  Draw();
}

static void view_applybgmesh_cb(Fl_Widget *w, void *data)
{
  int index =  (int)(long)data;
  if(index >= 0 && index < (int)PView::list.size()){
    GModel::current()->getFields()->set_background_mesh(index);
  }
}

// The static menus (we cannot use the 'g', 'm' 's' and 'p' mnemonics
// since they are already defined as global shortcuts)
static Fl_Menu_Item bar_table[] = {
  {"&File", 0, 0, 0, FL_SUBMENU},
    {"&New...",     FL_CTRL+'n', (Fl_Callback *)file_new_cb, 0},
    {"&Open...",    FL_CTRL+'o', (Fl_Callback *)file_open_cb, 0},
    {"M&erge...",   FL_CTRL+FL_SHIFT+'o', (Fl_Callback *)file_merge_cb, 0, FL_MENU_DIVIDER},
    {"&Rename...",  FL_CTRL+'r', (Fl_Callback *)file_rename_cb, 0},
    {"Save &As...", FL_CTRL+'s', (Fl_Callback *)file_save_as_cb, 0},
    {"Sa&ve Mesh",  FL_CTRL+FL_SHIFT+'s', (Fl_Callback *)mesh_save_cb, 0, FL_MENU_DIVIDER},
    {"&Quit",       FL_CTRL+'q', (Fl_Callback *)file_quit_cb, 0},
    {0},
  {"&Tools", 0, 0, 0, FL_SUBMENU},
    {"&Options...",      FL_CTRL+FL_SHIFT+'n', (Fl_Callback *)options_cb, 0},
    {"Pl&ugins...",      FL_CTRL+FL_SHIFT+'u', (Fl_Callback *)plugin_cb, (void*)(-1)},
    {"&Visibility",      FL_CTRL+FL_SHIFT+'v', (Fl_Callback *)visibility_cb, 0},
    {"&Clipping",        FL_CTRL+FL_SHIFT+'c', (Fl_Callback *)clip_cb, 0},
    {"&Manipulator",     FL_CTRL+FL_SHIFT+'m', (Fl_Callback *)manip_cb, 0, FL_MENU_DIVIDER},
    {"S&tatistics",      FL_CTRL+'i', (Fl_Callback *)statistics_cb, 0},
    {"M&essage Console", FL_CTRL+'l', (Fl_Callback *)message_cb, 0},
    {0},
  {"&Help", 0, 0, 0, FL_SUBMENU},
    {"On&line Documentation", 0, (Fl_Callback *)help_online_cb, 0, FL_MENU_DIVIDER},
    {"M&ouse Actions",        0, (Fl_Callback *)help_mouse_cb, 0},
    {"&Keyboard Shortcuts",   0, (Fl_Callback *)help_short_cb, 0},
    {"C&ommand Line Options", 0, (Fl_Callback *)help_command_line_cb, 0},
    {"&Current Options",      0, (Fl_Callback *)status_xyz1p_cb, (void*)"?", FL_MENU_DIVIDER},
    {"&About Gmsh...",        0, (Fl_Callback *)help_about_cb, 0},
    {0},
  {0}
};

#if defined(__APPLE__)

#if (FL_MAJOR_VERSION == 1) && (FL_MINOR_VERSION == 1) && (FL_PATCH_VERSION <= 6)
#undef FL_META
#define FL_META FL_CTRL
#endif

// Alternative items for the MacOS system menu bar (removed '&'
// shortcuts: they would cause spurious menu items to appear on the
// menu window; removed File->Quit)
static Fl_Menu_Item sysbar_table[] = {
  {"File", 0, 0, 0, FL_SUBMENU},
    {"New...",     FL_META+'n', (Fl_Callback *)file_new_cb, 0},
    {"Open...",    FL_META+'o', (Fl_Callback *)file_open_cb, 0},
    {"Merge...",   FL_META+FL_SHIFT+'o', (Fl_Callback *)file_merge_cb, 0, FL_MENU_DIVIDER},
    {"Rename...",  FL_META+'r', (Fl_Callback *)file_rename_cb, 0},
    {"Save As...", FL_META+'s', (Fl_Callback *)file_save_as_cb, 0},
    {"Save Mesh",  FL_META+FL_SHIFT+'s', (Fl_Callback *)mesh_save_cb, 0},
    {0},
  {"Tools", 0, 0, 0, FL_SUBMENU},
    {"Options...",      FL_META+FL_SHIFT+'n', (Fl_Callback *)options_cb, 0},
    {"Plugins...",      FL_META+FL_SHIFT+'u', (Fl_Callback *)plugin_cb, (void*)(-1)},
    {"Visibility",      FL_META+FL_SHIFT+'v', (Fl_Callback *)visibility_cb, 0},
    {"Clipping",        FL_META+FL_SHIFT+'c', (Fl_Callback *)clip_cb, 0},
    {"Manipulator",     FL_META+FL_SHIFT+'m', (Fl_Callback *)manip_cb, 0, FL_MENU_DIVIDER},
    {"Statistics",      FL_META+'i', (Fl_Callback *)statistics_cb, 0},
    {"Message Console", FL_META+'l', (Fl_Callback *)message_cb, 0},
    {0},
  {"Window", 0, 0, 0, FL_SUBMENU},
    {"Minimize",           FL_META+'m', (Fl_Callback *)window_cb, (void*)"minimize"},
    {"Zoom",               0, (Fl_Callback *)window_cb, (void*)"zoom", FL_MENU_DIVIDER},
    {"Bring All to Front", 0, (Fl_Callback *)window_cb, (void*)"front"},
    {0},
  {"Help", 0, 0, 0, FL_SUBMENU},
    {"Online Documentation", 0, (Fl_Callback *)help_online_cb, 0, FL_MENU_DIVIDER},
    {"Mouse Actions",        0, (Fl_Callback *)help_mouse_cb, 0},
    {"Keyboard Shortcuts",   0, (Fl_Callback *)help_short_cb, 0},
    {"Command Line Options", 0, (Fl_Callback *)help_command_line_cb, 0},
    {"Current Options",      0, (Fl_Callback *)status_xyz1p_cb, (void*)"?", FL_MENU_DIVIDER},
    {"About Gmsh...",        0, (Fl_Callback *)help_about_cb, 0},
    {0},
  {0}
};
#endif

static Fl_Menu_Item module_table[] = {
  {"Geometry",        'g', (Fl_Callback *)mod_geometry_cb, 0},
  {"Mesh",            'm', (Fl_Callback *)mod_mesh_cb, 0},
  {"Solver",          's', (Fl_Callback *)mod_solver_cb, 0},
  {"Post-processing", 'p', (Fl_Callback *)mod_post_cb, 0},
  {0}
};

// Dynamic menus contexts
contextItem menu_geometry[] = {
  {"0Geometry", NULL} ,
  {"Elementary entities", (Fl_Callback *)geometry_elementary_cb} ,
  {"Physical groups",     (Fl_Callback *)geometry_physical_cb} ,
  {"Edit",                (Fl_Callback *)geometry_edit_cb} , 
  {"Reload",              (Fl_Callback *)geometry_reload_cb} , 
  {0}
};  
  contextItem menu_geometry_elementary[] = {
    {"0Geometry>Elementary", NULL} ,
    {"Add",       (Fl_Callback *)geometry_elementary_add_cb} ,
    {"Delete",    (Fl_Callback *)geometry_elementary_delete_cb, (void*)0} ,
    {"Translate", (Fl_Callback *)geometry_elementary_translate_cb, (void*)0} ,
    {"Rotate",    (Fl_Callback *)geometry_elementary_rotate_cb, (void*)0} ,
    {"Split",     (Fl_Callback *)geometry_elementary_split_cb, (void*)0} ,
    {"Scale",     (Fl_Callback *)geometry_elementary_scale_cb, (void*)0} ,
    {"Symmetry",  (Fl_Callback *)geometry_elementary_symmetry_cb, (void*)0} ,
    {"Extrude",   (Fl_Callback *)geometry_elementary_extrude_cb, (void*)0} ,
    {"Coherence", (Fl_Callback *)geometry_elementary_coherence_cb} ,
    {0} 
  };  
    contextItem menu_geometry_elementary_add[] = {
      {"0Geometry>Elementary>Add", NULL} ,
      {"New",       (Fl_Callback *)geometry_elementary_add_new_cb, (void*)0} ,
      {"Translate", (Fl_Callback *)geometry_elementary_add_translate_cb, (void*)0} ,
      {"Rotate",    (Fl_Callback *)geometry_elementary_add_rotate_cb, (void*)0} ,
      {"Scale",     (Fl_Callback *)geometry_elementary_add_scale_cb, (void*)0} ,
      {"Symmetry",  (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)0} ,
      {0} 
    };  
      contextItem menu_geometry_elementary_add_new[] = {
        {"0Geometry>Elementary>Add>New", NULL} ,
        {"Parameter",     (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Parameter"} ,
        {"Point",         (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Point"} ,
        {"Straight line", (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Line"} ,
        {"Spline",        (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Spline"} ,
        {"B-Spline",      (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"BSpline"} ,
        {"Circle arc",    (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Circle"} ,
        {"Ellipse arc",   (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Ellipse"} ,
        {"Plane surface", (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Plane Surface"} ,
        {"Ruled surface", (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Ruled Surface"} ,
        {"Volume",        (Fl_Callback *)geometry_elementary_add_new_cb, (void*)"Volume"} ,
        {0} 
      };  
      contextItem menu_geometry_elementary_add_translate[] = {
        {"0Geometry>Elementary>Add>Translate", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_add_translate_cb, (void*)"Point"} ,  
        {"Line",    (Fl_Callback *)geometry_elementary_add_translate_cb, (void*)"Line"} ,	  
        {"Surface", (Fl_Callback *)geometry_elementary_add_translate_cb, (void*)"Surface"} ,
        {"Volume",  (Fl_Callback *)geometry_elementary_add_translate_cb, (void*)"Volume"} , 
        {0} 
      };  
      contextItem menu_geometry_elementary_add_rotate[] = {
        {"0Geometry>Elementary>Add>Rotate", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_add_rotate_cb, (void*)"Point"} ,  
        {"Line",    (Fl_Callback *)geometry_elementary_add_rotate_cb, (void*)"Line"} ,	  
        {"Surface", (Fl_Callback *)geometry_elementary_add_rotate_cb, (void*)"Surface"} ,
        {"Volume",  (Fl_Callback *)geometry_elementary_add_rotate_cb, (void*)"Volume"} , 
        {0} 
      };  
      contextItem menu_geometry_elementary_add_scale[] = {
        {"0Geometry>Elementary>Add>Scale", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_add_scale_cb, (void*)"Point"} ,  
        {"Line",    (Fl_Callback *)geometry_elementary_add_scale_cb, (void*)"Line"} ,	  
        {"Surface", (Fl_Callback *)geometry_elementary_add_scale_cb, (void*)"Surface"} ,
        {"Volume",  (Fl_Callback *)geometry_elementary_add_scale_cb, (void*)"Volume"} , 
        {0} 
      };  
      contextItem menu_geometry_elementary_add_symmetry[] = {
        {"0Geometry>Elementary>Add>Symmetry", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Point"} ,  
        {"Line",    (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Line"} ,	  
        {"Surface", (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Surface"} ,
        {"Volume",  (Fl_Callback *)geometry_elementary_add_symmetry_cb, (void*)"Volume"} , 
        {0} 
      };  
    contextItem menu_geometry_elementary_delete[] = {
      {"0Geometry>Elementary>Delete", NULL} ,
      {"Point",   (Fl_Callback *)geometry_elementary_delete_cb, (void*)"Point"} ,
      {"Line",    (Fl_Callback *)geometry_elementary_delete_cb, (void*)"Line"} ,
      {"Surface", (Fl_Callback *)geometry_elementary_delete_cb, (void*)"Surface"} ,
      {"Volume",  (Fl_Callback *)geometry_elementary_delete_cb, (void*)"Volume"} ,
      {0} 
    };  
    contextItem menu_geometry_elementary_split[] = {
      {"0Geometry>Elementary>Split",NULL},
        {"Line",(Fl_Callback *)geometry_elementary_split_cb,(void*)"Line"},
        {0}
    };
    contextItem menu_geometry_elementary_translate[] = {
      {"0Geometry>Elementary>Translate", NULL} ,
      {"Point",   (Fl_Callback *)geometry_elementary_translate_cb, (void*)"Point"} ,  
      {"Line",    (Fl_Callback *)geometry_elementary_translate_cb, (void*)"Line"} ,	  
      {"Surface", (Fl_Callback *)geometry_elementary_translate_cb, (void*)"Surface"} ,
      {"Volume",  (Fl_Callback *)geometry_elementary_translate_cb, (void*)"Volume"} , 
      {0} 
    };  
    contextItem menu_geometry_elementary_rotate[] = {
      {"0Geometry>Elementary>Rotate", NULL} ,
      {"Point",   (Fl_Callback *)geometry_elementary_rotate_cb, (void*)"Point"} ,  
      {"Line",    (Fl_Callback *)geometry_elementary_rotate_cb, (void*)"Line"} ,	  
      {"Surface", (Fl_Callback *)geometry_elementary_rotate_cb, (void*)"Surface"} ,
      {"Volume",  (Fl_Callback *)geometry_elementary_rotate_cb, (void*)"Volume"} , 
      {0} 
    };  
    contextItem menu_geometry_elementary_scale[] = {
      {"0Geometry>Elementary>Scale", NULL} ,
      {"Point",   (Fl_Callback *)geometry_elementary_scale_cb, (void*)"Point"} ,  
      {"Line",    (Fl_Callback *)geometry_elementary_scale_cb, (void*)"Line"} ,	  
      {"Surface", (Fl_Callback *)geometry_elementary_scale_cb, (void*)"Surface"} ,
      {"Volume",  (Fl_Callback *)geometry_elementary_scale_cb, (void*)"Volume"} , 
      {0} 
    };  
    contextItem menu_geometry_elementary_symmetry[] = {
      {"0Geometry>Elementary>Symmetry", NULL} ,
      {"Point",   (Fl_Callback *)geometry_elementary_symmetry_cb, (void*)"Point"} ,  
      {"Line",    (Fl_Callback *)geometry_elementary_symmetry_cb, (void*)"Line"} ,	  
      {"Surface", (Fl_Callback *)geometry_elementary_symmetry_cb, (void*)"Surface"} ,
      {"Volume",  (Fl_Callback *)geometry_elementary_symmetry_cb, (void*)"Volume"} , 
      {0} 
    };  
    contextItem menu_geometry_elementary_extrude[] = {
      {"0Geometry>Elementary>Extrude", NULL} ,
      {"Translate", (Fl_Callback *)geometry_elementary_extrude_translate_cb, (void*)0} ,
      {"Rotate",    (Fl_Callback *)geometry_elementary_extrude_rotate_cb, (void*)0} ,
      {0} 
    };  
      contextItem menu_geometry_elementary_extrude_translate[] = {
        {"0Geometry>Elementary>Extrude>Translate", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_extrude_translate_cb, (void*)"Point"} ,
        {"Line",    (Fl_Callback *)geometry_elementary_extrude_translate_cb, (void*)"Line"} ,
        {"Surface", (Fl_Callback *)geometry_elementary_extrude_translate_cb, (void*)"Surface"} ,
        {0} 
      };  
      contextItem menu_geometry_elementary_extrude_rotate[] = {
        {"0Geometry>Elementary>Extrude>Rotate", NULL} ,
        {"Point",   (Fl_Callback *)geometry_elementary_extrude_rotate_cb, (void*)"Point"} ,
        {"Line",    (Fl_Callback *)geometry_elementary_extrude_rotate_cb, (void*)"Line"} ,
        {"Surface", (Fl_Callback *)geometry_elementary_extrude_rotate_cb, (void*)"Surface"} ,
        {0} 
      };  
  contextItem menu_geometry_physical[] = {
    {"0Geometry>Physical", NULL} ,
    {"Add",    (Fl_Callback *)geometry_physical_add_cb, (void*)0} ,
    {0} 
  };  
    contextItem menu_geometry_physical_add[] = {
      {"0Geometry>Physical>Add", NULL} ,
      {"Point",   (Fl_Callback *)geometry_physical_add_cb, (void*)"Point" } ,
      {"Line",    (Fl_Callback *)geometry_physical_add_cb, (void*)"Line" } ,
      {"Surface", (Fl_Callback *)geometry_physical_add_cb, (void*)"Surface" } ,
      {"Volume",  (Fl_Callback *)geometry_physical_add_cb, (void*)"Volume" } ,
      {0} 
    };  

contextItem menu_mesh[] = {
  {"1Mesh", NULL} ,
  {"Define",       (Fl_Callback *)mesh_define_cb} ,
  {"Inspect",      (Fl_Callback *)mesh_inspect_cb} , 
  {"Delete",       (Fl_Callback *)mesh_delete_cb} , 
  {"1D",           (Fl_Callback *)mesh_1d_cb} ,
  {"2D",           (Fl_Callback *)mesh_2d_cb} , 
  {"3D",           (Fl_Callback *)mesh_3d_cb} , 
  {"First order",  (Fl_Callback *)mesh_degree_cb, (void*)1 } , 
  {"Second order", (Fl_Callback *)mesh_degree_cb, (void*)2 } , 
  {"Refine",       (Fl_Callback *)mesh_refine_cb} ,
  {"Optimize",     (Fl_Callback *)mesh_optimize_cb} , 
#if defined(HAVE_NETGEN)
  {"Optimize (Netgen)", (Fl_Callback *)mesh_optimize_netgen_cb} , 
#endif
#if defined(HAVE_METIS) || defined(HAVE_CHACO)
  {"Partition", (Fl_Callback *)mesh_partition_cb} ,
#endif
#if defined(HAVE_FOURIER_MODEL)
  //{"Reparameterize",   (Fl_Callback *)mesh_parameterize_cb} , 
#endif
  {"Reclassify",   (Fl_Callback *)mesh_classify_cb} , 
  {"Save",         (Fl_Callback *)mesh_save_cb} ,
  {0} 
};  
  contextItem menu_mesh_define[] = {
    {"1Mesh>Define", NULL} ,
    {"Fields",      (Fl_Callback *)field_cb},
    {"Characteristic length", (Fl_Callback *)mesh_define_length_cb  } ,
    {"Recombine",   (Fl_Callback *)mesh_define_recombine_cb  } ,
    {"Transfinite", (Fl_Callback *)mesh_define_transfinite_cb  } , 
    {0} 
  };  
    contextItem menu_mesh_define_transfinite[] = {
      {"1Mesh>Define>Transfinite", NULL} ,
      {"Line",    (Fl_Callback *)mesh_define_transfinite_line_cb} ,
      {"Surface", (Fl_Callback *)mesh_define_transfinite_surface_cb} ,
      {"Volume",  (Fl_Callback *)mesh_define_transfinite_volume_cb} , 
      {0} 
    };  
  contextItem menu_mesh_delete[] = {
    {"1Mesh>Edit>Delete", NULL} ,
    {"Elements", (Fl_Callback *)mesh_delete_parts_cb, (void*)"elements"} ,
    {"Lines",    (Fl_Callback *)mesh_delete_parts_cb, (void*)"lines"} ,
    {"Surfaces", (Fl_Callback *)mesh_delete_parts_cb, (void*)"surfaces"} ,
    {"Volumes",  (Fl_Callback *)mesh_delete_parts_cb, (void*)"volumes"} ,
    {0} 
  };  

contextItem menu_solver[] = {
  {"2Solver", NULL} ,
  {"Solver 0", (Fl_Callback *)solver_cb , (void*)0} ,
  {"Solver 1", (Fl_Callback *)solver_cb , (void*)1} ,
  {"Solver 2", (Fl_Callback *)solver_cb , (void*)2} ,
  {"Solver 3", (Fl_Callback *)solver_cb , (void*)3} ,
  {"Solver 4", (Fl_Callback *)solver_cb , (void*)4} ,
  {0} 
};

contextItem menu_post[] = {
  {"3Post-processing", NULL} ,
  {0} 
};

menuWindow::menuWindow(int fontsize) : _fontsize(fontsize)
{
  int width = 14 * fontsize;

  // this is the initial height: no dynamic button is shown
#if defined(__APPLE__)
  if(CTX.system_menu_bar){
    _MH = BH + 6; // the menu bar is not in the application
  }
  else{
#endif
    _MH = BH + BH + 6;
#if defined(__APPLE__)
  }
#endif

  win = new mainWindow(width, _MH + NB_BUTT_SCROLL * BH, CTX.non_modal_windows, "Gmsh");
  win->box(GMSH_WINDOW_BOX);
  win->callback(file_quit_cb);

  int y;
#if defined(__APPLE__)
  if(CTX.system_menu_bar){
    // the system menubar is kind of a hack in fltk < 1.1.7: it still
    // creates a real (invisible) menubar. To avoid spurious mouse
    // click events we make it a 1x1 pixel rectangle, 1 pixel off the
    // edge (so it falls behind the navigation buttons)
    sysbar = new Fl_Sys_Menu_Bar(1, 1, 1, 1);
    sysbar->menu(sysbar_table);
    sysbar->global();
    Fl_Box *o = new Fl_Box(0, 0, width, BH + 6);
    o->box(FL_UP_BOX);
    y = 3;
  }
  else{
#endif
    bar = new Fl_Menu_Bar(0, 0, width, BH);
    bar->menu(bar_table);
    bar->box(FL_UP_BOX);
    bar->global();
    Fl_Box *o = new Fl_Box(0, BH, width, BH + 6);
    o->box(FL_UP_BOX);
    y = BH + 3;
#if defined(__APPLE__)
  }
#endif

  navig[0] = new Fl_Button(1, y, 18, BH / 2, "@#-1<");
  navig[0]->labeltype(FL_SYMBOL_LABEL);
  navig[0]->box(FL_FLAT_BOX);
  navig[0]->selection_color(FL_WHITE);
  navig[0]->callback(mod_back_cb);
  navig[0]->tooltip("Go back one in the menu history (<)");

  navig[1] = new Fl_Button(1, y + BH / 2, 18, BH / 2, "@#-1>");
  navig[1]->labeltype(FL_SYMBOL_LABEL);
  navig[1]->box(FL_FLAT_BOX);
  navig[1]->selection_color(FL_WHITE);
  navig[1]->callback(mod_forward_cb);
  navig[1]->tooltip("Go forward one in the menu history (>)");

  module = new Fl_Choice(19, y, width - 24, BH);
  module->menu(module_table);
  module->box(FL_THIN_DOWN_BOX);
  // force the executation of the callback even if we didn't change
  // the selection (we want to go back to the top-level menu every
  // time we select one of the categories, even if the category is not
  // changed):
  module->when(FL_WHEN_RELEASE_ALWAYS);

  // create an empty scroll area that will get populated dynamically
  // in set_context()
  scroll = new Fl_Scroll(0, _MH, width, NB_BUTT_SCROLL * BH); 
  scroll->type(Fl_Scroll::VERTICAL);
  scroll->end();

  win->size(width, _MH);
  win->position(CTX.position[0], CTX.position[1]);
  
  win->end();
}

void menuWindow::setContext(contextItem *menu_asked, int flag)
{
  static int nb_back = 0, nb_forward = 0, init_context = 0;
  static contextItem *menu_history[NB_HISTORY_MAX];
  contextItem *menu;

  if(!init_context) {
    init_context = 1;
    for(int i = 0; i < NB_HISTORY_MAX; i++) {
      menu_history[i] = NULL;
    }
  }

  if(nb_back > NB_HISTORY_MAX - 2)
    nb_back = 1; // we should do a circular list

  if(flag == -1) {
    if(nb_back > 1) {
      nb_back--;
      nb_forward++;
      menu = menu_history[nb_back - 1];
    }
    else
      return;
  }
  else if(flag == 1) {
    if(nb_forward > 0) {
      nb_back++;
      nb_forward--;
      menu = menu_history[nb_back - 1];
    }
    else
      return;
  }
  else {
    menu = menu_asked;
    if(!nb_back || menu_history[nb_back - 1] != menu) {
      menu_history[nb_back++] = menu;
    }
    nb_forward = 0;
  }

  if(menu[0].label[0] == '0'){
    module->value(0);
  }
  else if(menu[0].label[0] == '1'){
    module->value(1);
  }
  else if(menu[0].label[0] == '2'){
    module->value(2);
    menu[1].label = opt_solver_name0(0, GMSH_GET, 0);
    menu[2].label = opt_solver_name1(0, GMSH_GET, 0);
    menu[3].label = opt_solver_name2(0, GMSH_GET, 0);
    menu[4].label = opt_solver_name3(0, GMSH_GET, 0);
    menu[5].label = opt_solver_name4(0, GMSH_GET, 0);
    for(int i = 0; i < MAX_NUM_SOLVERS; i++) {
      if(!strlen(menu[i + 1].label))
        menu[i + 1].label = NULL;
    }
  }
  else if(menu[0].label[0] == '3'){
    module->value(3);
  }
  else {
    Msg::Warning("Something is wrong in your dynamic context definition");
    return;
  }

  Msg::StatusBar(1, false, menu[0].label + 1);

  // cannot use scroll->clear() in fltk 1.1 (should be fixed in 1.3)
  for(unsigned int i = 0; i < push.size(); i++){
    scroll->remove(push[i]);
    Fl::delete_widget(push[i]);
  }
  for(unsigned int i = 0; i < toggle.size(); i++){
    scroll->remove(toggle[i]);
    Fl::delete_widget(toggle[i]);
  }
  for(unsigned int i = 0; i < toggle2.size(); i++){
    scroll->remove(toggle2[i]);
    Fl::delete_widget(toggle2[i]);
  }
  for(unsigned int i = 0; i < popup.size(); i++){
    scroll->remove(popup[i]);
    Fl::delete_widget(popup[i]);
  }
  for(unsigned int i = 0; i < popup2.size(); i++){
    scroll->remove(popup2[i]);
    Fl::delete_widget(popup2[i]);
  }

  // reset the vectors
  push.clear();
  toggle.clear();
  toggle2.clear();
  popup.clear();
  popup2.clear();
  for(unsigned int i = 0; i < label.size(); i++)
    delete [] label[i];
  label.clear();

  int width = win->w();
  int popw = 4 * _fontsize + 3;

  // construct the dynamic menu
  int nb = 0;
  if(module->value() == 3){ // post-processing context
    for(nb = 0; nb < (int)PView::list.size(); nb++) {
      PViewData *data = PView::list[nb]->getData();
      PViewOptions *opt = PView::list[nb]->getOptions();
      
      Fl_Light_Button *b1 = new Fl_Light_Button(0, _MH + nb * BH, width - popw, BH);
      b1->callback(view_toggle_cb, (void *)nb);
      b1->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
      b1->value(opt->Visible);
      b1->copy_label(data->getName().c_str());
      b1->tooltip(data->getFileName().c_str());
      
      char *tmp = new char[32];
      sprintf(tmp, "[%d]@#-1>", nb);
      Fl_Button *b2 = new Fl_Button(width - popw, _MH + nb * BH, popw, BH, tmp);
      label.push_back(tmp);
      b2->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
      b2->tooltip("Show view option menu (Shift+w)");
  
      popupButton *p[2];
      p[0] = new popupButton(width - popw, _MH + nb * BH, popw, BH);
      p[0]->type(Fl_Menu_Button::POPUP123);
      p[1] = new popupButton(0, _MH + nb * BH, width - popw, BH);
      p[1]->type(Fl_Menu_Button::POPUP3);
  
      for(int j = 0; j < 2; j++) {
        p[j]->add("Reload/View", 'r', 
                  (Fl_Callback *) view_reload_cb, (void *)nb, 0);
        p[j]->add("Reload/Visible Views", 0, 
                  (Fl_Callback *) view_reload_visible_cb, (void *)nb, 0);
        p[j]->add("Reload/All Views", 0, 
                  (Fl_Callback *) view_reload_all_cb, (void *)nb, 0);
        p[j]->add("Remove/View", FL_Delete, 
                  (Fl_Callback *) view_remove_cb, (void *)nb, 0);
        p[j]->add("Remove/Other Views", 0, 
                  (Fl_Callback *) view_remove_other_cb, (void *)nb, 0);
        p[j]->add("Remove/Visible Views", 0, 
                  (Fl_Callback *) view_remove_visible_cb, (void *)nb, 0);
        p[j]->add("Remove/Invisible Views", 0, 
                  (Fl_Callback *) view_remove_invisible_cb, (void *)nb, 0);
        p[j]->add("Remove/Empty Views", 0, 
                  (Fl_Callback *) view_remove_empty_cb, (void *)nb, 0);
        p[j]->add("Remove/All Views", 0, 
                  (Fl_Callback *) view_remove_all_cb, (void *)nb, 0);
        p[j]->add("Alias/View without Options", 0, 
                  (Fl_Callback *) view_alias_cb, (void *)nb, 0);
        p[j]->add("Alias/View with Options", 0, 
                  (Fl_Callback *) view_alias_with_options_cb, (void *)nb, 0);
        p[j]->add("Combine Elements/From Visible Views", 0, 
                  (Fl_Callback *) view_combine_space_visible_cb, (void *)nb, 0);
        p[j]->add("Combine Elements/From All Views", 0, 
                  (Fl_Callback *) view_combine_space_all_cb, (void *)nb, 0);
        p[j]->add("Combine Elements/By View Name", 0, 
                  (Fl_Callback *) view_combine_space_by_name_cb, (void *)nb, 0);
        p[j]->add("Combine Time Steps/From Visible Views", 0, 
                  (Fl_Callback *) view_combine_time_visible_cb, (void *)nb, 0);
        p[j]->add("Combine Time Steps/From All Views", 0, 
                  (Fl_Callback *) view_combine_time_all_cb, (void *)nb, 0);
        p[j]->add("Combine Time Steps/By View Name", 0, 
                 (Fl_Callback *) view_combine_time_by_name_cb, (void *)nb, 0);
        p[j]->add("Set Visibility/All On", 0, 
                  (Fl_Callback *) view_all_visible_cb, (void *)1, 0);
        p[j]->add("Set Visibility/All Off", 0, 
                  (Fl_Callback *) view_all_visible_cb, (void *)0, 0);
        p[j]->add("Set Visibility/Invert", 0, 
                  (Fl_Callback *) view_all_visible_cb, (void *)-1, 0);
        p[j]->add("Save As/Parsed View...", 0, 
                  (Fl_Callback *) view_save_parsed_cb, (void *)nb, 0);
        p[j]->add("Save As/ASCII View...", 0, 
                  (Fl_Callback *) view_save_ascii_cb, (void *)nb, 0);
        p[j]->add("Save As/Binary View...", 0, 
                  (Fl_Callback *) view_save_binary_cb, (void *)nb, 0);
        p[j]->add("Save As/STL Triangulation...", 0, 
                  (Fl_Callback *) view_save_stl_cb, (void *)nb, 0);
        p[j]->add("Save As/Raw Text...", 0, 
                  (Fl_Callback *) view_save_txt_cb, (void *)nb, 0);
        p[j]->add("Save As/Gmsh Mesh...", 0, 
                  (Fl_Callback *) view_save_msh_cb, (void *)nb, 0);
#if defined(HAVE_MED)
        p[j]->add("Save As/MED file...", 0, 
                  (Fl_Callback *) view_save_med_cb, (void *)nb, 0);
#endif
        p[j]->add("Apply As Background Mesh", 0, 
                  (Fl_Callback *) view_applybgmesh_cb, (void *)nb, FL_MENU_DIVIDER);
        p[j]->add("Options...", 'o', 
                  (Fl_Callback *) view_options_cb, (void *)nb, 0);
        p[j]->add("Plugins...", 'p', 
                  (Fl_Callback *) plugin_cb, (void *)nb, 0);
      }

      toggle.push_back(b1);
      toggle2.push_back(b2);
      popup.push_back(p[0]);
      popup2.push_back(p[1]);
      scroll->add(b1);
      scroll->add(b2);
      scroll->add(p[0]);
      scroll->add(p[1]);
    }
  }
  else{ // geometry, mesh and solver contexts
    while(menu[nb + 1].label) {
      Fl_Button *b = new Fl_Button(0, _MH + nb * BH, width, BH);
      b->copy_label(menu[nb + 1].label);
      b->callback(menu[nb + 1].callback, menu[nb + 1].arg);
      push.push_back(b);
      scroll->add(b);
      nb++;
    }
  }

  scroll->redraw();

  if(nb <= NB_BUTT_SCROLL)
    win->size(width, _MH + nb * BH);
  else
    win->size(width, _MH + NB_BUTT_SCROLL * BH);
}