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