1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 
6 #include "GmshConfig.h"
7 #include <sstream>
8 #include <string.h>
9 #include <FL/Fl.H>
10 #include <FL/Fl_Tooltip.H>
11 #include <FL/Fl_Shared_Image.H>
12 #include <FL/Fl_File_Icon.H>
13 #include <FL/fl_draw.H>
14 #include <FL/fl_ask.H>
15 #include "FlGui.h"
16 #include "drawContextFltk.h"
17 #include "drawContextFltkCairo.h"
18 #include "graphicWindow.h"
19 #include "optionWindow.h"
20 #include "fieldWindow.h"
21 #include "pluginWindow.h"
22 #include "statisticsWindow.h"
23 #include "visibilityWindow.h"
24 #include "highOrderToolsWindow.h"
25 #include "clippingWindow.h"
26 #include "manipWindow.h"
27 #include "contextWindow.h"
28 #include "onelabContextWindow.h"
29 #include "onelabGroup.h"
30 #include "helpWindow.h"
31 #include "colorbarWindow.h"
32 #include "fileDialogs.h"
33 #include "GmshDefines.h"
34 #include "GmshMessage.h"
35 #include "GModel.h"
36 #include "OS.h"
37 #include "MElement.h"
38 #include "PView.h"
39 #include "Field.h"
40 #include "Plugin.h"
41 #include "PluginManager.h"
42 #include "OpenFile.h"
43 #include "XpmIcon.h"
44 #include "Options.h"
45 #include "CommandLine.h"
46 #include "Context.h"
47 #include "StringUtils.h"
48 #include "gl2ps.h"
49 #include "gmshPopplerWrapper.h"
50 #include "PixelBuffer.h"
51 #if defined(HAVE_TOUCHBAR)
52 #include "touchBar.h"
53 #endif
54 #if defined(HAVE_3M)
55 #include "3M.h"
56 #endif
57 
58 FlGui *FlGui::_instance = nullptr;
59 std::string FlGui::_openedThroughMacFinder = "";
60 bool FlGui::_finishedProcessingCommandLine = false;
61 std::atomic<int> FlGui::_locked(0);
62 
63 // check (now!) if there are any pending events, and process them
check(bool rateLimited)64 void FlGui::check(bool rateLimited)
65 {
66   if(Msg::GetThreadNum() > 0 || _locked > 0) return;
67 
68   static double lastRefresh = 0.;
69   double start = TimeOfDay();
70   if(rateLimited && CTX::instance()->guiRefreshRate > 0) {
71     if(start - lastRefresh > 1. / CTX::instance()->guiRefreshRate) {
72       lastRefresh = start;
73       Fl::check();
74     }
75   }
76   else {
77     lastRefresh = start;
78     Fl::check();
79   }
80 }
81 
82 // wait (possibly indefinitely) for any events, then process them
wait(bool force)83 void FlGui::wait(bool force)
84 {
85   if((Msg::GetThreadNum() > 0 || _locked > 0) && !force) return;
86   Fl::wait();
87 }
88 
89 // wait (at most time seconds) for any events, then process them
wait(double time,bool force)90 void FlGui::wait(double time, bool force)
91 {
92   if((Msg::GetThreadNum() > 0 || _locked > 0) && !force) return;
93   Fl::wait(time);
94 }
95 
lock()96 void FlGui::lock()
97 {
98   _locked++;
99   Fl::lock();
100 }
101 
unlock()102 void FlGui::unlock()
103 {
104   _locked--;
105   Fl::unlock();
106 }
107 
locked()108 int FlGui::locked() { return _locked; }
109 
awake_cb(void * data)110 static void awake_cb(void *data)
111 {
112   if(data) FlGui::instance()->updateViews(true, false);
113 }
114 
awake(const std::string & action)115 void FlGui::awake(const std::string &action)
116 {
117   if(action.empty())
118     Fl::awake(awake_cb, nullptr);
119   else
120     Fl::awake(awake_cb, (void *)"update");
121 }
122 
setOpenedThroughMacFinder(const std::string & name)123 void FlGui::setOpenedThroughMacFinder(const std::string &name)
124 {
125   _openedThroughMacFinder = name;
126 }
127 
getOpenedThroughMacFinder()128 std::string FlGui::getOpenedThroughMacFinder()
129 {
130   return _openedThroughMacFinder;
131 }
132 
setFinishedProcessingCommandLine()133 void FlGui::setFinishedProcessingCommandLine()
134 {
135   _finishedProcessingCommandLine = true;
136 }
137 
getFinishedProcessingCommandLine()138 bool FlGui::getFinishedProcessingCommandLine()
139 {
140   return _finishedProcessingCommandLine;
141 }
142 
globalShortcut(int event)143 static int globalShortcut(int event)
144 {
145   if(!FlGui::available()) return 0;
146   return FlGui::instance()->testGlobalShortcuts(event);
147 }
148 
simple_right_box_draw(int x,int y,int w,int h,Fl_Color c)149 static void simple_right_box_draw(int x, int y, int w, int h, Fl_Color c)
150 {
151   fl_color(c);
152   fl_rectf(x, y, w, h);
153   fl_color(FL_DARK2);
154   fl_line(x + w - 1, y, x + w - 1, y + h);
155 }
156 
simple_top_box_draw(int x,int y,int w,int h,Fl_Color c)157 static void simple_top_box_draw(int x, int y, int w, int h, Fl_Color c)
158 {
159   fl_color(c);
160   fl_rectf(x, y, w, h);
161   fl_color(FL_DARK2);
162   fl_line(x, y, x + w, y);
163 }
164 
165 // Icons for the satus bar
166 #define vv(x, y) fl_vertex(x, y)
167 #define bl fl_begin_loop()
168 #define el fl_end_loop()
169 
gmsh_play(Fl_Color c)170 static void gmsh_play(Fl_Color c)
171 {
172   fl_color(c);
173   bl;
174   vv(-0.3, 0.8);
175   vv(0.5, 0.0);
176   vv(-0.3, -0.8);
177   el;
178 }
179 
gmsh_pause(Fl_Color c)180 static void gmsh_pause(Fl_Color c)
181 {
182   fl_color(c);
183   bl;
184   vv(-0.8, -0.8);
185   vv(-0.3, -0.8);
186   vv(-0.3, 0.8);
187   vv(-0.8, 0.8);
188   el;
189   bl;
190   vv(0.0, -0.8);
191   vv(0.5, -0.8);
192   vv(0.5, 0.8);
193   vv(0.0, 0.8);
194   el;
195 }
196 
gmsh_rewind(Fl_Color c)197 static void gmsh_rewind(Fl_Color c)
198 {
199   fl_color(c);
200   bl;
201   vv(-0.8, -0.8);
202   vv(-0.3, -0.8);
203   vv(-0.3, 0.8);
204   vv(-0.8, 0.8);
205   el;
206   bl;
207   vv(-0.3, 0.0);
208   vv(0.5, -0.8);
209   vv(0.5, 0.8);
210   el;
211 }
212 
gmsh_forward(Fl_Color c)213 static void gmsh_forward(Fl_Color c)
214 {
215   fl_color(c);
216   bl;
217   vv(0.0, 0.8);
218   vv(0.8, 0.0);
219   vv(0.0, -0.8);
220   el;
221   bl;
222   vv(-0.8, 0.8);
223   vv(-0.3, 0.8);
224   vv(-0.3, -0.8);
225   vv(-0.8, -0.8);
226   el;
227 }
228 
gmsh_back(Fl_Color c)229 static void gmsh_back(Fl_Color c)
230 {
231   fl_rotate(180);
232   gmsh_forward(c);
233 }
234 
gmsh_rotate(Fl_Color c)235 static void gmsh_rotate(Fl_Color c)
236 {
237   fl_color(c);
238   fl_begin_line();
239   fl_arc(0.0, -0.1, 0.7, 0.0, 270.0);
240   fl_end_line();
241   fl_begin_polygon();
242   vv(0.5, 0.6);
243   vv(-0.1, 0.9);
244   vv(-0.1, 0.3);
245   fl_end_polygon();
246 }
247 
gmsh_models(Fl_Color c)248 static void gmsh_models(Fl_Color c)
249 {
250   fl_color(c);
251   bl;
252   vv(-0.8, -0.7);
253   vv(0.8, -0.7);
254   el;
255   bl;
256   vv(-0.8, -0.2);
257   vv(0.8, -0.2);
258   el;
259   bl;
260   vv(-0.8, 0.3);
261   vv(0.8, 0.3);
262   el;
263   bl;
264   vv(-0.8, 0.8);
265   vv(0.8, 0.8);
266   el;
267 }
268 
gmsh_gear(Fl_Color c)269 static void gmsh_gear(Fl_Color c)
270 {
271   fl_color(c);
272   double w = 0.12;
273   double h1 = 0.5;
274 #if defined(WIN32)
275   double h2 = 1.0;
276 #else
277   double h2 = 1.05;
278 #endif
279   fl_line_style(FL_SOLID, 3);
280   fl_begin_line();
281   fl_circle(0, 0, 0.5);
282   fl_end_line();
283   fl_line_style(FL_SOLID);
284   for(int i = 0; i < 8; i++) {
285     fl_rotate(45);
286     fl_begin_polygon();
287     fl_vertex(h1, -w);
288     fl_vertex(h2, -w);
289     fl_vertex(h2, w);
290     fl_vertex(h1, w);
291     fl_end_polygon();
292   }
293 }
294 
gmsh_graph(Fl_Color c)295 static void gmsh_graph(Fl_Color c)
296 {
297   fl_color(c);
298   fl_begin_line();
299   vv(-0.8, -0.8);
300   vv(-0.8, 0.8);
301   vv(0.8, 0.8);
302   fl_end_line();
303   fl_begin_line();
304   vv(-0.8, 0.3);
305   vv(-0.2, -0.2);
306   vv(0.3, 0.1);
307   vv(0.8, -0.4);
308   fl_end_line();
309 }
310 
gmsh_search(Fl_Color col)311 static void gmsh_search(Fl_Color col)
312 {
313   double e = 0.5;
314   fl_color(col);
315   fl_begin_polygon();
316   vv(.6 - e, .33);
317   vv(1.2 - e, .93);
318   vv(.93 - e, 1.2);
319   vv(.33 - e, .6);
320   fl_end_polygon();
321   fl_line_style(FL_SOLID, 2);
322   fl_begin_loop();
323   fl_circle(0 - e, 0, .6);
324   fl_end_loop();
325   fl_line_style(FL_SOLID);
326 }
327 
gmsh_colormap(Fl_Color col)328 static void gmsh_colormap(Fl_Color col)
329 {
330   fl_color(FL_RED);
331   fl_begin_polygon();
332   vv(-0.8, -0.8);
333   vv(-0.3, -0.8);
334   vv(-0.3, 0.8);
335   vv(-0.8, 0.8);
336   fl_end_polygon();
337   fl_color(FL_GREEN);
338   fl_begin_polygon();
339   vv(-0.3, -0.8);
340   vv(0.2, -0.8);
341   vv(0.2, 0.8);
342   vv(-0.3, 0.8);
343   fl_end_polygon();
344   fl_color(FL_BLUE);
345   fl_begin_polygon();
346   vv(0.2, -0.8);
347   vv(0.7, -0.8);
348   vv(0.7, 0.8);
349   vv(0.2, 0.8);
350   fl_end_polygon();
351 }
352 
353 #undef vv
354 #undef bl
355 #undef el
356 
357 // question presence of gamepad every 3. seconds
gamepad_handler(void * data)358 static void gamepad_handler(void *data)
359 {
360   if(CTX::instance()->gamepad && CTX::instance()->gamepad->active) {
361     CTX::instance()->gamepad->read_event();
362     Fl::add_timeout(CTX::instance()->gamepad->frequency, gamepad_handler, data);
363   }
364   else {
365     Fl::add_timeout(.5, gamepad_handler, data);
366   }
367 }
368 
applyColorScheme(bool redraw)369 void FlGui::applyColorScheme(bool redraw)
370 {
371   static int first = true;
372   int N = 4 + FL_NUM_GRAY;
373   static std::vector<unsigned char> r(N, 0), g(N, 0), b(N, 0);
374 
375   if(first) {
376     // store default (OS-dependent) interface colors:
377     Fl::get_system_colors();
378     Fl::get_color(FL_BACKGROUND_COLOR, r[0], g[0], b[0]);
379     Fl::get_color(FL_BACKGROUND2_COLOR, r[1], g[1], b[1]);
380     Fl::get_color(FL_FOREGROUND_COLOR, r[2], g[2], b[2]);
381     Fl::get_color(FL_SELECTION_COLOR, r[3], g[3], b[3]);
382     for(int i = 0; i < FL_NUM_GRAY; i++) {
383       Fl::get_color(fl_gray_ramp(i), r[4 + i], g[4 + i], b[4 + i]);
384     }
385   }
386 
387   if(CTX::instance()->guiColorScheme == 1) { // dark mode
388     Fl::set_color(FL_BACKGROUND_COLOR, 20, 20, 20);
389     Fl::set_color(FL_BACKGROUND2_COLOR, 70, 70, 70);
390     Fl::set_color(FL_FOREGROUND_COLOR, 220, 220, 220);
391     for(int i = 0; i < FL_NUM_GRAY; i++) {
392       double min = 0., max = 70.;
393       int d = (int)(min + i * (max - min) / (FL_NUM_GRAY - 1.));
394       Fl::set_color(fl_gray_ramp(i), d, d, d);
395     }
396     Fl::set_color(FL_SELECTION_COLOR, 200, 200, 200);
397   }
398   else if(!first && available() && CTX::instance()->guiColorScheme == 0) {
399     // retore default colors (only if not calling the routine from the
400     // constructor)
401     Fl::set_color(FL_BACKGROUND_COLOR, r[0], g[0], b[0]);
402     Fl::set_color(FL_BACKGROUND2_COLOR, r[1], g[1], b[1]);
403     Fl::set_color(FL_FOREGROUND_COLOR, r[2], g[2], b[2]);
404     for(int i = 0; i < FL_NUM_GRAY; i++) {
405       Fl::set_color(fl_gray_ramp(i), r[4 + i], g[4 + i], b[4 + i]);
406     }
407     Fl::set_color(FL_SELECTION_COLOR, r[3], g[3], b[3]);
408   }
409 
410   first = false;
411 
412   // also change default box type here (to thin versions)
413   Fl::set_boxtype(FL_UP_BOX, FL_THIN_UP_BOX);
414   Fl::set_boxtype(FL_DOWN_BOX, FL_THIN_DOWN_BOX);
415   Fl::set_boxtype(FL_UP_FRAME, FL_THIN_UP_FRAME);
416   Fl::set_boxtype(FL_DOWN_FRAME, FL_THIN_DOWN_FRAME);
417 
418   // thinner scrollbars
419   Fl::scrollbar_size(std::max(10, FL_NORMAL_SIZE));
420 
421   if(redraw && available()) {
422     updateViews(true, true);
423     for(Fl_Window *win = Fl::first_window(); win; win = Fl::next_window(win)) {
424       win->redraw();
425     }
426   }
427 }
428 
default_error_handler(const char * fmt,...)429 static void default_error_handler(const char *fmt, ...)
430 {
431   char str[5000];
432   va_list args;
433   va_start(args, fmt);
434   vsnprintf(str, sizeof(str), fmt, args);
435   va_end(args);
436   if(!strcmp(str, "Insufficient GL support")) { // this should be fatal
437     CTX::instance()->terminal = 1;
438     Msg::Error("%s (FLTK internal error)", str);
439     Msg::Error("Your system does not seem to support OpenGL - aborting");
440     Msg::Exit(1);
441   }
442   else {
443     Msg::Error("%s (FLTK internal error)", str);
444   }
445 }
446 
default_fatal_error_handler(const char * fmt,...)447 static void default_fatal_error_handler(const char *fmt, ...)
448 {
449   char str[5000];
450   va_list args;
451   va_start(args, fmt);
452   vsnprintf(str, sizeof(str), fmt, args);
453   va_end(args);
454   Msg::Error("%s (FLTK internal error)", str);
455   Msg::Exit(1);
456 }
457 
FlGui(int argc,char ** argv,bool quitShouldExit,void (* error_handler)(const char * fmt,...))458 FlGui::FlGui(int argc, char **argv, bool quitShouldExit,
459              void (*error_handler)(const char *fmt, ...))
460   : _quitShouldExit(quitShouldExit), lastContextWindow(0)
461 {
462   if(error_handler) {
463     Fl::error = error_handler;
464     Fl::fatal = error_handler;
465   }
466   else {
467     Fl::error = default_error_handler;
468     Fl::fatal = default_fatal_error_handler;
469   }
470 
471 #if defined(__APPLE__)
472   // the defaults use %@, which leads to (lowercase) gmsh
473   Fl_Mac_App_Menu::about = "About Gmsh";
474   Fl_Mac_App_Menu::hide = "Hide Gmsh";
475   Fl_Mac_App_Menu::quit = "Quit Gmsh";
476   Fl_Mac_App_Menu::print = ""; // this sometimes crashes
477 #endif
478 
479   // tell fltk we're (potentially) in multi-threaded mode
480   Fl::lock();
481 
482   // set X display
483   if(CTX::instance()->display.size())
484     Fl::display(CTX::instance()->display.c_str());
485 
486   // add new box types (dx dy dw dh)
487   Fl::set_boxtype(GMSH_SIMPLE_RIGHT_BOX, simple_right_box_draw, 0, 0, 1, 0);
488   Fl::set_boxtype(GMSH_SIMPLE_TOP_BOX, simple_top_box_draw, 0, 1, 0, 1);
489 
490   // apply color scheme before widget creation (noop if default color scheme is
491   // selected), so that there's no color "flashing"
492   applyColorScheme();
493 
494   // add gamepad handler
495   if(CTX::instance()->gamepad)
496     Fl::add_timeout(5., gamepad_handler, (void *)nullptr);
497 
498   // add global shortcuts
499   Fl::add_handler(globalShortcut);
500 
501   // make sure a global drawing context is setup
502   if(!drawContext::global()) drawContext::setGlobal(new drawContextFltk);
503 
504   // set default font size
505   FL_NORMAL_SIZE = drawContext::global()->getFontSize();
506 
507   // handle themes and tooltip options
508   if(CTX::instance()->guiTheme.size())
509     Fl::scheme(CTX::instance()->guiTheme.c_str());
510   Fl_Tooltip::size(FL_NORMAL_SIZE);
511 
512   // use retina resolution if available
513 #if(FL_MAJOR_VERSION == 1) && (FL_MINOR_VERSION == 3) && (FL_PATCH_VERSION >= 4)
514   Fl::use_high_res_GL(CTX::instance()->highResolutionGraphics);
515 #elif(FL_MAJOR_VERSION == 1) && (FL_MINOR_VERSION >= 4)
516   Fl::use_high_res_GL(CTX::instance()->highResolutionGraphics);
517 #endif
518 
519   // register image formats not in core fltk library (jpeg/png)
520   fl_register_images();
521 
522   // add our own icons
523   fl_add_symbol("gmsh_rewind", gmsh_rewind, 1);
524   fl_add_symbol("gmsh_back", gmsh_back, 1);
525   fl_add_symbol("gmsh_play", gmsh_play, 1);
526   fl_add_symbol("gmsh_pause", gmsh_pause, 1);
527   fl_add_symbol("gmsh_forward", gmsh_forward, 1);
528   fl_add_symbol("gmsh_rotate", gmsh_rotate, 1);
529   fl_add_symbol("gmsh_models", gmsh_models, 1);
530   fl_add_symbol("gmsh_gear", gmsh_gear, 1);
531   fl_add_symbol("gmsh_graph", gmsh_graph, 1);
532   fl_add_symbol("gmsh_search", gmsh_search, 1);
533   fl_add_symbol("gmsh_colormap", gmsh_colormap, 1);
534 
535   // load default system icons (for file browser)
536   Fl_File_Icon::load_system_icons();
537 
538   // FLTK >= 1.3.3 allows one to set a default global window icon
539   Fl_RGB_Image icon(&gmsh_icon_pixmap);
540   Fl_Window::default_icon(&icon);
541 
542   // add callback to respond to Mac Finder
543 #if defined(__APPLE__)
544   fl_open_callback(OpenProjectMacFinder);
545   fl_mac_set_about(help_about_cb, nullptr);
546 #endif
547 
548   // don't move input dialogs to follow mouse
549   fl_message_hotspot(0);
550 
551   // create main graphic window (note that we create all the windows even if
552   // some are not displayed, since the shortcuts should be valid even for hidden
553   // windows, and we don't want to test for widget existence every time)
554   graph.push_back(
555     new graphicWindow(true, CTX::instance()->numTiles,
556                       CTX::instance()->detachedMenu ? true : false));
557 
558   graph[0]->getWindow()->show(argc > 0 ? 1 : 0, argv);
559   if(graph[0]->getMenuWindow()) graph[0]->getMenuWindow()->show();
560 
561   // re-apply color scheme (necessary after open_display to get the selection
562   // color and boxtypes right)
563   applyColorScheme();
564 
565   // graphic window should have the initial focus (so we can e.g. directly loop
566   // through time steps with the keyboard)
567   Fl::focus(graph[0]->gl[0]);
568 
569   // get onelab tree group (FIXME: should clean this up)
570   onelab = graph.back()->getMenu();
571 
572   // create additional graphic windows
573   for(int i = 1; i < CTX::instance()->numWindows; i++) {
574     graphicWindow *g = new graphicWindow(false, CTX::instance()->numTiles);
575     g->getWindow()->resize(
576       graph.back()->getWindow()->x() + 10, graph.back()->getWindow()->y() + 10,
577       graph.back()->getWindow()->w(), graph.back()->getWindow()->h());
578     g->getWindow()->show();
579     graph.push_back(g);
580   }
581   setGraphicTitle(GModel::current()->getFileName());
582 
583   // create window that will be used for fullscreen display
584   fullscreen = new openglWindow(100, 100, 100, 100);
585   int mode = FL_RGB | FL_DEPTH | (CTX::instance()->db ? FL_DOUBLE : FL_SINGLE);
586   if(CTX::instance()->antialiasing) mode |= FL_MULTISAMPLE;
587   if(CTX::instance()->stereo) {
588     mode |= FL_DOUBLE;
589     mode |= FL_STEREO;
590   }
591   fullscreen->mode(mode);
592   fullscreen->end();
593 
594   // create all other windows
595   options = new optionWindow(CTX::instance()->deltaFontSize);
596   fields = new fieldWindow(CTX::instance()->deltaFontSize);
597   plugins = new pluginWindow(CTX::instance()->deltaFontSize);
598   stats = new statisticsWindow(CTX::instance()->deltaFontSize);
599   visibility = new visibilityWindow(CTX::instance()->deltaFontSize);
600   highordertools = new highOrderToolsWindow(CTX::instance()->deltaFontSize);
601   clipping = new clippingWindow(CTX::instance()->deltaFontSize);
602   manip = new manipWindow(CTX::instance()->deltaFontSize);
603   elementaryContext =
604     new elementaryContextWindow(CTX::instance()->deltaFontSize);
605   transformContext = new transformContextWindow(CTX::instance()->deltaFontSize);
606   meshContext = new meshContextWindow(CTX::instance()->deltaFontSize);
607   physicalContext = new physicalContextWindow(CTX::instance()->deltaFontSize);
608   onelabContext = new onelabContextWindow(CTX::instance()->deltaFontSize);
609   help = new helpWindow();
610 
611   // draw
612   for(std::size_t i = 0; i < graph.size(); i++)
613     for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
614       graph[i]->gl[j]->redraw();
615 
616   if(CTX::instance()->showOptionsOnStartup) options->win->show();
617   if(CTX::instance()->showMessagesOnStartup) graph[0]->showMessages();
618 
619 #if defined(HAVE_TOUCHBAR)
620   showTouchBar();
621 #endif
622 }
623 
~FlGui()624 FlGui::~FlGui()
625 {
626   for(std::size_t i = 0; i < graph.size(); i++) delete graph[i];
627   delete options;
628   delete fields;
629   delete plugins;
630   delete stats;
631   delete visibility;
632   delete highordertools;
633   delete clipping;
634   delete manip;
635   delete elementaryContext;
636   delete transformContext;
637   delete physicalContext;
638   delete onelabContext;
639   delete meshContext;
640   delete help;
641   delete fullscreen;
642 }
643 
available()644 bool FlGui::available() { return _instance != nullptr; }
645 
instance(int argc,char ** argv,bool quitShouldExit,void (* error_handler)(const char * fmt,...))646 FlGui *FlGui::instance(int argc, char **argv, bool quitShouldExit,
647                        void (*error_handler)(const char *fmt, ...))
648 {
649   if(!_instance) {
650     _instance = new FlGui(argc, argv, quitShouldExit, error_handler);
651     // set all options in the new GUI
652     InitOptionsGUI(0);
653     // say welcome!
654     Msg::StatusBar(false, "Gmsh %s", GetGmshVersion());
655     // log the following for bug reports
656     Msg::Direct("-------------------------------------------------------");
657     PrintBuildInfo();
658     Msg::Direct("-------------------------------------------------------");
659     // update views (in case the GUI is created after some data has been loaded)
660     _instance->updateViews(true, true);
661     // set global bounding box in CTX (necessary if we run the gui without any
662     // model/post-processing data)
663     SetBoundingBox();
664   }
665   return _instance;
666 }
667 
destroy()668 void FlGui::destroy()
669 {
670   if(!_instance) return;
671   delete _instance;
672   _instance = nullptr;
673 }
674 
run()675 int FlGui::run()
676 {
677   // draw the scene
678   drawContext::global()->draw(false);
679 
680 #if defined(HAVE_TOUCHBAR)
681   updateTouchBar();
682 #endif
683 
684   return Fl::run();
685 }
686 
testGlobalShortcuts(int event)687 int FlGui::testGlobalShortcuts(int event)
688 {
689   // we only handle shortcuts here
690   if(event != FL_SHORTCUT) return 0;
691 
692   int status = 0;
693 
694   if(Fl::test_shortcut('0')) {
695     geometry_reload_cb(nullptr, nullptr);
696     status = 1;
697   }
698   if(Fl::test_shortcut(FL_CTRL + '0') || Fl::test_shortcut(FL_META + '0') ||
699      Fl::test_shortcut('9')) { // for Bruno
700     onelab_reload_cb(nullptr, nullptr);
701     status = 1;
702   }
703   else if(Fl::test_shortcut('1') || Fl::test_shortcut(FL_F + 1)) {
704     mesh_1d_cb(nullptr, nullptr);
705     status = 1;
706   }
707   else if(Fl::test_shortcut('2') || Fl::test_shortcut(FL_F + 2)) {
708     mesh_2d_cb(nullptr, nullptr);
709     status = 1;
710   }
711   else if(Fl::test_shortcut('3') || Fl::test_shortcut(FL_F + 3)) {
712     mesh_3d_cb(nullptr, nullptr);
713     status = 1;
714   }
715   else if(Fl::test_shortcut(FL_CTRL + 'q') ||
716           Fl::test_shortcut(FL_META + 'q')) {
717     // only necessary when using the system menu bar, but hey, it cannot hurt...
718     file_quit_cb(nullptr, nullptr);
719     status = 1;
720   }
721   else if(Fl::test_shortcut(FL_CTRL + 't') ||
722           Fl::test_shortcut(FL_META + 't')) {
723     show_hide_menu_cb(nullptr, nullptr);
724     status = 1;
725   }
726   else if(Fl::test_shortcut('g')) {
727     FlGui::instance()->openModule("Geometry");
728     status = 1;
729   }
730   else if(Fl::test_shortcut('m')) {
731     FlGui::instance()->openModule("Mesh");
732     status = 1;
733   }
734   else if(Fl::test_shortcut('s')) {
735     FlGui::instance()->openModule("Solver");
736     status = 1;
737   }
738   else if(Fl::test_shortcut('p')) {
739     FlGui::instance()->openModule("Post-processing");
740     status = 1;
741   }
742   else if(Fl::test_shortcut('w')) {
743     file_watch_cb(nullptr, nullptr);
744     status = 1;
745   }
746   else if(Fl::test_shortcut('e')) {
747     for(std::size_t i = 0; i < graph.size(); i++)
748       for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
749         graph[i]->gl[j]->endSelection = 1;
750     status = 0; // trick: do as if we didn't use it
751   }
752   else if(Fl::test_shortcut('u')) {
753     for(std::size_t i = 0; i < graph.size(); i++)
754       for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
755         graph[i]->gl[j]->undoSelection = 1;
756     status = 0; // trick: do as if we didn't use it
757   }
758   else if(Fl::test_shortcut('i')) {
759     for(std::size_t i = 0; i < graph.size(); i++)
760       for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
761         graph[i]->gl[j]->invertSelection = 1;
762     status = 0; // trick: do as if we didn't use it
763   }
764   else if(Fl::test_shortcut('q')) {
765     for(std::size_t i = 0; i < graph.size(); i++)
766       for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
767         graph[i]->gl[j]->quitSelection = 1;
768     status = 0; // trick: do as if we didn't use it
769   }
770   else if(Fl::test_shortcut('-')) {
771     for(std::size_t i = 0; i < graph.size(); i++)
772       for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
773         graph[i]->gl[j]->invertSelection = 1;
774     status = 0; // trick: do as if we didn't use it
775   }
776   else if(Fl::test_shortcut('x')) {
777     elementaryContext->butt[0]->value(!elementaryContext->butt[0]->value());
778     status = 1;
779   }
780   else if(Fl::test_shortcut('y')) {
781     elementaryContext->butt[1]->value(!elementaryContext->butt[1]->value());
782     status = 1;
783   }
784   else if(Fl::test_shortcut('z')) {
785     elementaryContext->butt[2]->value(!elementaryContext->butt[2]->value());
786     status = 1;
787   }
788   else if(Fl::test_shortcut(FL_SHIFT + 'x')) {
789     elementaryContext->butt[0]->value(0);
790     elementaryContext->butt[1]->value(1);
791     elementaryContext->butt[2]->value(1);
792     status = 1;
793   }
794   else if(Fl::test_shortcut(FL_SHIFT + 'y')) {
795     elementaryContext->butt[0]->value(1);
796     elementaryContext->butt[1]->value(0);
797     elementaryContext->butt[2]->value(1);
798     status = 1;
799   }
800   else if(Fl::test_shortcut(FL_SHIFT + 'z')) {
801     elementaryContext->butt[0]->value(1);
802     elementaryContext->butt[1]->value(1);
803     elementaryContext->butt[2]->value(0);
804     status = 1;
805   }
806   else if(Fl::test_shortcut(FL_Escape) ||
807           Fl::test_shortcut(FL_META + FL_Escape) ||
808           Fl::test_shortcut(FL_SHIFT + FL_Escape) ||
809           Fl::test_shortcut(FL_CTRL + FL_Escape) ||
810           Fl::test_shortcut(FL_ALT + FL_Escape)) {
811     if(fullscreen->shown()) {
812       window_cb(nullptr, (void *)"fullscreen");
813       status = 1;
814     }
815     else {
816       bool lasso = false;
817       for(std::size_t i = 0; i < graph.size(); i++)
818         for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
819           if(graph[i]->gl[j]->lassoMode) lasso = true;
820       if(lasso) {
821         for(std::size_t i = 0; i < graph.size(); i++)
822           for(std::size_t j = 0; j < graph[i]->gl.size(); j++)
823             graph[i]->gl[j]->lassoMode = false;
824         status = 2;
825       }
826       else {
827         status_options_cb(nullptr, (void *)"S");
828         status = 1;
829       }
830     }
831   }
832   else if(Fl::test_shortcut(FL_SHIFT + 'a')) {
833     window_cb(nullptr, (void *)"front");
834     status = 1;
835   }
836   else if(Fl::test_shortcut(FL_SHIFT + 'o')) {
837     general_options_cb(nullptr, nullptr);
838     status = 1;
839   }
840   else if(Fl::test_shortcut(FL_SHIFT + 'g')) {
841     geometry_options_cb(nullptr, nullptr);
842     status = 1;
843   }
844   else if(Fl::test_shortcut(FL_SHIFT + 'm')) {
845     mesh_options_cb(nullptr, nullptr);
846     status = 1;
847   }
848   else if(Fl::test_shortcut(FL_SHIFT + 's')) {
849     solver_options_cb(nullptr, nullptr);
850     status = 1;
851   }
852   else if(Fl::test_shortcut(FL_SHIFT + 'p')) {
853     post_options_cb(nullptr, nullptr);
854     status = 1;
855   }
856   else if(Fl::test_shortcut(FL_SHIFT + 'w')) {
857     view_options_cb(nullptr, (void *)-1);
858     status = 1;
859   }
860   else if(Fl::test_shortcut(FL_SHIFT + 'u')) {
861     if(PView::list.size()) {
862       if(options->view.index >= 0 &&
863          options->view.index < (int)PView::list.size())
864         plugins->show(options->view.index);
865       else
866         plugins->show(0);
867     }
868     status = 1;
869   }
870   else if(Fl::test_shortcut(FL_ALT + 'f')) {
871     opt_general_fast_redraw(0, GMSH_SET | GMSH_GUI,
872                             !opt_general_fast_redraw(0, GMSH_GET, 0));
873     status = 2;
874   }
875   else if(Fl::test_shortcut(FL_ALT + 'b')) {
876     opt_general_draw_bounding_box(
877       0, GMSH_SET | GMSH_GUI, !opt_general_draw_bounding_box(0, GMSH_GET, 0));
878     status = 2;
879   }
880   else if(Fl::test_shortcut(FL_ALT + 'i')) {
881     for(std::size_t i = 0; i < PView::list.size(); i++)
882       if(opt_view_visible(i, GMSH_GET, 0))
883         opt_view_show_scale(i, GMSH_SET | GMSH_GUI,
884                             !opt_view_show_scale(i, GMSH_GET, 0));
885     status = 2;
886   }
887   else if(Fl::test_shortcut(FL_ALT + 'c')) {
888     opt_general_color_scheme(0, GMSH_SET | GMSH_GUI,
889                              opt_general_color_scheme(0, GMSH_GET, 0) + 1);
890     status = 2;
891   }
892   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'c')) {
893     for(std::size_t i = 0; i < PView::list.size(); i++)
894       if(opt_view_visible(i, GMSH_GET, 0))
895         opt_view_colormap_number(i, GMSH_SET | GMSH_GUI,
896                                  opt_view_colormap_number(i, GMSH_GET, 0) + 1);
897     status = 2;
898   }
899   else if(Fl::test_shortcut(FL_ALT + 'w')) {
900     opt_geometry_light(0, GMSH_SET | GMSH_GUI,
901                        !opt_geometry_light(0, GMSH_GET, 0));
902     opt_mesh_light(0, GMSH_SET | GMSH_GUI, !opt_mesh_light(0, GMSH_GET, 0));
903     for(std::size_t i = 0; i < PView::list.size(); i++)
904       if(opt_view_visible(i, GMSH_GET, 0))
905         opt_view_light(i, GMSH_SET | GMSH_GUI, !opt_view_light(i, GMSH_GET, 0));
906     status = 2;
907   }
908   else if(Fl::test_shortcut(FL_ALT + 'x') ||
909           Fl::test_shortcut(FL_ALT + FL_SHIFT + 'x')) {
910     status_xyz1p_cb(nullptr, (void *)"x");
911     status = 1;
912   }
913   else if(Fl::test_shortcut(FL_ALT + 'y') ||
914           Fl::test_shortcut(FL_ALT + FL_SHIFT + 'y')) {
915     status_xyz1p_cb(nullptr, (void *)"y");
916     status = 1;
917   }
918   else if(Fl::test_shortcut(FL_ALT + 'z') ||
919           Fl::test_shortcut(FL_ALT + FL_SHIFT + 'z')) {
920     status_xyz1p_cb(nullptr, (void *)"z");
921     status = 1;
922   }
923   else if(Fl::test_shortcut(FL_ALT + '1') ||
924           Fl::test_shortcut(FL_ALT + FL_SHIFT + '1') ||
925           Fl::test_shortcut(FL_ALT + FL_CTRL + '1') ||
926           Fl::test_shortcut(FL_ALT + FL_META + '1')) {
927     status_xyz1p_cb(nullptr, (void *)"1:1");
928     status = 1;
929   }
930   else if(Fl::test_shortcut(FL_ALT + 'o')) {
931     status_options_cb(nullptr, (void *)"p");
932     status = 1;
933   }
934   else if(Fl::test_shortcut(FL_ALT + 'a')) {
935     opt_general_axes(0, GMSH_SET | GMSH_GUI,
936                      opt_general_axes(0, GMSH_GET, 0) + 1);
937     for(std::size_t i = 0; i < PView::list.size(); i++)
938       if(opt_view_visible(i, GMSH_GET, 0))
939         opt_view_axes(i, GMSH_SET | GMSH_GUI,
940                       opt_view_axes(i, GMSH_GET, 0) + 1);
941     status = 2;
942   }
943   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'a')) {
944     opt_general_small_axes(0, GMSH_SET | GMSH_GUI,
945                            !opt_general_small_axes(0, GMSH_GET, 0));
946     status = 2;
947   }
948   else if(Fl::test_shortcut(FL_ALT + 'p')) {
949     opt_geometry_points(0, GMSH_SET | GMSH_GUI,
950                         !opt_geometry_points(0, GMSH_GET, 0));
951     status = 2;
952   }
953   else if(Fl::test_shortcut(FL_ALT + 'l')) {
954     opt_geometry_curves(0, GMSH_SET | GMSH_GUI,
955                         !opt_geometry_curves(0, GMSH_GET, 0));
956     status = 2;
957   }
958   else if(Fl::test_shortcut(FL_ALT + 's')) {
959     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI,
960                           !opt_geometry_surfaces(0, GMSH_GET, 0));
961     status = 2;
962   }
963   else if(Fl::test_shortcut(FL_ALT + 'v')) {
964     opt_geometry_volumes(0, GMSH_SET | GMSH_GUI,
965                          !opt_geometry_volumes(0, GMSH_GET, 0));
966     status = 2;
967   }
968   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'p')) {
969     opt_mesh_nodes(0, GMSH_SET | GMSH_GUI, !opt_mesh_nodes(0, GMSH_GET, 0));
970     status = 2;
971   }
972   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'l')) {
973     opt_mesh_lines(0, GMSH_SET | GMSH_GUI, !opt_mesh_lines(0, GMSH_GET, 0));
974     status = 2;
975   }
976   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 's')) {
977     opt_mesh_surface_edges(0, GMSH_SET | GMSH_GUI,
978                            !opt_mesh_surface_edges(0, GMSH_GET, 0));
979     status = 2;
980   }
981   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'v')) {
982     opt_mesh_volume_edges(0, GMSH_SET | GMSH_GUI,
983                           !opt_mesh_volume_edges(0, GMSH_GET, 0));
984     status = 2;
985   }
986   else if(Fl::test_shortcut(FL_ALT + 'd')) {
987     opt_geometry_surface_type(0, GMSH_SET | GMSH_GUI,
988                               opt_geometry_surface_type(0, GMSH_GET, 0) + 1);
989     status = 2;
990   }
991   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'd')) {
992     opt_mesh_surface_faces(0, GMSH_SET | GMSH_GUI,
993                            !opt_mesh_surface_faces(0, GMSH_GET, 0));
994     status = 2;
995   }
996   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 'b')) {
997     opt_mesh_volume_faces(0, GMSH_SET | GMSH_GUI,
998                           !opt_mesh_volume_faces(0, GMSH_GET, 0));
999     status = 2;
1000   }
1001   else if(Fl::test_shortcut(FL_ALT + 'm')) {
1002     quick_access_cb(nullptr, (void *)"mesh_toggle");
1003     status = 2;
1004   }
1005   else if(Fl::test_shortcut(FL_ALT + 't')) {
1006     for(std::size_t i = 0; i < PView::list.size(); i++)
1007       if(opt_view_visible(i, GMSH_GET, 0)) {
1008         int t = opt_view_intervals_type(i, GMSH_GET, 0) + 1;
1009         if(t == 4) t = 1; // skip numeric display
1010         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI, t);
1011       }
1012     status = 2;
1013   }
1014   else if(Fl::test_shortcut(FL_ALT + FL_SHIFT + 't')) {
1015     for(std::size_t i = 0; i < PView::list.size(); i++)
1016       if(opt_view_visible(i, GMSH_GET, 0))
1017         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI,
1018                                 opt_view_intervals_type(i, GMSH_GET, 0) + 1);
1019     status = 2;
1020   }
1021   else if(Fl::test_shortcut(FL_ALT + 'r')) {
1022     for(std::size_t i = 0; i < PView::list.size(); i++)
1023       if(opt_view_visible(i, GMSH_GET, 0))
1024         opt_view_range_type(i, GMSH_SET | GMSH_GUI,
1025                             opt_view_range_type(i, GMSH_GET, 0) + 1);
1026     status = 2;
1027   }
1028   else if(Fl::test_shortcut(FL_ALT + 'n')) {
1029     for(std::size_t i = 0; i < PView::list.size(); i++)
1030       if(opt_view_visible(i, GMSH_GET, 0))
1031         opt_view_draw_strings(i, GMSH_SET | GMSH_GUI,
1032                               !opt_view_draw_strings(i, GMSH_GET, 0));
1033     status = 2;
1034   }
1035   else if(Fl::test_shortcut(FL_ALT + 'e') ||
1036           Fl::test_shortcut(FL_ALT + FL_SHIFT + 'e')) {
1037     for(std::size_t i = 0; i < PView::list.size(); i++)
1038       if(opt_view_visible(i, GMSH_GET, 0))
1039         opt_view_show_element(i, GMSH_SET | GMSH_GUI,
1040                               !opt_view_show_element(i, GMSH_GET, 0));
1041     status = 2;
1042   }
1043   else if(Fl::test_shortcut(FL_ALT + 'h')) {
1044     static int show = 0;
1045     for(std::size_t i = 0; i < PView::list.size(); i++)
1046       opt_view_visible(i, GMSH_SET | GMSH_GUI, show);
1047     show = !show;
1048     status = 2;
1049   }
1050   else if(testArrowShortcuts()) {
1051     status = 1;
1052   }
1053 
1054 #if defined(HAVE_TOUCHBAR)
1055   updateTouchBar();
1056 #endif
1057 
1058   if(status == 2) {
1059     drawContext::global()->draw();
1060     return 1;
1061   }
1062   else if(status == 1)
1063     return 1;
1064   else
1065     return 0;
1066 }
1067 
testArrowShortcuts()1068 int FlGui::testArrowShortcuts()
1069 {
1070   if(Fl::test_shortcut(FL_Left)) {
1071     status_play_manual(1, -CTX::instance()->post.animStep);
1072     return 1;
1073   }
1074   else if(Fl::test_shortcut(FL_Right)) {
1075     status_play_manual(1, CTX::instance()->post.animStep);
1076     return 1;
1077   }
1078   else if(Fl::test_shortcut(FL_Up)) {
1079     status_play_manual(0, -CTX::instance()->post.animStep);
1080     return 1;
1081   }
1082   else if(Fl::test_shortcut(FL_Down)) {
1083     status_play_manual(0, CTX::instance()->post.animStep);
1084     return 1;
1085   }
1086 #if defined(HAVE_POPPLER)
1087   else if(Fl::test_shortcut('u') || Fl::test_shortcut(FL_Page_Up)) {
1088     gmshPopplerWrapper::setCurrentPageDown();
1089     drawContext::global()->draw();
1090     return 1;
1091   }
1092   else if(Fl::test_shortcut('d') || Fl::test_shortcut(FL_Page_Down)) {
1093     gmshPopplerWrapper::setCurrentPageUp();
1094     drawContext::global()->draw();
1095     return 1;
1096   }
1097 #endif
1098   return 0;
1099 }
1100 
setGraphicTitle(const std::string & title)1101 void FlGui::setGraphicTitle(const std::string &title)
1102 {
1103   for(std::size_t i = 0; i < graph.size(); i++) {
1104     std::ostringstream sstream;
1105     if(title.empty())
1106       sstream << "Gmsh";
1107     else if(!i)
1108       sstream << "Gmsh - " << title;
1109     else
1110       sstream << "Gmsh - " << title << " [" << i << "]";
1111     graph[i]->setTitle(sstream.str());
1112   }
1113 }
1114 
updateViews(bool numberOfViewsHasChanged,bool deleteWidgets)1115 void FlGui::updateViews(bool numberOfViewsHasChanged, bool deleteWidgets)
1116 {
1117   for(std::size_t i = 0; i < graph.size(); i++) graph[i]->checkAnimButtons();
1118   if(numberOfViewsHasChanged) {
1119     if(onelab) onelab->rebuildTree(deleteWidgets);
1120     if(onelabContext) onelabContext->rebuild(deleteWidgets);
1121     options->resetBrowser();
1122     options->resetExternalViewList();
1123     fields->loadFieldViewList();
1124     plugins->resetViewBrowser();
1125     clipping->resetBrowser();
1126   }
1127 }
1128 
updateFields()1129 void FlGui::updateFields()
1130 {
1131   fields->editField(GModel::current()->getFields()->get(fields->selected_id));
1132 }
1133 
resetVisibility()1134 void FlGui::resetVisibility()
1135 {
1136   if(visibility->win->shown()) visibility_cb(nullptr, nullptr);
1137   if(help->options->shown()) help_options_cb(nullptr, nullptr);
1138 }
1139 
getCurrentOpenglWindow()1140 openglWindow *FlGui::getCurrentOpenglWindow()
1141 {
1142   if(openglWindow::getLastHandled())
1143     return openglWindow::getLastHandled();
1144   else
1145     return graph[0]->gl[0];
1146 }
1147 
setCurrentOpenglWindow(int which)1148 void FlGui::setCurrentOpenglWindow(int which)
1149 {
1150   int ii = 0;
1151   for(std::size_t i = 0; i < graph.size(); i++) {
1152     for(std::size_t j = 0; j < graph[i]->gl.size(); j++) {
1153       if(which == ii++) {
1154         openglWindow::setLastHandled(graph[i]->gl[j]);
1155         return;
1156       }
1157     }
1158   }
1159   openglWindow::setLastHandled(graph[0]->gl[0]);
1160 }
1161 
splitCurrentOpenglWindow(char how,double ratio)1162 void FlGui::splitCurrentOpenglWindow(char how, double ratio)
1163 {
1164   openglWindow *g = getCurrentOpenglWindow();
1165   for(std::size_t i = 0; i < graph.size(); i++) {
1166     if(graph[i]->split(g, how, ratio)) break;
1167   }
1168 }
1169 
copyCurrentOpenglWindowToClipboard()1170 void FlGui::copyCurrentOpenglWindowToClipboard()
1171 {
1172 #if defined(WIN32)
1173   GLint width = getCurrentOpenglWindow()->w();
1174   GLint height = getCurrentOpenglWindow()->h();
1175 
1176   // lines have to be 32 bytes aligned, suppose 24 bits per pixel; just crop it
1177   width -= width % 4;
1178 
1179   // get pixels
1180   PixelBuffer *buffer =
1181     new PixelBuffer(width, height, GL_RGB, GL_UNSIGNED_BYTE);
1182   buffer->fill(0);
1183   unsigned char *pixels = (unsigned char *)buffer->getPixels();
1184 
1185   // swap R and B since Windows bitmap format is BGR
1186   int nBytes = 3 * width * height;
1187   for(int i = 0; i < nBytes; i += 3) {
1188     unsigned char tmp = pixels[i];
1189     pixels[i] = pixels[i + 2];
1190     pixels[i + 2] = tmp;
1191   }
1192 
1193   // fill header
1194   BITMAPINFOHEADER header;
1195   header.biWidth = width;
1196   header.biHeight = height;
1197   header.biSizeImage = nBytes;
1198   header.biSize = 40;
1199   header.biPlanes = 1;
1200   header.biBitCount = 3 * 8;
1201   header.biCompression = BI_RGB;
1202   header.biXPelsPerMeter = 0;
1203   header.biYPelsPerMeter = 0;
1204   header.biClrUsed = 0;
1205   header.biClrImportant = 0;
1206 
1207   // generate handle
1208   HANDLE handle =
1209     (HANDLE)::GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER) + nBytes);
1210   if(handle != nullptr) {
1211     // lock handle
1212     char *pData = (char *)::GlobalLock((HGLOBAL)handle);
1213     // copy header and data
1214     memcpy(pData, &header, sizeof(BITMAPINFOHEADER));
1215     memcpy(pData + sizeof(BITMAPINFOHEADER), pixels, nBytes);
1216     // unlock
1217     ::GlobalUnlock((HGLOBAL)handle);
1218     // push DIB in clipboard
1219     OpenClipboard(nullptr);
1220     EmptyClipboard();
1221     SetClipboardData(CF_DIB, handle);
1222     CloseClipboard();
1223   }
1224 
1225   delete buffer;
1226 #endif
1227 }
1228 
getCurrentDrawContext()1229 drawContext *FlGui::getCurrentDrawContext()
1230 {
1231   return getCurrentOpenglWindow()->getDrawContext();
1232 }
1233 
selectEntity(int type)1234 char FlGui::selectEntity(int type)
1235 {
1236   return getCurrentOpenglWindow()->selectEntity(
1237     type, selectedVertices, selectedEdges, selectedFaces, selectedRegions,
1238     selectedElements, selectedPoints, selectedViews);
1239 }
1240 
setStatus(const std::string & msg,bool opengl)1241 void FlGui::setStatus(const std::string &msg, bool opengl)
1242 {
1243   if(Msg::GetThreadNum() > 0) return;
1244   if(!opengl) {
1245     _lastStatus = msg;
1246     static char buff[1024];
1247     std::string tmp = std::string(" ") + msg;
1248     int ne = Msg::GetErrorCount(), nw = Msg::GetWarningCount();
1249     if((ne || nw) && graph[0]->getMessageHeight() < FL_NORMAL_SIZE) {
1250       tmp += "  -  ";
1251       char n[128];
1252       sprintf(n, "%d", ne ? ne : nw);
1253       tmp += n;
1254       tmp += (ne > 1) ? " Errors" :
1255              ne       ? " Error" :
1256              (nw > 1) ? " Warnings" :
1257                         " Warning";
1258       tmp += " : Click to show messages [ ... ";
1259       tmp += (ne ? Msg::GetFirstError() : Msg::GetFirstWarning());
1260       tmp += " ... ]";
1261     }
1262     strncpy(buff, tmp.c_str(), sizeof(buff) - 1);
1263     buff[sizeof(buff) - 1] = '\0';
1264     for(std::size_t i = 0; i < graph.size(); i++) {
1265       graph[i]->getProgress()->label(buff);
1266       graph[i]->getProgress()->redraw();
1267     }
1268   }
1269   else {
1270     openglWindow *gl = getCurrentOpenglWindow();
1271     std::vector<std::string> m = SplitString(msg, '\n');
1272     if(m.size() > 0)
1273       gl->screenMessage[0] = m[0];
1274     else
1275       gl->screenMessage[0].clear();
1276     if(m.size() > 1)
1277       gl->screenMessage[1] = m[1];
1278     else
1279       gl->screenMessage[1].clear();
1280     drawContext::global()->draw();
1281   }
1282 }
1283 
setLastStatus(int col)1284 void FlGui::setLastStatus(int col)
1285 {
1286   if(Msg::GetThreadNum() > 0) return;
1287   for(std::size_t i = 0; i < graph.size(); i++) {
1288     if(col >= 0 && graph[0]->getMessageHeight() < FL_NORMAL_SIZE) {
1289       if(CTX::instance()->guiColorScheme) // dark
1290         graph[i]->getProgress()->color(col);
1291       else
1292         graph[i]->getProgress()->labelcolor(col);
1293     }
1294     else {
1295       graph[i]->getProgress()->color(FL_BACKGROUND_COLOR);
1296       graph[i]->getProgress()->labelcolor(FL_FOREGROUND_COLOR);
1297     }
1298   }
1299   setStatus(_lastStatus);
1300 }
1301 
setProgress(const std::string & msg,double val,double min,double max)1302 void FlGui::setProgress(const std::string &msg, double val, double min,
1303                         double max)
1304 {
1305   if(Msg::GetThreadNum() > 0) return;
1306   for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++) {
1307     if(FlGui::instance()->graph[i]->getProgress()->value() != val)
1308       FlGui::instance()->graph[i]->getProgress()->value(val);
1309     if(FlGui::instance()->graph[i]->getProgress()->minimum() != min)
1310       FlGui::instance()->graph[i]->getProgress()->minimum(min);
1311     if(FlGui::instance()->graph[i]->getProgress()->maximum() != max)
1312       FlGui::instance()->graph[i]->getProgress()->maximum(max);
1313   }
1314   setStatus(msg);
1315 }
1316 
storeCurrentWindowsInfo()1317 void FlGui::storeCurrentWindowsInfo()
1318 {
1319   CTX::instance()->glPosition[0] = graph[0]->getWindow()->x();
1320   CTX::instance()->glPosition[1] = graph[0]->getWindow()->y();
1321   CTX::instance()->glSize[0] = graph[0]->getGlWidth();
1322   CTX::instance()->glSize[1] = graph[0]->getGlHeight();
1323   CTX::instance()->msgSize = graph[0]->getMessageHeight() ?
1324                                graph[0]->getMessageHeight() :
1325                                CTX::instance()->msgSize;
1326   CTX::instance()->menuSize[0] = graph[0]->getMenuWidth();
1327   if(graph[0]->isMenuDetached()) {
1328     CTX::instance()->detachedMenu = 1;
1329     CTX::instance()->menuSize[1] = graph[0]->getMenuHeight();
1330     CTX::instance()->menuPosition[0] = graph[0]->getMenuPositionX();
1331     CTX::instance()->menuPosition[1] = graph[0]->getMenuPositionY();
1332   }
1333   else
1334     CTX::instance()->detachedMenu = 0;
1335   CTX::instance()->optPosition[0] = options->win->x();
1336   CTX::instance()->optPosition[1] = options->win->y();
1337   CTX::instance()->pluginPosition[0] = plugins->win->x();
1338   CTX::instance()->pluginPosition[1] = plugins->win->y();
1339   CTX::instance()->pluginSize[0] = plugins->win->w();
1340   CTX::instance()->pluginSize[1] = plugins->win->h();
1341   CTX::instance()->fieldPosition[0] = fields->win->x();
1342   CTX::instance()->fieldPosition[1] = fields->win->y();
1343   CTX::instance()->fieldSize[0] = fields->win->w();
1344   CTX::instance()->fieldSize[1] = fields->win->h();
1345   CTX::instance()->statPosition[0] = stats->win->x();
1346   CTX::instance()->statPosition[1] = stats->win->y();
1347   CTX::instance()->visPosition[0] = visibility->win->x();
1348   CTX::instance()->visPosition[1] = visibility->win->y();
1349   CTX::instance()->hotPosition[0] = highordertools->win->x();
1350   CTX::instance()->hotPosition[1] = highordertools->win->y();
1351   CTX::instance()->clipPosition[0] = clipping->win->x();
1352   CTX::instance()->clipPosition[1] = clipping->win->y();
1353   CTX::instance()->manipPosition[0] = manip->win->x();
1354   CTX::instance()->manipPosition[1] = manip->win->y();
1355   if(lastContextWindow == 4) {
1356     CTX::instance()->ctxPosition[0] = onelabContext->win->x();
1357     CTX::instance()->ctxPosition[1] = onelabContext->win->y();
1358   }
1359   else if(lastContextWindow == 3) {
1360     CTX::instance()->ctxPosition[0] = physicalContext->win->x();
1361     CTX::instance()->ctxPosition[1] = physicalContext->win->y();
1362   }
1363   else if(lastContextWindow == 2) {
1364     CTX::instance()->ctxPosition[0] = meshContext->win->x();
1365     CTX::instance()->ctxPosition[1] = meshContext->win->y();
1366   }
1367   else if(lastContextWindow == 1) {
1368     CTX::instance()->ctxPosition[0] = transformContext->win->x();
1369     CTX::instance()->ctxPosition[1] = transformContext->win->y();
1370   }
1371   else {
1372     CTX::instance()->ctxPosition[0] = elementaryContext->win->x();
1373     CTX::instance()->ctxPosition[1] = elementaryContext->win->y();
1374   }
1375 #if defined(HAVE_3M)
1376   storeWindowPosition3M();
1377 #endif
1378 
1379   fileChooserGetPosition(&CTX::instance()->fileChooserPosition[0],
1380                          &CTX::instance()->fileChooserPosition[1]);
1381 }
1382 
1383 // Callbacks
1384 
redraw_cb(Fl_Widget * w,void * data)1385 void redraw_cb(Fl_Widget *w, void *data) { drawContext::global()->draw(); }
1386 
window_cb(Fl_Widget * w,void * data)1387 void window_cb(Fl_Widget *w, void *data)
1388 {
1389   static int oldx = 0, oldy = 0, oldw = 0, oldh = 0, zoom = 0, fullscreen = 0;
1390 
1391   std::string str((const char *)data);
1392 
1393   if(str == "minimize") {
1394     for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1395       if(FlGui::instance()->graph[i]->getWindow()->shown())
1396         FlGui::instance()->graph[i]->getWindow()->iconize();
1397     if(FlGui::instance()->options->win->shown())
1398       FlGui::instance()->options->win->iconize();
1399     if(FlGui::instance()->plugins->win->shown())
1400       FlGui::instance()->plugins->win->iconize();
1401     if(FlGui::instance()->fields->win->shown())
1402       FlGui::instance()->fields->win->iconize();
1403     if(FlGui::instance()->visibility->win->shown())
1404       FlGui::instance()->visibility->win->iconize();
1405     if(FlGui::instance()->highordertools->win->shown())
1406       FlGui::instance()->highordertools->win->iconize();
1407     if(FlGui::instance()->clipping->win->shown())
1408       FlGui::instance()->clipping->win->iconize();
1409     if(FlGui::instance()->manip->win->shown())
1410       FlGui::instance()->manip->win->iconize();
1411     if(FlGui::instance()->stats->win->shown())
1412       FlGui::instance()->stats->win->iconize();
1413   }
1414   else if(str == "zoom") {
1415     if(!zoom) {
1416       oldx = FlGui::instance()->graph[0]->getWindow()->x();
1417       oldy = FlGui::instance()->graph[0]->getWindow()->y();
1418       oldw = FlGui::instance()->graph[0]->getWindow()->w();
1419       oldh = FlGui::instance()->graph[0]->getWindow()->h();
1420       FlGui::instance()->graph[0]->getWindow()->resize(Fl::x(), Fl::y(),
1421                                                        Fl::w(), Fl::h());
1422       zoom = 1;
1423     }
1424     else {
1425       FlGui::instance()->graph[0]->getWindow()->resize(oldx, oldy, oldw, oldh);
1426       zoom = 0;
1427     }
1428   }
1429   else if(str == "fullscreen") {
1430     if(!fullscreen) {
1431       int x, y, w, h;
1432       Fl::screen_xywh(x, y, w, h);
1433       FlGui::instance()->fullscreen->resize(x, y, w, h);
1434       FlGui::instance()->fullscreen->valid(0);
1435       FlGui::instance()->fullscreen->show();
1436       FlGui::instance()->fullscreen->fullscreen();
1437       while(!FlGui::instance()->fullscreen->valid()) FlGui::wait();
1438       FlGui::instance()->fullscreen->getDrawContext()->copyViewAttributes(
1439         FlGui::instance()->getCurrentOpenglWindow()->getDrawContext());
1440       openglWindow::setLastHandled(FlGui::instance()->fullscreen);
1441       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1442         FlGui::instance()->graph[i]->getWindow()->hide();
1443       drawContext::global()->draw();
1444       fullscreen = 1;
1445     }
1446     else {
1447       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1448         FlGui::instance()->graph[i]->gl[0]->valid(0);
1449       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1450         FlGui::instance()->graph[i]->getWindow()->show();
1451       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1452         while(!FlGui::instance()->graph[i]->gl[0]->valid()) FlGui::wait();
1453       FlGui::instance()->graph[0]->gl[0]->getDrawContext()->copyViewAttributes(
1454         FlGui::instance()->getCurrentOpenglWindow()->getDrawContext());
1455       openglWindow::setLastHandled(FlGui::instance()->graph[0]->gl[0]);
1456       FlGui::instance()->fullscreen->fullscreen_off();
1457       FlGui::instance()->fullscreen->hide();
1458       drawContext::global()->draw();
1459       fullscreen = 0;
1460     }
1461   }
1462   else if(str == "front") {
1463     // the order is important!
1464     for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
1465       FlGui::instance()->graph[i]->getWindow()->show();
1466     if(FlGui::instance()->options->win->shown())
1467       FlGui::instance()->options->win->show();
1468     if(FlGui::instance()->plugins->win->shown())
1469       FlGui::instance()->plugins->win->show();
1470     if(FlGui::instance()->fields->win->shown())
1471       FlGui::instance()->fields->win->show();
1472     if(FlGui::instance()->elementaryContext->win->shown())
1473       FlGui::instance()->elementaryContext->win->show();
1474     if(FlGui::instance()->transformContext->win->shown())
1475       FlGui::instance()->transformContext->win->show();
1476     if(FlGui::instance()->physicalContext->win->shown())
1477       FlGui::instance()->physicalContext->win->show();
1478     if(FlGui::instance()->onelabContext->win->shown())
1479       FlGui::instance()->onelabContext->win->show();
1480     if(FlGui::instance()->meshContext->win->shown())
1481       FlGui::instance()->meshContext->win->show();
1482     if(FlGui::instance()->visibility->win->shown())
1483       FlGui::instance()->visibility->win->show();
1484     if(FlGui::instance()->highordertools->win->shown())
1485       FlGui::instance()->highordertools->win->show();
1486     if(FlGui::instance()->clipping->win->shown())
1487       FlGui::instance()->clipping->win->show();
1488     if(FlGui::instance()->manip->win->shown())
1489       FlGui::instance()->manip->win->show();
1490     if(FlGui::instance()->stats->win->shown())
1491       FlGui::instance()->stats->win->show();
1492   }
1493 }
1494 
addMessage(const char * msg)1495 void FlGui::addMessage(const char *msg)
1496 {
1497   for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++) {
1498     FlGui::instance()->graph[i]->addMessage(msg);
1499   }
1500 }
1501 
saveMessages(const char * fileName)1502 void FlGui::saveMessages(const char *fileName)
1503 {
1504   FlGui::instance()->graph[0]->saveMessages(fileName);
1505 }
1506 
rebuildTree(bool deleteWidgets)1507 void FlGui::rebuildTree(bool deleteWidgets)
1508 {
1509   if(onelab) onelab->rebuildTree(deleteWidgets);
1510   if(onelabContext) onelabContext->rebuild(deleteWidgets);
1511 }
1512 
openModule(const std::string & name)1513 void FlGui::openModule(const std::string &name)
1514 {
1515   if(!onelab) return;
1516   if(!onelab->isManuallyClosed("0Modules/" + name))
1517     onelab->openTreeItem("0Modules/" + name);
1518 }
1519 
openTreeItem(const std::string & name)1520 void FlGui::openTreeItem(const std::string &name)
1521 {
1522   if(!onelab) return;
1523   onelab->openTreeItem(name);
1524 }
1525 
closeTreeItem(const std::string & name)1526 void FlGui::closeTreeItem(const std::string &name)
1527 {
1528   if(!onelab) return;
1529   onelab->closeTreeItem(name);
1530 }
1531 
showOnelabContext(int dim,int tag)1532 void FlGui::showOnelabContext(int dim, int tag)
1533 {
1534   if(!onelabContext) return;
1535   onelabContext->show(dim, tag);
1536 }
1537