From 34a7daef4dcf592173928a19d784fff8b86f048a Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Thu, 16 Sep 2004 19:15:28 +0000
Subject: [PATCH] - the main menu is now scrollable when there are more than 25
 buttons and it can handle an arbitrary number of buttons (this removes the
 "100 views max in the GUI" limitation)

- the view number is now also displayed in the menu (to make it easier
to find the view when one has many many views)

- added "Remove empty views" menu item
---
 Common/Options.cpp           |   8 +-
 Fltk/Callbacks.cpp           |  17 +-
 Fltk/Callbacks.h             |   1 +
 Fltk/GUI.cpp                 | 313 +++++++++++++++++------------------
 Fltk/GUI.h                   |  18 +-
 Makefile                     |   4 +-
 Plugin/CutMap.cpp            |   6 +-
 Plugin/Extract.cpp           |  26 ++-
 Plugin/Levelset.cpp          |  19 +--
 Plugin/Skin.cpp              |  26 ++-
 doc/VERSIONS                 |   5 +-
 doc/texinfo/opt_general.texi |  34 +++-
 doc/texinfo/opt_plugin.texi  |   4 +-
 doc/texinfo/opt_view.texi    |   7 +-
 14 files changed, 256 insertions(+), 232 deletions(-)

diff --git a/Common/Options.cpp b/Common/Options.cpp
index 3c15f566e4..1331c4cf24 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -1,4 +1,4 @@
-// $Id: Options.cpp,v 1.182 2004-09-12 17:02:13 geuzaine Exp $
+// $Id: Options.cpp,v 1.183 2004-09-16 19:15:26 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -1723,7 +1723,7 @@ char *opt_solver_fifth_button_command4(OPT_ARGS_STR)
 int _gui_action_valid(int action, int num)
 {
   return ((WID) &&
-	  (num < NB_BUTT_MAX) &&
+	  (num < (int)WID->m_toggle_butt.size()) &&
 	  (action & GMSH_GUI) && 
 	  (num == WID->view_number));
 }
@@ -1735,7 +1735,7 @@ char *opt_view_name(OPT_ARGS_STR)
   if(action & GMSH_SET) {
     strcpy(v->Name, val);
 #if defined(HAVE_FLTK)
-    if(WID && (num < NB_BUTT_MAX)) {
+    if(WID && (num < (int)WID->m_toggle_butt.size())) {
       // this is OK even if v->Name is not static or allocated, since
       // we reset it correctly in the main GUI routines when the view
       // associated with the button changes (i.e., when views are
@@ -4424,7 +4424,7 @@ double opt_view_visible(OPT_ARGS_NUM)
     v->Visible = (int)val;
   }
 #if defined(HAVE_FLTK)
-  if(WID && (action & GMSH_GUI) && (num < NB_BUTT_MAX))
+  if(WID && (action & GMSH_GUI) && (num < (int)WID->m_toggle_butt.size()))
     WID->m_toggle_butt[num]->value(v->Visible);
 #endif
   Msg(DEBUG1, "View %d", v->Num);
diff --git a/Fltk/Callbacks.cpp b/Fltk/Callbacks.cpp
index f530ba82f1..0ec4881844 100644
--- a/Fltk/Callbacks.cpp
+++ b/Fltk/Callbacks.cpp
@@ -1,4 +1,4 @@
-// $Id: Callbacks.cpp,v 1.268 2004-09-11 06:38:22 geuzaine Exp $
+// $Id: Callbacks.cpp,v 1.269 2004-09-16 19:15:26 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -3112,6 +3112,21 @@ void view_remove_invisible_cb(CALLBACK_ARGS)
   Draw();
 }
 
+void view_remove_empty_cb(CALLBACK_ARGS)
+{
+  int i;
+  if(!CTX.post.list)
+    return;
+  REMOVE_ALL_VIEWS = 1;
+  for(i = List_Nbr(CTX.post.list) - 1; i >= 0; i--){
+    Post_View *v = (Post_View*) List_Pointer(CTX.post.list, i);
+    if(v->empty())
+      view_remove_cb(NULL, (void *)i);
+  }
+  REMOVE_ALL_VIEWS = 0;
+  Draw();
+}
+
 void view_remove_cb(CALLBACK_ARGS)
 {
   RemoveViewByIndex((long int)data);
diff --git a/Fltk/Callbacks.h b/Fltk/Callbacks.h
index 90d4c7c83a..a01c68706e 100644
--- a/Fltk/Callbacks.h
+++ b/Fltk/Callbacks.h
@@ -107,6 +107,7 @@ void view_remove_cb(CALLBACK_ARGS) ;
 void view_remove_all_cb(CALLBACK_ARGS) ;
 void view_remove_visible_cb(CALLBACK_ARGS) ;
 void view_remove_invisible_cb(CALLBACK_ARGS) ;
+void view_remove_empty_cb(CALLBACK_ARGS) ;
 void view_save_ascii_cb(CALLBACK_ARGS) ;
 void view_save_binary_cb(CALLBACK_ARGS) ;
 void view_duplicate_cb(CALLBACK_ARGS) ;
diff --git a/Fltk/GUI.cpp b/Fltk/GUI.cpp
index e805a44c6c..b6d3045570 100644
--- a/Fltk/GUI.cpp
+++ b/Fltk/GUI.cpp
@@ -1,4 +1,4 @@
-// $Id: GUI.cpp,v 1.341 2004-09-14 17:43:40 geuzaine Exp $
+// $Id: GUI.cpp,v 1.342 2004-09-16 19:15:27 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -55,11 +55,14 @@
 #include "CommandLine.h"
 #include "Solvers.h"
 
-#define WINDOW_BOX FL_FLAT_BOX
-#define TOGGLE_BOX FL_DOWN_BOX
+#define NB_BUTT_SCROLL 25
+#define NB_HISTORY_MAX 1000
+
+#define WINDOW_BOX   FL_FLAT_BOX
+#define TOGGLE_BOX   FL_DOWN_BOX
 #define TOGGLE_COLOR FL_BLACK
-#define RADIO_BOX  FL_ROUND_DOWN_BOX
-#define RADIO_COLOR FL_BLACK
+#define RADIO_BOX    FL_ROUND_DOWN_BOX
+#define RADIO_COLOR  FL_BLACK
 
 #define IW (10*fontsize)  // input field width
 #define BB (7*fontsize)   // width of a button with internal label
@@ -812,7 +815,6 @@ void GUI::wait()
 
 // Create the menu window
 
-
 void GUI::add_post_plugins(Fl_Menu_Button * button, int iView)
 {
   char name[256], menuname[256];
@@ -831,14 +833,14 @@ void GUI::add_post_plugins(Fl_Menu_Button * button, int iView)
 
 void GUI::create_menu_window(int argc, char **argv)
 {
-  int i, y;
+  int y;
 
   if(m_window) {
     m_window->show(1, argv);
     return;
   }
 
-  int width = 13 * fontsize - fontsize / 2 - 2;
+  int width = 14 * fontsize;
 
   // this is the initial height: no dynamic button is shown!
 #if defined(__APPLE__) && defined(HAVE_FL_SYS_MENU_BAR)
@@ -852,7 +854,7 @@ void GUI::create_menu_window(int argc, char **argv)
   }
 #endif
 
-  m_window = new Fl_Window(width, MH, "Gmsh");
+  m_window = new Fl_Window(width, MH + NB_BUTT_SCROLL * BH, "Gmsh");
   m_window->box(WINDOW_BOX);
   m_window->callback(file_quit_cb);
 
@@ -883,17 +885,15 @@ void GUI::create_menu_window(int argc, char **argv)
   }
 #endif
 
-  m_navig_butt[0] = new Fl_Button(1, y, 18, BH / 2, "@#<");
+  m_navig_butt[0] = new Fl_Button(1, y, 18, BH / 2, "@#-1<");
   m_navig_butt[0]->labeltype(FL_SYMBOL_LABEL);
-  m_navig_butt[0]->labelsize(11);
   m_navig_butt[0]->box(FL_FLAT_BOX);
   m_navig_butt[0]->selection_color(FL_WHITE);
   m_navig_butt[0]->callback(mod_back_cb);
   m_navig_butt[0]->tooltip("Go back one in the menu history (<)");
 
-  m_navig_butt[1] = new Fl_Button(1, y + BH / 2, 18, BH / 2, "@#>");
+  m_navig_butt[1] = new Fl_Button(1, y + BH / 2, 18, BH / 2, "@#-1>");
   m_navig_butt[1]->labeltype(FL_SYMBOL_LABEL);
-  m_navig_butt[1]->labelsize(11);
   m_navig_butt[1]->box(FL_FLAT_BOX);
   m_navig_butt[1]->selection_color(FL_WHITE);
   m_navig_butt[1]->callback(mod_forward_cb);
@@ -907,78 +907,16 @@ void GUI::create_menu_window(int argc, char **argv)
   // time we select one of the categories, even if the category is not
   // changed):
   m_module_butt->when(FL_WHEN_RELEASE_ALWAYS);
-  y = MH;
-
-  for(i = 0; i < NB_BUTT_MAX; i++) {
-    m_push_butt[i] = new Fl_Button(0, y + i * BH, width, BH);
-    m_push_butt[i]->hide();
-
-    m_toggle_butt[i] = new Fl_Light_Button(0, y + i * BH, width - (fontsize + 4), BH);
-    m_toggle_butt[i]->callback(view_toggle_cb, (void *)i);
-    m_toggle_butt[i]->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
-    m_toggle_butt[i]->hide();
-
-    m_toggle2_butt[i] = new Fl_Button(width - (fontsize + 4), y + i * BH, (fontsize + 4), BH, "@#>");
-    m_toggle2_butt[i]->labeltype(FL_SYMBOL_LABEL);
-    m_toggle2_butt[i]->labelsize(11);
-    m_toggle2_butt[i]->align(FL_ALIGN_CENTER);
-    m_toggle2_butt[i]->hide();
-    m_toggle2_butt[i]->tooltip("Show view option menu (Shift+w)");
-
-    m_popup_butt[i] = new Fl_Menu_Button(width - (fontsize + 4), y + i * BH, (fontsize + 4), BH);
-    m_popup_butt[i]->type(Fl_Menu_Button::POPUP123);
-
-    m_popup2_butt[i] = new Fl_Menu_Button(0, y + i * BH, width - (fontsize + 4), BH);
-    m_popup2_butt[i]->type(Fl_Menu_Button::POPUP3);
-
-    for(int j = 0; j < 2; j++) {
-      Fl_Menu_Button *pop = j ? m_popup2_butt[i] : m_popup_butt[i];
-      pop->add("Reload/View", 0, (Fl_Callback *) view_reload_cb, (void *)i, 0);
-      pop->add("Reload/All views", 0, (Fl_Callback *) view_reload_all_cb, (void *)i, 0);
-      pop->add("Reload/All visible views", 0, (Fl_Callback *) view_reload_visible_cb, (void *)i, 0);
-      pop->add("Remove/View", FL_Delete, (Fl_Callback *) view_remove_cb, (void *)i, 0);
-      pop->add("Remove/All views", 0, (Fl_Callback *) view_remove_all_cb, (void *)i, 0);
-      pop->add("Remove/All visible views", 0, (Fl_Callback *) view_remove_visible_cb, (void *)i, 0);
-      pop->add("Remove/All invisible views", 0, (Fl_Callback *) view_remove_invisible_cb, (void *)i, 0);
-      pop->add("Duplicate/View without options", 0, (Fl_Callback *) view_duplicate_cb, (void *)i, 0);
-      pop->add("Duplicate/View with options", 0, (Fl_Callback *) view_duplicate_with_options_cb, (void *)i, 0);
-      pop->add("Combine/Elements/From all views", 0, 
-	       (Fl_Callback *) view_combine_all_cb, (void *)i, 0);
-      pop->add("Combine/Elements/From all views (and remove originals)", 0, 
-	       (Fl_Callback *) view_combine_all_and_remove_cb, (void *)i, 0);
-      pop->add("Combine/Elements/From visible views", 0, 
-	       (Fl_Callback *) view_combine_visible_cb, (void *)i, 0);
-      pop->add("Combine/Elements/From visible views (and remove originals)", 0, 
-	       (Fl_Callback *) view_combine_visible_and_remove_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/From all views", 0, 
-	       (Fl_Callback *) view_combine_time_all_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/From all views (and remove originals)", 0, 
-	       (Fl_Callback *) view_combine_time_all_and_remove_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/From visible views", 0, 
-	       (Fl_Callback *) view_combine_time_visible_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/From visible views (and remove originals)", 0, 
-	       (Fl_Callback *) view_combine_time_visible_and_remove_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/By view name", 0, 
-	       (Fl_Callback *) view_combine_time_by_name_cb, (void *)i, 0);
-      pop->add("Combine/Time steps/By view name (and remove originals)", 0, 
-	       (Fl_Callback *) view_combine_time_by_name_and_remove_cb, (void *)i, 0);
-      pop->add("Save as/ASCII view...", 0, (Fl_Callback *) view_save_ascii_cb, (void *)i, 0);
-      pop->add("Save as/Binary view...", 0, (Fl_Callback *) view_save_binary_cb, (void *)i, 0);
-      add_post_plugins(pop, i);
-      pop->add("Apply as background mesh", 0, (Fl_Callback *) view_applybgmesh_cb, (void *)i, FL_MENU_DIVIDER);
-      pop->add("Options...", 'o', (Fl_Callback *) view_options_cb, (void *)i, 0);
-      pop->hide();
-    }
-  }
-  m_window->position(CTX.position[0], CTX.position[1]);
-  m_window->end();
-}
 
-// Dynamically set the height of the menu window
+  // create an empty scroll area that will get populated dynamically
+  // in set_context()
+  m_scroll = new Fl_Scroll(0, MH, width, NB_BUTT_SCROLL * BH); 
+  m_scroll->type(Fl_Scroll::VERTICAL);
+  m_scroll->end();
 
-void GUI::set_menu_size(int nb_butt)
-{
-  m_window->size(m_window->w(), MH + nb_butt * BH);
+  m_window->size(width, MH);
+  m_window->position(CTX.position[0], CTX.position[1]);
+  m_window->end();
 }
 
 // Dynamically set the context
@@ -988,18 +926,16 @@ void GUI::set_context(Context_Item * menu_asked, int flag)
   static int nb_back = 0, nb_forward = 0, init_context = 0;
   static Context_Item *menu_history[NB_HISTORY_MAX];
   Context_Item *menu;
-  Post_View *v;
-  int i;
 
   if(!init_context) {
     init_context = 1;
-    for(i = 0; i < NB_HISTORY_MAX; i++) {
+    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
+    nb_back = 1; // we should do a circular list
 
   if(flag == -1) {
     if(nb_back > 1) {
@@ -1027,16 +963,27 @@ void GUI::set_context(Context_Item * menu_asked, int flag)
     nb_forward = 0;
   }
 
-  int nb = 0;
-
-  if(menu[0].label[0] == '0')
+  if(menu[0].label[0] == '0'){
     m_module_butt->value(0);
-  else if(menu[0].label[0] == '1')
+  }
+  else if(menu[0].label[0] == '1'){
     m_module_butt->value(1);
-  else if(menu[0].label[0] == '2')
+  }
+  else if(menu[0].label[0] == '2'){
     m_module_butt->value(2);
-  else if(menu[0].label[0] == '3')
+    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 < MAXSOLVERS; i++) {
+      if(!strlen(menu[i + 1].label))
+	menu[i + 1].label = NULL;
+    }
+  }
+  else if(menu[0].label[0] == '3'){
     m_module_butt->value(3);
+  }
   else {
     Msg(WARNING, "Something is wrong in your dynamic context definition");
     return;
@@ -1044,70 +991,127 @@ void GUI::set_context(Context_Item * menu_asked, int flag)
 
   Msg(STATUS2N, menu[0].label + 1);
 
-  switch (m_module_butt->value()) {
-  case 3:      // post-processing contexts
-    for(i = 0; i < List_Nbr(CTX.post.list); i++) {
-      if(i == NB_BUTT_MAX)
-        break;
-      nb++;
-      v = (Post_View *) List_Pointer(CTX.post.list, i);
-      m_push_butt[i]->hide();
-      m_toggle_butt[i]->show();
-      m_toggle_butt[i]->value(v->Visible);
-      m_toggle_butt[i]->label(v->Name);
-      m_toggle_butt[i]->tooltip(v->FileName);
-      m_toggle2_butt[i]->show();
-      m_popup_butt[i]->show();
-      m_popup2_butt[i]->show();
-    }
-    for(i = List_Nbr(CTX.post.list); i < NB_BUTT_MAX; i++) {
-      m_push_butt[i]->hide();
-      m_toggle_butt[i]->hide();
-      m_toggle2_butt[i]->hide();
-      m_popup_butt[i]->hide();
-      m_popup2_butt[i]->hide();
-    }
-    break;
-  default:     // geometry, mesh, solver contexts
-    if(m_module_butt->value() == 2) {   //solver
-      // should handle MAXSOLVERS
-      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(i = 0; i < MAXSOLVERS; i++) {
-        if(!strlen(menu[i + 1].label))
-          menu[i + 1].label = NULL;
-      }
-    }
-    for(i = 0; i < NB_BUTT_MAX; i++) {
-      m_toggle_butt[i]->hide();
-      m_toggle2_butt[i]->hide();
-      m_popup_butt[i]->hide();
-      m_popup2_butt[i]->hide();
-      if(menu[i + 1].label) {
-        m_push_butt[i]->label(menu[i + 1].label);
-        m_push_butt[i]->callback(menu[i + 1].callback, menu[i + 1].arg);
-        m_push_butt[i]->redraw();
-        m_push_butt[i]->show();
-        nb++;
+  // free all the children (m_push*, m_toggle*, m_pop*)
+  m_scroll->clear();
+  // reset the vectors
+  m_push_butt.clear();
+  m_toggle_butt.clear();
+  m_toggle2_butt.clear();
+  m_popup_butt.clear();
+  m_popup2_butt.clear();
+  for(unsigned int i = 0; i < m_pop_label.size(); i++)
+    delete [] m_pop_label[i];
+  m_pop_label.clear();
+
+  int width = m_window->w();
+  int right_pop_width = 4 * fontsize + 3;
+
+  // construct the dynamic menu
+  int nb = 0;
+  if(m_module_butt->value() == 3){ // post-processing context
+    for(nb = 0; nb < List_Nbr(CTX.post.list); nb++) {
+      Post_View *v = (Post_View *) List_Pointer(CTX.post.list, nb);
+      
+      Fl_Light_Button *b1 = new Fl_Light_Button(0, MH + nb * BH, width - right_pop_width, BH);
+      b1->callback(view_toggle_cb, (void *)nb);
+      b1->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
+      b1->value(v->Visible);
+      b1->label(v->Name);
+      b1->tooltip(v->FileName);
+      
+      char *tmp = new char[32];
+      sprintf(tmp, "[%d]@#-1>", nb);
+      Fl_Button *b2 = new Fl_Button(width - right_pop_width, MH + nb * BH, right_pop_width, BH, tmp);
+      m_pop_label.push_back(tmp);
+      b2->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
+      b2->tooltip("Show view option menu (Shift+w)");
+  
+      Fl_Menu_Button *p[2];
+      p[0] = new Fl_Menu_Button(width - right_pop_width, MH + nb * BH, right_pop_width, BH);
+      p[0]->type(Fl_Menu_Button::POPUP123);
+  
+      p[1] = new Fl_Menu_Button(0, MH + nb * BH, width - right_pop_width, BH);
+      p[1]->type(Fl_Menu_Button::POPUP3);
+  
+      for(int j = 0; j < 2; j++) {
+	p[j]->add("Reload/View", 0, 
+		  (Fl_Callback *) view_reload_cb, (void *)nb, 0);
+	p[j]->add("Reload/All views", 0, 
+		  (Fl_Callback *) view_reload_all_cb, (void *)nb, 0);
+	p[j]->add("Reload/All visible views", 0, 
+		  (Fl_Callback *) view_reload_visible_cb, (void *)nb, 0);
+	p[j]->add("Remove/View", FL_Delete, 
+		  (Fl_Callback *) view_remove_cb, (void *)nb, 0);
+	p[j]->add("Remove/All views", 0, 
+		  (Fl_Callback *) view_remove_all_cb, (void *)nb, 0);
+	p[j]->add("Remove/All visible views", 0, 
+		  (Fl_Callback *) view_remove_visible_cb, (void *)nb, 0);
+	p[j]->add("Remove/All invisible views", 0, 
+		  (Fl_Callback *) view_remove_invisible_cb, (void *)nb, 0);
+	p[j]->add("Remove/All empty views", 0, 
+		  (Fl_Callback *) view_remove_empty_cb, (void *)nb, 0);
+	p[j]->add("Duplicate/View without options", 0, 
+		  (Fl_Callback *) view_duplicate_cb, (void *)nb, 0);
+	p[j]->add("Duplicate/View with options", 0, 
+		  (Fl_Callback *) view_duplicate_with_options_cb, (void *)nb, 0);
+	p[j]->add("Combine/Elements/From all views", 0, 
+		  (Fl_Callback *) view_combine_all_cb, (void *)nb, 0);
+	p[j]->add("Combine/Elements/From all views (and remove originals)", 0, 
+		  (Fl_Callback *) view_combine_all_and_remove_cb, (void *)nb, 0);
+	p[j]->add("Combine/Elements/From visible views", 0, 
+		  (Fl_Callback *) view_combine_visible_cb, (void *)nb, 0);
+	p[j]->add("Combine/Elements/From visible views (and remove originals)", 0, 
+		  (Fl_Callback *) view_combine_visible_and_remove_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/From all views (and remove originals)", 0, 
+		  (Fl_Callback *) view_combine_time_all_and_remove_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 visible views (and remove originals)", 0, 
+		  (Fl_Callback *) view_combine_time_visible_and_remove_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("Combine/Time steps/By view name (and remove originals)", 0, 
+		  (Fl_Callback *) view_combine_time_by_name_and_remove_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);
+	add_post_plugins(p[j], nb);
+	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);
       }
-      else
-        break;
+
+      m_toggle_butt.push_back(b1);
+      m_toggle2_butt.push_back(b2);
+      m_popup_butt.push_back(p[0]);
+      m_popup2_butt.push_back(p[1]);
+      m_scroll->add(b1);
+      m_scroll->add(b2);
+      m_scroll->add(p[0]);
+      m_scroll->add(p[1]);
     }
-    for(i = nb; i < NB_BUTT_MAX; i++) {
-      m_toggle_butt[i]->hide();
-      m_toggle2_butt[i]->hide();
-      m_popup_butt[i]->hide();
-      m_popup2_butt[i]->hide();
-      m_push_butt[i]->hide();
+  }
+  else{ // geometry, mesh and solver contexts
+    while(menu[nb + 1].label) {
+      Fl_Button *b = new Fl_Button(0, MH + nb * BH, width, BH);
+      b->label(menu[nb + 1].label);
+      b->callback(menu[nb + 1].callback, menu[nb + 1].arg);
+      m_push_butt.push_back(b);
+      m_scroll->add(b);
+      nb++;
     }
-    break;
   }
 
-  set_menu_size(nb);
+  m_scroll->redraw();
 
+  if(nb <= NB_BUTT_SCROLL)
+    m_window->size(width, MH + nb * BH);
+  else
+    m_window->size(width, MH + NB_BUTT_SCROLL * BH);
 }
 
 int GUI::get_context()
@@ -1115,7 +1119,6 @@ int GUI::get_context()
   return m_module_butt->value();
 }
 
-
 // Create the graphic window
 
 void GUI::create_graphic_window(int argc, char **argv)
@@ -1402,8 +1405,6 @@ void GUI::reset_option_browser()
   opt_browser->add("Solver");
   opt_browser->add("Post-processing");
   for(i = 0; i < List_Nbr(CTX.post.list); i++) {
-    if(i == NB_BUTT_MAX)
-      break;
     sprintf(str, "View [%d]", i);
     opt_browser->add(str);
   }
@@ -1459,6 +1460,7 @@ void GUI::create_option_window()
   // Selection browser
 
   opt_browser = new Fl_Hold_Browser(WB, WB, BROWSERW - WB, height - 3 * WB - BH);
+  opt_browser->has_scrollbar(Fl_Browser_::VERTICAL);
   reset_option_browser();
   opt_browser->callback(options_browser_cb);
   opt_browser->value(1);
@@ -3276,8 +3278,6 @@ void GUI::reset_clip_browser()
   clip_browser->add("Geometry");
   clip_browser->add("Mesh");
   for(int i = 0; i < List_Nbr(CTX.post.list); i++) {
-    if(i == NB_BUTT_MAX)
-      break;
     sprintf(str, "View [%d]", i);
     clip_browser->add(str);
   }
@@ -3792,4 +3792,3 @@ void GUI::create_solver_window(int num)
   solver[num].window->position(CTX.solver_position[0], CTX.solver_position[1]);
   solver[num].window->end();
 }
-
diff --git a/Fltk/GUI.h b/Fltk/GUI.h
index 81089f5d12..8a6c13e1f2 100644
--- a/Fltk/GUI.h
+++ b/Fltk/GUI.h
@@ -47,12 +47,11 @@
 #include <FL/fl_ask.H>
 #include <FL/Fl_Tooltip.H>
 
+#include <vector>
+
 #include "Opengl_Window.h"
 #include "Colorbar_Window.h"
 
-#define NB_BUTT_MAX    100
-#define NB_HISTORY_MAX 1000
-
 #if defined(__APPLE__) && defined(HAVE_FL_SYS_MENU_BAR)
 #include <FL/Fl_Sys_Menu_Bar.H>
 #endif
@@ -119,6 +118,7 @@ struct SolverDialogBox
 class GUI{
 
   int MH, fontsize ;
+  Fl_Scroll *m_scroll;
 
   // Bitmaps
   Fl_Bitmap  *abort_bmp, *start_bmp, *stop_bmp, *rewind_bmp, *about_bmp ;
@@ -137,11 +137,12 @@ public:
   Fl_Menu_Bar      *m_menu_bar ;
   Fl_Choice        *m_module_butt ;
   Fl_Button        *m_navig_butt  [2] ;
-  Fl_Button        *m_push_butt   [NB_BUTT_MAX] ;
-  Fl_Light_Button  *m_toggle_butt [NB_BUTT_MAX] ;
-  Fl_Button        *m_toggle2_butt[NB_BUTT_MAX] ;
-  Fl_Menu_Button   *m_popup_butt  [NB_BUTT_MAX] ;
-  Fl_Menu_Button   *m_popup2_butt [NB_BUTT_MAX] ;
+  std::vector<Fl_Button*>       m_push_butt ;
+  std::vector<Fl_Light_Button*> m_toggle_butt ;
+  std::vector<Fl_Button*>       m_toggle2_butt ;
+  std::vector<Fl_Menu_Button*>  m_popup_butt ;
+  std::vector<Fl_Menu_Button*>  m_popup2_butt ;
+  std::vector<char*>            m_pop_label ;
 
   // graphic window
   Fl_Window        *g_window ;
@@ -273,7 +274,6 @@ public:
   void make_opengl_current();
   void redraw_opengl();
   void set_size(int w, int h);
-  void set_menu_size(int nb_butt);
   void set_context(Context_Item menu[], int flag);
   int  get_context();
   void set_anim_buttons(int mode);
diff --git a/Makefile b/Makefile
index 741998e570..fe25877d34 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.368 2004-09-12 04:13:59 geuzaine Exp $
+# $Id: Makefile,v 1.369 2004-09-16 19:15:26 geuzaine Exp $
 #
 # Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 #
@@ -23,7 +23,7 @@ include variables
 
 GMSH_MAJOR_VERSION = 1
 GMSH_MINOR_VERSION = 55
-GMSH_PATCH_VERSION = 1
+GMSH_PATCH_VERSION = 2
 GMSH_EXTRA_VERSION = "-cvs"
 
 GMSH_VERSION = ${GMSH_MAJOR_VERSION}.${GMSH_MINOR_VERSION}.${GMSH_PATCH_VERSION}${GMSH_EXTRA_VERSION}
diff --git a/Plugin/CutMap.cpp b/Plugin/CutMap.cpp
index c3da508e12..f855fe5142 100644
--- a/Plugin/CutMap.cpp
+++ b/Plugin/CutMap.cpp
@@ -1,4 +1,4 @@
-// $Id: CutMap.cpp,v 1.37 2004-05-16 20:04:43 geuzaine Exp $
+// $Id: CutMap.cpp,v 1.38 2004-09-16 19:15:27 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -65,8 +65,8 @@ void GMSH_CutMapPlugin::getInfos(char *author, char *copyright,
 	 "corresponding time step in `dView'. If `dView'\n"
 	 "< 0, the plugin uses `iView' as the value source.\n"
 	 "\n"
-	 "Plugin(CutMap) creates (at most) as many views\n"
-	 "as there are time steps in `iView'.\n");
+	 "Plugin(CutMap) creates as many views as there are\n"
+	 "time steps in `iView'.\n");
 }
 
 int GMSH_CutMapPlugin::getNbOptions() const
diff --git a/Plugin/Extract.cpp b/Plugin/Extract.cpp
index 0cdcb06f1d..de62b443cf 100644
--- a/Plugin/Extract.cpp
+++ b/Plugin/Extract.cpp
@@ -1,4 +1,4 @@
-// $Id: Extract.cpp,v 1.11 2004-05-16 20:04:43 geuzaine Exp $
+// $Id: Extract.cpp,v 1.12 2004-09-16 19:15:27 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -244,19 +244,13 @@ Post_View *GMSH_ExtractPlugin::execute(Post_View * v)
   extract(expr, v1->VY, v1->NbVY, v2->SY, &v2->NbSY, v2->VY, &v2->NbVY, v1->NbTimeStep, 5, 3);
   extract(expr, v1->TY, v1->NbTY, v2->SY, &v2->NbSY, v2->VY, &v2->NbVY, v1->NbTimeStep, 5, 9);
 
-  if(v2->empty()) {
-    RemoveViewByNumber(v2->Num);
-    return v1;
-  }
-  else{
-    // copy time data
-    for(int i = 0; i < List_Nbr(v1->Time); i++)
-      List_Add(v2->Time, List_Pointer(v1->Time, i));
-    // finalize
-    char name[1024], filename[1024];
-    sprintf(name, "%s_Extract", v1->Name);
-    sprintf(filename, "%s_Extract.pos", v1->Name);
-    EndView(v2, 1, filename, name);
-    return v2;
-  }
+  // copy time data
+  for(int i = 0; i < List_Nbr(v1->Time); i++)
+    List_Add(v2->Time, List_Pointer(v1->Time, i));
+  // finalize
+  char name[1024], filename[1024];
+  sprintf(name, "%s_Extract", v1->Name);
+  sprintf(filename, "%s_Extract.pos", v1->Name);
+  EndView(v2, 1, filename, name);
+  return v2;
 }
diff --git a/Plugin/Levelset.cpp b/Plugin/Levelset.cpp
index d6df1e5d00..3af76c0739 100644
--- a/Plugin/Levelset.cpp
+++ b/Plugin/Levelset.cpp
@@ -1,4 +1,4 @@
-// $Id: Levelset.cpp,v 1.14 2004-05-13 17:48:56 geuzaine Exp $
+// $Id: Levelset.cpp,v 1.15 2004-09-16 19:15:27 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -521,8 +521,7 @@ Post_View *GMSH_LevelsetPlugin::execute(Post_View * v)
   executeList(v, v->TY, v->NbTY, 9, w, w->TY, w->NbTY, 9, 5, 8, exnPyr, out);
 
   for(unsigned int i = 0; i < out.size(); i++) {
-    // create time data
-    // FIXME: todo
+    // FIXME: create time data
     // finalize
     char name[1024], filename[1024];
     sprintf(name, "%s_Levelset_%d", v->Name, i);
@@ -530,19 +529,5 @@ Post_View *GMSH_LevelsetPlugin::execute(Post_View * v)
     EndView(out[i], 1, filename, name);
   }
 
-  // remove empty views
-  List_T *to_remove = List_Create(10, 10, sizeof(int));
-  for(int i = 0; i < List_Nbr(CTX.post.list); i++) {
-    w = (Post_View*) List_Pointer(CTX.post.list, i);
-    if(w->empty())
-      List_Insert(to_remove, &w->Num, fcmp_int);
-  }
-  for(int i = 0; i < List_Nbr(to_remove); i++) {
-    int num;
-    List_Read(to_remove, i, &num);
-    RemoveViewByNumber(num);
-  }
-  List_Delete(to_remove);
-
   return 0;
 }
diff --git a/Plugin/Skin.cpp b/Plugin/Skin.cpp
index 26c1794f96..a503c1ae11 100644
--- a/Plugin/Skin.cpp
+++ b/Plugin/Skin.cpp
@@ -1,4 +1,4 @@
-// $Id: Skin.cpp,v 1.27 2004-05-16 20:04:43 geuzaine Exp $
+// $Id: Skin.cpp,v 1.28 2004-09-16 19:15:27 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -274,19 +274,13 @@ Post_View *GMSH_SkinPlugin::execute(Post_View * v)
   Tree_Action(_skin, addInView);
   Tree_Delete(_skin);
 
-  if(v2->empty()) {
-    RemoveViewByNumber(v2->Num);
-    return v1;
-  }
-  else{
-    // copy time data
-    for(int i = 0; i < List_Nbr(v1->Time); i++)
-      List_Add(v2->Time, List_Pointer(v1->Time, i));
-    // finalize
-    char name[1024], filename[1024];
-    sprintf(name, "%s_Skin", v1->Name);
-    sprintf(filename, "%s_Skin.pos", v1->Name);
-    EndView(v2, 1, filename, name);
-    return v2;
-  }
+  // copy time data
+  for(int i = 0; i < List_Nbr(v1->Time); i++)
+    List_Add(v2->Time, List_Pointer(v1->Time, i));
+  // finalize
+  char name[1024], filename[1024];
+  sprintf(name, "%s_Skin", v1->Name);
+  sprintf(filename, "%s_Skin.pos", v1->Name);
+  EndView(v2, 1, filename, name);
+  return v2;
 }
diff --git a/doc/VERSIONS b/doc/VERSIONS
index 8194e7e0b9..95c0f14255 100644
--- a/doc/VERSIONS
+++ b/doc/VERSIONS
@@ -1,9 +1,10 @@
-$Id: VERSIONS,v 1.244 2004-09-01 20:23:50 geuzaine Exp $
+$Id: VERSIONS,v 1.245 2004-09-16 19:15:27 geuzaine Exp $
 
 New since 1.55: new post-processing option to draw a scalar view
 raised by a displacement view without using Plugin(DisplacementRaise)
 (makes drawing arbitrary scalar fields on deformed meshes much
-easier); small bug fixes.
+easier); better post-processing menu (arbitrary number of
+views+scrollable+show view number); small bug fixes.
 
 New in 1.55: added background mesh support for Triangle; meshes can
 now be displayed using "smoothed" normals (like post-processing
diff --git a/doc/texinfo/opt_general.texi b/doc/texinfo/opt_general.texi
index f349a51423..46ff1079bd 100644
--- a/doc/texinfo/opt_general.texi
+++ b/doc/texinfo/opt_general.texi
@@ -36,7 +36,7 @@ Saved in: @code{General.SessionFileName}
 
 @item General.TextEditor
 System command to launch a text editor@*
-Default value: @code{"emacs %s &"}@*
+Default value: @code{"open -e %s"}@*
 Saved in: @code{General.OptionsFileName}
 
 @item General.TmpFileName
@@ -46,7 +46,7 @@ Saved in: @code{General.SessionFileName}
 
 @item General.WebBrowser
 System command to launch a web browser@*
-Default value: @code{"if [[ $(ps -e|grep mozilla|grep -v grep) ]]; then mozilla -remote 'openurl(%s)' ; else mozilla %s ; fi &"}@*
+Default value: @code{"open %s"}@*
 Saved in: @code{General.OptionsFileName}
 
 @item General.AlphaBlending
@@ -329,6 +329,11 @@ Z position of light source 0@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light0W
+Divisor of the X, Y and Z coordinates of light source 0 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Light1
 Enable light source 1@*
 Default value: @code{0}@*
@@ -349,6 +354,11 @@ Z position of light source 1@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light1W
+Divisor of the X, Y and Z coordinates of light source 1 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Light2
 Enable light source 2@*
 Default value: @code{0}@*
@@ -369,6 +379,11 @@ Z position of light source 2@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light2W
+Divisor of the X, Y and Z coordinates of light source 2 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Light3
 Enable light source 3@*
 Default value: @code{0}@*
@@ -389,6 +404,11 @@ Z position of light source 3@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light3W
+Divisor of the X, Y and Z coordinates of light source 3 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Light4
 Enable light source 4@*
 Default value: @code{0}@*
@@ -409,6 +429,11 @@ Z position of light source 4@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light4W
+Divisor of the X, Y and Z coordinates of light source 4 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.Light5
 Enable light source 5@*
 Default value: @code{0}@*
@@ -429,6 +454,11 @@ Z position of light source 5@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.Light5W
+Divisor of the X, Y and Z coordinates of light source 5 (W=0 means infinitely far source)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item General.LineWidth
 Display width of lines (in pixels)@*
 Default value: @code{1}@*
diff --git a/doc/texinfo/opt_plugin.texi b/doc/texinfo/opt_plugin.texi
index 1e36dd642e..6507b08437 100644
--- a/doc/texinfo/opt_plugin.texi
+++ b/doc/texinfo/opt_plugin.texi
@@ -47,8 +47,8 @@ plugin uses, for each time step in `iView', the
 corresponding time step in `dView'. If `dView'
 < 0, the plugin uses `iView' as the value source.
 
-Plugin(CutMap) creates (at most) as many views
-as there are time steps in `iView'.
+Plugin(CutMap) creates as many views as there are
+time steps in `iView'.
 
 Numeric options:
 @table @code
diff --git a/doc/texinfo/opt_view.texi b/doc/texinfo/opt_view.texi
index 36bb7d77f9..a3a47d8580 100644
--- a/doc/texinfo/opt_view.texi
+++ b/doc/texinfo/opt_view.texi
@@ -244,6 +244,11 @@ Vertical position (in pixels) of the upper left corner of the scale or 2D graph@
 Default value: @code{50}@*
 Saved in: @code{General.OptionsFileName}
 
+@item View.RaisedScalarView
+Index of the scalar view raised by the displacement field@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item View.RaiseX
 Elevation of the view along X-axis (in model coordinates)@*
 Default value: @code{0}@*
@@ -315,7 +320,7 @@ Default value: @code{1}@*
 Saved in: @code{-}
 
 @item View.VectorType
-Vector display type (1=segment, 2=arrow, 3=pyramid, 4=3D arrow, 5=displacement)@*
+Vector display type (1=segment, 2=arrow, 3=pyramid, 4=3D arrow, 5=displacement, 6=raised scalar view)@*
 Default value: @code{4}@*
 Saved in: @code{General.OptionsFileName}
 
-- 
GitLab