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 "GmshDefines.h"
8 #include "GmshVersion.h"
9 #if !defined(HAVE_NO_STDINT_H)
10 #include <stdint.h>
11 #elif defined(HAVE_NO_INTPTR_T)
12 typedef unsigned long intptr_t;
13 #endif
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <time.h>
18 #include <sstream>
19 #include <fstream>
20 #include <string>
21 #include <algorithm>
22 #include <FL/Fl_Box.H>
23 #include <FL/fl_ask.H>
24 #include <FL/filename.H>
25 #include <FL/Fl_Tree.H>
26 #include "FlGui.h"
27 #include "mainWindow.h"
28 #include "paletteWindow.h"
29 #include "graphicWindow.h"
30 #include "optionWindow.h"
31 #include "gamepadWindow.h"
32 #include "statisticsWindow.h"
33 #include "contextWindow.h"
34 #include "visibilityWindow.h"
35 #include "highOrderToolsWindow.h"
36 #include "clippingWindow.h"
37 #include "manipWindow.h"
38 #include "fieldWindow.h"
39 #include "pluginWindow.h"
40 #include "helpWindow.h"
41 #include "openglWindow.h"
42 #include "onelabContextWindow.h"
43 #include "onelabGroup.h"
44 #include "messageBrowser.h"
45 #include "gmshLocalNetworkClient.h"
46 #include "fileDialogs.h"
47 #include "extraDialogs.h"
48 #include "partitionDialog.h"
49 #include "classificationEditor.h"
50 #include "GModel.h"
51 #include "PView.h"
52 #include "PViewData.h"
53 #include "PViewOptions.h"
54 #include "OpenFile.h"
55 #include "CreateFile.h"
56 #include "findLinks.h"
57 #include "scriptStringInterface.h"
58 #include "CommandLine.h"
59 #include "Options.h"
60 #include "Context.h"
61 #include "StringUtils.h"
62 #include "OS.h"
63 #include "onelabUtils.h"
64 #include "gmshCrossFields.h"
65 #if defined(HAVE_3M)
66 #include "3M.h"
67 #endif
68 #if defined(HAVE_TOUCHBAR)
69 #include "touchBar.h"
70 #endif
71 
file_new_cb(Fl_Widget * w,void * data)72 static void file_new_cb(Fl_Widget *w, void *data)
73 {
74 test:
75   if(fileChooser(FILE_CHOOSER_CREATE, "New", "")) {
76     std::string name = fileChooserGetName(1);
77     std::vector<std::string> split = SplitFileName(name);
78     if(split[2] != ".geo") {
79       if(fl_choice("File '%s' does not have the '.geo' extension.\n\n"
80                    "Do you want to continue as-is?",
81                    "Continue as-is", "Use '.geo' extension", nullptr,
82                    name.c_str()))
83         name = split[0] + split[1] + ".geo";
84     }
85     if(!StatFile(name)) {
86       if(fl_choice("File '%s' already exists.\n\nDo you want to delete it?",
87                    "Cancel", "Delete", nullptr, name.c_str()))
88         UnlinkFile(name);
89       else
90         goto test;
91     }
92     FILE *fp = Fopen(name.c_str(), "w");
93     if(!fp) {
94       Msg::Error("Unable to open file '%s'", name.c_str());
95       return;
96     }
97     int factory = fl_choice("Which geometry kernel do you want to use?",
98                             "Built-in", "OpenCASCADE", nullptr);
99     time_t now;
100     time(&now);
101     fprintf(fp, "// Gmsh project created on %s", ctime(&now));
102     if(factory) fprintf(fp, "SetFactory(\"OpenCASCADE\");\n");
103     fclose(fp);
104     OpenProject(name);
105     drawContext::global()->draw();
106   }
107 }
108 
109 static const char *input_formats =
110   "All Files\t*.*\n"
111   "Geometry - Gmsh GEO\t*.geo\n"
112 #if defined(HAVE_ACIS)
113   "Geometry - ACIS\t*.sat\n"
114 #endif
115 #if defined(HAVE_OCC)
116   "Geometry - OpenCASCADE BRep\t*.brep\n"
117 #endif
118 #if defined(HAVE_PARASOLID)
119   "Geometry - Parasolid XMT\t*.xmt_txt\n"
120 #endif
121 #if defined(HAVE_OCC) || defined(HAVE_PARASOLID_STEP)
122   "Geometry - STEP\t*.{stp,step}\n"
123 #endif
124 #if defined(HAVE_OCC)
125   "Geometry - IGES\t*.{igs,iges}\n"
126 #endif
127   "Mesh - Gmsh MSH\t*.msh\n"
128   "Mesh - Diffpack 3D\t*.diff\n"
129   "Mesh - I-deas Universal\t*.unv\n"
130 #if defined(HAVE_MED)
131   "Mesh - MED\t*.{med,mmed}\n"
132 #endif
133   "Mesh - INRIA Medit\t*.mesh\n"
134   "Mesh - Nastran Bulk Data File\t*.{bdf,nas}\n"
135   "Mesh - Object File Format\t*.off\n"
136   "Mesh - Plot3D Structured Mesh\t*.p3d\n"
137   "Mesh - STL Surface\t*.stl\n"
138   "Mesh - VTK\t*.vtk\n"
139   "Mesh - VRML Surface\t*.{wrl,vrml}\n"
140   "Mesh - PLY2 Surface\t*.ply2\n"
141   "Post-processing - Gmsh POS\t*.pos\n"
142 #if defined(HAVE_MED)
143   "Post-processing - MED\t*.rmed\n"
144 #endif
145   "Image - BMP\t*.bmp\n"
146 #if defined(HAVE_LIBJPEG)
147   "Image - JPEG\t*.{jpg,jpeg}\n"
148 #endif
149   "Image - PBM\t*.pbm\n"
150   "Image - PGM\t*.pgm\n"
151 #if defined(HAVE_LIBPNG)
152   "Image - PNG\t*.png\n"
153 #endif
154   "Image - PNM\t*.pnm\n"
155   "Image - PPM\t*.ppm\n";
156 
file_open_merge_cb(Fl_Widget * w,void * data)157 static void file_open_merge_cb(Fl_Widget *w, void *data)
158 {
159   if(!data) return;
160   std::string mode((char *)data);
161   int n = PView::list.size();
162   int f = fileChooser(FILE_CHOOSER_MULTI, (mode == "open") ? "Open" : "Merge",
163                       input_formats);
164   if(f) {
165     for(int i = 1; i <= f; i++) {
166       if(mode == "open")
167         OpenProject(fileChooserGetName(i));
168       else
169         MergeFile(fileChooserGetName(i));
170     }
171     if(n != (int)PView::list.size())
172       FlGui::instance()->openModule("Post-processing");
173     if(CTX::instance()->launchSolverAtStartup >= 0)
174       solver_cb(nullptr,
175                 (void *)(intptr_t)CTX::instance()->launchSolverAtStartup);
176     else if(onelabUtils::haveSolverToRun())
177       onelab_cb(nullptr, (void *)"check");
178     drawContext::global()->draw();
179   }
180 }
181 
file_open_recent_cb(Fl_Widget * w,void * data)182 static void file_open_recent_cb(Fl_Widget *w, void *data)
183 {
184   if(!data) return;
185   std::string str((const char *)data);
186   int n = PView::list.size();
187   OpenProject(str);
188   drawContext::global()->draw();
189   if(n != (int)PView::list.size())
190     FlGui::instance()->openModule("Post-processing");
191   if(CTX::instance()->launchSolverAtStartup >= 0)
192     solver_cb(nullptr,
193               (void *)(intptr_t)CTX::instance()->launchSolverAtStartup);
194   else if(onelabUtils::haveSolverToRun())
195     onelab_cb(nullptr, (void *)"check");
196 }
197 
file_clear_cb(Fl_Widget * w,void * data)198 static void file_clear_cb(Fl_Widget *w, void *data)
199 {
200   if(CTX::instance()->lock || FlGui::instance()->onelab->isBusy()) {
201     Msg::Info("I'm busy! Ask me that later...");
202     return;
203   }
204   ClearProject();
205   if(onelabUtils::haveSolverToRun())
206     onelab_cb(nullptr, (void *)"reset"); // this will call OpenProject
207   else
208     OpenProject(GModel::current()->getFileName());
209   drawContext::global()->draw();
210 }
211 
file_remote_cb(Fl_Widget * w,void * data)212 static void file_remote_cb(Fl_Widget *w, void *data)
213 {
214   onelab::localNetworkClient *c;
215   auto it = onelab::server::instance()->findClient("GmshRemote");
216   if(it == onelab::server::instance()->lastClient()) {
217     c = new gmshLocalNetworkClient("GmshRemote", "");
218     c->setSocketSwitch("-socket");
219   }
220   else
221     c = (onelab::localNetworkClient *)(*it);
222   GmshServer *server = c->getGmshServer();
223 
224   std::string str((const char *)data);
225 
226   if(str == "start") {
227     if(server) {
228       Msg::Error("Cannot start: remote Gmsh is already running");
229       return;
230     }
231     c->setExecutable(connectionChooser());
232     if(c->getExecutable().size()) c->run();
233   }
234   else {
235     if(!server) {
236       Msg::Error("Cannot %s: remote Gmsh not running", str.c_str());
237       return;
238     }
239     if(str == "stop") {
240       server->SendString(GmshSocket::GMSH_STOP, "Disconnect!");
241     }
242     else if(str == "merge") {
243       const char *file = fl_input("Merge", "/tmp/data.pos");
244       if(file) server->SendString(GmshSocket::GMSH_MERGE_FILE, file);
245     }
246     else if(str == "clear") {
247       server->SendString(GmshSocket::GMSH_PARSE_STRING, "Delete All;");
248       for(int i = PView::list.size() - 1; i >= 0; i--)
249         if(PView::list[i]->getData()->isRemote()) delete PView::list[i];
250       FlGui::instance()->updateViews(true, true);
251       drawContext::global()->draw();
252     }
253     else if(str == "test") {
254       server->SendString(GmshSocket::GMSH_SPEED_TEST, "Speed test");
255     }
256   }
257 }
258 
file_window_cb(Fl_Widget * w,void * data)259 static void file_window_cb(Fl_Widget *w, void *data)
260 {
261   std::string str((const char *)data);
262   if(str == "new") {
263     graphicWindow *g1 = FlGui::instance()->graph.back();
264     graphicWindow *g2 = new graphicWindow(false, CTX::instance()->numTiles);
265     FlGui::instance()->graph.push_back(g2);
266     g2->getWindow()->resize(g1->getWindow()->x() + 10,
267                             g1->getWindow()->y() + 10, g1->getWindow()->w(),
268                             g1->getWindow()->h());
269     g2->getWindow()->show();
270   }
271   else if(str == "split_h") {
272     FlGui::instance()->splitCurrentOpenglWindow('h', 0.5);
273   }
274   else if(str == "split_v") {
275     FlGui::instance()->splitCurrentOpenglWindow('v', 0.5);
276   }
277   else if(str == "split_u") {
278     FlGui::instance()->splitCurrentOpenglWindow('u');
279   }
280   else if(str == "copy") {
281     FlGui::instance()->copyCurrentOpenglWindowToClipboard();
282   }
283   drawContext::global()->draw();
284   FlGui::instance()->setGraphicTitle(GModel::current()->getFileName());
285 }
286 
_save_msh(const char * name)287 static int _save_msh(const char *name) { return mshFileDialog(name); }
_save_mesh_stat(const char * name)288 static int _save_mesh_stat(const char *name)
289 {
290   return meshStatFileDialog(name);
291 }
_save_options(const char * name)292 static int _save_options(const char *name) { return optionsFileDialog(name); }
_save_geo(const char * name)293 static int _save_geo(const char *name) { return geoFileDialog(name); }
_save_brep(const char * name)294 static int _save_brep(const char *name)
295 {
296   CreateOutputFile(name, FORMAT_BREP);
297   return 1;
298 }
_save_step(const char * name)299 static int _save_step(const char *name)
300 {
301   CreateOutputFile(name, FORMAT_STEP);
302   return 1;
303 }
_save_xmt(const char * name)304 static int _save_xmt(const char *name)
305 {
306   CreateOutputFile(name, FORMAT_XMT);
307   return 1;
308 }
_save_cgns(const char * name)309 static int _save_cgns(const char *name) { return cgnsFileDialog(name); }
_save_unv(const char * name)310 static int _save_unv(const char *name)
311 {
312   return unvinpFileDialog(name, "UNV Options", FORMAT_UNV);
313 }
_save_vtk(const char * name)314 static int _save_vtk(const char *name)
315 {
316   return genericMeshFileDialog(name, "VTK Options", FORMAT_VTK, true, false);
317 }
_save_tochnog(const char * name)318 static int _save_tochnog(const char *name)
319 {
320   return genericMeshFileDialog(name, "Tochnog Options", FORMAT_TOCHNOG, true,
321                                false);
322 }
_save_diff(const char * name)323 static int _save_diff(const char *name)
324 {
325   return genericMeshFileDialog(name, "Diffpack Options", FORMAT_DIFF, true,
326                                false);
327 }
_save_inp(const char * name)328 static int _save_inp(const char *name)
329 {
330   return unvinpFileDialog(name, "Abaqus INP Options", FORMAT_INP);
331 }
_save_key(const char * name)332 static int _save_key(const char *name)
333 {
334   return keyFileDialog(name, "LSDYNA KEY Options", FORMAT_KEY);
335 }
_save_celum(const char * name)336 static int _save_celum(const char *name)
337 {
338   return genericMeshFileDialog(name, "CELUM Options", FORMAT_CELUM, false,
339                                false);
340 }
_save_su2(const char * name)341 static int _save_su2(const char *name)
342 {
343   return genericMeshFileDialog(name, "SU2 Options", FORMAT_SU2, false, false);
344 }
_save_med(const char * name)345 static int _save_med(const char *name)
346 {
347   return genericMeshFileDialog(name, "MED Options", FORMAT_MED, false, false);
348 }
_save_mesh(const char * name)349 static int _save_mesh(const char *name)
350 {
351   return genericMeshFileDialog(name, "MESH Options", FORMAT_MESH, false, true);
352 }
_save_off(const char * name)353 static int _save_off(const char *name)
354 {
355   return genericMeshFileDialog(name, "OFF Options", FORMAT_OFF, false, false);
356 }
_save_mail(const char * name)357 static int _save_mail(const char *name)
358 {
359   return genericMeshFileDialog(name, "MAIL Options", FORMAT_MAIL, false, false);
360 }
_save_matlab(const char * name)361 static int _save_matlab(const char *name)
362 {
363   return genericMeshFileDialog(name, "MATLAB Options", FORMAT_MATLAB, false,
364                                false);
365 }
_save_bdf(const char * name)366 static int _save_bdf(const char *name) { return bdfFileDialog(name); }
_save_p3d(const char * name)367 static int _save_p3d(const char *name)
368 {
369   return genericMeshFileDialog(name, "P3D Options", FORMAT_P3D, false, false);
370 }
_save_ir3(const char * name)371 static int _save_ir3(const char *name)
372 {
373   return genericMeshFileDialog(name, "Iridium Options", FORMAT_IR3, false,
374                                true);
375 }
_save_stl(const char * name)376 static int _save_stl(const char *name) { return stlFileDialog(name); }
_save_vrml(const char * name)377 static int _save_vrml(const char *name)
378 {
379   return genericMeshFileDialog(name, "VRML Options", FORMAT_VRML, false, false);
380 }
_save_ply2(const char * name)381 static int _save_ply2(const char *name)
382 {
383   return genericMeshFileDialog(name, "PLY2 Options", FORMAT_PLY2, false, false);
384 }
_save_neu(const char * name)385 static int _save_neu(const char *name)
386 {
387   return genericMeshFileDialog(name, "NEU Options", FORMAT_NEU, false, false);
388 }
_save_eps(const char * name)389 static int _save_eps(const char *name)
390 {
391   return gl2psFileDialog(name, "EPS Options", FORMAT_EPS);
392 }
_save_gif(const char * name)393 static int _save_gif(const char *name) { return gifFileDialog(name); }
_save_jpeg(const char * name)394 static int _save_jpeg(const char *name)
395 {
396   return genericBitmapFileDialog(name, "JPEG Options", FORMAT_JPEG);
397 }
_save_mpeg(const char * name)398 static int _save_mpeg(const char *name) { return mpegFileDialog(name); }
_save_tex(const char * name)399 static int _save_tex(const char *name) { return latexFileDialog(name); }
_save_pdf(const char * name)400 static int _save_pdf(const char *name)
401 {
402   return gl2psFileDialog(name, "PDF Options", FORMAT_PDF);
403 }
_save_png(const char * name)404 static int _save_png(const char *name)
405 {
406   return genericBitmapFileDialog(name, "PNG Options", FORMAT_PNG);
407 }
_save_pgf(const char * name)408 static int _save_pgf(const char *name)
409 {
410   return pgfBitmapFileDialog(name, "PGF Options", FORMAT_PGF);
411 }
_save_ps(const char * name)412 static int _save_ps(const char *name)
413 {
414   return gl2psFileDialog(name, "PS Options", FORMAT_PS);
415 }
_save_ppm(const char * name)416 static int _save_ppm(const char *name)
417 {
418   return genericBitmapFileDialog(name, "PPM Options", FORMAT_PPM);
419 }
_save_svg(const char * name)420 static int _save_svg(const char *name)
421 {
422   return gl2psFileDialog(name, "SVG Options", FORMAT_SVG);
423 }
_save_tikz(const char * name)424 static int _save_tikz(const char *name)
425 {
426   return gl2psFileDialog(name, "TIKZ Options", FORMAT_TIKZ);
427 }
_save_yuv(const char * name)428 static int _save_yuv(const char *name)
429 {
430   return genericBitmapFileDialog(name, "YUV Options", FORMAT_YUV);
431 }
_save_view_pos(const char * name)432 static int _save_view_pos(const char *name) { return posFileDialog(name); }
_save_view_adapt_pvtu(const char * name)433 static int _save_view_adapt_pvtu(const char *name)
434 {
435   return pvtuAdaptFileDialog(name);
436 }
_save_view_med(const char * name)437 static int _save_view_med(const char *name)
438 {
439   return genericViewFileDialog(name, "MED Options", 6);
440 }
_save_view_txt(const char * name)441 static int _save_view_txt(const char *name)
442 {
443   return genericViewFileDialog(name, "TXT Options", 4);
444 }
_save_view_x3d(const char * name)445 static int _save_view_x3d(const char *name)
446 {
447   return x3dViewFileDialog(name, "X3D Options", 7);
448 }
449 
_save_auto(const char * name)450 static int _save_auto(const char *name)
451 {
452   switch(GuessFileFormatFromFileName(name)) {
453   case FORMAT_MSH: return _save_msh(name);
454   case FORMAT_POS: return _save_view_pos(name);
455   case FORMAT_X3D: return _save_view_x3d(name);
456   case FORMAT_PVTU: return _save_view_adapt_pvtu(name);
457   case FORMAT_TXT: return _save_view_txt(name);
458   case FORMAT_OPT: return _save_options(name);
459   case FORMAT_GEO: return _save_geo(name);
460   case FORMAT_BREP: return _save_brep(name);
461   case FORMAT_STEP: return _save_step(name);
462   case FORMAT_CGNS: return _save_cgns(name);
463   case FORMAT_UNV: return _save_unv(name);
464   case FORMAT_VTK: return _save_vtk(name);
465   case FORMAT_TOCHNOG: return _save_tochnog(name);
466   case FORMAT_MED: return _save_med(name);
467   case FORMAT_RMED: return _save_view_med(name);
468   case FORMAT_MESH: return _save_mesh(name);
469   case FORMAT_OFF: return _save_off(name);
470   case FORMAT_MAIL: return _save_mail(name);
471   case FORMAT_MATLAB: return _save_matlab(name);
472   case FORMAT_BDF: return _save_bdf(name);
473   case FORMAT_DIFF: return _save_diff(name);
474   case FORMAT_INP: return _save_inp(name);
475   case FORMAT_KEY: return _save_key(name);
476   case FORMAT_CELUM: return _save_celum(name);
477   case FORMAT_SU2: return _save_su2(name);
478   case FORMAT_P3D: return _save_p3d(name);
479   case FORMAT_IR3: return _save_ir3(name);
480   case FORMAT_STL: return _save_stl(name);
481   case FORMAT_VRML: return _save_vrml(name);
482   case FORMAT_PLY2: return _save_ply2(name);
483   case FORMAT_NEU: return _save_neu(name);
484   case FORMAT_EPS: return _save_eps(name);
485   case FORMAT_GIF: return _save_gif(name);
486   case FORMAT_JPEG: return _save_jpeg(name);
487   case FORMAT_MPEG: return _save_mpeg(name);
488   case FORMAT_TEX: return _save_tex(name);
489   case FORMAT_PDF: return _save_pdf(name);
490   case FORMAT_PNG: return _save_png(name);
491   case FORMAT_PGF: return _save_pgf(name);
492   case FORMAT_PS: return _save_ps(name);
493   case FORMAT_PPM: return _save_ppm(name);
494   case FORMAT_SVG: return _save_svg(name);
495   case FORMAT_TIKZ: return _save_tikz(name);
496   case FORMAT_YUV: return _save_yuv(name);
497   case FORMAT_XMT: return _save_xmt(name);
498   default: CreateOutputFile(name, FORMAT_AUTO); return 1;
499   }
500 }
501 
502 typedef struct {
503   const char *pat;
504   int (*func)(const char *name);
505 } patXfunc;
506 
file_export_cb(Fl_Widget * w,void * data)507 static void file_export_cb(Fl_Widget *w, void *data)
508 {
509   static patXfunc formats[] = {
510     {"Guess From Extension\t*.*", _save_auto},
511     {"Geometry - Gmsh Options\t*.opt", _save_options},
512     {"Geometry - Gmsh Unrolled GEO\t*.geo_unrolled", _save_geo},
513 #if defined(HAVE_OCC)
514     {"Geometry - OpenCASCADE BRep\t*.brep", _save_brep},
515 #endif
516 #if defined(HAVE_PARASOLID)
517     {"Geometry - Parasolid XMT\t*.xmt_txt", _save_xmt},
518 #endif
519 #if defined(HAVE_OCC) || defined(HAVE_PARASOLID_STEP)
520     {"Geometry - STEP\t*.step", _save_step},
521 #endif
522     {"Mesh - Gmsh MSH\t*.msh", _save_msh},
523     {"Mesh - Abaqus INP\t*.inp", _save_inp},
524     {"Mesh - LSDYNA KEY\t*.key", _save_key},
525     {"Mesh - CELUM\t*.celum", _save_celum},
526 #if defined(HAVE_LIBCGNS)
527     {"Mesh - CGNS (Experimental)\t*.cgns", _save_cgns},
528 #endif
529     {"Mesh - Diffpack 3D\t*.diff", _save_diff},
530     {"Mesh - I-deas Universal\t*.unv", _save_unv},
531     {"Mesh - Iridum\t*.ir3", _save_ir3},
532 #if defined(HAVE_MED)
533     {"Mesh - MED\t*.med", _save_med},
534 #endif
535     {"Mesh - INRIA Medit\t*.mesh", _save_mesh},
536     {"Mesh - CEA Triangulation\t*.mail", _save_mail},
537     {"Mesh - Matlab\t*.m", _save_matlab},
538     {"Mesh - Nastran Bulk Data File\t*.bdf", _save_bdf},
539     {"Mesh - Object File Format\t*.off", _save_off},
540     {"Mesh - Plot3D Structured Mesh\t*.p3d", _save_p3d},
541     {"Mesh - STL Surface\t*.stl", _save_stl},
542     {"Mesh - VRML Surface\t*.wrl", _save_vrml},
543     {"Mesh - VTK\t*.vtk", _save_vtk},
544     {"Mesh - Tochnog\t*.dat", _save_tochnog},
545     {"Mesh - PLY2 Surface\t*.ply2", _save_ply2},
546     {"Mesh - SU2\t*.su2", _save_su2},
547     {"Mesh - GAMBIT Neutral File\t*.neu", _save_neu},
548     {"Post-processing - Gmsh POS\t*.pos", _save_view_pos},
549     {"Post-processing - X3D (X3D)\t*.x3d", _save_view_x3d},
550 #if defined(HAVE_MED)
551     {"Post-processing - MED\t*.rmed", _save_view_med},
552 #endif
553     {"Post-processing - Generic TXT\t*.txt", _save_view_txt},
554     {"Post-processing - Mesh Statistics\t*.pos", _save_mesh_stat},
555     {"Post-processing - Adapted data\t*.pvtu", _save_view_adapt_pvtu},
556     {"Image - Encapsulated PostScript\t*.eps", _save_eps},
557     {"Image - GIF\t*.gif", _save_gif},
558 #if defined(HAVE_LIBJPEG)
559     {"Image - JPEG\t*.jpg", _save_jpeg},
560 #endif
561     {"Image - LaTeX\t*.tex", _save_tex},
562     {"Image - PDF\t*.pdf", _save_pdf},
563 #if defined(HAVE_LIBPNG)
564     {"Image - PNG\t*.png", _save_png},
565     {"Image - PGF\t*.pgf", _save_pgf},
566 #endif
567     {"Image - PostScript\t*.ps", _save_ps},
568     {"Image - PPM\t*.ppm", _save_ppm},
569     {"Image - SVG\t*.svg", _save_svg},
570     {"Image - TIKZ\t*.tikz", _save_tikz},
571     {"Image - YUV\t*.yuv", _save_yuv},
572 #if defined(HAVE_MPEG_ENCODE)
573     {"Movie - MPEG\t*.mpg", _save_mpeg},
574 #endif
575   };
576   int nbformats = sizeof(formats) / sizeof(formats[0]);
577   static char *pat = nullptr;
578   if(!pat) {
579     pat = new char[nbformats * 256];
580     strcpy(pat, formats[0].pat);
581     for(int i = 1; i < nbformats; i++) {
582       strcat(pat, "\n");
583       strcat(pat, formats[i].pat);
584     }
585   }
586 
587 test:
588   if(fileChooser(FILE_CHOOSER_CREATE, "Export", pat)) {
589     std::string name = fileChooserGetName(1);
590     bool confirmOverwrite = CTX::instance()->confirmOverwrite;
591 #if defined(__APPLE__)
592     // handled directly by the native macOS file chooser
593     if(CTX::instance()->nativeFileChooser) confirmOverwrite = false;
594 #endif
595     if(confirmOverwrite) {
596       if(!StatFile(name))
597         if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?",
598                       "Cancel", "Replace", nullptr, name.c_str()))
599           goto test;
600     }
601     int i = fileChooserGetFilter();
602     if(i >= 0 && i < nbformats) {
603       if(!formats[i].func(name.c_str())) goto test;
604     }
605     else { // handle any additional automatic fltk filter
606       if(!_save_auto(name.c_str())) goto test;
607     }
608   }
609 }
610 
file_options_save_cb(Fl_Widget * w,void * data)611 static void file_options_save_cb(Fl_Widget *w, void *data)
612 {
613   std::string str((const char *)data), fileName;
614   if(str == "file")
615     fileName = GModel::current()->getFileName() + ".opt";
616   else
617     fileName = CTX::instance()->homeDir + CTX::instance()->optionsFileName;
618   Msg::StatusBar(true, "Writing '%s'...", fileName.c_str());
619   if(str == "file")
620     PrintOptions(0, GMSH_FULLRC, 0, 0, fileName.c_str());
621   else
622     PrintOptions(0, GMSH_OPTIONSRC, 1, 1, fileName.c_str());
623   Msg::StatusBar(true, "Done writing '%s'", fileName.c_str());
624 }
625 
file_rename_cb(Fl_Widget * w,void * data)626 static void file_rename_cb(Fl_Widget *w, void *data)
627 {
628 test:
629   if(fileChooser(FILE_CHOOSER_CREATE, "Rename", "")) {
630     std::string name = fileChooserGetName(1);
631     bool confirmOverwrite = CTX::instance()->confirmOverwrite;
632 #if defined(__APPLE__)
633     // handled directly by the native macOS file chooser
634     if(CTX::instance()->nativeFileChooser) confirmOverwrite = false;
635 #endif
636     if(confirmOverwrite) {
637       if(!StatFile(name))
638         if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?",
639                       "Cancel", "Replace", nullptr, name.c_str()))
640           goto test;
641     }
642     rename(GModel::current()->getFileName().c_str(), name.c_str());
643     GModel::current()->setFileName(name);
644     GModel::current()->setName(SplitFileName(name)[1]);
645     Msg::SetOnelabChanged(3);
646     if(onelabUtils::haveSolverToRun()) onelab_cb(nullptr, (void *)"check");
647     drawContext::global()->draw();
648   }
649 }
650 
file_delete_cb(Fl_Widget * w,void * data)651 static void file_delete_cb(Fl_Widget *w, void *data)
652 {
653   if(fl_choice("Do you really want to delete file '%s'?", "Cancel", "Delete",
654                nullptr, GModel::current()->getFileName().c_str())) {
655     UnlinkFile(GModel::current()->getFileName());
656     Msg::Info("Deleted `%s'", GModel::current()->getFileName().c_str());
657     file_clear_cb(nullptr, nullptr);
658   }
659 }
660 
file_quit_cb(Fl_Widget * w,void * data)661 void file_quit_cb(Fl_Widget *w, void *data)
662 {
663   // save persistent info to disk
664   if(CTX::instance()->sessionSave)
665     PrintOptions(
666       0, GMSH_SESSIONRC, 0, 0,
667       (CTX::instance()->homeDir + CTX::instance()->sessionFileName).c_str());
668   if(CTX::instance()->optionsSave == 1)
669     PrintOptions(
670       0, GMSH_OPTIONSRC, 1, 0,
671       (CTX::instance()->homeDir + CTX::instance()->optionsFileName).c_str());
672   else if(CTX::instance()->optionsSave == 2) {
673     std::string fileName = GModel::current()->getFileName() + ".opt";
674     PrintOptions(0, GMSH_FULLRC, 1, 0, fileName.c_str());
675   }
676 
677   if(FlGui::instance()->quitShouldExit()) { Msg::Exit(0); }
678   else {
679     FlGui::instance()->onelabContext->disableRedraw();
680 
681     // hide all windows (in case they are not tracked by FlGui)...
682     std::vector<Fl_Window *> wins;
683     for(Fl_Window *win = Fl::first_window(); win; win = Fl::next_window(win))
684       wins.push_back(win);
685     for(std::size_t i = 0; i < wins.size(); i++) wins[i]->hide();
686 
687     // process remaining events
688     FlGui::check();
689     // ... and destroy the GUI
690     FlGui::instance()->destroy();
691   }
692 }
693 
file_watch_cb(Fl_Widget * w,void * data)694 void file_watch_cb(Fl_Widget *w, void *data)
695 {
696   if(w) CTX::instance()->watchFilePattern = patternChooser();
697 
698   if(CTX::instance()->watchFilePattern.empty()) return;
699 
700   std::string pattern = FixRelativePath(GModel::current()->getFileName(),
701                                         CTX::instance()->watchFilePattern);
702   std::string directory = SplitFileName(pattern)[0];
703   if(directory.empty()) directory = "./";
704 
705   dirent **files = nullptr;
706   int num = fl_filename_list(directory.c_str(), &files, fl_numericsort);
707   if(num <= 0) return;
708   std::vector<std::string> matches;
709   for(int i = 0; i < num; i++) {
710     std::string name = directory + files[i]->d_name;
711     if(fl_filename_match(name.c_str(), pattern.c_str()))
712       matches.push_back(name);
713     free((void *)files[i]);
714   }
715   if(files) free((void *)files);
716 
717   Msg::Info("%d match%s for pattern '%s'", (int)matches.size(),
718             (matches.size() > 1) ? "es" : "", pattern.c_str());
719 
720   std::set<std::string> allFiles;
721   for(std::size_t i = 0; i < GModel::list.size(); i++)
722     allFiles.insert(GetFileNameWithoutPath(GModel::list[i]->getFileName()));
723   for(std::size_t i = 0; i < PView::list.size(); i++)
724     for(int j = 0; j < PView::list[i]->getData()->getNumTimeSteps(); j++)
725       allFiles.insert(
726         GetFileNameWithoutPath(PView::list[i]->getData()->getFileName(j)));
727 
728   for(std::size_t i = 0; i < matches.size(); i++)
729     if(allFiles.find(GetFileNameWithoutPath(matches[i])) == allFiles.end())
730       MergeFile(matches[i]);
731   drawContext::global()->draw();
732 }
733 
help_online_cb(Fl_Widget * w,void * data)734 static void help_online_cb(Fl_Widget *w, void *data)
735 {
736   if(std::string(GMSH_EXTRA_VERSION) == "")
737     fl_open_uri("https://gmsh.info/doc/texinfo/gmsh.html");
738   else
739     fl_open_uri("https://gmsh.info/dev/doc/texinfo/gmsh.html");
740 }
741 
help_basic_cb(Fl_Widget * w,void * data)742 static void help_basic_cb(Fl_Widget *w, void *data)
743 {
744   FlGui::instance()->help->basic->show();
745 }
746 
help_about_cb(Fl_Widget * w,void * data)747 void help_about_cb(Fl_Widget *w, void *data)
748 {
749   FlGui::instance()->help->about->show();
750 }
751 
geometry_edit_cb(Fl_Widget * w,void * data)752 static void geometry_edit_cb(Fl_Widget *w, void *data)
753 {
754   std::string prog = FixWindowsPath(CTX::instance()->editor);
755   std::string file = FixWindowsPath(GModel::current()->getFileName());
756   SystemCall(ReplaceSubString("%s", file, prog));
757 }
758 
onelab_reload_cb(Fl_Widget * w,void * data)759 void onelab_reload_cb(Fl_Widget *w, void *data)
760 {
761   if(CTX::instance()->lock || FlGui::instance()->onelab->isBusy()) {
762     Msg::Info("I'm busy! Ask me that later...");
763     return;
764   }
765 
766   std::string fileName = GModel::current()->getFileName();
767   ClearProject();
768   GModel::current()->setFileName(fileName);
769   onelab_cb(nullptr, (void *)"reset"); // will call OpenProject
770   drawContext::global()->draw();
771 }
772 
geometry_reload_cb(Fl_Widget * w,void * data)773 void geometry_reload_cb(Fl_Widget *w, void *data)
774 {
775   if(onelabUtils::haveSolverToRun()) {
776     onelab_cb(nullptr, (void *)"check_always");
777   }
778   else
779     OpenProject(GModel::current()->getFileName());
780   drawContext::global()->draw();
781 }
782 
geometry_remove_last_command_cb(Fl_Widget * w,void * data)783 void geometry_remove_last_command_cb(Fl_Widget *w, void *data)
784 {
785   scriptRemoveLastCommand(GModel::current()->getFileName());
786   drawContext::global()->draw();
787 }
788 
add_new_point_based_entity(const std::string & what,int pane)789 static void add_new_point_based_entity(const std::string &what, int pane)
790 {
791   opt_general_axes(0, GMSH_SET | GMSH_GUI, 3);
792   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
793   drawContext::global()->draw();
794 
795   FlGui::instance()->elementaryContext->show(pane);
796 
797   while(1) {
798     if(!FlGui::available()) return;
799 
800     for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
801       for(std::size_t j = 0; j < FlGui::instance()->graph[i]->gl.size(); j++)
802         FlGui::instance()->graph[i]->gl[j]->addPointMode = 1;
803     std::string name = what;
804     std::transform(name.begin(), name.end(), name.begin(), ::tolower);
805     Msg::StatusGl(
806       "Move mouse and/or enter coordinates\n"
807       "[Press 'Shift' to hold position, 'e' to add %s or 'q' to abort]",
808       name.c_str());
809     char ib = FlGui::instance()->selectEntity(ENT_NONE);
810     if(!FlGui::available()) return;
811     if(ib == 'e') {
812       switch(pane) {
813       case 1:
814         scriptAddPoint(GModel::current()->getFileName(),
815                        FlGui::instance()->elementaryContext->input[4]->value(),
816                        FlGui::instance()->elementaryContext->input[5]->value(),
817                        FlGui::instance()->elementaryContext->input[6]->value(),
818                        FlGui::instance()->elementaryContext->input[7]->value());
819         break;
820       case 2:
821         scriptAddCircle(
822           GModel::current()->getFileName(),
823           FlGui::instance()->elementaryContext->input[8]->value(),
824           FlGui::instance()->elementaryContext->input[9]->value(),
825           FlGui::instance()->elementaryContext->input[10]->value(),
826           FlGui::instance()->elementaryContext->input[11]->value(),
827           FlGui::instance()->elementaryContext->input[12]->value(),
828           FlGui::instance()->elementaryContext->input[13]->value());
829         break;
830       case 3:
831         scriptAddEllipse(
832           GModel::current()->getFileName(),
833           FlGui::instance()->elementaryContext->input[14]->value(),
834           FlGui::instance()->elementaryContext->input[15]->value(),
835           FlGui::instance()->elementaryContext->input[16]->value(),
836           FlGui::instance()->elementaryContext->input[17]->value(),
837           FlGui::instance()->elementaryContext->input[18]->value(),
838           FlGui::instance()->elementaryContext->input[19]->value(),
839           FlGui::instance()->elementaryContext->input[20]->value());
840         break;
841       case 4:
842         scriptAddDisk(GModel::current()->getFileName(),
843                       FlGui::instance()->elementaryContext->input[21]->value(),
844                       FlGui::instance()->elementaryContext->input[22]->value(),
845                       FlGui::instance()->elementaryContext->input[23]->value(),
846                       FlGui::instance()->elementaryContext->input[24]->value(),
847                       FlGui::instance()->elementaryContext->input[25]->value());
848         break;
849       case 5:
850         scriptAddRectangle(
851           GModel::current()->getFileName(),
852           FlGui::instance()->elementaryContext->input[26]->value(),
853           FlGui::instance()->elementaryContext->input[27]->value(),
854           FlGui::instance()->elementaryContext->input[28]->value(),
855           FlGui::instance()->elementaryContext->input[29]->value(),
856           FlGui::instance()->elementaryContext->input[30]->value(),
857           FlGui::instance()->elementaryContext->input[31]->value());
858         break;
859       case 6:
860         scriptAddSphere(
861           GModel::current()->getFileName(),
862           FlGui::instance()->elementaryContext->input[32]->value(),
863           FlGui::instance()->elementaryContext->input[33]->value(),
864           FlGui::instance()->elementaryContext->input[34]->value(),
865           FlGui::instance()->elementaryContext->input[35]->value(),
866           FlGui::instance()->elementaryContext->input[36]->value(),
867           FlGui::instance()->elementaryContext->input[37]->value(),
868           FlGui::instance()->elementaryContext->input[38]->value());
869         break;
870       case 7:
871         scriptAddCylinder(
872           GModel::current()->getFileName(),
873           FlGui::instance()->elementaryContext->input[39]->value(),
874           FlGui::instance()->elementaryContext->input[40]->value(),
875           FlGui::instance()->elementaryContext->input[41]->value(),
876           FlGui::instance()->elementaryContext->input[42]->value(),
877           FlGui::instance()->elementaryContext->input[43]->value(),
878           FlGui::instance()->elementaryContext->input[44]->value(),
879           FlGui::instance()->elementaryContext->input[45]->value(),
880           FlGui::instance()->elementaryContext->input[46]->value());
881         break;
882       case 8:
883         scriptAddBox(GModel::current()->getFileName(),
884                      FlGui::instance()->elementaryContext->input[47]->value(),
885                      FlGui::instance()->elementaryContext->input[48]->value(),
886                      FlGui::instance()->elementaryContext->input[49]->value(),
887                      FlGui::instance()->elementaryContext->input[50]->value(),
888                      FlGui::instance()->elementaryContext->input[51]->value(),
889                      FlGui::instance()->elementaryContext->input[52]->value());
890         break;
891       case 9:
892         scriptAddTorus(
893           GModel::current()->getFileName(),
894           FlGui::instance()->elementaryContext->input[53]->value(),
895           FlGui::instance()->elementaryContext->input[54]->value(),
896           FlGui::instance()->elementaryContext->input[55]->value(),
897           FlGui::instance()->elementaryContext->input[56]->value(),
898           FlGui::instance()->elementaryContext->input[57]->value(),
899           FlGui::instance()->elementaryContext->input[58]->value());
900         break;
901       case 10:
902         scriptAddCone(GModel::current()->getFileName(),
903                       FlGui::instance()->elementaryContext->input[59]->value(),
904                       FlGui::instance()->elementaryContext->input[60]->value(),
905                       FlGui::instance()->elementaryContext->input[61]->value(),
906                       FlGui::instance()->elementaryContext->input[62]->value(),
907                       FlGui::instance()->elementaryContext->input[63]->value(),
908                       FlGui::instance()->elementaryContext->input[64]->value(),
909                       FlGui::instance()->elementaryContext->input[65]->value(),
910                       FlGui::instance()->elementaryContext->input[66]->value(),
911                       FlGui::instance()->elementaryContext->input[67]->value());
912         break;
913       case 11:
914         scriptAddWedge(
915           GModel::current()->getFileName(),
916           FlGui::instance()->elementaryContext->input[68]->value(),
917           FlGui::instance()->elementaryContext->input[69]->value(),
918           FlGui::instance()->elementaryContext->input[70]->value(),
919           FlGui::instance()->elementaryContext->input[71]->value(),
920           FlGui::instance()->elementaryContext->input[72]->value(),
921           FlGui::instance()->elementaryContext->input[73]->value(),
922           FlGui::instance()->elementaryContext->input[74]->value());
923         break;
924       }
925       FlGui::instance()->resetVisibility();
926       drawContext::global()->draw();
927     }
928     if(ib == 'q') {
929       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
930         for(std::size_t j = 0; j < FlGui::instance()->graph[i]->gl.size(); j++)
931           FlGui::instance()->graph[i]->gl[j]->addPointMode = 0;
932       break;
933     }
934   }
935 
936   FlGui::instance()->elementaryContext->hide();
937   drawContext::setDrawGeomTransientFunction(nullptr);
938 
939   // at the end, not during creation to avoid having things jumping around
940   SetBoundingBox();
941   Msg::StatusGl("");
942 }
943 
add_new_multiline(const std::string & type)944 static void add_new_multiline(const std::string &type)
945 {
946   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
947   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
948   drawContext::global()->draw();
949 
950   std::vector<int> p;
951   while(1) {
952     if(!FlGui::available()) return;
953 
954     if(p.empty())
955       Msg::StatusGl("Select control points\n"
956                     "[Press 'e' to end selection or 'q' to abort]");
957     else
958       Msg::StatusGl("Select control points\n"
959                     "[Press 'e' to end selection, 'u' to undo last selection "
960                     "or 'q' to abort]");
961     char ib = FlGui::instance()->selectEntity(ENT_POINT);
962     if(!FlGui::available()) return;
963     if(ib == 'l') {
964       for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size();
965           i++) {
966         FlGui::instance()->selectedVertices[i]->setSelection(1);
967         p.push_back(FlGui::instance()->selectedVertices[i]->tag());
968       }
969       drawContext::global()->draw();
970     }
971     if(ib == 'r') {
972       Msg::Warning(
973         "Entity de-selection not supported yet during multi-line creation");
974     }
975     if(ib == 'e') {
976       if(p.size() >= 2)
977         scriptAddCurve(type, p, GModel::current()->getFileName());
978       FlGui::instance()->resetVisibility();
979       GModel::current()->setSelection(0);
980       drawContext::global()->draw();
981       p.clear();
982     }
983     if(ib == 'u') {
984       if(p.size()) {
985         GVertex *gv = GModel::current()->getVertexByTag(p.back());
986         if(gv) gv->setSelection(0);
987         drawContext::global()->draw();
988         p.pop_back();
989       }
990     }
991     if(ib == 'q') {
992       GModel::current()->setSelection(0);
993       drawContext::global()->draw();
994       break;
995     }
996   }
997 
998   Msg::StatusGl("");
999 }
1000 
add_new_line()1001 static void add_new_line()
1002 {
1003   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
1004   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1005   drawContext::global()->draw();
1006 
1007   std::vector<int> p;
1008   while(1) {
1009     if(!FlGui::available()) return;
1010 
1011     if(p.empty())
1012       Msg::StatusGl("Select start point\n"
1013                     "[Press 'q' to abort]");
1014     if(p.size() == 1)
1015       Msg::StatusGl("Select end point\n"
1016                     "[Press 'u' to undo last selection or 'q' to abort]");
1017     char ib = FlGui::instance()->selectEntity(ENT_POINT);
1018     if(!FlGui::available()) return;
1019     if(ib == 'l') {
1020       FlGui::instance()->selectedVertices[0]->setSelection(1);
1021       drawContext::global()->draw();
1022       p.push_back(FlGui::instance()->selectedVertices[0]->tag());
1023     }
1024     if(ib == 'r') {
1025       Msg::Warning(
1026         "Entity de-selection not supported yet during curve creation");
1027     }
1028     if(ib == 'u') {
1029       if(p.size()) {
1030         GVertex *gv = GModel::current()->getVertexByTag(p.back());
1031         if(gv) gv->setSelection(0);
1032         drawContext::global()->draw();
1033         p.pop_back();
1034       }
1035     }
1036     if(ib == 'q') {
1037       GModel::current()->setSelection(0);
1038       drawContext::global()->draw();
1039       break;
1040     }
1041     if(p.size() == 2) {
1042       scriptAddCurve("Line", p, GModel::current()->getFileName());
1043       FlGui::instance()->resetVisibility();
1044       GModel::current()->setSelection(0);
1045       drawContext::global()->draw();
1046       p.clear();
1047     }
1048   }
1049 
1050   Msg::StatusGl("");
1051 }
1052 
add_new_circle_arc()1053 static void add_new_circle_arc()
1054 {
1055   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
1056   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1057   drawContext::global()->draw();
1058 
1059   std::vector<int> p;
1060   while(1) {
1061     if(!FlGui::available()) return;
1062 
1063     if(p.empty())
1064       Msg::StatusGl("Select start point\n"
1065                     "[Press 'q' to abort]");
1066     if(p.size() == 1)
1067       Msg::StatusGl("Select center point\n"
1068                     "[Press 'u' to undo last selection or 'q' to abort]");
1069     if(p.size() == 2)
1070       Msg::StatusGl("Select end point\n"
1071                     "[Press 'u' to undo last selection or 'q' to abort]");
1072     char ib = FlGui::instance()->selectEntity(ENT_POINT);
1073     if(!FlGui::available()) return;
1074     if(ib == 'l') {
1075       FlGui::instance()->selectedVertices[0]->setSelection(1);
1076       drawContext::global()->draw();
1077       p.push_back(FlGui::instance()->selectedVertices[0]->tag());
1078     }
1079     if(ib == 'r') {
1080       Msg::Warning(
1081         "Entity de-selection not supported yet during circle creation");
1082     }
1083     if(ib == 'u') {
1084       if(p.size()) {
1085         GVertex *gv = GModel::current()->getVertexByTag(p.back());
1086         if(gv) gv->setSelection(0);
1087         drawContext::global()->draw();
1088         p.pop_back();
1089       }
1090     }
1091     if(ib == 'q') {
1092       GModel::current()->setSelection(0);
1093       drawContext::global()->draw();
1094       break;
1095     }
1096     if(p.size() == 3) {
1097       // begin, center, end
1098       scriptAddCircleArc(p[0], p[1], p[2], GModel::current()->getFileName());
1099       FlGui::instance()->resetVisibility();
1100       GModel::current()->setSelection(0);
1101       drawContext::global()->draw();
1102       p.clear();
1103     }
1104   }
1105 
1106   Msg::StatusGl("");
1107 }
1108 
add_new_ellipse_arc()1109 static void add_new_ellipse_arc()
1110 {
1111   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
1112   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1113   drawContext::global()->draw();
1114 
1115   std::vector<int> p;
1116   while(1) {
1117     if(!FlGui::available()) return;
1118 
1119     if(p.empty())
1120       Msg::StatusGl("Select start point\n"
1121                     "[Press 'q' to abort]");
1122     if(p.size() == 1)
1123       Msg::StatusGl("Select center point\n"
1124                     "[Press 'u' to undo last selection or 'q' to abort]");
1125     if(p.size() == 2)
1126       Msg::StatusGl("Select major axis point\n"
1127                     "[Press 'u' to undo last selection or 'q' to abort]");
1128     if(p.size() == 3)
1129       Msg::StatusGl("Select end point\n"
1130                     "[Press 'u' to undo last selection or 'q' to abort]");
1131     char ib = FlGui::instance()->selectEntity(ENT_POINT);
1132     if(!FlGui::available()) return;
1133     if(ib == 'l') {
1134       FlGui::instance()->selectedVertices[0]->setSelection(1);
1135       drawContext::global()->draw();
1136       p.push_back(FlGui::instance()->selectedVertices[0]->tag());
1137     }
1138     if(ib == 'r') {
1139       Msg::Warning(
1140         "Entity de-selection not supported yet during ellipse creation");
1141     }
1142     if(ib == 'u') {
1143       if(p.size()) {
1144         GVertex *gv = GModel::current()->getVertexByTag(p.back());
1145         if(gv) gv->setSelection(0);
1146         drawContext::global()->draw();
1147         p.pop_back();
1148       }
1149     }
1150     if(ib == 'q') {
1151       GModel::current()->setSelection(0);
1152       drawContext::global()->draw();
1153       break;
1154     }
1155     if(p.size() == 4) {
1156       scriptAddEllipseArc(p[0], p[1], p[2], p[3],
1157                           GModel::current()->getFileName());
1158       FlGui::instance()->resetVisibility();
1159       GModel::current()->setSelection(0);
1160       drawContext::global()->draw();
1161       p.clear();
1162     }
1163   }
1164 
1165   Msg::StatusGl("");
1166 }
1167 
selectContour(int type,int num,List_T * List)1168 static int selectContour(int type, int num, List_T *List)
1169 {
1170   int k = 0;
1171 
1172   switch(type) {
1173   case ENT_CURVE:
1174     k = allEdgesLinked(num, List);
1175     for(int i = 0; i < List_Nbr(List); i++) {
1176       int ip;
1177       List_Read(List, i, &ip);
1178       GEdge *ge = GModel::current()->getEdgeByTag(abs(ip));
1179       if(ge) ge->setSelection(1);
1180     }
1181     break;
1182   case ENT_SURFACE:
1183     k = allFacesLinked(num, List);
1184     for(int i = 0; i < List_Nbr(List); i++) {
1185       int ip;
1186       List_Read(List, i, &ip);
1187       GFace *gf = GModel::current()->getFaceByTag(abs(ip));
1188       if(gf) gf->setSelection(1);
1189     }
1190     break;
1191   }
1192 
1193   drawContext::global()->draw();
1194   return k;
1195 }
1196 
add_new_surface_volume(int mode)1197 static void add_new_surface_volume(int mode)
1198 {
1199   List_T *List1 = List_Create(10, 10, sizeof(int));
1200   List_T *List2 = List_Create(10, 10, sizeof(int));
1201   int type;
1202   if(mode == 2) {
1203     type = ENT_SURFACE;
1204     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
1205     opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
1206   }
1207   else {
1208     type = ENT_CURVE;
1209     opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1210     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
1211   }
1212   drawContext::global()->draw();
1213 
1214   while(1) {
1215     if(!FlGui::available()) return;
1216 
1217     List_Reset(List1);
1218     List_Reset(List2);
1219 
1220     while(1) {
1221       if(!FlGui::available()) return;
1222 
1223       if(type == ENT_CURVE) {
1224         if(!List_Nbr(List1))
1225           Msg::StatusGl("Select surface boundary\n"
1226                         "[Press 'q' to abort]");
1227         else
1228           Msg::StatusGl("Select surface boundary\n"
1229                         "[Press 'u' to undo last selection or 'q' to abort]");
1230       }
1231       else {
1232         if(!List_Nbr(List1))
1233           Msg::StatusGl("Select volume boundary\n"
1234                         "[Press 'q' to abort]");
1235         else
1236           Msg::StatusGl("Select volume boundary\n"
1237                         "[Press 'u' to undo last selection or 'q' to abort]");
1238       }
1239 
1240       char ib = FlGui::instance()->selectEntity(type);
1241       if(!FlGui::available()) return;
1242       if(ib == 'q') {
1243         GModel::current()->setSelection(0);
1244         drawContext::global()->draw();
1245         goto stopall;
1246       }
1247       if(ib == 'u') {
1248         if(List_Nbr(List1) > 0) {
1249           int num;
1250           List_Read(List1, List_Nbr(List1) - 1, &num);
1251           if(type == ENT_CURVE) {
1252             GEdge *ge = GModel::current()->getEdgeByTag(abs(num));
1253             if(ge) ge->setSelection(0);
1254           }
1255           else {
1256             GFace *gf = GModel::current()->getFaceByTag(abs(num));
1257             if(gf) gf->setSelection(0);
1258           }
1259           List_Pop(List1);
1260           drawContext::global()->draw();
1261         }
1262       }
1263       if(ib == 'r') {
1264         Msg::Warning("Entity de-selection not supported yet during "
1265                      "surface/volume creation");
1266       }
1267       if(ib == 'l') {
1268         int num = (type == ENT_CURVE) ?
1269                     FlGui::instance()->selectedEdges[0]->tag() :
1270                     FlGui::instance()->selectedFaces[0]->tag();
1271         if(selectContour(type, num, List1)) {
1272           if(type == ENT_CURVE)
1273             scriptAddCurveLoop(List1, GModel::current()->getFileName(), &num);
1274           else
1275             scriptAddSurfaceLoop(List1, GModel::current()->getFileName(), &num);
1276           List_Reset(List1);
1277           List_Add(List2, &num);
1278           while(1) {
1279             if(!FlGui::available()) return;
1280 
1281             if(!List_Nbr(List1))
1282               Msg::StatusGl("Select hole boundaries (if none, press 'e')\n"
1283                             "[Press 'e' to end selection or 'q' to abort]");
1284             else
1285               Msg::StatusGl(
1286                 "Select hole boundaries\n"
1287                 "[Press 'e' to end selection, 'u' to undo last selection "
1288                 "or 'q' to abort]");
1289             ib = FlGui::instance()->selectEntity(type);
1290             if(!FlGui::available()) return;
1291             if(ib == 'q') {
1292               GModel::current()->setSelection(0);
1293               drawContext::global()->draw();
1294               goto stopall;
1295             }
1296             if(ib == 'e') {
1297               GModel::current()->setSelection(0);
1298               drawContext::global()->draw();
1299               List_Reset(List1);
1300               break;
1301             }
1302             if(ib == 'u') {
1303               if(List_Nbr(List1) > 0) {
1304                 int num;
1305                 List_Read(List1, List_Nbr(List1) - 1, &num);
1306                 if(type == ENT_CURVE) {
1307                   GEdge *ge = GModel::current()->getEdgeByTag(abs(num));
1308                   if(ge) ge->setSelection(0);
1309                 }
1310                 else {
1311                   GFace *gf = GModel::current()->getFaceByTag(abs(num));
1312                   if(gf) gf->setSelection(0);
1313                 }
1314                 List_Pop(List1);
1315                 drawContext::global()->draw();
1316               }
1317             }
1318             if(ib == 'l') {
1319               int size = (type == ENT_CURVE) ?
1320                            FlGui::instance()->selectedEdges.size() :
1321                            FlGui::instance()->selectedFaces.size();
1322               for(int i = 0; i < size; i++) {
1323                 int num = (type == ENT_CURVE) ?
1324                             FlGui::instance()->selectedEdges[i]->tag() :
1325                             FlGui::instance()->selectedFaces[i]->tag();
1326                 if(selectContour(type, num, List1)) {
1327                   if(type == ENT_CURVE)
1328                     scriptAddCurveLoop(List1, GModel::current()->getFileName(),
1329                                        &num);
1330                   else
1331                     scriptAddSurfaceLoop(
1332                       List1, GModel::current()->getFileName(), &num);
1333                   List_Reset(List1);
1334                   List_Add(List2, &num);
1335                 }
1336               }
1337             }
1338             if(ib == 'r') {
1339               Msg::Warning("Entity de-selection not supported yet during "
1340                            "surface/volume creation");
1341             }
1342           }
1343           List_Unique(List2, fcmp_absint);
1344           if(List_Nbr(List2)) {
1345             switch(mode) {
1346             case 0:
1347               scriptAddSurface("Plane Surface", List2,
1348                                GModel::current()->getFileName());
1349               break;
1350             case 1:
1351               scriptAddSurface("Surface", List2,
1352                                GModel::current()->getFileName());
1353               break;
1354             case 2:
1355               scriptAddVolume(List2, GModel::current()->getFileName());
1356               break;
1357             }
1358             FlGui::instance()->resetVisibility();
1359             GModel::current()->setSelection(0);
1360             drawContext::global()->draw();
1361             break;
1362           }
1363         } // if selectContour
1364       }
1365     }
1366   }
1367 
1368 stopall:
1369   List_Delete(List1);
1370   List_Delete(List2);
1371 
1372   Msg::StatusGl("");
1373 }
1374 
geometry_elementary_set_factory_cb(Fl_Widget * w,void * data)1375 static void geometry_elementary_set_factory_cb(Fl_Widget *w, void *data)
1376 {
1377   if(!data) return;
1378   std::string str((const char *)data);
1379   scriptSetFactory(str, GModel::current()->getFileName());
1380   if(FlGui::available())
1381     Msg::StatusBar(false, "Setting %s factory", str.c_str());
1382 }
1383 
geometry_elementary_add_new_cb(Fl_Widget * w,void * data)1384 static void geometry_elementary_add_new_cb(Fl_Widget *w, void *data)
1385 {
1386   if(!data) return;
1387 
1388   std::string str((const char *)data);
1389   if(str == "Parameter")
1390     FlGui::instance()->elementaryContext->show(0);
1391   else if(str == "Point")
1392     add_new_point_based_entity(str, 1);
1393   else if(str == "Line")
1394     add_new_line();
1395   else if(str == "Spline")
1396     add_new_multiline(str);
1397   else if(str == "BSpline")
1398     add_new_multiline(str);
1399   else if(str == "Bezier")
1400     add_new_multiline(str);
1401   else if(str == "Circle arc")
1402     add_new_circle_arc();
1403   else if(str == "Circle")
1404     add_new_point_based_entity(str, 2);
1405   else if(str == "Ellipse arc")
1406     add_new_ellipse_arc();
1407   else if(str == "Ellipse")
1408     add_new_point_based_entity(str, 3);
1409   else if(str == "Disk")
1410     add_new_point_based_entity(str, 4);
1411   else if(str == "Rectangle")
1412     add_new_point_based_entity(str, 5);
1413   else if(str == "Sphere")
1414     add_new_point_based_entity(str, 6);
1415   else if(str == "Cylinder")
1416     add_new_point_based_entity(str, 7);
1417   else if(str == "Box")
1418     add_new_point_based_entity(str, 8);
1419   else if(str == "Torus")
1420     add_new_point_based_entity(str, 9);
1421   else if(str == "Cone")
1422     add_new_point_based_entity(str, 10);
1423   else if(str == "Wedge")
1424     add_new_point_based_entity(str, 11);
1425   else if(str == "Plane Surface")
1426     add_new_surface_volume(0);
1427   else if(str == "Surface")
1428     add_new_surface_volume(1);
1429   else if(str == "Volume")
1430     add_new_surface_volume(2);
1431   else
1432     Msg::Error("Unknown entity to create: %s", str.c_str());
1433 }
1434 
action_point_line_surface_volume(int action,const std::string & onwhat="")1435 static void action_point_line_surface_volume(int action,
1436                                              const std::string &onwhat = "")
1437 {
1438   drawContext::global()->draw();
1439 
1440   std::string what(onwhat);
1441   if(what == "Point")
1442     opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
1443   else if(what == "Curve")
1444     opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1445   else if(what == "Surface")
1446     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
1447   else if(what == "Volume")
1448     opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
1449 
1450   std::vector<std::pair<int, int> > dimTags, dimTagsSaved;
1451   while(1) {
1452     if(!FlGui::available()) return;
1453 
1454     std::string str;
1455     int type;
1456     if(what == "Point") {
1457       str = "points";
1458       type = ENT_POINT;
1459     }
1460     else if(what == "Curve") {
1461       str = "curves";
1462       type = ENT_CURVE;
1463     }
1464     else if(what == "Surface") {
1465       str = "surfaces";
1466       type = ENT_SURFACE;
1467     }
1468     else if(what == "Volume") {
1469       str = "volumes";
1470       type = ENT_VOLUME;
1471     }
1472     else {
1473       switch(FlGui::instance()->transformContext->choice->value()) {
1474       case 1:
1475         str = "points";
1476         type = ENT_POINT;
1477         break;
1478       case 2:
1479         str = "curves";
1480         type = ENT_CURVE;
1481         break;
1482       case 3:
1483         str = "surfaces";
1484         type = ENT_SURFACE;
1485         break;
1486       case 4:
1487         str = "volumes";
1488         type = ENT_VOLUME;
1489         break;
1490       default:
1491         str = "entities";
1492         type = ENT_ALL;
1493         break;
1494       }
1495     }
1496 
1497     if(dimTags.empty())
1498       Msg::StatusGl("Select %s\n"
1499                     "[Press 'e' to end selection or 'q' to abort]",
1500                     str.c_str());
1501     else
1502       Msg::StatusGl("Select %s\n"
1503                     "[Press 'e' to end selection, 'u' to undo last selection "
1504                     "or 'q' to abort]",
1505                     str.c_str());
1506 
1507     char ib = FlGui::instance()->selectEntity(type);
1508     if(!FlGui::available()) return;
1509     if(ib == 'l') {
1510       for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size();
1511           i++) {
1512         FlGui::instance()->selectedVertices[i]->setSelection(1);
1513         std::pair<int, int> t(0, FlGui::instance()->selectedVertices[i]->tag());
1514         if(std::find(dimTags.begin(), dimTags.end(), t) == dimTags.end())
1515           dimTags.push_back(t);
1516       }
1517       for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size(); i++) {
1518         FlGui::instance()->selectedEdges[i]->setSelection(1);
1519         std::pair<int, int> t(1, FlGui::instance()->selectedEdges[i]->tag());
1520         if(std::find(dimTags.begin(), dimTags.end(), t) == dimTags.end())
1521           dimTags.push_back(t);
1522       }
1523       for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size(); i++) {
1524         FlGui::instance()->selectedFaces[i]->setSelection(1);
1525         std::pair<int, int> t(2, FlGui::instance()->selectedFaces[i]->tag());
1526         if(std::find(dimTags.begin(), dimTags.end(), t) == dimTags.end())
1527           dimTags.push_back(t);
1528       }
1529       for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
1530           i++) {
1531         FlGui::instance()->selectedRegions[i]->setSelection(1);
1532         std::pair<int, int> t(3, FlGui::instance()->selectedRegions[i]->tag());
1533         if(std::find(dimTags.begin(), dimTags.end(), t) == dimTags.end())
1534           dimTags.push_back(t);
1535       }
1536       drawContext::global()->draw();
1537     }
1538     if(ib == 'r') {
1539       for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size();
1540           i++) {
1541         std::pair<int, int> t(0, FlGui::instance()->selectedVertices[i]->tag());
1542         auto it = std::find(dimTags.begin(), dimTags.end(), t);
1543         if(it != dimTags.end()) {
1544           dimTags.erase(it);
1545           GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1546           if(ge) ge->setSelection(0);
1547         }
1548       }
1549       for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size(); i++) {
1550         std::pair<int, int> t(1, FlGui::instance()->selectedEdges[i]->tag());
1551         auto it = std::find(dimTags.begin(), dimTags.end(), t);
1552         if(it != dimTags.end()) {
1553           dimTags.erase(it);
1554           GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1555           if(ge) ge->setSelection(0);
1556         }
1557       }
1558       for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size(); i++) {
1559         std::pair<int, int> t(2, FlGui::instance()->selectedFaces[i]->tag());
1560         auto it = std::find(dimTags.begin(), dimTags.end(), t);
1561         if(it != dimTags.end()) {
1562           dimTags.erase(it);
1563           GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1564           if(ge) ge->setSelection(0);
1565         }
1566       }
1567       for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
1568           i++) {
1569         std::pair<int, int> t(3, FlGui::instance()->selectedRegions[i]->tag());
1570         auto it = std::find(dimTags.begin(), dimTags.end(), t);
1571         if(it != dimTags.end()) {
1572           dimTags.erase(it);
1573           GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1574           if(ge) ge->setSelection(0);
1575         }
1576       }
1577       drawContext::global()->draw();
1578     }
1579     if(ib == 'u') {
1580       if(dimTags.size()) {
1581         std::pair<int, int> t = dimTags.back();
1582         GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1583         if(ge) ge->setSelection(0);
1584         dimTags.pop_back();
1585         drawContext::global()->draw();
1586       }
1587     }
1588     if(ib == 'i') { Msg::Error("Inverting selection!"); }
1589     if(ib == 'e') {
1590       if(dimTags.size()) {
1591         switch(action) {
1592         case 0:
1593           scriptTranslate(
1594             GModel::current()->getFileName(), dimTags,
1595             FlGui::instance()->transformContext->input[0]->value(),
1596             FlGui::instance()->transformContext->input[1]->value(),
1597             FlGui::instance()->transformContext->input[2]->value(),
1598             FlGui::instance()->transformContext->butt[0]->value());
1599           break;
1600         case 1:
1601           scriptRotate(GModel::current()->getFileName(), dimTags,
1602                        FlGui::instance()->transformContext->input[6]->value(),
1603                        FlGui::instance()->transformContext->input[7]->value(),
1604                        FlGui::instance()->transformContext->input[8]->value(),
1605                        FlGui::instance()->transformContext->input[3]->value(),
1606                        FlGui::instance()->transformContext->input[4]->value(),
1607                        FlGui::instance()->transformContext->input[5]->value(),
1608                        FlGui::instance()->transformContext->input[9]->value(),
1609                        FlGui::instance()->transformContext->butt[1]->value());
1610           break;
1611         case 2:
1612           scriptDilate(GModel::current()->getFileName(), dimTags,
1613                        FlGui::instance()->transformContext->input[10]->value(),
1614                        FlGui::instance()->transformContext->input[11]->value(),
1615                        FlGui::instance()->transformContext->input[12]->value(),
1616                        FlGui::instance()->transformContext->input[13]->value(),
1617                        FlGui::instance()->transformContext->input[14]->value(),
1618                        FlGui::instance()->transformContext->input[15]->value(),
1619                        FlGui::instance()->transformContext->butt[2]->value());
1620           break;
1621         case 3:
1622           scriptMirror(GModel::current()->getFileName(), dimTags,
1623                        FlGui::instance()->transformContext->input[16]->value(),
1624                        FlGui::instance()->transformContext->input[17]->value(),
1625                        FlGui::instance()->transformContext->input[18]->value(),
1626                        FlGui::instance()->transformContext->input[19]->value(),
1627                        FlGui::instance()->transformContext->butt[3]->value());
1628           break;
1629         case 4:
1630           scriptExtrude(GModel::current()->getFileName(), dimTags,
1631                         FlGui::instance()->transformContext->input[0]->value(),
1632                         FlGui::instance()->transformContext->input[1]->value(),
1633                         FlGui::instance()->transformContext->input[2]->value(),
1634                         FlGui::instance()->transformContext->butt[7]->value(),
1635                         FlGui::instance()->transformContext->input[21]->value(),
1636                         FlGui::instance()->transformContext->butt[8]->value());
1637           break;
1638         case 5:
1639           scriptProtude(GModel::current()->getFileName(), dimTags,
1640                         FlGui::instance()->transformContext->input[6]->value(),
1641                         FlGui::instance()->transformContext->input[7]->value(),
1642                         FlGui::instance()->transformContext->input[8]->value(),
1643                         FlGui::instance()->transformContext->input[3]->value(),
1644                         FlGui::instance()->transformContext->input[4]->value(),
1645                         FlGui::instance()->transformContext->input[5]->value(),
1646                         FlGui::instance()->transformContext->input[9]->value(),
1647                         FlGui::instance()->transformContext->butt[9]->value(),
1648                         FlGui::instance()->transformContext->input[22]->value(),
1649                         FlGui::instance()->transformContext->butt[10]->value());
1650           break;
1651         case 6:
1652           scriptDeleteEntities(
1653             GModel::current()->getFileName(), dimTags,
1654             FlGui::instance()->transformContext->butt[6]->value());
1655           break;
1656         case 7:
1657         case 11: {
1658           std::vector<int> tags;
1659           int dim = 0;
1660           for(std::size_t i = 0; i < dimTags.size(); i++) {
1661             if((dimTags[i].first == 0 && what == "Point") ||
1662                (dimTags[i].first == 1 && what == "Curve") ||
1663                (dimTags[i].first == 2 && what == "Surface") ||
1664                (dimTags[i].first == 3 && what == "Volume")) {
1665               dim = dimTags[i].first;
1666               tags.push_back(dimTags[i].second);
1667             }
1668           }
1669           scriptAddRemovePhysicalGroup(
1670             GModel::current()->getFileName(), what, tags,
1671             FlGui::instance()->physicalContext->selectedName,
1672             FlGui::instance()->physicalContext->selectedTag,
1673             FlGui::instance()->physicalContext->append,
1674             FlGui::instance()->physicalContext->mode);
1675           if(!FlGui::available()) return;
1676 
1677           // ask clients to update using the new physical definition
1678           onelab_cb(nullptr, (void *)"check");
1679 
1680           // if onelab context parameters are defined for the physical group,
1681           // show the parameter definition window and abort the physical group
1682           // creation loop (otherwise events cannot be processed by outside
1683           // codes through the api, as we are stuck in the while(1) for the
1684           // physical creation mode)
1685           std::vector<std::string> param;
1686           onelab::server::instance()->getParameterNames(
1687             param, "ONELAB Context/" + what + " Template");
1688           if(tags.size() && param.size() && action == 7) {
1689             FlGui::instance()->getCurrentOpenglWindow()->quitSelection = 1;
1690             FlGui::instance()->getCurrentOpenglWindow()->selectionMode = false;
1691             GModel::current()->setSelection(0);
1692             FlGui::instance()->onelabContext->show(dim, tags[0]);
1693             ib = 'z';
1694           }
1695           else {
1696             FlGui::instance()->physicalContext->show(what, action == 7 ? false :
1697                                                                          true);
1698           }
1699         } break;
1700         case 8: {
1701           std::vector<int> tags;
1702           for(std::size_t i = 0; i < dimTags.size(); i++) {
1703             if(dimTags[i].first == 0 && what == "Point")
1704               tags.push_back(dimTags[i].second);
1705           }
1706           if(tags.size())
1707             scriptSetMeshSize(
1708               GModel::current()->getFileName(), tags,
1709               FlGui::instance()->meshContext->input[0]->value());
1710         } break;
1711         case 9: {
1712           std::vector<int> tags;
1713           for(std::size_t i = 0; i < dimTags.size(); i++) {
1714             if(dimTags[i].first == 2 && what == "Surface")
1715               tags.push_back(dimTags[i].second);
1716           }
1717           scriptRecombineSurface(GModel::current()->getFileName(), tags);
1718         } break;
1719         case 10: {
1720           std::vector<int> tags;
1721           for(std::size_t i = 0; i < dimTags.size(); i++) {
1722             if((dimTags[i].first == 1 && what == "Curve") ||
1723                (dimTags[i].first == 2 && what == "Surface") ||
1724                (dimTags[i].first == 3 && what == "Volume"))
1725               tags.push_back(dimTags[i].second);
1726           }
1727           scriptSetCompound(GModel::current()->getFileName(), what, tags);
1728         } break;
1729         case 12:
1730           if(dimTagsSaved.empty()) {
1731             dimTagsSaved = dimTags;
1732             dimTags.clear();
1733             what = "Curve";
1734             continue;
1735           }
1736           else {
1737             std::vector<int> l;
1738             for(std::size_t i = 0; i < dimTags.size(); i++) {
1739               if(dimTags[i].first == 1) l.push_back(dimTags[i].second);
1740             }
1741             scriptAddPipe(GModel::current()->getFileName(), dimTagsSaved, l);
1742             dimTagsSaved.clear();
1743           }
1744           break;
1745         default: Msg::Error("Unknown action on selected entities"); break;
1746         }
1747         dimTags.clear();
1748         FlGui::instance()->resetVisibility();
1749         if(ib == 'z') break; // done: onelab context now shown
1750         GModel::current()->setSelection(0);
1751         if(action <= 6 || action >= 12) SetBoundingBox();
1752         drawContext::global()->draw();
1753       }
1754     }
1755     if(ib == 'q') {
1756       GModel::current()->setSelection(0);
1757       drawContext::global()->draw();
1758       break;
1759     }
1760   }
1761 
1762   Msg::StatusGl("");
1763 }
1764 
geometry_elementary_translate_cb(Fl_Widget * w,void * data)1765 static void geometry_elementary_translate_cb(Fl_Widget *w, void *data)
1766 {
1767   FlGui::instance()->transformContext->show(0);
1768   action_point_line_surface_volume(0);
1769   if(!FlGui::available()) return;
1770   FlGui::instance()->transformContext->hide();
1771 }
1772 
geometry_elementary_rotate_cb(Fl_Widget * w,void * data)1773 static void geometry_elementary_rotate_cb(Fl_Widget *w, void *data)
1774 {
1775   FlGui::instance()->transformContext->show(1);
1776   action_point_line_surface_volume(1);
1777   if(!FlGui::available()) return;
1778   FlGui::instance()->transformContext->hide();
1779 }
1780 
geometry_elementary_scale_cb(Fl_Widget * w,void * data)1781 static void geometry_elementary_scale_cb(Fl_Widget *w, void *data)
1782 {
1783   FlGui::instance()->transformContext->show(2);
1784   action_point_line_surface_volume(2);
1785   if(!FlGui::available()) return;
1786   FlGui::instance()->transformContext->hide();
1787 }
1788 
geometry_elementary_symmetry_cb(Fl_Widget * w,void * data)1789 static void geometry_elementary_symmetry_cb(Fl_Widget *w, void *data)
1790 {
1791   FlGui::instance()->transformContext->show(3);
1792   action_point_line_surface_volume(3);
1793   if(!FlGui::available()) return;
1794   FlGui::instance()->transformContext->hide();
1795 }
1796 
geometry_elementary_extrude_translate_cb(Fl_Widget * w,void * data)1797 static void geometry_elementary_extrude_translate_cb(Fl_Widget *w, void *data)
1798 {
1799   FlGui::instance()->transformContext->show(0, true);
1800   action_point_line_surface_volume(4);
1801   if(!FlGui::available()) return;
1802   FlGui::instance()->transformContext->hide();
1803 }
1804 
geometry_elementary_extrude_rotate_cb(Fl_Widget * w,void * data)1805 static void geometry_elementary_extrude_rotate_cb(Fl_Widget *w, void *data)
1806 {
1807   FlGui::instance()->transformContext->show(1, true);
1808   action_point_line_surface_volume(5);
1809   if(!FlGui::available()) return;
1810   FlGui::instance()->transformContext->hide();
1811 }
1812 
geometry_elementary_pipe_cb(Fl_Widget * w,void * data)1813 static void geometry_elementary_pipe_cb(Fl_Widget *w, void *data)
1814 {
1815   FlGui::instance()->transformContext->show(-1, true);
1816   action_point_line_surface_volume(12);
1817   if(!FlGui::available()) return;
1818   FlGui::instance()->transformContext->hide();
1819 }
1820 
geometry_elementary_delete_cb(Fl_Widget * w,void * data)1821 static void geometry_elementary_delete_cb(Fl_Widget *w, void *data)
1822 {
1823   FlGui::instance()->transformContext->show(6);
1824   action_point_line_surface_volume(6);
1825   if(!FlGui::available()) return;
1826   FlGui::instance()->transformContext->hide();
1827 }
1828 
geometry_elementary_boolean_cb(Fl_Widget * w,void * data)1829 static void geometry_elementary_boolean_cb(Fl_Widget *w, void *data)
1830 {
1831   if(!data) return;
1832   FlGui::instance()->transformContext->show(4);
1833 
1834   std::string mode((const char *)data);
1835   bool selectObject = true;
1836   std::vector<std::pair<int, int> > object, tool;
1837 
1838   while(1) {
1839     if(!FlGui::available()) return;
1840 
1841     if(object.empty())
1842       Msg::StatusGl("Select object\n"
1843                     "[Press 'e' to end selection or 'q' to abort]");
1844     else if(selectObject)
1845       Msg::StatusGl(
1846         "Select object\n"
1847         "[Press 'e' to end selection, 'u' to undo last selection or "
1848         "'q' to abort]");
1849     else if(tool.empty())
1850       Msg::StatusGl("Select tool\n"
1851                     "[Press 'e' to end selection or 'q' to abort]");
1852     else
1853       Msg::StatusGl(
1854         "Select tool\n"
1855         "[Press 'e' to end selection, 'u' to undo last selection or "
1856         "'q' to abort]");
1857 
1858     int type = ENT_ALL;
1859     switch(FlGui::instance()->transformContext->choice->value()) {
1860     case 1: type = ENT_POINT; break;
1861     case 2: type = ENT_CURVE; break;
1862     case 3: type = ENT_SURFACE; break;
1863     case 4: type = ENT_VOLUME; break;
1864     }
1865 
1866     char ib = FlGui::instance()->selectEntity(type);
1867     if(!FlGui::available()) return;
1868     if(ib == 'l') {
1869       for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size(); i++) {
1870         if(FlGui::instance()->selectedVertices[i]->getSelection() != 1) {
1871           FlGui::instance()->selectedVertices[i]->setSelection(1);
1872           std::pair<int, int> t(0, FlGui::instance()->selectedVertices[i]->tag());
1873           if(selectObject)
1874             object.push_back(t);
1875           else
1876             tool.push_back(t);
1877         }
1878       }
1879       for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size(); i++) {
1880         if(FlGui::instance()->selectedEdges[i]->getSelection() != 1) {
1881           FlGui::instance()->selectedEdges[i]->setSelection(1);
1882           std::pair<int, int> t(1, FlGui::instance()->selectedEdges[i]->tag());
1883           if(selectObject)
1884             object.push_back(t);
1885           else
1886             tool.push_back(t);
1887         }
1888       }
1889       for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size(); i++) {
1890         if(FlGui::instance()->selectedFaces[i]->getSelection() != 1) {
1891           FlGui::instance()->selectedFaces[i]->setSelection(1);
1892           std::pair<int, int> t(2, FlGui::instance()->selectedFaces[i]->tag());
1893           if(selectObject)
1894             object.push_back(t);
1895           else
1896             tool.push_back(t);
1897         }
1898       }
1899       for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
1900           i++) {
1901         if(FlGui::instance()->selectedRegions[i]->getSelection() != 1) {
1902           FlGui::instance()->selectedRegions[i]->setSelection(1);
1903           std::pair<int, int> t(3,
1904                                 FlGui::instance()->selectedRegions[i]->tag());
1905           if(selectObject)
1906             object.push_back(t);
1907           else
1908             tool.push_back(t);
1909         }
1910       }
1911     }
1912     if(ib == 'r') {
1913       Msg::Warning(
1914         "Entity de-selection not supported yet during boolean operation");
1915     }
1916     if(ib == 'u') {
1917       if(selectObject && object.size()) {
1918         std::pair<int, int> t = object.back();
1919         GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1920         if(ge) ge->setSelection(0);
1921         object.pop_back();
1922       }
1923       else if(tool.size()) {
1924         std::pair<int, int> t = tool.back();
1925         GEntity *ge = GModel::current()->getEntityByTag(t.first, t.second);
1926         if(ge) ge->setSelection(0);
1927         tool.pop_back();
1928       }
1929     }
1930     if(ib == 'e') {
1931       if(selectObject) {
1932         if(object.empty())
1933           Msg::Error("At least one object must be selected");
1934         else
1935           selectObject = false;
1936       }
1937       else if(tool.empty() && mode != "BooleanFragments") {
1938         Msg::Error("At least one tool must be selected");
1939       }
1940       else {
1941         scriptBoolean(GModel::current()->getFileName(), mode, object, tool,
1942                       FlGui::instance()->transformContext->butt[4]->value(),
1943                       tool.size() ?
1944                         FlGui::instance()->transformContext->butt[5]->value() :
1945                         0);
1946         GModel::current()->setSelection(0);
1947         selectObject = true;
1948         object.clear();
1949         tool.clear();
1950       }
1951     }
1952     if(ib == 'q') {
1953       GModel::current()->setSelection(0);
1954       break;
1955     }
1956   }
1957 
1958   FlGui::instance()->transformContext->hide();
1959   drawContext::global()->draw();
1960   Msg::StatusGl("");
1961 }
1962 
geometry_elementary_fillet_cb(Fl_Widget * w,void * data)1963 static void geometry_elementary_fillet_cb(Fl_Widget *w, void *data)
1964 {
1965   opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1);
1966   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
1967 
1968   FlGui::instance()->transformContext->show(5, false, false);
1969 
1970   bool selectRegions = true;
1971   std::vector<int> regions, edges;
1972 
1973   while(1) {
1974     if(!FlGui::available()) return;
1975 
1976     if(regions.empty())
1977       Msg::StatusGl("Select volume\n"
1978                     "[Press 'e' to end selection or 'q' to abort]");
1979     else if(selectRegions)
1980       Msg::StatusGl(
1981         "Select volume\n"
1982         "[Press 'e' to end selection, 'u' to undo last selection or "
1983         "'q' to abort]");
1984     else if(edges.empty())
1985       Msg::StatusGl("Select curve\n"
1986                     "[Press 'e' to end selection or 'q' to abort]");
1987     else
1988       Msg::StatusGl(
1989         "Select curve\n"
1990         "[Press 'e' to end selection, 'u' to undo last selection or "
1991         "'q' to abort]");
1992 
1993     char ib =
1994       FlGui::instance()->selectEntity(selectRegions ? ENT_VOLUME : ENT_CURVE);
1995     if(!FlGui::available()) return;
1996     if(ib == 'l') {
1997       for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size(); i++) {
1998         if(FlGui::instance()->selectedEdges[i]->getSelection() != 1) {
1999           FlGui::instance()->selectedEdges[i]->setSelection(1);
2000           edges.push_back(FlGui::instance()->selectedEdges[i]->tag());
2001         }
2002       }
2003       for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
2004           i++) {
2005         if(FlGui::instance()->selectedRegions[i]->getSelection() != 1) {
2006           FlGui::instance()->selectedRegions[i]->setSelection(1);
2007           regions.push_back(FlGui::instance()->selectedRegions[i]->tag());
2008         }
2009       }
2010     }
2011     if(ib == 'r') {
2012       Msg::Warning(
2013         "Entity de-selection not supported yet during boolean operation");
2014     }
2015     if(ib == 'u') {
2016       if(selectRegions && regions.size()) {
2017         GRegion *ge = GModel::current()->getRegionByTag(regions.back());
2018         if(ge) ge->setSelection(0);
2019         regions.pop_back();
2020       }
2021       else if(edges.size()) {
2022         GEdge *ge = GModel::current()->getEdgeByTag(edges.back());
2023         if(ge) ge->setSelection(0);
2024         edges.pop_back();
2025       }
2026     }
2027     if(ib == 'e') {
2028       if(selectRegions) {
2029         if(regions.empty())
2030           Msg::Error("At least one volume must be selected");
2031         else
2032           selectRegions = false;
2033       }
2034       else if(edges.empty()) {
2035         Msg::Error("At least one curve must be selected");
2036       }
2037       else {
2038         scriptFillet(GModel::current()->getFileName(), regions, edges,
2039                      FlGui::instance()->transformContext->input[20]->value());
2040         GModel::current()->setSelection(0);
2041         selectRegions = true;
2042         regions.clear();
2043         edges.clear();
2044       }
2045     }
2046     if(ib == 'q') {
2047       GModel::current()->setSelection(0);
2048       break;
2049     }
2050   }
2051 
2052   FlGui::instance()->transformContext->hide();
2053   drawContext::global()->draw();
2054   Msg::StatusGl("");
2055 }
2056 
geometry_elementary_split_cb(Fl_Widget * w,void * data)2057 static void geometry_elementary_split_cb(Fl_Widget *w, void *data)
2058 {
2059   if(!data) return;
2060   opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
2061   drawContext::global()->draw();
2062   Msg::StatusGl("Select curve to split\n"
2063                 "[Press 'q' to abort]");
2064   GEdge *edge_to_split = nullptr;
2065   while(1) {
2066     if(!FlGui::available()) return;
2067 
2068     char ib = FlGui::instance()->selectEntity(ENT_CURVE);
2069     if(!FlGui::available()) return;
2070     if(ib == 'q') { break; }
2071     if(!FlGui::instance()->selectedEdges.empty()) {
2072       edge_to_split = FlGui::instance()->selectedEdges[0];
2073       edge_to_split->setSelection(1);
2074       break;
2075     }
2076   }
2077   Msg::StatusGl("");
2078   if(FlGui::instance()->selectedEdges.empty()) return;
2079   List_T *List1 = List_Create(5, 5, sizeof(int));
2080   Msg::StatusGl("Select break points\n"
2081                 "[Press 'e' to end selection or 'q' to abort]");
2082   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
2083   drawContext::global()->draw();
2084   while(1) {
2085     if(!FlGui::available()) return;
2086 
2087     char ib = FlGui::instance()->selectEntity(ENT_POINT);
2088     if(!FlGui::available()) return;
2089     if(ib == 'q') { break; }
2090     if(ib == 'e' && edge_to_split) {
2091       scriptSplitCurve(edge_to_split->tag(), List1,
2092                        GModel::current()->getFileName());
2093       break;
2094     }
2095     for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size();
2096         i++) {
2097       int tag = FlGui::instance()->selectedVertices[i]->tag();
2098       int index = List_ISearchSeq(List1, &tag, fcmp_int);
2099       if(index < 0) List_Add(List1, &tag);
2100       FlGui::instance()->selectedVertices[i]->setSelection(1);
2101     }
2102   }
2103   Msg::StatusGl("");
2104   FlGui::instance()->resetVisibility();
2105   GModel::current()->setSelection(0);
2106   drawContext::global()->draw();
2107 }
2108 
geometry_elementary_coherence_cb(Fl_Widget * w,void * data)2109 static void geometry_elementary_coherence_cb(Fl_Widget *w, void *data)
2110 {
2111   scriptCoherence(GModel::current()->getFileName());
2112 }
2113 
geometry_physical_add_cb(Fl_Widget * w,void * data)2114 static void geometry_physical_add_cb(Fl_Widget *w, void *data)
2115 {
2116   if(!data) return;
2117   std::string what((const char *)data);
2118   FlGui::instance()->physicalContext->show(what, false);
2119   action_point_line_surface_volume(7, what);
2120   if(!FlGui::available()) return;
2121   FlGui::instance()->physicalContext->hide();
2122 }
2123 
geometry_physical_remove_cb(Fl_Widget * w,void * data)2124 static void geometry_physical_remove_cb(Fl_Widget *w, void *data)
2125 {
2126   if(!data) return;
2127   std::string what((const char *)data);
2128   FlGui::instance()->physicalContext->show(what, true);
2129   action_point_line_surface_volume(11, what);
2130   if(FlGui::available()) FlGui::instance()->physicalContext->hide();
2131 }
2132 
mesh_save_cb(Fl_Widget * w,void * data)2133 void mesh_save_cb(Fl_Widget *w, void *data)
2134 {
2135   std::string name = CTX::instance()->outputFileName;
2136   if(name.empty()) {
2137     if(CTX::instance()->mesh.fileFormat == FORMAT_AUTO)
2138       name = GetDefaultFileName(FORMAT_MSH);
2139     else
2140       name = GetDefaultFileName(CTX::instance()->mesh.fileFormat);
2141   }
2142   if(CTX::instance()->confirmOverwrite) {
2143     if(!StatFile(name))
2144       if(!fl_choice("File '%s' already exists.\n\nDo you want to replace it?",
2145                     "Cancel", "Replace", nullptr, name.c_str()))
2146         return;
2147   }
2148   CreateOutputFile(name, CTX::instance()->mesh.fileFormat);
2149 }
2150 
mesh_1d_cb(Fl_Widget * w,void * data)2151 void mesh_1d_cb(Fl_Widget *w, void *data)
2152 {
2153   GModel::current()->mesh(1);
2154   drawContext::global()->draw();
2155 }
2156 
mesh_2d_cb(Fl_Widget * w,void * data)2157 void mesh_2d_cb(Fl_Widget *w, void *data)
2158 {
2159   GModel::current()->mesh(2);
2160   drawContext::global()->draw();
2161 }
2162 
mesh_3d_cb(Fl_Widget * w,void * data)2163 void mesh_3d_cb(Fl_Widget *w, void *data)
2164 {
2165   GModel::current()->mesh(3);
2166   drawContext::global()->draw();
2167 }
2168 
mesh_modify_parts(Fl_Widget * w,void * data,const std::string & action)2169 static void mesh_modify_parts(Fl_Widget *w, void *data,
2170                               const std::string &action)
2171 {
2172   const char *str = (const char *)data;
2173   int what;
2174 
2175   if(!strcmp(str, "elements")) {
2176     CTX::instance()->pickElements = 1;
2177     what = ENT_ALL;
2178   }
2179   else if(!strcmp(str, "curves")) {
2180     CTX::instance()->pickElements = 0;
2181     what = ENT_CURVE;
2182   }
2183   else if(!strcmp(str, "surfaces")) {
2184     CTX::instance()->pickElements = 0;
2185     what = ENT_SURFACE;
2186   }
2187   else if(!strcmp(str, "volumes")) {
2188     CTX::instance()->pickElements = 0;
2189     what = ENT_VOLUME;
2190   }
2191   else
2192     return;
2193 
2194   std::vector<MElement *> ele;
2195   std::vector<GEntity *> ent;
2196 
2197   while(1) {
2198     if(!FlGui::available()) return;
2199 
2200     CTX::instance()->mesh.changed = ENT_ALL;
2201     drawContext::global()->draw();
2202 
2203     if(ele.size() || ent.size())
2204       Msg::StatusGl(
2205         "Select %s\n"
2206         "[Press 'e' to end selection, 'u' to undo last selection or "
2207         "'q' to abort]",
2208         str);
2209     else
2210       Msg::StatusGl("Select %s\n"
2211                     "[Press 'e' to end selection or 'q' to abort]",
2212                     str);
2213 
2214     char ib = FlGui::instance()->selectEntity(what);
2215     if(!FlGui::available()) return;
2216     if(ib == 'l') {
2217       if(CTX::instance()->pickElements) {
2218         for(std::size_t i = 0; i < FlGui::instance()->selectedElements.size();
2219             i++) {
2220           if(FlGui::instance()->selectedElements[i]->getVisibility() != 2) {
2221             FlGui::instance()->selectedElements[i]->setVisibility(2);
2222             ele.push_back(FlGui::instance()->selectedElements[i]);
2223           }
2224         }
2225       }
2226       else {
2227         for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size();
2228             i++) {
2229           if(FlGui::instance()->selectedEdges[i]->getSelection() != 1) {
2230             FlGui::instance()->selectedEdges[i]->setSelection(1);
2231             ent.push_back(FlGui::instance()->selectedEdges[i]);
2232           }
2233         }
2234         for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size();
2235             i++) {
2236           if(FlGui::instance()->selectedFaces[i]->getSelection() != 1) {
2237             FlGui::instance()->selectedFaces[i]->setSelection(1);
2238             ent.push_back(FlGui::instance()->selectedFaces[i]);
2239           }
2240         }
2241         for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
2242             i++) {
2243           if(FlGui::instance()->selectedRegions[i]->getSelection() != 1) {
2244             FlGui::instance()->selectedRegions[i]->setSelection(1);
2245             ent.push_back(FlGui::instance()->selectedRegions[i]);
2246           }
2247         }
2248       }
2249     }
2250     if(ib == 'r') {
2251       if(CTX::instance()->pickElements) {
2252         for(std::size_t i = 0; i < FlGui::instance()->selectedElements.size();
2253             i++)
2254           FlGui::instance()->selectedElements[i]->setVisibility(1);
2255       }
2256       else {
2257         for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size(); i++)
2258           FlGui::instance()->selectedEdges[i]->setSelection(0);
2259         for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size(); i++)
2260           FlGui::instance()->selectedFaces[i]->setSelection(0);
2261         for(std::size_t i = 0; i < FlGui::instance()->selectedRegions.size();
2262             i++)
2263           FlGui::instance()->selectedRegions[i]->setSelection(0);
2264       }
2265     }
2266     if(ib == 'u') {
2267       if(CTX::instance()->pickElements) {
2268         if(ele.size()) {
2269           ele[ele.size() - 1]->setVisibility(1);
2270           ele.pop_back();
2271         }
2272       }
2273       else {
2274         if(ent.size()) {
2275           ent[ent.size() - 1]->setSelection(0);
2276           ent.pop_back();
2277         }
2278       }
2279     }
2280     if(ib == 'e') {
2281       if(CTX::instance()->pickElements) {
2282         for(std::size_t i = 0; i < ele.size(); i++)
2283           if(ele[i]->getVisibility() == 2) ele[i]->setVisibility(0);
2284       }
2285       else {
2286         for(std::size_t i = 0; i < ent.size(); i++)
2287           if(ent[i]->getSelection() == 1) {
2288             ent[i]->setVisibility(0);
2289             ent[i]->setSelection(0);
2290           }
2291       }
2292       if(action == "delete")
2293         GModel::current()->removeInvisibleElements();
2294       else if(action == "reverse")
2295         GModel::current()->reverseInvisibleElements();
2296       else
2297         Msg::Error("Unknown action on mesh part");
2298       ele.clear();
2299       ent.clear();
2300     }
2301     if(ib == 'q') {
2302       GModel::current()->setSelection(0);
2303       break;
2304     }
2305   }
2306 
2307   CTX::instance()->mesh.changed = ENT_ALL;
2308   CTX::instance()->pickElements = 0;
2309   drawContext::global()->draw();
2310   Msg::StatusGl("");
2311 }
2312 
mesh_delete_parts_cb(Fl_Widget * w,void * data)2313 static void mesh_delete_parts_cb(Fl_Widget *w, void *data)
2314 {
2315   mesh_modify_parts(w, data, "delete");
2316 }
2317 
mesh_reverse_parts_cb(Fl_Widget * w,void * data)2318 static void mesh_reverse_parts_cb(Fl_Widget *w, void *data)
2319 {
2320   mesh_modify_parts(w, data, "reverse");
2321 }
2322 
mesh_inspect_cb(Fl_Widget * w,void * data)2323 static void mesh_inspect_cb(Fl_Widget *w, void *data)
2324 {
2325   CTX::instance()->pickElements = 1;
2326   CTX::instance()->mesh.changed = ENT_ALL;
2327   drawContext::global()->draw();
2328 
2329   while(1) {
2330     if(!FlGui::available()) return;
2331 
2332     Msg::StatusGl("Select element\n[Press 'q' to abort]");
2333     char ib = FlGui::instance()->selectEntity(ENT_ALL);
2334     if(!FlGui::available()) return;
2335     if(ib == 'l') {
2336       if(FlGui::instance()->selectedElements.size()) {
2337         MElement *ele = FlGui::instance()->selectedElements[0];
2338         GModel::current()->setSelection(0);
2339         ele->setVisibility(2);
2340         CTX::instance()->mesh.changed = ENT_ALL;
2341         drawContext::global()->draw();
2342         std::vector<std::string> info =
2343           SplitString(ele->getInfoString(true), '\n');
2344         for(std::size_t i = 0; i < info.size(); i++)
2345           Msg::Direct("%s", info[i].c_str());
2346         if(CTX::instance()->tooltips) {
2347           std::string str;
2348           for(std::size_t i = 0; i < info.size(); i++) str += info[i] + "\n";
2349           FlGui::instance()->getCurrentOpenglWindow()->drawTooltip(str);
2350         }
2351       }
2352     }
2353     if(ib == 'q') {
2354       GModel::current()->setSelection(0);
2355       break;
2356     }
2357   }
2358 
2359   CTX::instance()->pickElements = 0;
2360   CTX::instance()->mesh.changed = ENT_ALL;
2361   drawContext::global()->draw();
2362   Msg::StatusGl("");
2363 }
2364 
mesh_degree_cb(Fl_Widget * w,void * data)2365 static void mesh_degree_cb(Fl_Widget *w, void *data)
2366 {
2367   int degree = (intptr_t)data;
2368   GModel::current()->setOrderN(degree, CTX::instance()->mesh.secondOrderLinear,
2369                                CTX::instance()->mesh.secondOrderIncomplete);
2370   drawContext::global()->draw();
2371 }
2372 
mesh_optimize_cb(Fl_Widget * w,void * data)2373 static void mesh_optimize_cb(Fl_Widget *w, void *data)
2374 {
2375   if(CTX::instance()->lock) {
2376     Msg::Info("I'm busy! Ask me that later...");
2377     return;
2378   }
2379   CTX::instance()->lock = 1;
2380   GModel::current()->optimizeMesh("");
2381   CTX::instance()->lock = 0;
2382   drawContext::global()->draw();
2383 }
2384 
mesh_optimize_quad_topo_cb(Fl_Widget * w,void * data)2385 static void mesh_optimize_quad_topo_cb(Fl_Widget *w, void *data)
2386 {
2387   CTX::instance()->lock = 1;
2388   GModel::current()->optimizeMesh("QuadQuasiStructured");
2389   CTX::instance()->lock = 0;
2390   drawContext::global()->draw();
2391 }
2392 
mesh_untangle_cb(Fl_Widget * w,void * data)2393 static void mesh_untangle_cb(Fl_Widget *w, void *data)
2394 {
2395   CTX::instance()->lock = 1;
2396   GModel::current()->optimizeMesh("UntangleMeshGeometry");
2397   CTX::instance()->lock = 0;
2398   drawContext::global()->draw();
2399 }
2400 
mesh_cross_compute_cb(Fl_Widget * w,void * data)2401 static void mesh_cross_compute_cb(Fl_Widget *w, void *data)
2402 {
2403   std::vector<int> tags;
2404   computeCrossField(GModel::current(), tags);
2405   drawContext::global()->draw();
2406 }
2407 
mesh_refine_cb(Fl_Widget * w,void * data)2408 static void mesh_refine_cb(Fl_Widget *w, void *data)
2409 {
2410   GModel::current()->refineMesh(CTX::instance()->mesh.secondOrderLinear,
2411                                 CTX::instance()->mesh.algoSubdivide);
2412   drawContext::global()->draw();
2413 }
2414 
mesh_smooth_cb(Fl_Widget * w,void * data)2415 static void mesh_smooth_cb(Fl_Widget *w, void *data)
2416 {
2417   GModel::current()->optimizeMesh("Laplace2D");
2418   drawContext::global()->draw();
2419 }
2420 
mesh_recombine_cb(Fl_Widget * w,void * data)2421 static void mesh_recombine_cb(Fl_Widget *w, void *data)
2422 {
2423   GModel::current()->recombineMesh();
2424   drawContext::global()->draw();
2425 }
2426 
2427 #if defined(HAVE_NETGEN)
mesh_optimize_netgen_cb(Fl_Widget * w,void * data)2428 static void mesh_optimize_netgen_cb(Fl_Widget *w, void *data)
2429 {
2430   if(CTX::instance()->lock) {
2431     Msg::Info("I'm busy! Ask me that later...");
2432     return;
2433   }
2434   CTX::instance()->lock = 1;
2435   GModel::current()->optimizeMesh("Netgen");
2436   CTX::instance()->lock = 0;
2437   drawContext::global()->draw();
2438 }
2439 #endif
2440 
mesh_partition_cb(Fl_Widget * w,void * data)2441 static void mesh_partition_cb(Fl_Widget *w, void *data) { partition_dialog(); }
2442 
mesh_unpartition_cb(Fl_Widget * w,void * data)2443 static void mesh_unpartition_cb(Fl_Widget *w, void *data)
2444 {
2445   int ier = GModel::current()->unpartitionMesh();
2446 
2447   // Update the screen
2448   if(!ier) {
2449     opt_mesh_zone_definition(0, GMSH_SET, 0.);
2450     opt_mesh_color_carousel(0, GMSH_SET | GMSH_GUI, 1.);
2451     CTX::instance()->mesh.changed = ENT_ALL;
2452     drawContext::global()->draw();
2453   }
2454 }
2455 
mesh_convert_old_partitioning_cb(Fl_Widget * w,void * data)2456 static void mesh_convert_old_partitioning_cb(Fl_Widget *w, void *data)
2457 {
2458   int ier = GModel::current()->convertOldPartitioningToNewOne();
2459 
2460   // Update the screen
2461   if(!ier) {
2462     opt_mesh_zone_definition(0, GMSH_SET, 0.);
2463     opt_mesh_color_carousel(0, GMSH_SET | GMSH_GUI, 1.);
2464     CTX::instance()->mesh.changed = ENT_ALL;
2465     drawContext::global()->draw();
2466   }
2467 }
2468 
mesh_define_length_cb(Fl_Widget * w,void * data)2469 static void mesh_define_length_cb(Fl_Widget *w, void *data)
2470 {
2471   FlGui::instance()->meshContext->show(0);
2472   action_point_line_surface_volume(8, "Point");
2473   FlGui::instance()->meshContext->hide();
2474 }
2475 
mesh_define_recombine_cb(Fl_Widget * w,void * data)2476 static void mesh_define_recombine_cb(Fl_Widget *w, void *data)
2477 {
2478   action_point_line_surface_volume(9, "Surface");
2479 }
2480 
mesh_define_transfinite(int dim)2481 static void mesh_define_transfinite(int dim)
2482 {
2483   opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
2484   switch(dim) {
2485   case 1: opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1); break;
2486   case 2: opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1); break;
2487   case 3: opt_geometry_volumes(0, GMSH_SET | GMSH_GUI, 1); break;
2488   }
2489   drawContext::global()->draw();
2490 
2491   std::vector<int> p;
2492   char ib;
2493   while(1) {
2494     if(!FlGui::available()) return;
2495 
2496     switch(dim) {
2497     case 1:
2498       if(p.empty())
2499         Msg::StatusGl("Select curves\n"
2500                       "[Press 'e' to end selection or 'q' to abort]");
2501       else
2502         Msg::StatusGl("Select curves\n"
2503                       "[Press 'e' to end selection, 'u' to undo last selection "
2504                       "or 'q' to abort]");
2505       ib = FlGui::instance()->selectEntity(ENT_CURVE);
2506       if(!FlGui::available()) return;
2507       break;
2508     case 2:
2509       Msg::StatusGl("Select surface\n[Press 'q' to abort]");
2510       ib = FlGui::instance()->selectEntity(ENT_SURFACE);
2511       if(!FlGui::available()) return;
2512       break;
2513     case 3:
2514       Msg::StatusGl("Select volume\n[Press 'q' to abort]");
2515       ib = FlGui::instance()->selectEntity(ENT_VOLUME);
2516       if(!FlGui::available()) return;
2517       break;
2518     default: ib = 'l'; break;
2519     }
2520 
2521     if(ib == 'e') {
2522       if(dim == 1) {
2523         if(p.size())
2524           scriptSetTransfiniteLine(
2525             p, GModel::current()->getFileName(),
2526             FlGui::instance()->meshContext->choice[0]->text(),
2527             FlGui::instance()->meshContext->input[2]->value(),
2528             FlGui::instance()->meshContext->input[1]->value());
2529       }
2530       GModel::current()->setSelection(0);
2531       drawContext::global()->draw();
2532       p.clear();
2533     }
2534     if(ib == 'u') {
2535       if(dim == 1) {
2536         if(p.size()) {
2537           GEdge *ge = GModel::current()->getEdgeByTag(p.back());
2538           if(ge) ge->setSelection(0);
2539           drawContext::global()->draw();
2540           p.pop_back();
2541         }
2542       }
2543     }
2544     if(ib == 'q') {
2545       GModel::current()->setSelection(0);
2546       drawContext::global()->draw();
2547       break;
2548     }
2549     if(ib == 'r') {
2550       Msg::Warning("Entity de-selection not supported yet during "
2551                    "transfinite definition");
2552     }
2553     if(ib == 'l') {
2554       switch(dim) {
2555       case 1:
2556         for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size();
2557             i++) {
2558           FlGui::instance()->selectedEdges[i]->setSelection(1);
2559           p.push_back(FlGui::instance()->selectedEdges[i]->tag());
2560         }
2561         drawContext::global()->draw();
2562         break;
2563       case 2:
2564       case 3:
2565         if(dim == 2) {
2566           FlGui::instance()->selectedFaces[0]->setSelection(1);
2567           drawContext::global()->draw();
2568           p.push_back(FlGui::instance()->selectedFaces[0]->tag());
2569         }
2570         else {
2571           FlGui::instance()->selectedRegions[0]->setSelection(1);
2572           drawContext::global()->draw();
2573           p.push_back(FlGui::instance()->selectedRegions[0]->tag());
2574         }
2575         while(1) {
2576           if(!FlGui::available()) return;
2577 
2578           if(p.size() == 1)
2579             Msg::StatusGl("Select (ordered) boundary points\n"
2580                           "[Press 'e' to end selection or 'q' to abort]");
2581           else
2582             Msg::StatusGl(
2583               "Select (ordered) boundary points\n"
2584               "[Press 'e' to end selection, 'u' to undo last selection "
2585               "or 'q' to abort]");
2586           ib = FlGui::instance()->selectEntity(ENT_POINT);
2587           if(!FlGui::available()) return;
2588           if(ib == 'l') {
2589             for(std::size_t i = 0;
2590                 i < FlGui::instance()->selectedVertices.size(); i++) {
2591               FlGui::instance()->selectedVertices[i]->setSelection(1);
2592               p.push_back(FlGui::instance()->selectedVertices[i]->tag());
2593               break;
2594             }
2595             drawContext::global()->draw();
2596           }
2597           if(ib == 'u') {
2598             if(p.size() > 1) {
2599               GVertex *gv = GModel::current()->getVertexByTag(p.back());
2600               if(gv) gv->setSelection(0);
2601               drawContext::global()->draw();
2602               p.pop_back();
2603             }
2604           }
2605           if(ib == 'r') {
2606             Msg::Warning("Entity de-selection not supported yet during "
2607                          "transfinite definition");
2608           }
2609           if(ib == 'e') {
2610             switch(dim) {
2611             case 2:
2612               if((p.size() == 0 + 1 || p.size() == 3 + 1 || p.size() == 4 + 1))
2613                 scriptSetTransfiniteSurface(
2614                   p, GModel::current()->getFileName(),
2615                   FlGui::instance()->meshContext->choice[1]->text());
2616               else
2617                 Msg::Error("Wrong number of points for mesh constraint");
2618               break;
2619             case 3:
2620               if(p.size() == 6 + 1 || p.size() == 8 + 1)
2621                 scriptSetTransfiniteVolume(p, GModel::current()->getFileName());
2622               else
2623                 Msg::Error("Wrong number of points for transfinite volume");
2624               break;
2625             }
2626             GModel::current()->setSelection(0);
2627             drawContext::global()->draw();
2628             p.clear();
2629             break;
2630           }
2631           if(ib == 'q') {
2632             GModel::current()->setSelection(0);
2633             drawContext::global()->draw();
2634             goto stopall;
2635           }
2636         }
2637         break;
2638       }
2639     }
2640   }
2641 
2642 stopall:
2643   Msg::StatusGl("");
2644 }
2645 
mesh_define_transfinite_line_cb(Fl_Widget * w,void * data)2646 static void mesh_define_transfinite_line_cb(Fl_Widget *w, void *data)
2647 {
2648   FlGui::instance()->meshContext->show(1);
2649   mesh_define_transfinite(1);
2650   FlGui::instance()->meshContext->hide();
2651 }
2652 
mesh_define_transfinite_surface_cb(Fl_Widget * w,void * data)2653 static void mesh_define_transfinite_surface_cb(Fl_Widget *w, void *data)
2654 {
2655   FlGui::instance()->meshContext->show(2);
2656   mesh_define_transfinite(2);
2657   FlGui::instance()->meshContext->hide();
2658 }
2659 
mesh_define_transfinite_volume_cb(Fl_Widget * w,void * data)2660 static void mesh_define_transfinite_volume_cb(Fl_Widget *w, void *data)
2661 {
2662   mesh_define_transfinite(3);
2663 }
2664 
mesh_define_embedded_cb(Fl_Widget * w,void * data)2665 static void mesh_define_embedded_cb(Fl_Widget *w, void *data)
2666 {
2667   if(!data) return;
2668   std::string what((const char *)data);
2669   std::vector<int> entities;
2670   bool selectEntities = true;
2671 
2672   int type;
2673   const char *str = "";
2674   if(what == "Surface") {
2675     type = ENT_SURFACE;
2676     str = "surfaces";
2677     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
2678   }
2679   else if(what == "Curve") {
2680     type = ENT_CURVE;
2681     str = "curves";
2682     opt_geometry_curves(0, GMSH_SET | GMSH_GUI, 1);
2683   }
2684   else if(what == "Point") {
2685     type = ENT_POINT;
2686     str = "points";
2687     opt_geometry_points(0, GMSH_SET | GMSH_GUI, 1);
2688   }
2689   else {
2690     Msg::Error("Unknown type of entity to embed: %s", what.c_str());
2691     return;
2692   }
2693   while(1) {
2694     if(!FlGui::available()) return;
2695 
2696     if(entities.empty())
2697       Msg::StatusGl("Select %s\n"
2698                     "[Press 'e' to end selection or 'q' to abort]",
2699                     str);
2700     else if(selectEntities)
2701       Msg::StatusGl(
2702         "Select %s\n"
2703         "[Press 'e' to end selection, 'u' to undo last selection or "
2704         "'q' to abort]",
2705         str);
2706     else
2707       Msg::StatusGl("Select entity in which to embed the %s\n"
2708                     "[Press 'q' to abort]",
2709                     str);
2710     int t = type;
2711     if(!selectEntities) {
2712       switch(FlGui::instance()->transformContext->choice->value()) {
2713       case 2: t = ENT_CURVE; break;
2714       case 3: t = ENT_SURFACE; break;
2715       case 4: t = ENT_VOLUME; break;
2716       default: t = ENT_ALL; break;
2717       }
2718     }
2719     char ib = FlGui::instance()->selectEntity(t);
2720     if(!FlGui::available()) return;
2721     if(ib == 'l') {
2722       if(selectEntities && what == "Point") {
2723         for(std::size_t i = 0; i < FlGui::instance()->selectedVertices.size();
2724             i++) {
2725           if(FlGui::instance()->selectedVertices[i]->getSelection() != 1) {
2726             FlGui::instance()->selectedVertices[i]->setSelection(1);
2727             entities.push_back(FlGui::instance()->selectedVertices[i]->tag());
2728           }
2729         }
2730       }
2731       else if(selectEntities && what == "Curve") {
2732         for(std::size_t i = 0; i < FlGui::instance()->selectedEdges.size();
2733             i++) {
2734           if(FlGui::instance()->selectedEdges[i]->getSelection() != 1) {
2735             FlGui::instance()->selectedEdges[i]->setSelection(1);
2736             entities.push_back(FlGui::instance()->selectedEdges[i]->tag());
2737           }
2738         }
2739       }
2740       else if(selectEntities && what == "Surface") {
2741         for(std::size_t i = 0; i < FlGui::instance()->selectedFaces.size();
2742             i++) {
2743           if(FlGui::instance()->selectedFaces[i]->getSelection() != 1) {
2744             FlGui::instance()->selectedFaces[i]->setSelection(1);
2745             entities.push_back(FlGui::instance()->selectedFaces[i]->tag());
2746           }
2747         }
2748       }
2749       else if(!selectEntities && (FlGui::instance()->selectedFaces.size() ||
2750                                   FlGui::instance()->selectedRegions.size())) {
2751         int dim = FlGui::instance()->selectedFaces.size() ? 2 : 3;
2752         if(dim == 2)
2753           FlGui::instance()->selectedFaces[0]->setSelection(1);
2754         else
2755           FlGui::instance()->selectedRegions[0]->setSelection(1);
2756         drawContext::global()->draw();
2757         int tag = (dim == 2) ? FlGui::instance()->selectedFaces[0]->tag() :
2758                                FlGui::instance()->selectedRegions[0]->tag();
2759         scriptEmbed(GModel::current()->getFileName(), what, entities, dim, tag);
2760         GModel::current()->setSelection(0);
2761         selectEntities = true;
2762         entities.clear();
2763       }
2764     }
2765     if(ib == 'r') {
2766       Msg::Warning(
2767         "Entity de-selection not supported yet during boolean operation");
2768     }
2769     if(ib == 'u') {
2770       if(selectEntities && entities.size()) {
2771         int dim = (what == "Surface") ? 2 : (what == "Curve") ? 1 : 0;
2772         GEntity *ge = GModel::current()->getEntityByTag(dim, entities.back());
2773         if(ge) ge->setSelection(0);
2774         entities.pop_back();
2775       }
2776     }
2777     if(ib == 'e') {
2778       if(selectEntities) {
2779         if(entities.empty())
2780           Msg::Error("At least one entity must be selected");
2781         else
2782           selectEntities = false;
2783       }
2784     }
2785     if(ib == 'q') {
2786       GModel::current()->setSelection(0);
2787       break;
2788     }
2789   }
2790 
2791   FlGui::instance()->transformContext->hide();
2792   drawContext::global()->draw();
2793   Msg::StatusGl("");
2794 }
2795 
mesh_define_compound_entity_cb(Fl_Widget * w,void * data)2796 static void mesh_define_compound_entity_cb(Fl_Widget *w, void *data)
2797 {
2798   action_point_line_surface_volume(10, (const char *)data);
2799 }
2800 
2801 // clang-format off
2802 
2803 // The static menus (we cannot use the 'g', 'm' 's' and 'p' mnemonics since they
2804 // are already defined as global shortcuts)
2805 static Fl_Menu_Item bar_table[] = {
2806   {"&File", 0, nullptr, nullptr, FL_SUBMENU},
2807     {"&New...",     FL_CTRL+'n', (Fl_Callback *)file_new_cb, nullptr},
2808     {"&Open...",    FL_CTRL+'o', (Fl_Callback *)file_open_merge_cb, (void*)"open"},
2809     {"Open Recent", 0, nullptr, nullptr, FL_SUBMENU},
2810       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2811       {"", FL_CTRL+FL_SHIFT+'r', (Fl_Callback *)file_open_recent_cb, nullptr},
2812       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2813       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2814       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2815       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2816       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2817       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2818       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2819       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2820       {nullptr},
2821     {"M&erge...",   FL_CTRL+FL_SHIFT+'o', (Fl_Callback *)file_open_merge_cb, (void*)"merge"},
2822     {"Watch Pattern...",    0, (Fl_Callback *)file_watch_cb, nullptr, FL_MENU_DIVIDER},
2823     {"&Clear",      0, (Fl_Callback *)file_clear_cb, nullptr},
2824     {"&Rename...",  FL_CTRL+'r', (Fl_Callback *)file_rename_cb, nullptr},
2825     {"Delete",      0, (Fl_Callback *)file_delete_cb, nullptr, FL_MENU_DIVIDER},
2826     {"Remote", 0, nullptr, nullptr, FL_MENU_DIVIDER | FL_SUBMENU},
2827       {"Start...",  0, (Fl_Callback *)file_remote_cb, (void*)"start"},
2828       {"Merge...",  0, (Fl_Callback *)file_remote_cb, (void*)"merge"},
2829       {"Clear",     0, (Fl_Callback *)file_remote_cb, (void*)"clear"},
2830       {"Stop",      0, (Fl_Callback *)file_remote_cb, (void*)"stop"},
2831       {nullptr},
2832     {"Sa&ve Mesh",  FL_CTRL+FL_SHIFT+'s', (Fl_Callback *)mesh_save_cb, nullptr},
2833     {"Save Model Options", FL_CTRL+'j', (Fl_Callback *)file_options_save_cb, (void*)"file"},
2834     {"Save Options As Default", FL_CTRL+FL_SHIFT+'j', (Fl_Callback *)file_options_save_cb, (void*)"default", FL_MENU_DIVIDER},
2835     {"&Export...",  FL_CTRL+'e', (Fl_Callback *)file_export_cb, nullptr, FL_MENU_DIVIDER},
2836     {"&Quit",       FL_CTRL+'q', (Fl_Callback *)file_quit_cb, nullptr},
2837     {nullptr},
2838   {"&Tools", 0, nullptr, nullptr, FL_SUBMENU},
2839     {"&Options",         FL_CTRL+FL_SHIFT+'n', (Fl_Callback *)options_cb, nullptr},
2840     {"Pl&ugins",         FL_CTRL+FL_SHIFT+'u', (Fl_Callback *)plugin_cb, (void*)(-1)},
2841     {"&Visibility",      FL_CTRL+FL_SHIFT+'v', (Fl_Callback *)visibility_cb, nullptr},
2842     {"&Clipping",        FL_CTRL+FL_SHIFT+'c', (Fl_Callback *)clip_cb, nullptr},
2843     {"&Manipulator",     FL_CTRL+FL_SHIFT+'m', (Fl_Callback *)manip_cb, nullptr, FL_MENU_DIVIDER},
2844 #if defined(HAVE_3M)
2845     {"&3M",              0, (Fl_Callback *)window3M_cb, 0, FL_MENU_DIVIDER},
2846 #endif
2847     {"S&tatistics",      FL_CTRL+'i', (Fl_Callback *)statistics_cb, nullptr},
2848     {"M&essage Console", FL_CTRL+'l', (Fl_Callback *)show_hide_message_cb, nullptr},
2849     {nullptr},
2850   {"&Window", 0, nullptr, nullptr, FL_SUBMENU},
2851     {"New Window", 0, (Fl_Callback *)file_window_cb, (void*)"new", FL_MENU_DIVIDER},
2852 #if defined(WIN32)
2853     {"Copy to Clipboard",  FL_CTRL+'c', (Fl_Callback *)file_window_cb, (void*)"copy", FL_MENU_DIVIDER},
2854 #endif
2855     {"Split Horizontally", 0, (Fl_Callback *)file_window_cb, (void*)"split_h"},
2856     {"Split Vertically",   0, (Fl_Callback *)file_window_cb, (void*)"split_v"},
2857     {"Unsplit",            0, (Fl_Callback *)file_window_cb, (void*)"split_u", FL_MENU_DIVIDER},
2858     {"Minimize",           FL_CTRL+'m', (Fl_Callback *)window_cb, (void*)"minimize"},
2859     {"Zoom",               0, (Fl_Callback *)window_cb, (void*)"zoom"},
2860     {"Enter Full Screen",  FL_CTRL+'f', (Fl_Callback *)window_cb, (void*)"fullscreen", FL_MENU_DIVIDER},
2861     {"Attach/Detach Menu", FL_CTRL+'d', (Fl_Callback *)attach_detach_menu_cb, nullptr, FL_MENU_DIVIDER},
2862     {"Bring All to Front", 0, (Fl_Callback *)window_cb, (void*)"front"},
2863     {nullptr},
2864   {"&Help", 0, nullptr, nullptr, FL_SUBMENU},
2865     {"On&line Documentation", 0, (Fl_Callback *)help_online_cb, nullptr, FL_MENU_DIVIDER},
2866     {"&Keyboard and Mouse Usage",  FL_CTRL+'h', (Fl_Callback *)help_basic_cb, nullptr, FL_MENU_DIVIDER},
2867     {"&Current Options and Workspace", FL_CTRL+FL_SHIFT+'h', (Fl_Callback *)status_options_cb, (void*)"?", 0},
2868     {"&Restore all Options to Default Settings", 0, (Fl_Callback *)options_restore_defaults_cb, nullptr, FL_MENU_DIVIDER},
2869     {"&About Gmsh",           0, (Fl_Callback *)help_about_cb, nullptr},
2870     {nullptr},
2871   {nullptr}
2872 };
2873 
2874 #if defined(__APPLE__)
2875 
2876 // Alternative items for the MacOS system menu bar (removed '&' shortcuts: they
2877 // would cause spurious menu items to appear on the menu window; removed
2878 // File->Quit)
2879 static Fl_Menu_Item sysbar_table[] = {
2880   {"File", 0, nullptr, nullptr, FL_SUBMENU},
2881     {"New...",     FL_META+'n', (Fl_Callback *)file_new_cb, nullptr},
2882     {"Open...",    FL_META+'o', (Fl_Callback *)file_open_merge_cb, (void*)"open"},
2883     {"Open Recent", 0, nullptr, nullptr, FL_SUBMENU},
2884       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2885       {"", FL_META+FL_SHIFT+'r', (Fl_Callback *)file_open_recent_cb, nullptr},
2886       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2887       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2888       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2889       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2890       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2891       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2892       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2893       {"", 0, (Fl_Callback *)file_open_recent_cb, nullptr},
2894       {nullptr},
2895     {"Merge...",   FL_META+FL_SHIFT+'o', (Fl_Callback *)file_open_merge_cb, (void*)"merge"},
2896     {"Watch Pattern...",   0, (Fl_Callback *)file_watch_cb, nullptr, FL_MENU_DIVIDER},
2897     {"Clear",      0, (Fl_Callback *)file_clear_cb, nullptr},
2898     {"Rename...",  FL_META+'r', (Fl_Callback *)file_rename_cb, nullptr},
2899     {"Delete",     0, (Fl_Callback *)file_delete_cb, nullptr, FL_MENU_DIVIDER},
2900     {"Remote", 0, nullptr, nullptr, FL_MENU_DIVIDER | FL_SUBMENU},
2901       {"Start...",  0, (Fl_Callback *)file_remote_cb, (void*)"start"},
2902       {"Merge...",  0, (Fl_Callback *)file_remote_cb, (void*)"merge"},
2903       {"Clear",     0, (Fl_Callback *)file_remote_cb, (void*)"clear"},
2904       {"Stop",      0, (Fl_Callback *)file_remote_cb, (void*)"stop"},
2905       {nullptr},
2906     {"Save Mesh",  FL_META+'s', (Fl_Callback *)mesh_save_cb, nullptr},
2907     {"Save Model Options", FL_META+'j', (Fl_Callback *)file_options_save_cb, (void*)"file"},
2908     {"Save Options As Default", FL_META+FL_SHIFT+'j', (Fl_Callback *)file_options_save_cb, (void*)"default", FL_MENU_DIVIDER},
2909     {"Export...",  FL_META+'e', (Fl_Callback *)file_export_cb, nullptr},
2910     {nullptr},
2911   {"Tools", 0, nullptr, nullptr, FL_SUBMENU},
2912     {"Options",         FL_META+FL_SHIFT+'n', (Fl_Callback *)options_cb, nullptr},
2913     {"Plugins",         FL_META+FL_SHIFT+'u', (Fl_Callback *)plugin_cb, (void*)(-1)},
2914     {"Visibility",      FL_META+FL_SHIFT+'v', (Fl_Callback *)visibility_cb, nullptr},
2915     {"Clipping",        FL_META+FL_SHIFT+'c', (Fl_Callback *)clip_cb, nullptr},
2916     {"Manipulator",     FL_META+FL_SHIFT+'m', (Fl_Callback *)manip_cb, nullptr, FL_MENU_DIVIDER},
2917 #if defined(HAVE_3M)
2918     {"3M",              0, (Fl_Callback *)window3M_cb, 0, FL_MENU_DIVIDER},
2919 #endif
2920     {"Statistics",      FL_META+'i', (Fl_Callback *)statistics_cb, nullptr},
2921     {"Message Console", FL_META+'l', (Fl_Callback *)show_hide_message_cb, nullptr},
2922     {nullptr},
2923   {"Window", 0, nullptr, nullptr, FL_SUBMENU},
2924     {"New Window", 0, (Fl_Callback *)file_window_cb, (void*)"new", FL_MENU_DIVIDER},
2925     {"Split Horizontally", 0, (Fl_Callback *)file_window_cb, (void*)"split_h"},
2926     {"Split Vertically",   0, (Fl_Callback *)file_window_cb, (void*)"split_v"},
2927     {"Unsplit",            0, (Fl_Callback *)file_window_cb, (void*)"split_u", FL_MENU_DIVIDER},
2928     {"Minimize",           FL_META+'m', (Fl_Callback *)window_cb, (void*)"minimize"},
2929     {"Zoom",               0, (Fl_Callback *)window_cb, (void*)"zoom"},
2930     {"Enter Full Screen",  FL_META+'f', (Fl_Callback *)window_cb, (void*)"fullscreen", FL_MENU_DIVIDER},
2931     {"Attach/Detach Menu", FL_META+'d', (Fl_Callback *)attach_detach_menu_cb, nullptr, FL_MENU_DIVIDER},
2932     {"Bring All to Front", 0, (Fl_Callback *)window_cb, (void*)"front"},
2933     {nullptr},
2934   {"Help", 0, nullptr, nullptr, FL_SUBMENU},
2935     {"Online Documentation", 0, (Fl_Callback *)help_online_cb, nullptr, FL_MENU_DIVIDER},
2936     {"Keyboard and Mouse Usage", 0, (Fl_Callback *)help_basic_cb, nullptr, FL_MENU_DIVIDER},
2937     {"Current Options and Workspace", FL_META+FL_SHIFT+'h', (Fl_Callback *)status_options_cb, (void*)"?"},
2938     {"Restore all Options to Default Settings", 0, (Fl_Callback *)options_restore_defaults_cb, nullptr},
2939     {nullptr},
2940   {nullptr}
2941 };
2942 
2943 #endif
2944 
2945 // clang-format on
2946 
getGraphicWindow(Fl_Widget * w)2947 static graphicWindow *getGraphicWindow(Fl_Widget *w)
2948 {
2949   if(!w || !w->parent()) return FlGui::instance()->graph[0];
2950   for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
2951     if(FlGui::instance()->graph[i]->getWindow() == w->parent())
2952       return FlGui::instance()->graph[i];
2953   return FlGui::instance()->graph[0];
2954 }
2955 
status_xyz1p_cb(Fl_Widget * w,void * data)2956 void status_xyz1p_cb(Fl_Widget *w, void *data)
2957 {
2958   const char *str = (const char *)data;
2959 
2960   std::vector<openglWindow *> gls;
2961   if(w)
2962     gls = getGraphicWindow(w)->gl;
2963   else
2964     gls.push_back(FlGui::instance()->getCurrentOpenglWindow());
2965 
2966   for(std::size_t i = 0; i < gls.size(); i++) {
2967     drawContext *ctx = gls[i]->getDrawContext();
2968     if(!strcmp(str, "r")) {
2969       // rotate +90 or -90 (shift) degress around axis perp to the
2970       // screen, or sync rotation with first window (Ctrl)
2971       double axis[3] = {0., 0., 1.};
2972       if(Fl::event_state(FL_CTRL) || Fl::event_state(FL_META)) {
2973         if(i != 0) {
2974           drawContext *ctx0 = gls[0]->getDrawContext();
2975           ctx->setQuaternion(ctx0->quaternion[0], ctx0->quaternion[1],
2976                              ctx0->quaternion[2], ctx0->quaternion[3]);
2977         }
2978       }
2979       else if(!Fl::event_state(FL_SHIFT)) {
2980         ctx->addQuaternionFromAxisAndAngle(axis, -90.);
2981         if(CTX::instance()->camera) ctx->camera.tiltHeadRight();
2982       }
2983       else {
2984         ctx->addQuaternionFromAxisAndAngle(axis, 90.);
2985         if(CTX::instance()->camera) ctx->camera.tiltHeadLeft();
2986       }
2987     }
2988     else if(!strcmp(str, "x")) {
2989       // set X-axis pointing out or into (shift) the screen
2990       if(CTX::instance()->camera) { ctx->camera.alongX(); }
2991       else {
2992         if(!Fl::event_state(FL_SHIFT)) {
2993           ctx->r[0] = -90.;
2994           ctx->r[1] = 0.;
2995           ctx->r[2] = -90.;
2996         }
2997         else {
2998           ctx->r[0] = -90.;
2999           ctx->r[1] = 0.;
3000           ctx->r[2] = 90.;
3001         }
3002         ctx->setQuaternionFromEulerAngles();
3003       }
3004     }
3005     else if(!strcmp(str, "y")) {
3006       // set Y-axis pointing out or into (shift) the screen
3007       if(CTX::instance()->camera) { ctx->camera.alongY(); }
3008       else {
3009         if(!Fl::event_state(FL_SHIFT)) {
3010           ctx->r[0] = -90.;
3011           ctx->r[1] = 0.;
3012           ctx->r[2] = 180.;
3013         }
3014         else {
3015           ctx->r[0] = -90.;
3016           ctx->r[1] = 0.;
3017           ctx->r[2] = 0.;
3018         }
3019         ctx->setQuaternionFromEulerAngles();
3020       }
3021     }
3022     else if(!strcmp(str, "z")) {
3023       // set Z-axis pointing out or into (shift) the screen
3024       if(CTX::instance()->camera) { ctx->camera.alongZ(); }
3025       else {
3026         if(!Fl::event_state(FL_SHIFT)) {
3027           ctx->r[0] = 0.;
3028           ctx->r[1] = 0.;
3029           ctx->r[2] = 0.;
3030         }
3031         else {
3032           ctx->r[0] = 0.;
3033           ctx->r[1] = 180.;
3034           ctx->r[2] = 0.;
3035         }
3036         ctx->setQuaternionFromEulerAngles();
3037       }
3038     }
3039     else if(!strcmp(str, "1:1")) {
3040       // if Shift is pressed, reset bounding box around visible
3041       // entities
3042       if(Fl::event_state(FL_SHIFT)) SetBoundingBox(true);
3043       // reset translation and scaling, or sync translation and
3044       // scaling with the first window (alt)
3045       if(CTX::instance()->camera) { ctx->camera.lookAtCg(); }
3046       else {
3047         if(Fl::event_state(FL_CTRL) || Fl::event_state(FL_META)) {
3048           if(i != 0) {
3049             drawContext *ctx0 = gls[0]->getDrawContext();
3050             for(int j = 0; j < 3; j++) {
3051               ctx->t[j] = ctx0->t[j];
3052               ctx->s[j] = ctx0->s[j];
3053             }
3054           }
3055         }
3056         else {
3057           ctx->t[0] = ctx->t[1] = ctx->t[2] = 0.;
3058           ctx->s[0] = ctx->s[1] = ctx->s[2] = 1.;
3059         }
3060       }
3061     }
3062     else if(!strcmp(str, "reset")) {
3063       if(CTX::instance()->camera) { ctx->camera.init(); }
3064       else {
3065         // reset everything
3066         ctx->t[0] = ctx->t[1] = ctx->t[2] = 0.;
3067         ctx->s[0] = ctx->s[1] = ctx->s[2] = 1.;
3068         ctx->r[0] = ctx->r[1] = ctx->r[2] = 0.;
3069         ctx->setQuaternionFromEulerAngles();
3070       }
3071     }
3072   }
3073   drawContext::global()->draw();
3074   FlGui::instance()->manip->update();
3075 }
3076 
quick_access_cb(Fl_Widget * w,void * data)3077 void quick_access_cb(Fl_Widget *w, void *data)
3078 {
3079   if(!data) return;
3080   std::string what((const char *)data);
3081   if(what == "general")
3082     general_options_cb(nullptr, nullptr);
3083   else if(what == "geometry")
3084     geometry_options_cb(nullptr, nullptr);
3085   else if(what == "mesh")
3086     mesh_options_cb(nullptr, nullptr);
3087   else if(what == "view")
3088     view_options_cb(nullptr, (void *)-1);
3089   else if(what == "reset_viewport") {
3090     status_xyz1p_cb(nullptr, (void *)"1:1");
3091     status_xyz1p_cb(nullptr, (void *)"z");
3092   }
3093   else if(what == "select_center") {
3094     opt_general_rotation_center_cg(0, GMSH_SET | GMSH_GUI, 0);
3095     general_options_ok_cb(nullptr, (void *)"rotation_center");
3096     general_options_rotation_center_select_cb(nullptr, nullptr);
3097   }
3098   else if(what == "hover_meshes") {
3099     opt_general_mouse_hover_meshes(
3100       0, GMSH_SET | GMSH_GUI, !opt_general_mouse_hover_meshes(0, GMSH_GET, 0));
3101   }
3102   else if(what == "split_hor") {
3103     file_window_cb(nullptr, (void *)"split_h");
3104   }
3105   else if(what == "split_ver") {
3106     file_window_cb(nullptr, (void *)"split_v");
3107   }
3108   else if(what == "unsplit") {
3109     file_window_cb(nullptr, (void *)"split_u");
3110   }
3111   else if(what == "axes") {
3112     int old = opt_general_axes(0, GMSH_GET, 0);
3113     opt_general_axes(0, GMSH_SET | GMSH_GUI, old ? 0 : 3);
3114     if(!old) {
3115       opt_general_axes_auto_position(0, GMSH_SET | GMSH_GUI, 0);
3116       general_options_axes_fit_cb(nullptr, nullptr);
3117     }
3118   }
3119   else if(what == "orthographic")
3120     opt_general_orthographic(0, GMSH_SET | GMSH_GUI, 1);
3121   else if(what == "perspective") {
3122     opt_general_orthographic(0, GMSH_SET | GMSH_GUI, 0);
3123     drawContext::global()->draw();
3124     numberOrStringOptionChooser("General", 0, "ClipFactor", true, "Factor",
3125                                 true, 0.1, 20., 0.1);
3126   }
3127   else if(what == "geometry_points")
3128     opt_geometry_points(0, GMSH_SET | GMSH_GUI,
3129                         !opt_geometry_points(0, GMSH_GET, 0));
3130   else if(what == "geometry_curves")
3131     opt_geometry_curves(0, GMSH_SET | GMSH_GUI,
3132                         !opt_geometry_curves(0, GMSH_GET, 0));
3133   else if(what == "geometry_surfaces")
3134     opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI,
3135                           !opt_geometry_surfaces(0, GMSH_GET, 0));
3136   else if(what == "geometry_volumes")
3137     opt_geometry_volumes(0, GMSH_SET | GMSH_GUI,
3138                          !opt_geometry_volumes(0, GMSH_GET, 0));
3139   else if(what == "mesh_nodes")
3140     opt_mesh_nodes(0, GMSH_SET | GMSH_GUI, !opt_mesh_nodes(0, GMSH_GET, 0));
3141   else if(what == "mesh_lines")
3142     opt_mesh_lines(0, GMSH_SET | GMSH_GUI, !opt_mesh_lines(0, GMSH_GET, 0));
3143   else if(what == "mesh_surface_edges")
3144     opt_mesh_surface_edges(0, GMSH_SET | GMSH_GUI,
3145                            !opt_mesh_surface_edges(0, GMSH_GET, 0));
3146   else if(what == "mesh_surface_faces")
3147     opt_mesh_surface_faces(0, GMSH_SET | GMSH_GUI,
3148                            !opt_mesh_surface_faces(0, GMSH_GET, 0));
3149   else if(what == "mesh_volume_edges")
3150     opt_mesh_volume_edges(0, GMSH_SET | GMSH_GUI,
3151                           !opt_mesh_volume_edges(0, GMSH_GET, 0));
3152   else if(what == "mesh_volume_faces")
3153     opt_mesh_volume_faces(0, GMSH_SET | GMSH_GUI,
3154                           !opt_mesh_volume_faces(0, GMSH_GET, 0));
3155   else if(what == "mesh_size")
3156     numberOrStringOptionChooser("Mesh", 0, "MeshSizeFactor", true, "Factor",
3157                                 true, 0.01, 100, 0.01);
3158   else if(what == "view_element_outlines") {
3159     int set = 0;
3160     for(std::size_t i = 0; i < PView::list.size(); i++)
3161       if(opt_view_visible(i, GMSH_GET, 0) &&
3162          (set = opt_view_show_element(i, GMSH_GET, 0)))
3163         break;
3164     for(std::size_t i = 0; i < PView::list.size(); i++)
3165       if(opt_view_visible(i, GMSH_GET, 0))
3166         opt_view_show_element(i, GMSH_SET | GMSH_GUI, !set);
3167   }
3168   else if(what == "view_normal_raise") {
3169     double val = 0.;
3170     for(std::size_t i = 0; i < PView::list.size(); i++) {
3171       if(opt_view_visible(i, GMSH_GET, 0)) {
3172         double maxval = std::max(fabs(opt_view_min(i, GMSH_GET, 0)),
3173                                  fabs(opt_view_max(i, GMSH_GET, 0)));
3174         if(!maxval) maxval = 1.;
3175         double val2 = 2. * CTX::instance()->lc / maxval;
3176         val =
3177           numberOrStringOptionChooser("View", i, "NormalRaise", true, "Raise",
3178                                       true, -val2, val2, val2 / 200.);
3179         break;
3180       }
3181     }
3182     for(std::size_t i = 0; i < PView::list.size(); i++)
3183       if(opt_view_visible(i, GMSH_GET, 0))
3184         opt_view_normal_raise(i, GMSH_SET | GMSH_GUI, val);
3185   }
3186   else if(what == "view_iso") {
3187     for(std::size_t i = 0; i < PView::list.size(); i++)
3188       if(opt_view_visible(i, GMSH_GET, 0))
3189         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI, 1);
3190     drawContext::global()->draw();
3191     double val = 0.;
3192     for(std::size_t i = 0; i < PView::list.size(); i++) {
3193       if(opt_view_visible(i, GMSH_GET, 0)) {
3194         val = numberOrStringOptionChooser("View", i, "NbIso", true, "Intervals",
3195                                           true, 1, 100, 1);
3196         break;
3197       }
3198     }
3199     for(std::size_t i = 0; i < PView::list.size(); i++)
3200       if(opt_view_visible(i, GMSH_GET, 0))
3201         opt_view_nb_iso(i, GMSH_SET | GMSH_GUI, val);
3202   }
3203   else if(what == "view_continous") {
3204     for(std::size_t i = 0; i < PView::list.size(); i++)
3205       if(opt_view_visible(i, GMSH_GET, 0))
3206         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI, 2);
3207   }
3208   else if(what == "view_filled") {
3209     for(std::size_t i = 0; i < PView::list.size(); i++)
3210       if(opt_view_visible(i, GMSH_GET, 0))
3211         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI, 3);
3212     drawContext::global()->draw();
3213     double val = 0.;
3214     for(std::size_t i = 0; i < PView::list.size(); i++) {
3215       if(opt_view_visible(i, GMSH_GET, 0)) {
3216         val = numberOrStringOptionChooser("View", i, "NbIso", true, "Intervals",
3217                                           true, 1, 100, 1);
3218         break;
3219       }
3220     }
3221     for(std::size_t i = 0; i < PView::list.size(); i++)
3222       if(opt_view_visible(i, GMSH_GET, 0))
3223         opt_view_nb_iso(i, GMSH_SET | GMSH_GUI, val);
3224   }
3225   else if(what == "view_numeric") {
3226     for(std::size_t i = 0; i < PView::list.size(); i++)
3227       if(opt_view_visible(i, GMSH_GET, 0))
3228         opt_view_intervals_type(i, GMSH_SET | GMSH_GUI, 4);
3229   }
3230   else if(what == "view_line") {
3231     for(std::size_t i = 0; i < PView::list.size(); i++)
3232       if(opt_view_visible(i, GMSH_GET, 0))
3233         opt_view_vector_type(i, GMSH_SET | GMSH_GUI, 1);
3234   }
3235   else if(what == "view_3d_arrow") {
3236     for(std::size_t i = 0; i < PView::list.size(); i++)
3237       if(opt_view_visible(i, GMSH_GET, 0))
3238         opt_view_vector_type(i, GMSH_SET | GMSH_GUI, 4);
3239   }
3240   else if(what == "view_displacement") {
3241     for(std::size_t i = 0; i < PView::list.size(); i++)
3242       if(opt_view_visible(i, GMSH_GET, 0))
3243         opt_view_vector_type(i, GMSH_SET | GMSH_GUI, 5);
3244     drawContext::global()->draw();
3245     double val = 0.;
3246     for(std::size_t i = 0; i < PView::list.size(); i++) {
3247       if(opt_view_visible(i, GMSH_GET, 0)) {
3248         double maxval = std::max(fabs(opt_view_min(i, GMSH_GET, 0)),
3249                                  fabs(opt_view_max(i, GMSH_GET, 0)));
3250         if(!maxval) maxval = 1.;
3251         double val3 = 2. * CTX::instance()->lc / maxval;
3252         val = numberOrStringOptionChooser("View", i, "DisplacementFactor", true,
3253                                           "Factor", true, 0, val3, val3 / 100.);
3254         break;
3255       }
3256     }
3257     for(std::size_t i = 0; i < PView::list.size(); i++)
3258       if(opt_view_visible(i, GMSH_GET, 0))
3259         opt_view_displacement_factor(i, GMSH_SET | GMSH_GUI, val);
3260   }
3261   else if(what == "view_glyph_barycenter") {
3262     for(std::size_t i = 0; i < PView::list.size(); i++)
3263       if(opt_view_visible(i, GMSH_GET, 0))
3264         opt_view_glyph_location(i, GMSH_SET | GMSH_GUI, 1);
3265   }
3266   else if(what == "view_glyph_node") {
3267     for(std::size_t i = 0; i < PView::list.size(); i++)
3268       if(opt_view_visible(i, GMSH_GET, 0))
3269         opt_view_glyph_location(i, GMSH_SET | GMSH_GUI, 2);
3270   }
3271   else if(what == "view_range_default") {
3272     for(std::size_t i = 0; i < PView::list.size(); i++)
3273       if(opt_view_visible(i, GMSH_GET, 0))
3274         opt_view_range_type(i, GMSH_SET | GMSH_GUI, 1);
3275   }
3276   else if(what == "view_range_per_step") {
3277     for(std::size_t i = 0; i < PView::list.size(); i++)
3278       if(opt_view_visible(i, GMSH_GET, 0))
3279         opt_view_range_type(i, GMSH_SET | GMSH_GUI, 3);
3280   }
3281   else if(what == "mesh_toggle") {
3282     static int value = 1;
3283     static int old_p = (int)opt_mesh_nodes(0, GMSH_GET, 0.);
3284     static int old_l = (int)opt_mesh_lines(0, GMSH_GET, 0.);
3285     static int old_se = (int)opt_mesh_surface_edges(0, GMSH_GET, 0.);
3286     static int old_sf = (int)opt_mesh_surface_faces(0, GMSH_GET, 0.);
3287     static int old_ve = (int)opt_mesh_volume_edges(0, GMSH_GET, 0.);
3288     static int old_vf = (int)opt_mesh_volume_faces(0, GMSH_GET, 0.);
3289     if(!value) { // retore visibility
3290       Msg::StatusBar(false, "Mesh display restored");
3291       value = 1;
3292       opt_mesh_nodes(0, GMSH_SET | GMSH_GUI, old_p);
3293       opt_mesh_lines(0, GMSH_SET | GMSH_GUI, old_l);
3294       opt_mesh_surface_edges(0, GMSH_SET | GMSH_GUI, old_se);
3295       opt_mesh_surface_faces(0, GMSH_SET | GMSH_GUI, old_sf);
3296       opt_mesh_volume_edges(0, GMSH_SET | GMSH_GUI, old_ve);
3297       opt_mesh_volume_faces(0, GMSH_SET | GMSH_GUI, old_vf);
3298     }
3299     else {
3300       Msg::StatusBar(false, "Mesh display OFF");
3301       value = 0;
3302       old_p = (int)opt_mesh_nodes(0, GMSH_GET, 0.);
3303       old_l = (int)opt_mesh_lines(0, GMSH_GET, 0.);
3304       old_se = (int)opt_mesh_surface_edges(0, GMSH_GET, 0.);
3305       old_sf = (int)opt_mesh_surface_faces(0, GMSH_GET, 0.);
3306       old_ve = (int)opt_mesh_volume_edges(0, GMSH_GET, 0.);
3307       old_vf = (int)opt_mesh_volume_faces(0, GMSH_GET, 0.);
3308       opt_mesh_nodes(0, GMSH_SET | GMSH_GUI, 0);
3309       opt_mesh_lines(0, GMSH_SET | GMSH_GUI, 0);
3310       opt_mesh_surface_edges(0, GMSH_SET | GMSH_GUI, 0);
3311       opt_mesh_surface_faces(0, GMSH_SET | GMSH_GUI, 0);
3312       opt_mesh_volume_edges(0, GMSH_SET | GMSH_GUI, 0);
3313       opt_mesh_volume_faces(0, GMSH_SET | GMSH_GUI, 0);
3314     }
3315   }
3316 
3317 #if defined(HAVE_TOUCHBAR)
3318   updateTouchBar();
3319 #endif
3320 }
3321 
model_switch_cb(Fl_Widget * w,void * data)3322 static void model_switch_cb(Fl_Widget *w, void *data)
3323 {
3324   int index = (intptr_t)data;
3325   GModel::current(index);
3326   SetBoundingBox();
3327   for(std::size_t i = 0; i < GModel::list.size(); i++)
3328     GModel::list[i]->setVisibility(0);
3329   GModel::current()->setVisibility(1);
3330   CTX::instance()->mesh.changed = ENT_ALL;
3331   Msg::SetWindowTitle(GModel::current()->getFileName());
3332   FlGui::instance()->resetVisibility();
3333   drawContext::global()->draw();
3334 }
3335 
status_options_cb(Fl_Widget * w,void * data)3336 void status_options_cb(Fl_Widget *w, void *data)
3337 {
3338   if(!data) return;
3339   std::string what((const char *)data);
3340 
3341   if(what == "model") { // model selection
3342     std::vector<char *> tofree;
3343     std::vector<Fl_Menu_Item> menu;
3344     int selected = 0;
3345     for(std::size_t i = 0; i < GModel::list.size(); i++) {
3346       std::ostringstream sstream;
3347       sstream << "Model " << i;
3348       if(GModel::list[i]->getName().size())
3349         sstream << " - " << GModel::list[i]->getName();
3350       sstream << " ";
3351       char *str = strdup(sstream.str().c_str());
3352       Fl_Menu_Item menuItem = {str, 0, model_switch_cb, (void *)(intptr_t)i,
3353                                FL_MENU_RADIO};
3354       if(GModel::list[i] == GModel::current()) {
3355         selected = i;
3356         menuItem.flags |= FL_MENU_VALUE;
3357       }
3358       menu.push_back(menuItem);
3359       tofree.push_back(str);
3360     }
3361     Fl_Menu_Item it = {nullptr};
3362     menu.push_back(it);
3363     Fl_Menu_Item *m = (Fl_Menu_Item *)(&menu[0])->popup(
3364       Fl::event_x(), Fl::event_y(), nullptr, &menu[selected], nullptr);
3365     if(m) m->do_callback(nullptr);
3366     for(std::size_t i = 0; i < tofree.size(); i++) free(tofree[i]);
3367     drawContext::global()->draw();
3368   }
3369   else if(what == "?") { // display options
3370     help_options_cb(nullptr, nullptr);
3371     FlGui::instance()->help->options->show();
3372   }
3373   else if(what == "p") { // toggle projection mode
3374     opt_general_orthographic(0, GMSH_SET | GMSH_GUI,
3375                              !opt_general_orthographic(0, GMSH_GET, 0));
3376     drawContext::global()->draw();
3377   }
3378   else if(what == "quick_access") { // quick access menu
3379     // clang-format off
3380     static Fl_Menu_Item menu[] = {
3381       { "Reset viewport", 0, quick_access_cb, (void*)"reset_viewport" },
3382       { "Select rotation center", 0, quick_access_cb, (void*)"select_center" },
3383       { "Split window", 0, nullptr, nullptr, FL_SUBMENU | FL_MENU_DIVIDER },
3384          { "Horizontally", 0, quick_access_cb, (void*)"split_hor"},
3385          { "Vertically", 0, quick_access_cb, (void*)"split_ver"},
3386          { "Unsplit", 0, quick_access_cb, (void*)"unsplit"},
3387          { nullptr },
3388       { "Axes", FL_ALT + 'a', quick_access_cb, (void*)"axes",
3389         FL_MENU_TOGGLE },
3390       { "Mouse hover over meshes", 0, quick_access_cb, (void*)"hover_meshes",
3391         FL_MENU_TOGGLE },
3392       { "Projection mode", 0, nullptr, nullptr, FL_SUBMENU },
3393          { "Orthographic", FL_ALT + 'o', quick_access_cb, (void*)"orthographic"},
3394          { "Perspective", 0, quick_access_cb, (void*)"perspective"},
3395          { nullptr },
3396       { "All general options...", 0, quick_access_cb, (void*)"general",
3397         FL_MENU_DIVIDER, 0, FL_ITALIC },
3398       { "Geometry visibility", 0, nullptr, nullptr, FL_SUBMENU },
3399          { "Points", FL_ALT + 'p', quick_access_cb, (void*)"geometry_points",
3400            FL_MENU_TOGGLE },
3401          { "Curves", FL_ALT + 'l', quick_access_cb, (void*)"geometry_curves",
3402            FL_MENU_TOGGLE },
3403          { "Surfaces ", FL_ALT + 's', quick_access_cb, (void*)"geometry_surfaces",
3404            FL_MENU_TOGGLE },
3405          { "Volumes", FL_ALT + 'v', quick_access_cb, (void*)"geometry_volumes",
3406            FL_MENU_TOGGLE },
3407          { nullptr },
3408       { "All geometry options...", 0, quick_access_cb, (void*)"geometry",
3409         FL_MENU_DIVIDER, 0, FL_ITALIC },
3410       { "Mesh visibility", 0, nullptr, nullptr, FL_SUBMENU },
3411          { "Nodes", FL_ALT + FL_SHIFT + 'p', quick_access_cb, (void*)"mesh_nodes",
3412            FL_MENU_TOGGLE },
3413          { "1D elements", FL_ALT + FL_SHIFT + 'l', quick_access_cb, (void*)"mesh_lines",
3414            FL_MENU_TOGGLE },
3415          { "2D element edges ", FL_ALT + FL_SHIFT + 's', quick_access_cb,
3416            (void*)"mesh_surface_edges", FL_MENU_TOGGLE },
3417          { "2D element faces", FL_ALT + FL_SHIFT + 'd', quick_access_cb,
3418            (void*)"mesh_surface_faces", FL_MENU_TOGGLE },
3419          { "3D element edges", FL_ALT + FL_SHIFT + 'v', quick_access_cb,
3420            (void*)"mesh_volume_edges", FL_MENU_TOGGLE },
3421          { "3D element faces", FL_ALT + FL_SHIFT + 'b', quick_access_cb,
3422            (void*)"mesh_volume_faces", FL_MENU_TOGGLE },
3423          { nullptr },
3424       { "Toggle mesh display", FL_ALT + 'm', quick_access_cb, (void*)"mesh_toggle" },
3425       { "Global mesh size factor", 0, quick_access_cb, (void*)"mesh_size" },
3426       { "All mesh options...", 0, quick_access_cb, (void*)"mesh",
3427         FL_MENU_DIVIDER, 0, FL_ITALIC },
3428       { "View element outlines ", FL_ALT + 'e', quick_access_cb,
3429         (void*)"view_element_outlines", FL_MENU_TOGGLE },
3430       { "View normal raise", 0, quick_access_cb, (void*)"view_normal_raise" },
3431       { "View intervals", 0, nullptr, nullptr, FL_SUBMENU },
3432          { "Iso-values", FL_ALT + 't', quick_access_cb, (void*)"view_iso"},
3433          { "Continuous map", 0, quick_access_cb, (void*)"view_continous"},
3434          { "Filled iso-values", 0, quick_access_cb, (void*)"view_filled"},
3435          { "Numeric values", 0, quick_access_cb, (void*)"view_numeric"},
3436          { nullptr },
3437       { "View range", 0, nullptr, nullptr, FL_SUBMENU },
3438          { "Default", 0, quick_access_cb, (void*)"view_range_default"},
3439          { "Per time step", 0, quick_access_cb, (void*)"view_range_per_step"},
3440          { nullptr },
3441       { "View vector display", 0, nullptr, nullptr, FL_SUBMENU },
3442          { "Line", 0, quick_access_cb, (void*)"view_line"},
3443          { "3D arrow", 0, quick_access_cb, (void*)"view_3d_arrow"},
3444          { "Displacement", 0, quick_access_cb, (void*)"view_displacement"},
3445          { nullptr },
3446       { "View glyph location", 0, nullptr, nullptr, FL_SUBMENU },
3447          { "Barycenter", 0, quick_access_cb, (void*)"view_glyph_barycenter"},
3448          { "Node", 0, quick_access_cb, (void*)"view_glyph_node"},
3449          { nullptr },
3450       { "All view options...", 0, quick_access_cb, (void*)"view", 0, 0, FL_ITALIC },
3451       { nullptr }
3452     };
3453     // clang-format on
3454     const int gen = 7, geo = 14, msh = 21, pos = 32, end = 54;
3455     if(opt_general_axes(0, GMSH_GET, 0))
3456       menu[gen + 0].set();
3457     else
3458       menu[gen + 0].clear();
3459     if(opt_general_mouse_hover_meshes(0, GMSH_GET, 0))
3460       menu[gen + 1].set();
3461     else
3462       menu[gen + 1].clear();
3463     for(std::size_t i = 0; i < PView::list.size(); i++)
3464       if(opt_view_visible(i, GMSH_GET, 0) && opt_view_axes(i, GMSH_GET, 0))
3465         menu[gen + 7].set();
3466     if(opt_geometry_points(0, GMSH_GET, 0))
3467       menu[geo + 1].set();
3468     else
3469       menu[geo + 1].clear();
3470     if(opt_geometry_curves(0, GMSH_GET, 0))
3471       menu[geo + 2].set();
3472     else
3473       menu[geo + 2].clear();
3474     if(opt_geometry_surfaces(0, GMSH_GET, 0))
3475       menu[geo + 3].set();
3476     else
3477       menu[geo + 3].clear();
3478     if(opt_geometry_volumes(0, GMSH_GET, 0))
3479       menu[geo + 4].set();
3480     else
3481       menu[geo + 4].clear();
3482     if(opt_mesh_nodes(0, GMSH_GET, 0))
3483       menu[msh + 1].set();
3484     else
3485       menu[msh + 1].clear();
3486     if(opt_mesh_lines(0, GMSH_GET, 0))
3487       menu[msh + 2].set();
3488     else
3489       menu[msh + 2].clear();
3490     if(opt_mesh_surface_edges(0, GMSH_GET, 0))
3491       menu[msh + 3].set();
3492     else
3493       menu[msh + 3].clear();
3494     if(opt_mesh_surface_faces(0, GMSH_GET, 0))
3495       menu[msh + 4].set();
3496     else
3497       menu[msh + 4].clear();
3498     if(opt_mesh_volume_edges(0, GMSH_GET, 0))
3499       menu[msh + 5].set();
3500     else
3501       menu[msh + 5].clear();
3502     if(opt_mesh_volume_faces(0, GMSH_GET, 0))
3503       menu[msh + 6].set();
3504     else
3505       menu[msh + 6].clear();
3506     if(PView::list.empty()) {
3507       // if there are no post-processing view, hide all entries below the mesh
3508       // options...
3509       menu[pos - 1].flags = 0;
3510       for(int i = pos; i <= end; i++) menu[i].hide();
3511     }
3512     else {
3513       // otherwise add a divider and show the post-pro view entries
3514       menu[pos - 1].flags = FL_MENU_DIVIDER;
3515       for(int i = pos; i <= end; i++) menu[i].show();
3516       menu[pos].clear();
3517       for(std::size_t i = 0; i < PView::list.size(); i++) {
3518         if(opt_view_visible(i, GMSH_GET, 0) &&
3519            opt_view_show_element(i, GMSH_GET, 0)) {
3520           menu[pos].set();
3521           break;
3522         }
3523       }
3524     }
3525     // popup the menu
3526     static Fl_Menu_Item *picked =
3527       &menu[msh + 8]; // toggle mesh display - the default
3528     picked = (Fl_Menu_Item *)menu->popup(
3529       Fl::event_x(), Fl::event_y(), nullptr,
3530       (picked && picked->visible()) ? picked : &menu[msh + 8], nullptr);
3531     if(picked && picked->callback())
3532       picked->do_callback(nullptr, picked->user_data());
3533     drawContext::global()->draw();
3534   }
3535   else if(what == "S") { // mouse selection
3536     if(CTX::instance()->mouseSelection) {
3537       opt_general_mouse_selection(0, GMSH_SET | GMSH_GUI, 0);
3538       for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
3539         for(std::size_t j = 0; j < FlGui::instance()->graph[i]->gl.size(); j++)
3540           FlGui::instance()->graph[i]->gl[j]->cursor(FL_CURSOR_DEFAULT,
3541                                                      FL_BLACK, FL_WHITE);
3542     }
3543     else
3544       opt_general_mouse_selection(0, GMSH_SET | GMSH_GUI, 1);
3545   }
3546 }
3547 
3548 static int stop_anim = 0, view_in_cycle = -1;
3549 
status_play_manual(int time,int incr,bool redraw)3550 void status_play_manual(int time, int incr, bool redraw)
3551 {
3552   // avoid firing this routine recursively (can happen e.g when
3553   // keeping the finger down on the arrow key: if the system generates
3554   // too many events, we can overflow the stack--that happened on my
3555   // powerbook with the new, optimzed FLTK event handler)
3556   static bool busy = false;
3557   if(busy) return;
3558   busy = true;
3559 
3560   // if we watch some files this is a good time to check for new data
3561   file_watch_cb(nullptr, nullptr);
3562 
3563   if(time) {
3564     for(std::size_t i = 0; i < PView::list.size(); i++) {
3565       if(opt_view_visible(i, GMSH_GET, 0)) {
3566         // skip empty steps
3567         int step = (int)opt_view_timestep(i, GMSH_GET, 0) + incr;
3568         int numSteps = (int)opt_view_nb_timestep(i, GMSH_GET, 0);
3569         for(int j = 0; j < numSteps; j++) {
3570           if(PView::list[i]->getData()->hasTimeStep(step))
3571             break;
3572           else
3573             step += incr;
3574           if(step < 0) step = numSteps - 1;
3575           if(step > numSteps - 1) step = 0;
3576         }
3577         opt_view_timestep(i, GMSH_SET | GMSH_GUI, step);
3578       }
3579     }
3580   }
3581   else { // hide all views except view_in_cycle
3582     if(incr == 0) {
3583       view_in_cycle = 0;
3584       for(int i = 0; i < (int)PView::list.size(); i++)
3585         opt_view_visible(i, GMSH_SET | GMSH_GUI, (i == view_in_cycle));
3586     }
3587     else if(incr > 0) {
3588       if((view_in_cycle += incr) >= (int)PView::list.size()) view_in_cycle = 0;
3589       for(int i = 0; i < (int)PView::list.size(); i++)
3590         opt_view_visible(i, GMSH_SET | GMSH_GUI, (i == view_in_cycle));
3591     }
3592     else {
3593       if((view_in_cycle += incr) < 0) view_in_cycle = PView::list.size() - 1;
3594       for(int i = PView::list.size() - 1; i >= 0; i--)
3595         opt_view_visible(i, GMSH_SET | GMSH_GUI, (i == view_in_cycle));
3596     }
3597   }
3598   if(redraw) drawContext::global()->draw();
3599   busy = false;
3600 }
3601 
status_play_cb(Fl_Widget * w,void * data)3602 static void status_play_cb(Fl_Widget *w, void *data)
3603 {
3604   static double anim_time;
3605   getGraphicWindow(w)->setAnimButtons(0);
3606   stop_anim = 0;
3607   anim_time = TimeOfDay();
3608   while(1) {
3609     if(!FlGui::available()) return;
3610 
3611     if(stop_anim) break;
3612     if(TimeOfDay() - anim_time > CTX::instance()->post.animDelay) {
3613       anim_time = TimeOfDay();
3614       status_play_manual(!CTX::instance()->post.animCycle,
3615                          CTX::instance()->post.animStep);
3616     }
3617     FlGui::check();
3618   }
3619 }
3620 
status_pause_cb(Fl_Widget * w,void * data)3621 static void status_pause_cb(Fl_Widget *w, void *data)
3622 {
3623   stop_anim = 1;
3624   getGraphicWindow(w)->setAnimButtons(1);
3625 }
3626 
status_rewind_cb(Fl_Widget * w,void * data)3627 static void status_rewind_cb(Fl_Widget *w, void *data)
3628 {
3629   if(!CTX::instance()->post.animCycle) {
3630     for(std::size_t i = 0; i < PView::list.size(); i++) {
3631       int step = PView::list[i]->getData()->getFirstNonEmptyTimeStep();
3632       opt_view_timestep(i, GMSH_SET | GMSH_GUI, step);
3633     }
3634   }
3635   else {
3636     view_in_cycle = 0;
3637     for(std::size_t i = 0; i < PView::list.size(); i++)
3638       opt_view_visible(i, GMSH_SET | GMSH_GUI, !i);
3639   }
3640   drawContext::global()->draw();
3641 }
3642 
status_stepbackward_cb(Fl_Widget * w,void * data)3643 static void status_stepbackward_cb(Fl_Widget *w, void *data)
3644 {
3645   status_play_manual(!CTX::instance()->post.animCycle,
3646                      -CTX::instance()->post.animStep);
3647 }
3648 
status_stepforward_cb(Fl_Widget * w,void * data)3649 static void status_stepforward_cb(Fl_Widget *w, void *data)
3650 {
3651   status_play_manual(!CTX::instance()->post.animCycle,
3652                      CTX::instance()->post.animStep);
3653 }
3654 
remove_graphic_window_cb(Fl_Widget * w,void * data)3655 static void remove_graphic_window_cb(Fl_Widget *w, void *data)
3656 {
3657   std::vector<graphicWindow *> graph2;
3658   graphicWindow *deleteMe = nullptr;
3659   for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++) {
3660     if(FlGui::instance()->graph[i]->getWindow() == w)
3661       deleteMe = FlGui::instance()->graph[i];
3662     else
3663       graph2.push_back(FlGui::instance()->graph[i]);
3664   }
3665   if(deleteMe) {
3666     openglWindow::setLastHandled(nullptr);
3667     FlGui::instance()->graph = graph2;
3668     delete deleteMe;
3669   }
3670 }
3671 
show_hide_message_cb(Fl_Widget * w,void * data)3672 void show_hide_message_cb(Fl_Widget *w, void *data)
3673 {
3674   graphicWindow *g =
3675     getGraphicWindow(FlGui::instance()->getCurrentOpenglWindow()->parent());
3676   g->showHideMessages();
3677   FlGui::check();
3678 }
3679 
show_hide_menu_cb(Fl_Widget * w,void * data)3680 void show_hide_menu_cb(Fl_Widget *w, void *data)
3681 {
3682   graphicWindow *g =
3683     getGraphicWindow(FlGui::instance()->getCurrentOpenglWindow()->parent());
3684   g->showHideMenu();
3685   FlGui::check();
3686 }
3687 
attach_detach_menu_cb(Fl_Widget * w,void * data)3688 void attach_detach_menu_cb(Fl_Widget *w, void *data)
3689 {
3690   graphicWindow *g =
3691     getGraphicWindow(FlGui::instance()->getCurrentOpenglWindow()->parent());
3692   g->attachDetachMenu();
3693   FlGui::check();
3694 }
3695 
message_menu_autoscroll_cb(Fl_Widget * w,void * data)3696 static void message_menu_autoscroll_cb(Fl_Widget *w, void *data)
3697 {
3698   graphicWindow *g = (graphicWindow *)data;
3699   g->setAutoScroll(!g->getAutoScroll());
3700 }
3701 
message_menu_clear_cb(Fl_Widget * w,void * data)3702 static void message_menu_clear_cb(Fl_Widget *w, void *data)
3703 {
3704   graphicWindow *g = (graphicWindow *)data;
3705   g->clearMessages();
3706 }
3707 
message_menu_save_cb(Fl_Widget * w,void * data)3708 static void message_menu_save_cb(Fl_Widget *w, void *data)
3709 {
3710   graphicWindow *g = (graphicWindow *)data;
3711   if(fileChooser(FILE_CHOOSER_CREATE, "Save Messages", ""))
3712     g->saveMessages(fileChooserGetName(1).c_str());
3713 }
3714 
message_browser_cb(Fl_Widget * w,void * data)3715 static void message_browser_cb(Fl_Widget *w, void *data)
3716 {
3717   graphicWindow *g = (graphicWindow *)data;
3718   g->copySelectedMessagesToClipboard();
3719 }
3720 
message_menu_search_cb(Fl_Widget * w,void * data)3721 static void message_menu_search_cb(Fl_Widget *w, void *data)
3722 {
3723   graphicWindow *g = (graphicWindow *)data;
3724   g->getMessageBrowser()->clear();
3725   for(int i = 0; i < (int)g->getMessages().size(); i++)
3726     g->getMessageBrowser()->add(g->getMessages()[i].c_str());
3727 }
3728 
tile_cb(Fl_Widget * w,void * data)3729 static void tile_cb(Fl_Widget *w, void *data)
3730 {
3731   if(Fl::event() == FL_RELEASE) {
3732     // rebuild the tree when we relase the mouse after resizing
3733     FlGui::instance()->rebuildTree(true);
3734   }
3735 }
3736 
3737 // This dummy box class permits to define a box widget that will not eat the
3738 // FL_ENTER/FL_LEAVE events (the box widget in fltk > 1.1 does that, so that
3739 // gl->handle() was not called when the mouse moved)
3740 class dummyBox : public Fl_Box {
3741 private:
handle(int)3742   int handle(int) { return 0; } // always!
3743 public:
dummyBox(int x,int y,int w,int h,const char * l=nullptr)3744   dummyBox(int x, int y, int w, int h, const char *l = nullptr)
3745     : Fl_Box(x, y, w, h, l)
3746   {
3747   }
3748 };
3749 
3750 // The main graphic window has a special resize behaviour forcing the message
3751 // tile to always keep its height
3752 class mainWindowSpecialResize : public mainWindow {
3753 public:
mainWindowSpecialResize(int w,int h,bool nonModal,const char * l=nullptr)3754   mainWindowSpecialResize(int w, int h, bool nonModal, const char *l = nullptr)
3755     : mainWindow(w, h, nonModal, l)
3756   {
3757   }
resize(int X,int Y,int W,int H)3758   virtual void resize(int X, int Y, int W, int H)
3759   {
3760     bool special = (FlGui::available() && shown() &&
3761                     this == FlGui::instance()->graph[0]->getWindow());
3762     int mh = 0;
3763     if(special) mh = FlGui::instance()->graph[0]->getMessageHeight();
3764     Fl_Window::resize(X, Y, W, H);
3765     const int minimum_non_message_height = 100;
3766     if(special && mh < h() - minimum_non_message_height)
3767       FlGui::instance()->graph[0]->setMessageHeight(mh);
3768   }
3769 };
3770 
3771 class mainWindowProgress : public Fl_Progress {
3772 public:
mainWindowProgress(int x,int y,int w,int h,const char * l=nullptr)3773   mainWindowProgress(int x, int y, int w, int h, const char *l = nullptr)
3774     : Fl_Progress(x, y, w, h, l)
3775   {
3776   }
handle(int event)3777   int handle(int event)
3778   {
3779     if(event == FL_PUSH) {
3780       if(FlGui::available()) {
3781         for(std::size_t i = 0; i < FlGui::instance()->graph.size(); i++)
3782           FlGui::instance()->graph[i]->showHideMessages();
3783       }
3784       return 1;
3785     }
3786     return Fl_Progress::handle(event);
3787   }
3788 };
3789 
graphicWindow(bool main,int numTiles,bool detachedMenu)3790 graphicWindow::graphicWindow(bool main, int numTiles, bool detachedMenu)
3791   : _autoScrollMessages(true)
3792 {
3793   int mh = main ? BH : 0; // menu bar height
3794 #if defined(__APPLE__)
3795   if(CTX::instance()->systemMenuBar) mh = 0;
3796 #endif
3797   int sh = 2 * FL_NORMAL_SIZE - 3; // status bar height
3798   int sw = FL_NORMAL_SIZE + 2; // status button width
3799 
3800   int mheight = main ? 2 * BH /* nonzero! */ : 0;
3801   int glheight = CTX::instance()->glSize[1] - mheight;
3802   int height = mh + glheight + mheight + sh;
3803   // make sure height < screen height
3804   if(height > Fl::h()) {
3805     height = Fl::h();
3806     glheight = height - mh - mheight - sh;
3807     CTX::instance()->glSize[1] = glheight + mheight;
3808   }
3809 
3810   int twidth = (main && !detachedMenu) ? 14 * sw : 0;
3811   int glwidth = CTX::instance()->glSize[0] - twidth;
3812   int width = glwidth + twidth;
3813   // make sure width < screen width
3814   if(width > Fl::w()) {
3815     width = Fl::w();
3816     glwidth = width - twidth;
3817     CTX::instance()->glSize[0] = glwidth + twidth;
3818   }
3819 
3820   // the graphic window should be a "normal" window (neither modal nor
3821   // non-modal)
3822   if(main) {
3823     _win = new mainWindowSpecialResize(width, height, false);
3824     _win->callback(file_quit_cb);
3825   }
3826   else {
3827     _win = new paletteWindow(width, height, false);
3828     _win->callback(remove_graphic_window_cb);
3829   }
3830 
3831 #if defined(__APPLE__)
3832   _sysbar = nullptr;
3833 #endif
3834   _bar = nullptr;
3835   if(main) {
3836 #if defined(__APPLE__)
3837     if(CTX::instance()->systemMenuBar) {
3838       _sysbar = new Fl_Sys_Menu_Bar(1, 1, 1, 1);
3839       _sysbar->menu(sysbar_table);
3840       _sysbar->global();
3841       fillRecentHistoryMenu();
3842     }
3843     else {
3844 #endif
3845       _bar = new Fl_Menu_Bar(0, 0, width, BH);
3846       _bar->menu(bar_table);
3847       _bar->global();
3848       fillRecentHistoryMenu();
3849 #if defined(__APPLE__)
3850     }
3851 #endif
3852   }
3853 
3854   // minimum width should be exactly the width of the status bar buttons (see
3855   // below)
3856   _minWidth = 10 + 11 * sw + 1.75 * FL_NORMAL_SIZE;
3857   _minHeight = 100;
3858   _win->size_range(_minWidth, _minHeight);
3859 
3860   // a dummy resizable box that won't eat events
3861   dummyBox *resbox = new dummyBox(_minWidth, mh, width - _minWidth, glheight);
3862   _win->resizable(resbox);
3863 
3864   // tiled windows (tree menu, opengl, messages)
3865   _tile = new Fl_Tile(0, mh, glwidth + twidth, glheight + mheight);
3866 
3867   int w2 = glwidth / 2, h2 = glheight / 2;
3868   if(numTiles == 2) {
3869     gl.push_back(new openglWindow(twidth, mh, w2, glheight));
3870     gl.back()->end();
3871     gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, glheight));
3872     gl.back()->end();
3873   }
3874   else if(numTiles == 3) {
3875     gl.push_back(new openglWindow(twidth, mh, w2, glheight));
3876     gl.back()->end();
3877     gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
3878     gl.back()->end();
3879     gl.push_back(
3880       new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
3881     gl.back()->end();
3882   }
3883   else if(numTiles == 4) {
3884     gl.push_back(new openglWindow(twidth, mh, w2, h2));
3885     gl.back()->end();
3886     gl.push_back(new openglWindow(twidth + w2, mh, glwidth - w2, h2));
3887     gl.back()->end();
3888     gl.push_back(new openglWindow(twidth, mh + h2, w2, glheight - h2));
3889     gl.back()->end();
3890     gl.push_back(
3891       new openglWindow(twidth + w2, mh + h2, glwidth - w2, glheight - h2));
3892     gl.back()->end();
3893   }
3894   else {
3895     gl.push_back(new openglWindow(twidth, mh, glwidth, glheight));
3896     gl.back()->end();
3897   }
3898 
3899   int mode = FL_RGB | FL_DEPTH | (CTX::instance()->db ? FL_DOUBLE : FL_SINGLE);
3900   if(CTX::instance()->antialiasing) mode |= FL_MULTISAMPLE;
3901   if(CTX::instance()->stereo) {
3902     mode |= FL_DOUBLE;
3903     mode |= FL_STEREO;
3904   }
3905   for(std::size_t i = 0; i < gl.size(); i++) gl[i]->mode(mode);
3906 
3907   if(main) {
3908     _browser = new messageBrowser(twidth, mh + glheight, glwidth, mheight);
3909     int s = CTX::instance()->msgFontSize;
3910     _browser->textsize(s <= 0 ? FL_NORMAL_SIZE - 2 : s);
3911     _browser->callback(message_browser_cb, this);
3912     _browser->search_callback(message_menu_search_cb, this);
3913     _browser->autoscroll_callback(message_menu_autoscroll_cb, this);
3914     _browser->save_callback(message_menu_save_cb, this);
3915     _browser->clear_callback(message_menu_clear_cb, this);
3916   }
3917   else {
3918     _browser = nullptr;
3919   }
3920 
3921   if(main && !detachedMenu) {
3922     _onelab = new onelabGroup(0, mh, twidth, height - mh - sh);
3923     _onelab->enableTreeWidgetResize(false);
3924   }
3925   else {
3926     _onelab = nullptr;
3927   }
3928 
3929   _tile->callback(tile_cb);
3930   _tile->end();
3931 
3932   // resize the tiles to match the prescribed sizes
3933   _tile->position(0, mh + glheight, 0, mh + CTX::instance()->glSize[1]);
3934 
3935   // if the tree widget is too small it will not be rebuilt correctly (probably
3936   // a bug)... so impose minimum width
3937   int minw = 3 * BB / 2 + 4 * WB;
3938   if(CTX::instance()->menuSize[0] < minw) CTX::instance()->menuSize[0] = minw;
3939   _tile->position(twidth, 0, CTX::instance()->menuSize[0], 0);
3940 
3941   // bottom button bar
3942   _bottom = new Fl_Box(0, mh + glheight + mheight, width, sh);
3943   _bottom->box(GMSH_SIMPLE_TOP_BOX);
3944 
3945   int x = 2;
3946   int sht = sh - 4; // leave a 2 pixel border at the bottom
3947 
3948   _butt[5] =
3949     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_models");
3950   _butt[5]->callback(status_options_cb, (void *)"model");
3951   _butt[5]->tooltip("Set current (active) model");
3952   x += sw;
3953   _butt[8] = new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "O");
3954   _butt[8]->callback(status_options_cb, (void *)"quick_access");
3955   _butt[8]->tooltip("Open quick access menu (also available by double-clicking "
3956                     "in the graphic window)");
3957   x += sw;
3958   _butt[0] = new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "X");
3959   _butt[0]->callback(status_xyz1p_cb, (void *)"x");
3960   _butt[0]->tooltip("Set +X or -X (Shift) view (Alt+x or Alt+Shift+x)");
3961   x += sw;
3962   _butt[1] = new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "Y");
3963   _butt[1]->callback(status_xyz1p_cb, (void *)"y");
3964   _butt[1]->tooltip("Set +Y or -Y (Shift) view (Alt+y or Alt+Shift+y)");
3965   x += sw;
3966   _butt[2] = new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "Z");
3967   _butt[2]->callback(status_xyz1p_cb, (void *)"z");
3968   _butt[2]->tooltip("Set +Z or -Z (Shift) view (Alt+z or Alt+Shift+z)");
3969   x += sw;
3970   _butt[4] =
3971     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_rotate");
3972   _butt[4]->callback(status_xyz1p_cb, (void *)"r");
3973   _butt[4]->tooltip(
3974     "Rotate +90 or -90 (Shift) degrees, or sync rotations (Ctrl)");
3975   x += sw;
3976   _butt[3] = new Fl_Button(x, mh + glheight + mheight + 2, 2 * FL_NORMAL_SIZE,
3977                            sht, "1:1");
3978   _butt[3]->callback(status_xyz1p_cb, (void *)"1:1");
3979   _butt[3]->tooltip("Set unit scale, sync scale between viewports (Ctrl), "
3980                     "or reset bounding box around visible entities (Shift) "
3981                     "(Alt+1, Alt+Ctrl+1, Alt+Shift+1)");
3982   x += 1.75 * FL_NORMAL_SIZE;
3983   _butt[9] = new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "S");
3984   _butt[9]->callback(status_options_cb, (void *)"S");
3985   _butt[9]->tooltip("Toggle mouse selection ON/OFF (Escape)");
3986   x += sw;
3987   x += 4;
3988   _butt[6] =
3989     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_rewind");
3990   _butt[6]->callback(status_rewind_cb);
3991   _butt[6]->tooltip("Rewind animation");
3992   _butt[6]->deactivate();
3993   x += sw;
3994   _butt[10] =
3995     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_back");
3996   _butt[10]->callback(status_stepbackward_cb);
3997   _butt[10]->tooltip("Step backward (Left arrow)");
3998   _butt[10]->deactivate();
3999   x += sw;
4000   _butt[7] =
4001     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_play");
4002   _butt[7]->callback(status_play_cb);
4003   _butt[7]->tooltip("Play/pause animation");
4004   _butt[7]->deactivate();
4005   x += sw;
4006   _butt[11] =
4007     new Fl_Button(x, mh + glheight + mheight + 2, sw, sht, "@-1gmsh_forward");
4008   _butt[11]->callback(status_stepforward_cb);
4009   _butt[11]->tooltip("Step forward (Right arrow)");
4010   _butt[11]->deactivate();
4011   x += sw;
4012 
4013   for(int i = 0; i < 12; i++) {
4014     _butt[i]->box(FL_FLAT_BOX);
4015     _butt[i]->selection_color(FL_WHITE);
4016     _butt[i]->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
4017   }
4018 
4019   x += 4;
4020   _label =
4021     new mainWindowProgress(x, mh + glheight + mheight + 2, width - x - 2, sht);
4022   _label->box(FL_FLAT_BOX);
4023   _label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
4024   if(CTX::instance()->guiColorScheme)
4025     _label->color(FL_BACKGROUND_COLOR, FL_LIGHT3);
4026   else
4027     _label->color(FL_BACKGROUND_COLOR, FL_DARK2);
4028 
4029   _win->position(CTX::instance()->glPosition[0],
4030                  CTX::instance()->glPosition[1]);
4031   _win->end();
4032 
4033   if(main && detachedMenu) {
4034     _menuwin =
4035       new mainWindow(CTX::instance()->menuSize[0], CTX::instance()->menuSize[1],
4036                      CTX::instance()->nonModalWindows ? true : false, "Gmsh");
4037     _menuwin->callback(file_quit_cb);
4038     _menuwin->box(GMSH_WINDOW_BOX);
4039     _onelab = new onelabGroup(0, 0, _menuwin->w(), _menuwin->h());
4040     _onelab->enableTreeWidgetResize(true);
4041     _menuwin->position(CTX::instance()->menuPosition[0],
4042                        CTX::instance()->menuPosition[1]);
4043     _menuwin->resizable(_onelab);
4044     _menuwin->size_range(_onelab->getMinWindowWidth(),
4045                          _onelab->getMinWindowHeight());
4046     _menuwin->end();
4047   }
4048   else {
4049     _menuwin = nullptr;
4050   }
4051 }
4052 
~graphicWindow()4053 graphicWindow::~graphicWindow()
4054 {
4055   openglWindow::setLastHandled(nullptr);
4056   _tile->clear();
4057   _win->clear();
4058   Fl::delete_widget(_win);
4059   if(_menuwin) {
4060     _menuwin->clear();
4061     Fl::delete_widget(_menuwin);
4062   }
4063 }
4064 
setTitle(const std::string & str)4065 void graphicWindow::setTitle(const std::string &str)
4066 {
4067   _win->copy_label(str.c_str());
4068 }
4069 
detachMenu()4070 void graphicWindow::detachMenu()
4071 {
4072   if(_menuwin || !_onelab || !_browser) return;
4073   if(_browser->h() == 0) setMessageHeight(1);
4074   int w = _onelab->w();
4075   _tile->remove(_onelab);
4076   _browser->resize(0, _browser->y(), _browser->w() + w, _browser->h());
4077   for(std::size_t i = 0; i < gl.size(); i++) {
4078     if(gl[i]->x() == w) {
4079       gl[i]->resize(0, gl[i]->y(), gl[i]->w() + w, gl[i]->h());
4080     }
4081   }
4082   _tile->redraw();
4083 
4084   _menuwin =
4085     new mainWindow(_onelab->w(), CTX::instance()->menuSize[1],
4086                    CTX::instance()->nonModalWindows ? true : false, "Gmsh");
4087   _menuwin->callback(file_quit_cb);
4088   _menuwin->box(GMSH_WINDOW_BOX);
4089   _onelab->box(FL_FLAT_BOX);
4090   _menuwin->add(_onelab);
4091   _onelab->resize(0, 0, _menuwin->w(), _menuwin->h());
4092   _menuwin->position(CTX::instance()->menuPosition[0],
4093                      CTX::instance()->menuPosition[1]);
4094   _menuwin->resizable(_onelab);
4095   _menuwin->size_range(_onelab->getMinWindowWidth(),
4096                        _onelab->getMinWindowHeight());
4097   _menuwin->end();
4098   _menuwin->show();
4099 
4100   _onelab->enableTreeWidgetResize(true);
4101   _onelab->rebuildTree(true);
4102 }
4103 
attachMenu()4104 void graphicWindow::attachMenu()
4105 {
4106   if(!_menuwin || !_onelab || !_browser) return;
4107   CTX::instance()->menuSize[1] = _menuwin->h();
4108   CTX::instance()->menuPosition[0] = _menuwin->x();
4109   CTX::instance()->menuPosition[1] = _menuwin->y();
4110   _menuwin->remove(_onelab);
4111   _menuwin->hide();
4112   delete _menuwin;
4113   _menuwin = nullptr;
4114   if(_browser->h() == 0) setMessageHeight(1);
4115   int w = _onelab->w();
4116   if(_browser->w() - w < 0) w = _browser->w() / 2;
4117   _browser->resize(w, _browser->y(), _browser->w() - w, _browser->h());
4118   for(std::size_t i = 0; i < gl.size(); i++) {
4119     if(gl[i]->x() == 0) {
4120       gl[i]->resize(w, gl[i]->y(), gl[i]->w() - w, gl[i]->h());
4121     }
4122   }
4123   _onelab->box(GMSH_SIMPLE_RIGHT_BOX);
4124   _tile->add(_onelab);
4125   _onelab->resize(_tile->x(), _tile->y(), w, _tile->h());
4126   _tile->redraw();
4127 
4128   _onelab->enableTreeWidgetResize(false);
4129   _onelab->rebuildTree(true);
4130 }
4131 
attachDetachMenu()4132 void graphicWindow::attachDetachMenu()
4133 {
4134   if(_menuwin)
4135     attachMenu();
4136   else
4137     detachMenu();
4138 }
4139 
showMenu()4140 void graphicWindow::showMenu()
4141 {
4142   if(_menuwin || !_onelab || !_win->shown()) return;
4143   if(_onelab->w() < FL_NORMAL_SIZE) {
4144     int width = CTX::instance()->menuSize[0];
4145     if(width < FL_NORMAL_SIZE) width = _onelab->getMinWindowWidth();
4146     int maxw = _win->w();
4147     if(width > maxw) width = maxw / 2;
4148     setMenuWidth(width);
4149     // necessary until resizing of 0-sized groups works
4150     _onelab->rebuildTree(true);
4151   }
4152 }
4153 
hideMenu()4154 void graphicWindow::hideMenu()
4155 {
4156   if(_menuwin || !_onelab) return;
4157   CTX::instance()->menuSize[0] = _onelab->w();
4158   setMenuWidth(0);
4159 }
4160 
showHideMenu()4161 void graphicWindow::showHideMenu()
4162 {
4163   if(_menuwin || !_onelab) return;
4164   if(_onelab->w() < FL_NORMAL_SIZE)
4165     showMenu();
4166   else
4167     hideMenu();
4168 }
4169 
getMenuWidth()4170 int graphicWindow::getMenuWidth()
4171 {
4172   if(!_onelab) return 0;
4173   return _onelab->w();
4174 }
4175 
getMenuHeight()4176 int graphicWindow::getMenuHeight()
4177 {
4178   if(!_menuwin) return 0;
4179   return _menuwin->h();
4180 }
4181 
getMenuPositionX()4182 int graphicWindow::getMenuPositionX()
4183 {
4184   if(!_menuwin) return 0;
4185   return _menuwin->x();
4186 }
4187 
getMenuPositionY()4188 int graphicWindow::getMenuPositionY()
4189 {
4190   if(!_menuwin) return 0;
4191   return _menuwin->y();
4192 }
4193 
split(openglWindow * g,char how,double ratio)4194 bool graphicWindow::split(openglWindow *g, char how, double ratio)
4195 {
4196   if(_tile->find(g) == _tile->children()) return false; // not found
4197 
4198   if(how == 'u') {
4199     // after many tries I cannot figure out how to do this cleanly, so let's be
4200     // brutal :-)
4201     int mode = g->mode();
4202     openglWindow::setLastHandled(nullptr);
4203     for(std::size_t i = 0; i < gl.size(); i++) {
4204       _tile->remove(gl[i]);
4205       delete gl[i];
4206     }
4207     gl.clear();
4208     openglWindow *g2 = new openglWindow(
4209       _tile->x() + (_onelab && !_menuwin ? _onelab->w() : 0), _tile->y(),
4210       _tile->w() - (_onelab && !_menuwin ? _onelab->w() : 0),
4211       _tile->h() - (_browser ? _browser->h() : 0));
4212     g2->end();
4213     g2->mode(mode);
4214     gl.push_back(g2);
4215     _tile->add(g2);
4216     g2->show();
4217     openglWindow::setLastHandled(g2);
4218   }
4219   else {
4220     double fact = (ratio <= 0.) ? 0.01 : (ratio >= 1.) ? 0.99 : ratio;
4221     // make sure browser is not zero-size when adding children
4222     if(_browser && _browser->h() == 0) setMessageHeight(1);
4223     int x1 = g->x();
4224     int y1 = g->y();
4225     int w1 = (how == 'h') ? (int)(g->w() * fact) : g->w();
4226     int h1 = (how == 'h') ? g->h() : (int)(g->h() * fact);
4227 
4228     int x2 = (how == 'h') ? (g->x() + w1) : g->x();
4229     int y2 = (how == 'h') ? g->y() : (g->y() + h1);
4230     int w2 = (how == 'h') ? (g->w() - w1) : g->w();
4231     int h2 = (how == 'h') ? g->h() : (g->h() - h1);
4232 
4233     openglWindow *g2 = new openglWindow(0, 0, w2, h2);
4234     g2->end();
4235     g2->mode(g->mode());
4236 
4237     gl.push_back(g2);
4238 
4239     g->resize(x1, y1, w1, h1);
4240     g2->resize(x2, y2, w2, h2);
4241 
4242     _tile->add(g2);
4243 
4244     g2->show();
4245     openglWindow::setLastHandled(g2);
4246   }
4247   return true;
4248 }
4249 
setStereo(bool st)4250 void graphicWindow::setStereo(bool st)
4251 {
4252   openglWindow::setLastHandled(nullptr);
4253   for(std::size_t i = 0; i < gl.size(); i++) {
4254     if(st) { gl[i]->mode(FL_RGB | FL_DEPTH | FL_DOUBLE | FL_STEREO); }
4255     else {
4256       gl[i]->mode(FL_RGB | FL_DEPTH | FL_DOUBLE);
4257     }
4258     gl[i]->show();
4259   }
4260   Msg::Info("new gl window for stereo vision!");
4261 }
4262 
setAnimButtons(int mode)4263 void graphicWindow::setAnimButtons(int mode)
4264 {
4265   if(mode) {
4266     _butt[7]->callback(status_play_cb);
4267     _butt[7]->label("@-1gmsh_play");
4268   }
4269   else {
4270     _butt[7]->callback(status_pause_cb);
4271     _butt[7]->label("@-1gmsh_pause");
4272   }
4273 }
4274 
checkAnimButtons()4275 void graphicWindow::checkAnimButtons()
4276 {
4277   bool play = false;
4278   if(CTX::instance()->post.animCycle) { play = true; }
4279   else {
4280     for(std::size_t i = 0; i < PView::list.size(); i++) {
4281       if(PView::list[i]->getData()->getNumTimeSteps() > 1) {
4282         play = true;
4283         break;
4284       }
4285     }
4286   }
4287   if(play) {
4288     _butt[6]->activate();
4289     _butt[7]->activate();
4290     _butt[10]->activate();
4291     _butt[11]->activate();
4292   }
4293   else {
4294     _butt[6]->deactivate();
4295     _butt[7]->deactivate();
4296     _butt[10]->deactivate();
4297     _butt[11]->deactivate();
4298   }
4299 }
4300 
setMenuWidth(int w)4301 void graphicWindow::setMenuWidth(int w)
4302 {
4303   if(!_onelab) return;
4304   if(_menuwin) {
4305     _menuwin->size(std::max(w, _onelab->getMinWindowWidth()), _menuwin->h());
4306     _menuwin->redraw();
4307     return;
4308   }
4309   if(!_browser) return;
4310   double dw = w - _onelab->w();
4311   if(!dw) return;
4312   for(std::size_t i = 0; i < gl.size(); i++) {
4313     if(gl[i]->x() == _onelab->x() + _onelab->w()) {
4314       gl[i]->resize(gl[i]->x() + dw, gl[i]->y(), gl[i]->w() - dw, gl[i]->h());
4315     }
4316   }
4317   _browser->resize(_browser->x() + dw, _browser->y(), _browser->w() - dw,
4318                    _browser->h());
4319   _onelab->resize(_onelab->x(), _onelab->y(), _onelab->w() + dw, _onelab->h());
4320   _tile->redraw();
4321 }
4322 
getGlHeight()4323 int graphicWindow::getGlHeight()
4324 {
4325   int h = _win->h() - _bottom->h(); // yes, ignore message browser
4326   if(_bar) h -= _bar->h();
4327   return h;
4328 }
4329 
getGlWidth()4330 int graphicWindow::getGlWidth() { return _win->w(); }
4331 
setGlWidth(int w)4332 void graphicWindow::setGlWidth(int w)
4333 {
4334   if(w == _win->w()) return;
4335   _win->size(std::max(w, _minWidth), _win->h());
4336   _win->redraw();
4337 }
4338 
setGlHeight(int h)4339 void graphicWindow::setGlHeight(int h)
4340 {
4341   int hh = h + _bottom->h();
4342   if(_bar) hh += _bar->h();
4343   if(hh == _win->h()) return;
4344   _win->size(_win->w(), std::max(hh, _minHeight));
4345   _win->redraw();
4346 }
4347 
setMessageHeight(int h)4348 void graphicWindow::setMessageHeight(int h)
4349 {
4350   if(!_browser) return;
4351   int dh = h - _browser->h();
4352   if(!dh) return;
4353   for(std::size_t i = 0; i < gl.size(); i++) {
4354     if(gl[i]->y() + gl[i]->h() == _browser->y()) {
4355       gl[i]->resize(gl[i]->x(), gl[i]->y(), gl[i]->w(), gl[i]->h() - dh);
4356     }
4357   }
4358   _browser->resize(_browser->x(), _browser->y() - dh, _browser->w(),
4359                    _browser->h() + dh);
4360   _tile->redraw();
4361 }
4362 
showMessages()4363 void graphicWindow::showMessages()
4364 {
4365   if(!_browser || !_win->shown()) return;
4366   Msg::ResetErrorCounter();
4367   if(_browser->h() < FL_NORMAL_SIZE) {
4368     int height = CTX::instance()->msgSize;
4369     if(height < FL_NORMAL_SIZE) height = 10 * FL_NORMAL_SIZE;
4370     int maxh = _win->h() - _bottom->h();
4371     if(height > maxh) height = maxh / 2;
4372     setMessageHeight(height);
4373   }
4374   if(_autoScrollMessages) _browser->bottomline(_browser->size());
4375 }
4376 
hideMessages()4377 void graphicWindow::hideMessages()
4378 {
4379   if(!_browser) return;
4380   CTX::instance()->msgSize = _browser->h();
4381   setMessageHeight(0);
4382 }
4383 
showHideMessages()4384 void graphicWindow::showHideMessages()
4385 {
4386   if(!_browser) return;
4387   if(_browser->h() < FL_NORMAL_SIZE)
4388     showMessages();
4389   else
4390     hideMessages();
4391 }
4392 
getMessageHeight()4393 int graphicWindow::getMessageHeight()
4394 {
4395   if(!_browser) return 0;
4396   return _browser->h();
4397 }
4398 
addMessage(const char * msg)4399 void graphicWindow::addMessage(const char *msg)
4400 {
4401   if(!_browser) return;
4402 
4403     // this routine can be called from multiple threads, e.g. via Msg::Info
4404     // calls in meshGFace(). We should use FlGui::lock/unlock, but currently
4405     // this does not seem to work (17/02/2017)
4406 #pragma omp critical
4407   {
4408     _messages.push_back(msg);
4409     _browser->add(msg);
4410     if(_autoScrollMessages && _win->shown() && _browser->h() >= FL_NORMAL_SIZE)
4411       _browser->bottomline(_browser->size());
4412   }
4413 }
4414 
clearMessages()4415 void graphicWindow::clearMessages()
4416 {
4417   _messages.clear();
4418   if(!_browser) return;
4419   _browser->clear();
4420 }
4421 
saveMessages(const char * filename)4422 void graphicWindow::saveMessages(const char *filename)
4423 {
4424   if(!_browser) return;
4425 
4426   FILE *fp = Fopen(filename, "w");
4427 
4428   if(!fp) {
4429     Msg::Error("Unable to open file '%s'", filename);
4430     return;
4431   }
4432 
4433   Msg::StatusBar(true, "Writing '%s'...", filename);
4434   for(int i = 1; i <= _browser->size(); i++) {
4435     const char *c = _browser->text(i);
4436     if(c[0] == '@')
4437       fprintf(fp, "%s\n", &c[5]);
4438     else
4439       fprintf(fp, "%s\n", c);
4440   }
4441   Msg::StatusBar(true, "Done writing '%s'", filename);
4442   fclose(fp);
4443 }
4444 
copySelectedMessagesToClipboard()4445 void graphicWindow::copySelectedMessagesToClipboard()
4446 {
4447   if(!_browser) return;
4448 
4449   std::string buff;
4450   for(int i = 1; i <= _browser->size(); i++) {
4451     if(_browser->selected(i)) {
4452       const char *c = _browser->text(i);
4453       if(strlen(c) > 5 && c[0] == '@')
4454         buff += std::string(&c[5]);
4455       else
4456         buff += std::string(c);
4457       buff += "\n";
4458     }
4459   }
4460   // bof bof bof
4461   Fl::copy(buff.c_str(), buff.size(), 0);
4462   Fl::copy(buff.c_str(), buff.size(), 1);
4463 }
4464 
setMessageFontSize(int size)4465 void graphicWindow::setMessageFontSize(int size)
4466 {
4467   if(!_browser) return;
4468   _browser->textsize(size <= 0 ? FL_NORMAL_SIZE - 2 : size);
4469   _browser->redraw();
4470 }
4471 
changeMessageFontSize(int incr)4472 void graphicWindow::changeMessageFontSize(int incr)
4473 {
4474   if(!_browser) return;
4475   setMessageFontSize(_browser->textsize() + incr);
4476 }
4477 
check_utf8(const std::string & string)4478 static bool check_utf8(const std::string &string)
4479 {
4480   for(int i = 0, ix = string.length(); i < ix; i++) {
4481     int n;
4482     int c = (unsigned char)string[i];
4483     if(0x00 <= c && c <= 0x7f)
4484       n = 0; // 0bbbbbbb
4485     else if((c & 0xE0) == 0xC0)
4486       n = 1; // 110bbbbb
4487     else if(c == 0xed && i < (ix - 1) &&
4488             ((unsigned char)string[i + 1] & 0xa0) == 0xa0)
4489       return false; // U+d800 to U+dfff
4490     else if((c & 0xF0) == 0xE0)
4491       n = 2; // 1110bbbb
4492     else if((c & 0xF8) == 0xF0)
4493       n = 3; // 11110bbb
4494     // else if (($c & 0xFC) == 0xF8) n=4; // 111110bb //byte 5, unnecessary in 4
4495     // byte UTF-8 else if (($c & 0xFE) == 0xFC) n=5; // 1111110b //byte 6,
4496     // unnecessary in 4 byte UTF-8
4497     else
4498       return false;
4499     for(int j = 0; j < n && i < ix; j++) { // n bytes matching 10bbbbbb follow ?
4500       if((++i == ix) || (((unsigned char)string[i] & 0xC0) != 0x80))
4501         return false;
4502     }
4503   }
4504   return true;
4505 }
4506 
fillRecentHistoryMenu()4507 void graphicWindow::fillRecentHistoryMenu()
4508 {
4509 #if defined(__APPLE__)
4510   if(CTX::instance()->systemMenuBar && !_sysbar) return;
4511 #endif
4512 
4513   Fl_Menu_Item *table = bar_table;
4514 #if defined(__APPLE__)
4515   if(CTX::instance()->systemMenuBar) table = sysbar_table;
4516 #endif
4517 
4518   static char recent[10][256];
4519   for(int i = 0; i < 10; i++) {
4520     if(i < (int)CTX::instance()->recentFiles.size()) {
4521       if(check_utf8(CTX::instance()->recentFiles[i]))
4522         strcpy(recent[i], CTX::instance()->recentFiles[i].c_str());
4523       else {
4524         Msg::Info("Ignoring invalid General.RecentFile%d", i);
4525         strcpy(recent[i], "");
4526       }
4527     }
4528     else
4529       strcpy(recent[i], "");
4530     table[4 + i].text = recent[i];
4531     table[4 + i].user_data_ = (void *)recent[i];
4532   }
4533 
4534 #if defined(__APPLE__)
4535   if(CTX::instance()->systemMenuBar) _sysbar->menu(table);
4536 #endif
4537 }
4538 
4539 typedef struct {
4540   std::string label;
4541   Fl_Callback *callback;
4542   void *arg;
4543 } menuItem;
4544 
4545 static menuItem static_modules[] = {
4546   {"0Modules/Geometry/Elementary entities/Set geometry kernel/Built-in",
4547    (Fl_Callback *)geometry_elementary_set_factory_cb, (void *)"Built-in"},
4548   {"0Modules/Geometry/Elementary entities/Set geometry kernel/OpenCASCADE",
4549    (Fl_Callback *)geometry_elementary_set_factory_cb, (void *)"OpenCASCADE"},
4550   {"0Modules/Geometry/Elementary entities/Add/Parameter",
4551    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Parameter"},
4552   {"0Modules/Geometry/Elementary entities/Add/Point",
4553    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Point"},
4554   {"0Modules/Geometry/Elementary entities/Add/Line",
4555    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Line"},
4556   {"0Modules/Geometry/Elementary entities/Add/Spline",
4557    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Spline"},
4558   {"0Modules/Geometry/Elementary entities/Add/Bezier",
4559    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Bezier"},
4560   {"0Modules/Geometry/Elementary entities/Add/B-Spline",
4561    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"BSpline"},
4562   {"0Modules/Geometry/Elementary entities/Add/Circle",
4563    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Circle"},
4564   {"0Modules/Geometry/Elementary entities/Add/Circle arc",
4565    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Circle arc"},
4566   {"0Modules/Geometry/Elementary entities/Add/Ellipse",
4567    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Ellipse"},
4568   {"0Modules/Geometry/Elementary entities/Add/Ellipse arc",
4569    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Ellipse arc"},
4570   {"0Modules/Geometry/Elementary entities/Add/Rectangle",
4571    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Rectangle"},
4572   {"0Modules/Geometry/Elementary entities/Add/Disk",
4573    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Disk"},
4574   {"0Modules/Geometry/Elementary entities/Add/Plane surface",
4575    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Plane Surface"},
4576   {"0Modules/Geometry/Elementary entities/Add/Surface filling",
4577    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Surface"},
4578   {"0Modules/Geometry/Elementary entities/Add/Sphere",
4579    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Sphere"},
4580   {"0Modules/Geometry/Elementary entities/Add/Cylinder",
4581    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Cylinder"},
4582   {"0Modules/Geometry/Elementary entities/Add/Box",
4583    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Box"},
4584   {"0Modules/Geometry/Elementary entities/Add/Torus",
4585    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Torus"},
4586   {"0Modules/Geometry/Elementary entities/Add/Cone",
4587    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Cone"},
4588   {"0Modules/Geometry/Elementary entities/Add/Wedge",
4589    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Wedge"},
4590   {"0Modules/Geometry/Elementary entities/Add/Volume",
4591    (Fl_Callback *)geometry_elementary_add_new_cb, (void *)"Volume"},
4592   {"0Modules/Geometry/Elementary entities/Transform/Translate",
4593    (Fl_Callback *)geometry_elementary_translate_cb},
4594   {"0Modules/Geometry/Elementary entities/Transform/Rotate",
4595    (Fl_Callback *)geometry_elementary_rotate_cb},
4596   {"0Modules/Geometry/Elementary entities/Transform/Scale",
4597    (Fl_Callback *)geometry_elementary_scale_cb},
4598   {"0Modules/Geometry/Elementary entities/Transform/Symmetry",
4599    (Fl_Callback *)geometry_elementary_symmetry_cb},
4600   {"0Modules/Geometry/Elementary entities/Extrude/Translate",
4601    (Fl_Callback *)geometry_elementary_extrude_translate_cb},
4602   {"0Modules/Geometry/Elementary entities/Extrude/Rotate",
4603    (Fl_Callback *)geometry_elementary_extrude_rotate_cb},
4604   {"0Modules/Geometry/Elementary entities/Extrude/Pipe",
4605    (Fl_Callback *)geometry_elementary_pipe_cb},
4606   {"0Modules/Geometry/Elementary entities/Boolean/Intersection",
4607    (Fl_Callback *)geometry_elementary_boolean_cb,
4608    (void *)"BooleanIntersection"},
4609   {"0Modules/Geometry/Elementary entities/Boolean/Union",
4610    (Fl_Callback *)geometry_elementary_boolean_cb, (void *)"BooleanUnion"},
4611   {"0Modules/Geometry/Elementary entities/Boolean/Difference",
4612    (Fl_Callback *)geometry_elementary_boolean_cb, (void *)"BooleanDifference"},
4613   {"0Modules/Geometry/Elementary entities/Boolean/Fragments",
4614    (Fl_Callback *)geometry_elementary_boolean_cb, (void *)"BooleanFragments"},
4615   {"0Modules/Geometry/Elementary entities/Fillet",
4616    (Fl_Callback *)geometry_elementary_fillet_cb},
4617   {"0Modules/Geometry/Elementary entities/Split curve",
4618    (Fl_Callback *)geometry_elementary_split_cb, (void *)"Curve"},
4619   {"0Modules/Geometry/Elementary entities/Delete",
4620    (Fl_Callback *)geometry_elementary_delete_cb},
4621   {"0Modules/Geometry/Elementary entities/Coherence",
4622    (Fl_Callback *)geometry_elementary_coherence_cb},
4623   {"0Modules/Geometry/Physical groups/Add/Point",
4624    (Fl_Callback *)geometry_physical_add_cb, (void *)"Point"},
4625   {"0Modules/Geometry/Physical groups/Add/Curve",
4626    (Fl_Callback *)geometry_physical_add_cb, (void *)"Curve"},
4627   {"0Modules/Geometry/Physical groups/Add/Surface",
4628    (Fl_Callback *)geometry_physical_add_cb, (void *)"Surface"},
4629   {"0Modules/Geometry/Physical groups/Add/Volume",
4630    (Fl_Callback *)geometry_physical_add_cb, (void *)"Volume"},
4631   {"0Modules/Geometry/Physical groups/Remove/Point",
4632    (Fl_Callback *)geometry_physical_remove_cb, (void *)"Point"},
4633   {"0Modules/Geometry/Physical groups/Remove/Curve",
4634    (Fl_Callback *)geometry_physical_remove_cb, (void *)"Curve"},
4635   {"0Modules/Geometry/Physical groups/Remove/Surface",
4636    (Fl_Callback *)geometry_physical_remove_cb, (void *)"Surface"},
4637   {"0Modules/Geometry/Physical groups/Remove/Volume",
4638    (Fl_Callback *)geometry_physical_remove_cb, (void *)"Volume"},
4639   {"0Modules/Geometry/Reload script", (Fl_Callback *)geometry_reload_cb},
4640   {"0Modules/Geometry/Remove last script command",
4641    (Fl_Callback *)geometry_remove_last_command_cb},
4642   {"0Modules/Geometry/Edit script", (Fl_Callback *)geometry_edit_cb},
4643   {"0Modules/Mesh/Define/Size at points", (Fl_Callback *)mesh_define_length_cb},
4644   {"0Modules/Mesh/Define/Size fields", (Fl_Callback *)field_cb},
4645   {"0Modules/Mesh/Define/Embedded/Point",
4646    (Fl_Callback *)mesh_define_embedded_cb, (void *)"Point"},
4647   {"0Modules/Mesh/Define/Embedded/Curve",
4648    (Fl_Callback *)mesh_define_embedded_cb, (void *)"Curve"},
4649   {"0Modules/Mesh/Define/Embedded/Surface",
4650    (Fl_Callback *)mesh_define_embedded_cb, (void *)"Surface"},
4651   {"0Modules/Mesh/Define/Transfinite/Curve",
4652    (Fl_Callback *)mesh_define_transfinite_line_cb},
4653   {"0Modules/Mesh/Define/Transfinite/Surface",
4654    (Fl_Callback *)mesh_define_transfinite_surface_cb},
4655   {"0Modules/Mesh/Define/Transfinite/Volume",
4656    (Fl_Callback *)mesh_define_transfinite_volume_cb},
4657   {"0Modules/Mesh/Define/Compound/Curve",
4658    (Fl_Callback *)mesh_define_compound_entity_cb, (void *)"Curve"},
4659   {"0Modules/Mesh/Define/Compound/Surface",
4660    (Fl_Callback *)mesh_define_compound_entity_cb, (void *)"Surface"},
4661   {"0Modules/Mesh/Define/Recombine", (Fl_Callback *)mesh_define_recombine_cb},
4662   {"0Modules/Mesh/1D", (Fl_Callback *)mesh_1d_cb},
4663   {"0Modules/Mesh/2D", (Fl_Callback *)mesh_2d_cb},
4664   {"0Modules/Mesh/3D", (Fl_Callback *)mesh_3d_cb},
4665   {"0Modules/Mesh/Optimize 3D", (Fl_Callback *)mesh_optimize_cb},
4666 #if defined(HAVE_NETGEN)
4667   {"0Modules/Mesh/Optimize 3D (Netgen)",
4668    (Fl_Callback *)mesh_optimize_netgen_cb},
4669 #endif
4670   {"0Modules/Mesh/Set order 1", (Fl_Callback *)mesh_degree_cb, (void *)1},
4671   {"0Modules/Mesh/Set order 2", (Fl_Callback *)mesh_degree_cb, (void *)2},
4672   {"0Modules/Mesh/Set order 3", (Fl_Callback *)mesh_degree_cb, (void *)3},
4673   {"0Modules/Mesh/High-order tools", (Fl_Callback *)highordertools_cb},
4674   {"0Modules/Mesh/Refine by splitting", (Fl_Callback *)mesh_refine_cb},
4675 #if defined(HAVE_METIS)
4676   {"0Modules/Mesh/Partition", (Fl_Callback *)mesh_partition_cb},
4677   {"0Modules/Mesh/Unpartition", (Fl_Callback *)mesh_unpartition_cb},
4678 #endif
4679   {"0Modules/Mesh/Smooth 2D", (Fl_Callback *)mesh_smooth_cb},
4680   {"0Modules/Mesh/Recombine 2D", (Fl_Callback *)mesh_recombine_cb},
4681   {"0Modules/Mesh/Reclassify 2D", (Fl_Callback *)mesh_classify_cb},
4682   {"0Modules/Mesh/Experimental/Compute quad layout",
4683    (Fl_Callback *)mesh_cross_compute_cb},
4684 #if defined(HAVE_METIS)
4685   {"0Modules/Mesh/Experimental/Convert old partitioning",
4686    (Fl_Callback *)mesh_convert_old_partitioning_cb},
4687 #endif
4688   {"0Modules/Mesh/Experimental/Optimize quad topology",
4689    (Fl_Callback *)mesh_optimize_quad_topo_cb},
4690   {"0Modules/Mesh/Experimental/Untangle geometry",
4691    (Fl_Callback *)mesh_untangle_cb},
4692   {"0Modules/Mesh/Reverse/Elements", (Fl_Callback *)mesh_reverse_parts_cb,
4693    (void *)"elements"},
4694   {"0Modules/Mesh/Reverse/Curves", (Fl_Callback *)mesh_reverse_parts_cb,
4695    (void *)"curves"},
4696   {"0Modules/Mesh/Reverse/Surfaces", (Fl_Callback *)mesh_reverse_parts_cb,
4697    (void *)"surfaces"},
4698   {"0Modules/Mesh/Reverse/Volumes", (Fl_Callback *)mesh_reverse_parts_cb,
4699    (void *)"volumes"},
4700   {"0Modules/Mesh/Delete/Elements", (Fl_Callback *)mesh_delete_parts_cb,
4701    (void *)"elements"},
4702   {"0Modules/Mesh/Delete/Curves", (Fl_Callback *)mesh_delete_parts_cb,
4703    (void *)"curves"},
4704   {"0Modules/Mesh/Delete/Surfaces", (Fl_Callback *)mesh_delete_parts_cb,
4705    (void *)"surfaces"},
4706   {"0Modules/Mesh/Delete/Volumes", (Fl_Callback *)mesh_delete_parts_cb,
4707    (void *)"volumes"},
4708   {"0Modules/Mesh/Inspect", (Fl_Callback *)mesh_inspect_cb},
4709   {"0Modules/Mesh/Save", (Fl_Callback *)mesh_save_cb},
4710 };
4711 
_addGmshMenus()4712 void onelabGroup::_addGmshMenus()
4713 {
4714   _tree->sortorder(FL_TREE_SORT_NONE);
4715 
4716   // add static geometry and mesh module items
4717   for(std::size_t i = 0; i < sizeof(static_modules) / sizeof(static_modules[0]);
4718       i++)
4719     _addMenu(static_modules[i].label, static_modules[i].callback,
4720              static_modules[i].arg);
4721 
4722   // add dynamic solver module items
4723   for(int i = 0; i < 5; i++) {
4724     std::string name = opt_solver_name(i, GMSH_GET, "");
4725     if(name.size()) _addSolverMenu(i);
4726   }
4727 
4728   // add dynamic post-processing module items
4729   for(std::size_t i = 0; i < PView::list.size(); i++) _addViewMenu(i);
4730 
4731   _tree->sortorder(FL_TREE_SORT_ASCENDING);
4732 
4733   if(_firstBuild) {
4734     _firstBuild = false;
4735     Fl_Tree_Item *n0 = _tree->find_item("0Modules");
4736     for(Fl_Tree_Item *n = n0; n; n = n->next()) {
4737       if(!n->is_root() && n->has_children() && n->depth() > 1) n->close();
4738     }
4739   }
4740 }
4741 
_getClosedGmshMenus()4742 std::set<std::string> onelabGroup::_getClosedGmshMenus()
4743 {
4744   std::set<std::string> closed;
4745   Fl_Tree_Item *n0 = _tree->find_item("0Modules");
4746   for(Fl_Tree_Item *n = n0; n; n = n->next()) {
4747     if(!n->is_root() && n->has_children() && n->is_close()) {
4748       char path[1024];
4749       _tree->item_pathname(path, sizeof(path), n);
4750       closed.insert(path);
4751     }
4752   }
4753   return closed;
4754 }
4755