From 7deb8b10a6dd16d22c3b4823411a031490d1ba8e Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Mon, 22 Feb 2016 15:40:04 +0000
Subject: [PATCH] better message browser with dynamic filter

---
 Common/CreateFile.cpp     |   1 +
 Common/GmshMessage.cpp    |   4 +-
 Common/GmshMessage.h      |   2 +-
 Common/OpenFile.cpp       |   3 +-
 Fltk/drawContextFltk.h    |   1 +
 Fltk/graphicWindow.cpp    | 203 +++++++++++++++++---------------------
 Fltk/graphicWindow.h      |  13 ++-
 Fltk/manipWindow.cpp      |   1 +
 Fltk/messageBrowser.h     |  98 ++++++++++++++++++
 Fltk/visibilityWindow.cpp |   1 +
 10 files changed, 210 insertions(+), 117 deletions(-)
 create mode 100644 Fltk/messageBrowser.h

diff --git a/Common/CreateFile.cpp b/Common/CreateFile.cpp
index 57b87fafb8..4a50810cce 100644
--- a/Common/CreateFile.cpp
+++ b/Common/CreateFile.cpp
@@ -20,6 +20,7 @@
 #if defined(HAVE_FLTK)
 #include "FlGui.h"
 #include "graphicWindow.h"
+#include "openglWindow.h"
 #include "gl2ps.h"
 #include "gl2gif.h"
 #include "gl2jpeg.h"
diff --git a/Common/GmshMessage.cpp b/Common/GmshMessage.cpp
index 8eadda1e06..d9283f963e 100644
--- a/Common/GmshMessage.cpp
+++ b/Common/GmshMessage.cpp
@@ -763,7 +763,8 @@ void Msg::SetOnelabNumber(std::string name, double val, bool visible,
 }
 
 void Msg::SetOnelabString(std::string name, std::string val, bool visible,
-                          bool persistent, bool readOnly, bool neverChanged)
+                          bool persistent, bool readOnly, bool neverChanged,
+                          const std::string &kind)
 {
 #if defined(HAVE_ONELAB)
   if(_onelabClient){
@@ -778,6 +779,7 @@ void Msg::SetOnelabString(std::string name, std::string val, bool visible,
     if(persistent) strings[0].setAttribute("Persistent", "1");
     strings[0].setReadOnly(readOnly);
     strings[0].setNeverChanged(neverChanged);
+    if(kind.size()) strings[0].setKind(kind);
     _onelabClient->set(strings[0]);
   }
 #endif
diff --git a/Common/GmshMessage.h b/Common/GmshMessage.h
index 4f13a6dc24..a7582c2764 100644
--- a/Common/GmshMessage.h
+++ b/Common/GmshMessage.h
@@ -121,7 +121,7 @@ class Msg {
                               bool neverChanged=false);
   static void SetOnelabString(std::string name, std::string val, bool visible=true,
                               bool persistent=false, bool readOnly=false,
-                              bool neverChanged=false);
+                              bool neverChanged=false, const std::string &kind="");
   static double GetOnelabNumber(std::string name, double defaultValue=0.,
                                 bool errorIfMissing=false);
   static std::string GetOnelabString(std::string name, const std::string &defaultValue="",
diff --git a/Common/OpenFile.cpp b/Common/OpenFile.cpp
index ed7047ab8e..a94faa3961 100644
--- a/Common/OpenFile.cpp
+++ b/Common/OpenFile.cpp
@@ -343,7 +343,8 @@ int MergeFile(const std::string &fileName, bool warnIfMissing, bool setBoundingB
   std::string solver = getSolverForExtension(ext);
   if(solver.size()){
     int num = defineSolver(solver);
-    Msg::SetOnelabString(solver + "/Model name", fileName, true, true);
+    Msg::SetOnelabString(solver + "/Model name", fileName, true, true,
+                         false, false, "file");
     if(GModel::current()->getName() == "" ||
        Msg::GetOnelabString("Gmsh/Model name").empty()){
       GModel::current()->setFileName(split[0] + split[1] + ".geo");
diff --git a/Fltk/drawContextFltk.h b/Fltk/drawContextFltk.h
index 63fba50486..c3d4f21734 100644
--- a/Fltk/drawContextFltk.h
+++ b/Fltk/drawContextFltk.h
@@ -12,6 +12,7 @@
 #include "drawContext.h"
 #include "graphicWindow.h"
 #include "optionWindow.h"
+#include "openglWindow.h"
 #include "Context.h"
 
 class drawContextFltk : public drawContextGlobal{
diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index c4adfd1509..0840444e78 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -33,6 +33,9 @@ typedef unsigned long intptr_t;
 #include "fieldWindow.h"
 #include "pluginWindow.h"
 #include "helpWindow.h"
+#include "openglWindow.h"
+#include "onelabGroup.h"
+#include "messageBrowser.h"
 #include "gmshLocalNetworkClient.h"
 #include "fileDialogs.h"
 #include "extraDialogs.h"
@@ -2776,7 +2779,7 @@ void attach_detach_menu_cb(Fl_Widget *w, void *data)
   FlGui::instance()->check();
 }
 
-static void message_menu_scroll_cb(Fl_Widget *w, void *data)
+static void message_menu_autoscroll_cb(Fl_Widget *w, void *data)
 {
   graphicWindow *g = (graphicWindow*)data;
   g->setAutoScroll(!g->getAutoScroll());
@@ -2795,39 +2798,18 @@ static void message_menu_save_cb(Fl_Widget *w, void *data)
     g->saveMessages(fileChooserGetName(1).c_str());
 }
 
-#if 0
-static void message_menu_increase_font_cb(Fl_Widget *w, void *data)
+static void message_browser_cb(Fl_Widget *w, void *data)
 {
   graphicWindow *g = (graphicWindow*)data;
-  g->changeMessageFontSize(1);
+  g->copySelectedMessagesToClipboard();
 }
 
-static void message_menu_decrease_font_cb(Fl_Widget *w, void *data)
+static void message_menu_search_cb(Fl_Widget *w, void *data)
 {
   graphicWindow *g = (graphicWindow*)data;
-  g->changeMessageFontSize(-1);
-}
-#endif
-
-static void message_browser_cb(Fl_Widget *w, void *data)
-{
-  graphicWindow *g = (graphicWindow*)data;
-
-  if(Fl::event_button() == 3 || Fl::event_state(FL_CTRL) || Fl::event_clicks()){
-    Fl_Menu_Item rclick_menu[] = {
-      { g->getAutoScroll() ? "Disable Auto-Scrolling" : "Enable Auto-Scrolling", 0,
-        message_menu_scroll_cb, g },
-      { "Clear Messages",   0, message_menu_clear_cb, g },
-      { "Save Messages...", 0, message_menu_save_cb, g },
-      //{ "Increase font size", 0, message_menu_increase_font_cb, g },
-      //{ "Decrease font size", 0, message_menu_decrease_font_cb, g },
-      { 0 }
-    };
-    const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
-    if(m) m->do_callback(0, m->user_data());
-  }
-  else
-    g->copySelectedMessagesToClipboard();
+  g->getMessageBrowser()->clear();
+  for(int i = 0; i < g->getMessages().size(); i++)
+    g->getMessageBrowser()->add(g->getMessages()[i].c_str());
 }
 
 static void tile_cb(Fl_Widget *w, void *data)
@@ -2895,7 +2877,7 @@ graphicWindow::graphicWindow(bool main, int numTiles, bool detachedMenu)
   int sh = 2 * FL_NORMAL_SIZE - 3; // status bar height
   int sw = FL_NORMAL_SIZE + 2; // status button width
 
-  int mheight = main ? 10 /* dummy, nonzero! */ : 0;
+  int mheight = main ? 2 * BH /* nonzero! */ : 0;
   int glheight = CTX::instance()->glSize[1] - mheight;
   int height = mh + glheight + mheight + sh;
   // make sure height < screen height
@@ -2950,6 +2932,85 @@ graphicWindow::graphicWindow(bool main, int numTiles, bool detachedMenu)
 #endif
   }
 
+  // tiled windows (tree menu, opengl, messages)
+  _tile = new Fl_Tile(0, mh, glwidth + twidth, glheight + mheight);
+
+  int w2 = glwidth / 2, h2 = glheight / 2;
+  if(numTiles == 2){
+    gl.push_back(new openglWindow(twidth, mh, w2, glheight));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, glheight));
+    gl.back()->end();
+  }
+  else if(numTiles == 3){
+    gl.push_back(new openglWindow(twidth, mh, w2, glheight));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
+    gl.back()->end();
+  }
+  else if(numTiles == 4){
+    gl.push_back(new openglWindow(twidth, mh, w2, h2));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth, mh + h2, w2, glheight - h2));
+    gl.back()->end();
+    gl.push_back(new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
+    gl.back()->end();
+  }
+  else{
+    gl.push_back(new openglWindow(twidth, mh, glwidth, glheight));
+    gl.back()->end();
+  }
+
+  int mode = FL_RGB | FL_DEPTH | (CTX::instance()->db ? FL_DOUBLE : FL_SINGLE);
+  if(CTX::instance()->antialiasing) mode |= FL_MULTISAMPLE;
+  if(CTX::instance()->stereo) {
+    mode |= FL_DOUBLE;
+    mode |= FL_STEREO;
+  }
+  for(unsigned int i = 0; i < gl.size(); i++) gl[i]->mode(mode);
+
+  if(main){
+    _browser = new messageBrowser(twidth, mh + glheight, glwidth, mheight);
+    int s = CTX::instance()->msgFontSize;
+#if defined(WIN32) // screen font on Windows is really small
+    _browser->textsize(s <= 0 ? FL_NORMAL_SIZE : s);
+#else
+    _browser->textsize(s <= 0 ? FL_NORMAL_SIZE - 2 : s);
+#endif
+    _browser->callback(message_browser_cb, this);
+    _browser->search_callback(message_menu_search_cb, this);
+    _browser->autoscroll_callback(message_menu_autoscroll_cb, this);
+    _browser->save_callback(message_menu_save_cb, this);
+    _browser->clear_callback(message_menu_clear_cb, this);
+  }
+  else{
+    _browser = 0;
+  }
+
+  if(main && !detachedMenu){
+    _onelab = new onelabGroup(0, mh, twidth, height - mh - sh);
+    _onelab->enableTreeWidgetResize(false);
+  }
+  else{
+    _onelab = 0;
+  }
+
+  _tile->callback(tile_cb);
+  _tile->end();
+
+  // resize the tiles to match the prescribed sizes
+  _tile->position(0, mh + glheight, 0, mh + CTX::instance()->glSize[1]);
+
+  // if the tree widget is too small it will not be rebuilt correctly (probably
+  // a bug)... so impose minimum width
+  int minw = 3 * BB/2 + 4 * WB;
+  if(CTX::instance()->menuSize[0] < minw) CTX::instance()->menuSize[0] = minw;
+  _tile->position(twidth, 0, CTX::instance()->menuSize[0], 0);
+
   // bottom button bar
   _bottom = new Fl_Box(0, mh + glheight + mheight, width, sh);
   _bottom->box(GMSH_SIMPLE_TOP_BOX);
@@ -3033,86 +3094,6 @@ graphicWindow::graphicWindow(bool main, int numTiles, bool detachedMenu)
   _minWidth = x;
   _minHeight = 100;
   _win->size_range(_minWidth, _minHeight);
-
-  // tiled windows (tree menu, opengl, messages)
-  _tile = new Fl_Tile(0, mh, glwidth + twidth, glheight + mheight);
-
-  int w2 = glwidth / 2, h2 = glheight / 2;
-  if(numTiles == 2){
-    gl.push_back(new openglWindow(twidth, mh, w2, glheight));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, glheight));
-    gl.back()->end();
-  }
-  else if(numTiles == 3){
-    gl.push_back(new openglWindow(twidth, mh, w2, glheight));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
-    gl.back()->end();
-  }
-  else if(numTiles == 4){
-    gl.push_back(new openglWindow(twidth, mh, w2, h2));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth, mh + h2, w2, glheight - h2));
-    gl.back()->end();
-    gl.push_back(new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
-    gl.back()->end();
-  }
-  else{
-    gl.push_back(new openglWindow(twidth, mh, glwidth, glheight));
-    gl.back()->end();
-  }
-
-  int mode = FL_RGB | FL_DEPTH | (CTX::instance()->db ? FL_DOUBLE : FL_SINGLE);
-  if(CTX::instance()->antialiasing) mode |= FL_MULTISAMPLE;
-  if(CTX::instance()->stereo) {
-    mode |= FL_DOUBLE;
-    mode |= FL_STEREO;
-  }
-  for(unsigned int i = 0; i < gl.size(); i++) gl[i]->mode(mode);
-
-  if(main){
-    _browser = new Fl_Browser(twidth, mh + glheight, glwidth, mheight);
-    _browser->box(GMSH_SIMPLE_TOP_BOX);
-    _browser->textfont(FL_SCREEN);
-    int s = CTX::instance()->msgFontSize;
-#if defined(WIN32) // screen font on Windows is really small
-    _browser->textsize(s <= 0 ? FL_NORMAL_SIZE : s);
-#else
-    _browser->textsize(s <= 0 ? FL_NORMAL_SIZE - 2 : s);
-#endif
-    _browser->type(FL_MULTI_BROWSER);
-    _browser->callback(message_browser_cb, this);
-    _browser->scrollbar_size(std::max(10, FL_NORMAL_SIZE - 2)); // thinner scrollbars
-  }
-  else{
-    _browser = 0;
-  }
-
-  if(main && !detachedMenu){
-    _onelab = new onelabGroup(0, mh, twidth, height - mh - sh);
-    _onelab->enableTreeWidgetResize(false);
-  }
-  else{
-    _onelab = 0;
-  }
-
-  _tile->callback(tile_cb);
-  _tile->end();
-
-  // resize the tiles to match the prescribed sizes
-  _tile->position(0, mh + glheight, 0, mh + CTX::instance()->glSize[1]);
-
-  // if the tree widget is too small it will not be rebuilt correctly (probably
-  // a bug)... so impose minimum width
-  int minw = 3 * BB/2 + 4 * WB;
-  if(CTX::instance()->menuSize[0] < minw) CTX::instance()->menuSize[0] = minw;
-  _tile->position(twidth, 0, CTX::instance()->menuSize[0], 0);
-
   _win->position(CTX::instance()->glPosition[0], CTX::instance()->glPosition[1]);
   _win->end();
 
@@ -3482,13 +3463,15 @@ int graphicWindow::getMessageHeight()
 void graphicWindow::addMessage(const char *msg)
 {
   if(!_browser) return;
-  _browser->add(msg, 0);
+  _messages.push_back(msg);
+  _browser->add(msg);
   if(_autoScrollMessages && _win->shown() && _browser->h() >= FL_NORMAL_SIZE)
     _browser->bottomline(_browser->size());
 }
 
 void graphicWindow::clearMessages()
 {
+  _messages.clear();
   if(!_browser) return;
   _browser->clear();
 }
diff --git a/Fltk/graphicWindow.h b/Fltk/graphicWindow.h
index 5a6559396c..e0c415ac9f 100644
--- a/Fltk/graphicWindow.h
+++ b/Fltk/graphicWindow.h
@@ -18,8 +18,10 @@
 #include <FL/Fl_Sys_Menu_Bar.H>
 #endif
 #include <FL/Fl_Menu_Bar.H>
-#include "openglWindow.h"
-#include "onelabGroup.h"
+
+class openglWindow;
+class onelabGroup;
+class messageBrowser;
 
 class graphicWindow{
  private:
@@ -30,13 +32,14 @@ class graphicWindow{
   Fl_Menu_Bar *_bar;
   Fl_Tile *_tile;
   Fl_Window *_win, *_menuwin;
-  Fl_Browser *_browser;
+  messageBrowser *_browser;
   onelabGroup *_onelab;
   Fl_Box *_bottom;
   Fl_Button *_butt[14];
   Fl_Progress *_label;
   int _minWidth, _minHeight;
- public:
+  std::vector<std::string> _messages;
+public:
   std::vector<openglWindow*> gl;
  public:
   graphicWindow(bool main=true, int numTiles=1, bool detachedMenu=false);
@@ -46,6 +49,8 @@ class graphicWindow{
   onelabGroup *getMenu(){ return _onelab; }
   Fl_Progress *getProgress(){ return _label; }
   Fl_Button *getSelectionButton(){ return _butt[9]; }
+  messageBrowser *getMessageBrowser(){ return _browser; }
+  std::vector<std::string> &getMessages(){ return _messages; }
   int getMinWidth(){ return _minWidth; }
   int getMinHeight(){ return _minHeight; }
   void setAutoScroll(bool val){ _autoScrollMessages = val; }
diff --git a/Fltk/manipWindow.cpp b/Fltk/manipWindow.cpp
index aa3f7dac57..8ecff4bc7c 100644
--- a/Fltk/manipWindow.cpp
+++ b/Fltk/manipWindow.cpp
@@ -9,6 +9,7 @@
 #include "manipWindow.h"
 #include "paletteWindow.h"
 #include "graphicWindow.h"
+#include "openglWindow.h"
 #include "Options.h"
 #include "Context.h"
 
diff --git a/Fltk/messageBrowser.h b/Fltk/messageBrowser.h
new file mode 100644
index 0000000000..065c48829c
--- /dev/null
+++ b/Fltk/messageBrowser.h
@@ -0,0 +1,98 @@
+// Gmsh - Copyright (C) 1997-2016 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to the public mailing list <gmsh@onelab.info>.
+
+#ifndef _MESSAGE_BROWSER_H_
+#define _MESSAGE_BROWSER_H_
+
+#include "FlGui.h"
+
+class messageBrowser : public Fl_Group{
+ private:
+  Fl_Browser *_browser;
+  Fl_Group *_box;
+  Fl_Check_Button *_autoscroll;
+  Fl_Button *_clear, *_save;
+  Fl_Input *_search;
+
+ public:
+  messageBrowser(int x, int y, int w, int h, const char *l=0)
+    : Fl_Group(x, y, w, h, l)
+  {
+    int bh = BH - 4; // button height
+    int wb = WB / 2; // border
+    int bb = BB - 3 * WB;
+    int sw = 3 * BB; // search field width
+
+    _box = new Fl_Group(x, y, w, bh + 2 * wb);
+    _box->box(GMSH_SIMPLE_TOP_BOX);
+
+    Fl_Group* o = new Fl_Group(x + wb, y + wb, sw, bh);
+    o->tooltip("Filter messages");
+    o->box(FL_THIN_DOWN_BOX);
+    o->color(FL_BACKGROUND2_COLOR);
+    _search = new Fl_Input(x + wb + bh, y + wb + 2, sw - bh - 2, bh - 4, "@-1gmsh_search");
+    _search->box(FL_FLAT_BOX);
+    _search->when(FL_WHEN_CHANGED);
+    _search->textsize(FL_NORMAL_SIZE - 1);
+    o->resizable(_search);
+    o->end();
+
+    _save = new Fl_Button(x + wb + sw + WB, y + wb, bb, bh, "Save");
+    _save->labelsize(FL_NORMAL_SIZE - 1);
+    _save->box(FL_THIN_UP_BOX);
+
+    _clear = new Fl_Button(x + sw + bb + 2 * WB, y + wb, bb, bh, "Clear");
+    _clear->labelsize(FL_NORMAL_SIZE - 1);
+    _clear->box(FL_THIN_UP_BOX);
+
+    _autoscroll = new Fl_Check_Button
+      (x + sw + 2 * bb + 3 * WB, y + wb, 2 * bb, bh, "Autoscroll messages");
+    _autoscroll->labelsize(FL_NORMAL_SIZE - 1);
+    _autoscroll->type(FL_TOGGLE_BUTTON);
+    _autoscroll->value(1);
+
+    _box->end();
+    _box->resizable(0);
+
+    _browser = new Fl_Browser(x, y + bh + 2 * wb, w, h - bh - 2 * wb, l);
+    _browser->box(GMSH_SIMPLE_TOP_BOX);
+    _browser->textfont(FL_SCREEN);
+    _browser->type(FL_MULTI_BROWSER);
+    _browser->scrollbar_size(std::max(10, FL_NORMAL_SIZE - 2)); // thinner scrollbars
+    _browser->end();
+    end();
+    resizable(_browser);
+  }
+  void box(Fl_Boxtype new_box) { _browser->box(new_box);}
+  void textfont(Fl_Font font) { _browser->textfont(font); }
+  void textsize(Fl_Fontsize newSize){ _browser->textsize(newSize); }
+  Fl_Fontsize textsize() const { return _browser->textsize(); }
+  void callback(Fl_Callback* cb, void* p) { _browser->callback(cb, p);}
+  void search_callback(Fl_Callback* cb, void* p) { _search->callback(cb, p);}
+  void autoscroll_callback(Fl_Callback* cb, void* p) { _autoscroll->callback(cb, p);}
+  void save_callback(Fl_Callback* cb, void* p) { _save->callback(cb, p);}
+  void clear_callback(Fl_Callback* cb, void* p) { _clear->callback(cb, p);}
+  void bottomline(int line) { _browser->bottomline(line); }
+  int size(){ return _browser->size(); }
+  void add(const char* newtext)
+  {
+    std::string search = _search->value();
+    if(search.empty()){
+      _browser->add(newtext);
+    }
+    else{
+      std::transform(search.begin(), search.end(), search.begin(), ::tolower);
+      std::string tmp(newtext);
+      std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
+      if(tmp.find(search) != std::string::npos)
+        _browser->add(newtext);
+    }
+  }
+  void clear(){ _browser->clear(); }
+  const char* text(int line) const { return _browser->text(line); }
+  int selected(int line) const { return _browser->selected(line); }
+};
+
+#endif
diff --git a/Fltk/visibilityWindow.cpp b/Fltk/visibilityWindow.cpp
index 334dad2a64..4095b501c6 100644
--- a/Fltk/visibilityWindow.cpp
+++ b/Fltk/visibilityWindow.cpp
@@ -24,6 +24,7 @@ typedef unsigned long intptr_t;
 #include "paletteWindow.h"
 #include "contextWindow.h"
 #include "graphicWindow.h"
+#include "openglWindow.h"
 #include "GmshDefines.h"
 #include "GmshMessage.h"
 #include "GModel.h"
-- 
GitLab