1 /*****************************************************************************
2  *                                                                           *
3  *  Elmer, A Finite Element Software for Multiphysical Problems              *
4  *                                                                           *
5  *  Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland   *
6  *                                                                           *
7  *  This program is free software; you can redistribute it and/or            *
8  *  modify it under the terms of the GNU General Public License              *
9  *  as published by the Free Software Foundation; either version 2           *
10  *  of the License, or (at your option) any later version.                   *
11  *                                                                           *
12  *  This program is distributed in the hope that it will be useful,          *
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
15  *  GNU General Public License for more details.                             *
16  *                                                                           *
17  *  You should have received a copy of the GNU General Public License        *
18  *  along with this program (in file fem/GPL-2); if not, write to the        *
19  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,         *
20  *  Boston, MA 02110-1301, USA.                                              *
21  *                                                                           *
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  *                                                                           *
26  *  ElmerGUI mainwindow                                                      *
27  *                                                                           *
28  *****************************************************************************
29  *                                                                           *
30  *  Authors: Mikko Lyly, Juha Ruokolainen and Peter R�back                   *
31  *  Email:   Juha.Ruokolainen@csc.fi                                         *
32  *  Web:     http://www.csc.fi/elmer                                         *
33  *  Address: CSC - IT Center for Science Ltd.                                *
34  *           Keilaranta 14                                                   *
35  *           02101 Espoo, Finland                                            *
36  *                                                                           *
37  *  Original Date: 15 Mar 2008                                               *
38  *                                                                           *
39  *****************************************************************************/
40 
41 #include <QAction>
42 #include <QContextMenuEvent>
43 #include <QDir>
44 #include <QFile>
45 #include <QFileInfo>
46 #include <QFont>
47 #include <QProgressBar>
48 #include <QStringList>
49 #include <QSystemTrayIcon>
50 #include <QTimeLine>
51 #include <QtGui>
52 
53 #include <fstream>
54 #include <iostream>
55 
56 #include <QDebug>
57 
58 #include "mainwindow.h"
59 #include "newprojectdialog.h"
60 
61 #ifdef EG_VTK
62 #include "vtkpost/vtkpost.h"
63 VtkPost *vtkp;
64 #endif
65 
66 #ifdef __APPLE__
67 #include <mach-o/dyld.h>
68 // #ifndef EG_OCC
69 // #define EG_OCC
70 // #endif
71 #endif
72 
73 using namespace std;
74 
75 #undef MPICH2
76 
77 // Construct main window...
78 //-----------------------------------------------------------------------------
MainWindow()79 MainWindow::MainWindow() {
80 #ifdef __APPLE__
81   // find "Home directory":
82   char executablePath[MAXPATHLENGTH] = {0};
83   uint32_t len = MAXPATHLENGTH;
84   this->homePath = "";
85   if (!_NSGetExecutablePath((char *)executablePath, &len)) {
86     // remove executable name from path:
87     *(strrchr(executablePath, '/')) = '\0';
88     // remove last path component name from path:
89     *(strrchr(executablePath, '/')) = '\0';
90     this->homePath = executablePath;
91   }
92 #else
93   homePath = "";
94 #endif
95 
96   // load ini file:
97   egIni = new EgIni(this);
98 
99   // splash screen:
100   setupSplash();
101 
102   // load splash screen:
103   updateSplash("Loading images...");
104 
105   // load tetlib:
106   updateSplash("Loading tetlib...");
107   tetlibAPI = new TetlibAPI;
108   tetlibPresent = tetlibAPI->loadTetlib();
109   this->in = tetlibAPI->in;
110   this->out = tetlibAPI->out;
111 
112   // load nglib:
113   updateSplash("Loading nglib...");
114   nglibAPI = new NglibAPI;
115   nglibPresent = true;
116 
117   // construct elmergrid:
118   updateSplash("Constructing elmergrid...");
119   elmergridAPI = new ElmergridAPI;
120 
121   // set dynamic limits:
122   limit = new Limit;
123   setDynamicLimits();
124 
125   // widgets and utilities:
126   updateSplash("ElmerGUI loading...");
127   glWidget = new GLWidget(this);
128 #ifdef WIN32
129   glWidget->stateDrawSharpEdges = false;
130 #endif
131   setCentralWidget(glWidget);
132   sifWindow = new SifWindow(this);
133   meshControl = new MeshControl(this);
134   boundaryDivide = new BoundaryDivide(this);
135   meshingThread = new MeshingThread(this);
136   meshutils = new Meshutils;
137   solverLogWindow = new SolverLogWindow(this);
138   solver = new QProcess(this);
139   post = new QProcess(this);
140   paraview = new QProcess(this);
141   compiler = new QProcess(this);
142   meshSplitter = new QProcess(this);
143   meshUnifier = new QProcess(this);
144   generalSetup = new GeneralSetup(this);
145   summaryEditor = new SummaryEditor(this);
146   sifGenerator = new SifGenerator;
147   sifGenerator->setLimit(this->limit);
148   elmerDefs = new QDomDocument;
149   edfEditor = new EdfEditor;
150   glControl = new GLcontrol(this);
151   parallel = new Parallel(this);
152   checkMpi = new CheckMpi;
153   materialLibrary = new MaterialLibrary(this);
154   twodView = new TwodView;
155   grabTimeLine = new QTimeLine(1000, this);
156 
157 #ifdef EG_QWT
158   convergenceView = new ConvergenceView(limit, this);
159 #endif
160 
161 #ifdef EG_VTK
162   vtkp = vtkPost = new VtkPost(this);
163   vtkPostMeshUnifierRunning = false;
164 #endif
165 
166 #ifdef EG_OCC
167   cadView = new CadView();
168   if (egIni->isPresent("deflection"))
169     cadView->setDeflection(egIni->value("deflection").toDouble());
170 #endif
171 
172   createActions();
173   createMenus();
174   createToolBars();
175   createStatusBar();
176   runPostProcessorAct->setMenu(selectPostMenu);
177 
178   // Always, when an action from the menu bar has been selected, synchronize
179   // menu to state:
180   connect(menuBar(), SIGNAL(triggered(QAction *)), this,
181           SLOT(menuBarTriggeredSlot(QAction *)));
182   connect(contextMenu, SIGNAL(triggered(QAction *)), this,
183           SLOT(menuBarTriggeredSlot(QAction *)));
184 
185   // glWidget emits (list_t*) when a boundary is selected by double clicking:
186   connect(glWidget, SIGNAL(signalBoundarySelected(list_t *)), this,
187           SLOT(boundarySelectedSlot(list_t *)));
188 
189   // glWidget emits (void) when esc has been pressed:
190   connect(glWidget, SIGNAL(escPressed()), this, SLOT(viewNormalModeSlot()));
191 
192   // meshingThread emits (void) when the mesh generation has finished or
193   // terminated:
194   connect(meshingThread, SIGNAL(started()), this, SLOT(meshingStartedSlot()));
195   connect(meshingThread, SIGNAL(finished()), this, SLOT(meshingFinishedSlot()));
196   connect(meshingThread, SIGNAL(terminated()), this,
197           SLOT(meshingTerminatedSlot()));
198 
199   // boundaryDivide emits (double) when "divide button" has been clicked:
200   connect(boundaryDivide, SIGNAL(signalDoDivideSurface(double)), this,
201           SLOT(doDivideSurfaceSlot(double)));
202 
203   // boundaryDivide emits (double) when "divide button" has been clicked:
204   connect(boundaryDivide, SIGNAL(signalDoDivideEdge(double)), this,
205           SLOT(doDivideEdgeSlot(double)));
206 
207   // solver emits (int) when finished:
208   connect(solver, SIGNAL(finished(int)), this, SLOT(solverFinishedSlot(int)));
209 
210   // solver emits (void) when there is something to read from stdout:
211   connect(solver, SIGNAL(readyReadStandardOutput()), this,
212           SLOT(solverStdoutSlot()));
213 
214   // solver emits (void) when there is something to read from stderr:
215   connect(solver, SIGNAL(readyReadStandardError()), this,
216           SLOT(solverStderrSlot()));
217 
218   // solver emits (QProcess::ProcessError) when error occurs:
219   connect(solver, SIGNAL(error(QProcess::ProcessError)), this,
220           SLOT(solverErrorSlot(QProcess::ProcessError)));
221 
222   // solver emits (QProcess::ProcessState) when state changed:
223   connect(solver, SIGNAL(stateChanged(QProcess::ProcessState)), this,
224           SLOT(solverStateChangedSlot(QProcess::ProcessState)));
225 
226   // compiler emits (int) when finished:
227   connect(compiler, SIGNAL(finished(int)), this,
228           SLOT(compilerFinishedSlot(int)));
229 
230   // compiler emits (void) when there is something to read from stdout:
231   connect(compiler, SIGNAL(readyReadStandardOutput()), this,
232           SLOT(compilerStdoutSlot()));
233 
234   // compiler emits (void) when there is something to read from stderr:
235   connect(compiler, SIGNAL(readyReadStandardError()), this,
236           SLOT(compilerStderrSlot()));
237 
238   // post emits (int) when finished:
239   connect(post, SIGNAL(finished(int)), this,
240           SLOT(postProcessFinishedSlot(int)));
241 
242   // paraview emits (int) when finished:
243   connect(paraview, SIGNAL(finished(int)), this,
244           SLOT(paraviewProcessFinishedSlot(int)));
245 
246   // meshSplitter emits (int) when finished:
247   connect(meshSplitter, SIGNAL(finished(int)), this,
248           SLOT(meshSplitterFinishedSlot(int)));
249 
250   // meshSplitter emits(void) when there is something to read from stdout:
251   connect(meshSplitter, SIGNAL(readyReadStandardOutput()), this,
252           SLOT(meshSplitterStdoutSlot()));
253 
254   // meshSplitter emits(void) when there is something to read from stderr:
255   connect(meshSplitter, SIGNAL(readyReadStandardError()), this,
256           SLOT(meshSplitterStderrSlot()));
257 
258   // meshUnifier emits (int) when finished:
259   connect(meshUnifier, SIGNAL(finished(int)), this,
260           SLOT(meshUnifierFinishedSlot(int)));
261 
262   // meshUnifier emits(void) when there is something to read from stdout:
263   connect(meshUnifier, SIGNAL(readyReadStandardOutput()), this,
264           SLOT(meshUnifierStdoutSlot()));
265 
266   // meshUnifier emits(void) when there is something to read from stderr:
267   connect(meshUnifier, SIGNAL(readyReadStandardError()), this,
268           SLOT(meshUnifierStderrSlot()));
269 
270   // grabTimeLine emits finished() when done:
271   connect(grabTimeLine, SIGNAL(finished()), this, SLOT(grabFrameSlot()));
272 
273   // set initial state:
274   operations = 0;
275   meshControl->nglibPresent = nglibPresent;
276   meshControl->tetlibPresent = tetlibPresent;
277   meshControl->defaultControls();
278   nglibInputOk = false;
279   tetlibInputOk = false;
280   activeGenerator = GEN_UNKNOWN;
281   bcEditActive = false;
282   bodyEditActive = false;
283   showConvergence = egIni->isSet("showconvergence");
284   geometryInputFileName = "";
285   occInputOk = false;
286 
287   // background image:
288   glWidget->stateUseBgImage = egIni->isSet("bgimage");
289   glWidget->stateStretchBgImage = egIni->isSet("bgimagestretch");
290   glWidget->stateAlignRightBgImage = egIni->isSet("bgimagealignright");
291   glWidget->bgImageFileName = egIni->value("bgimagefile");
292 
293   // set font for text editors:
294   // QFont sansFont("Courier", 10);
295   // sifWindow->getTextEdit()->setCurrentFont(sansFont);
296   // solverLogWindow->getTextEdit()->setCurrentFont(sansFont);
297 
298   // load definition files:
299   updateSplash("Loading definitions...");
300   loadDefinitions();
301 
302   // initialization ready:
303   synchronizeMenuToState();
304   setWindowTitle(tr("ElmerGUI"));
305   setWindowIcon(QIcon(":/icons/Mesh3D.png"));
306   finalizeSplash();
307   setupSysTrayIcon();
308 
309   // default size:
310   int defW = egIni->value("width").toInt();
311   int defH = egIni->value("height").toInt();
312   if (defW <= 200)
313     defW = 200;
314   if (defH <= 200)
315     defH = 200;
316   this->resize(defW, defH);
317 
318   loadSettings();
319 }
320 
321 // dtor...
322 //-----------------------------------------------------------------------------
~MainWindow()323 MainWindow::~MainWindow() {
324   saveSettings();
325   qApp->closeAllWindows();
326 }
327 
328 // Set limits for dynamic editors, materials, bcs, etc...
329 //-----------------------------------------------------------------------------
setDynamicLimits()330 void MainWindow::setDynamicLimits() {
331   // Values defined in "edf/egini.xml" that override default limits:
332 
333   // Deprecated ** 23/04/09 **
334   if (egIni->isPresent("max_boundaries")) {
335     limit->setMaxBoundaries(egIni->value("max_boundaries").toInt());
336     // cout << "Max boundaries: " << limit->maxBoundaries() << endl;
337   }
338 
339   // Deprecated ** 23/04/09 **
340   if (egIni->isPresent("max_solvers")) {
341     limit->setMaxSolvers(egIni->value("max_solvers").toInt());
342     // cout << "Max solvers: " << limit->maxSolvers() << endl;
343   }
344 
345   // Deprecated ** 23/04/09 **
346   if (egIni->isPresent("max_bodies")) {
347     limit->setMaxBodies(egIni->value("max_bodies").toInt());
348     // cout << "Max bodies: " << limit->maxBodies() << endl;
349   }
350 
351   // Deprecated ** 21/04/09 **
352   if (egIni->isPresent("max_equations")) {
353     limit->setMaxEquations(egIni->value("max_equations").toInt());
354     // cout << "Max equations: " << limit->maxEquations() << endl;
355   }
356 
357   // Deprecated ** 21/04/09 **
358   if (egIni->isPresent("max_materials")) {
359     limit->setMaxMaterials(egIni->value("max_materials").toInt());
360     // cout << "Max materials: " << limit->maxMaterials() << endl;
361   }
362 
363   // Deprecated ** 21/04/09 **
364   if (egIni->isPresent("max_bodyforces")) {
365     limit->setMaxBodyforces(egIni->value("max_bodyforces").toInt());
366     // cout << "Max bodyforces: " << limit->maxBodyforces() << endl;
367   }
368 
369   // Deprecated ** 21/04/09 **
370   if (egIni->isPresent("max_initialconditions")) {
371     limit->setMaxInitialconditions(
372         egIni->value("max_initialconditions").toInt());
373     // cout << "Max initialconditions: " << limit->maxInitialconditions() <<
374     // endl;
375   }
376 
377   // Deprecated ** 21/04/09 **
378   if (egIni->isPresent("max_bcs")) {
379     limit->setMaxBcs(egIni->value("max_bcs").toInt());
380     // cout << "Max bcs: " << limit->maxBcs() << endl;
381   }
382 }
383 
384 // Always synchronize menu to state when the menubar has been triggered...
385 //-----------------------------------------------------------------------------
menuBarTriggeredSlot(QAction * act)386 void MainWindow::menuBarTriggeredSlot(QAction *act) {
387   synchronizeMenuToState();
388 }
389 
390 // Create actions...
391 //-----------------------------------------------------------------------------
createActions()392 void MainWindow::createActions() {
393   // File -> Open file
394   openAct =
395       new QAction(QIcon(":/icons/document-open.png"), tr("&Open..."), this);
396   openAct->setShortcut(tr("Ctrl+O"));
397   openAct->setStatusTip(tr("Open geometry input file"));
398   connect(openAct, SIGNAL(triggered()), this, SLOT(openSlot()));
399 
400   // File -> Load mesh...
401   loadAct = new QAction(QIcon(":/icons/document-open-folder.png"),
402                         tr("&Load mesh..."), this);
403   loadAct->setStatusTip(tr("Load Elmer mesh files"));
404   connect(loadAct, SIGNAL(triggered()), this, SLOT(loadSlot()));
405 
406   // File -> Load project...
407   loadProjectAct = new QAction(QIcon(":/icons/document-import.png"),
408                                tr("Load &project..."), this);
409   loadProjectAct->setStatusTip(tr("Load previously saved project"));
410   connect(loadProjectAct, SIGNAL(triggered()), this, SLOT(loadProjectSlot()));
411 
412   // File -> New project...
413   newProjectAct = new QAction(QIcon(":/icons/document-new.png"),
414                               tr("&New project..."), this);
415   newProjectAct->setStatusTip(tr("Create a new project"));
416   connect(newProjectAct, SIGNAL(triggered()), this, SLOT(newProjectSlot()));
417 
418   // File -> Recent Projects
419   recentProject0Act = new QAction("", this);
420   connect(recentProject0Act, SIGNAL(triggered()), this,
421           SLOT(loadRecentProject0Slot()));
422   recentProject1Act = new QAction("", this);
423   connect(recentProject1Act, SIGNAL(triggered()), this,
424           SLOT(loadRecentProject1Slot()));
425   recentProject2Act = new QAction("", this);
426   connect(recentProject2Act, SIGNAL(triggered()), this,
427           SLOT(loadRecentProject2Slot()));
428   recentProject3Act = new QAction("", this);
429   connect(recentProject3Act, SIGNAL(triggered()), this,
430           SLOT(loadRecentProject3Slot()));
431   recentProject4Act = new QAction("", this);
432   connect(recentProject4Act, SIGNAL(triggered()), this,
433           SLOT(loadRecentProject4Slot()));
434 
435   // File -> Definitions...
436   editDefinitionsAct = new QAction(QIcon(":/icons/games-config-custom.png"),
437                                    tr("&Definitions..."), this);
438   editDefinitionsAct->setStatusTip(
439       tr("Load and edit Elmer sif definitions file"));
440   connect(editDefinitionsAct, SIGNAL(triggered()), this,
441           SLOT(editDefinitionsSlot()));
442 
443   // File -> Save...
444   saveAct =
445       new QAction(QIcon(":/icons/document-save.png"), tr("&Save..."), this);
446   saveAct->setShortcut(tr("Ctrl+S"));
447   saveAct->setStatusTip(tr("Save Elmer mesh and sif-files"));
448   connect(saveAct, SIGNAL(triggered()), this, SLOT(saveSlot()));
449 
450   // File -> Save as...
451   saveAsAct = new QAction(QIcon(":/icons/document-save-as.png"),
452                           tr("&Save as..."), this);
453   saveAsAct->setStatusTip(tr("Save Elmer mesh and sif-files"));
454   connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAsSlot()));
455 
456   // File -> Save project
457   saveProjectAct = new QAction(QIcon(":/icons/document-export.png"),
458                                tr("&Save project"), this);
459   saveProjectAct->setStatusTip(tr("Save current project"));
460   connect(saveProjectAct, SIGNAL(triggered()), this, SLOT(saveProjectSlot()));
461 
462   // File -> Save project as...
463   saveProjectAsAct = new QAction(QIcon(":/icons/edit-copy.png"),
464                                  tr("&Save project as..."), this);
465   saveProjectAsAct->setStatusTip(
466       tr("Save current project by specifing directory"));
467   connect(saveProjectAsAct, SIGNAL(triggered()), this,
468           SLOT(saveProjectAsSlot()));
469 
470   // File -> Save picture as...
471   savePictureAct = new QAction(QIcon(":/icons/view-preview.png"),
472                                tr("&Save picture as..."), this);
473   savePictureAct->setStatusTip(tr("Save picture in file"));
474   connect(savePictureAct, SIGNAL(triggered()), this, SLOT(savePictureSlot()));
475 
476   // File -> Exit
477   exitAct =
478       new QAction(QIcon(":/icons/application-exit.png"), tr("E&xit"), this);
479   exitAct->setShortcut(tr("Ctrl+Q"));
480   exitAct->setStatusTip(tr("Exit"));
481   connect(exitAct, SIGNAL(triggered()), this, SLOT(closeMainWindowSlot()));
482 
483   // Model -> Setup...
484   modelSetupAct = new QAction(QIcon(), tr("Setup..."), this);
485   modelSetupAct->setStatusTip(tr("Setup simulation environment"));
486   connect(modelSetupAct, SIGNAL(triggered()), this, SLOT(modelSetupSlot()));
487 
488   // Model -> Equation...
489   addEquationAct = new QAction(QIcon(), tr("Add..."), this);
490   addEquationAct->setStatusTip(tr("Add a PDE-system to the equation list"));
491   connect(addEquationAct, SIGNAL(triggered()), this, SLOT(addEquationSlot()));
492 
493   // Model -> Material...
494   addMaterialAct = new QAction(QIcon(), tr("Add..."), this);
495   addMaterialAct->setStatusTip(tr("Add a material set to the material list"));
496   connect(addMaterialAct, SIGNAL(triggered()), this, SLOT(addMaterialSlot()));
497 
498   // Model -> Body force...
499   addBodyForceAct = new QAction(QIcon(), tr("Add..."), this);
500   addBodyForceAct->setStatusTip(tr("Add body forces..."));
501   connect(addBodyForceAct, SIGNAL(triggered()), this, SLOT(addBodyForceSlot()));
502 
503   // Model -> Initial condition...
504   addInitialConditionAct = new QAction(QIcon(), tr("Add..."), this);
505   addInitialConditionAct->setStatusTip(tr("Add initial conditions..."));
506   connect(addInitialConditionAct, SIGNAL(triggered()), this,
507           SLOT(addInitialConditionSlot()));
508 
509   // Model -> Boundary condition...
510   addBoundaryConditionAct = new QAction(QIcon(), tr("Add..."), this);
511   addBoundaryConditionAct->setStatusTip(tr("Add boundary conditions..."));
512   connect(addBoundaryConditionAct, SIGNAL(triggered()), this,
513           SLOT(addBoundaryConditionSlot()));
514 
515   // Model -> Set body properties
516   bodyEditAct = new QAction(QIcon(":/icons/set-body-property.png"),
517                             tr("Set body properties"), this);
518   bodyEditAct->setStatusTip(
519       tr("Set body properties (equivalent to holding down the SHIFT key)"));
520   connect(bodyEditAct, SIGNAL(triggered()), this, SLOT(bodyEditSlot()));
521   bodyEditAct->setCheckable(true);
522 
523   // Model -> Set boundary conditions
524   bcEditAct = new QAction(QIcon(":/icons/set-boundary-property.png"),
525                           tr("Set boundary properties"), this);
526   bcEditAct->setStatusTip(
527       tr("Set boundary properties (equivalent to holding down the ALT key)"));
528   connect(bcEditAct, SIGNAL(triggered()), this, SLOT(bcEditSlot()));
529   bcEditAct->setCheckable(true);
530 
531   // Model -> Summary...
532   modelSummaryAct = new QAction(QIcon(), tr("Summary..."), this);
533   modelSummaryAct->setStatusTip(tr("Model summary"));
534   connect(modelSummaryAct, SIGNAL(triggered()), this, SLOT(modelSummarySlot()));
535 
536   // Model -> Clear
537   modelClearAct = new QAction(QIcon(), tr("Clear all"), this);
538   modelClearAct->setStatusTip(tr("Clear all model definitions"));
539   connect(modelClearAct, SIGNAL(triggered()), this, SLOT(modelClearSlot()));
540 
541   // Edit -> Generate sif
542   generateSifAct = new QAction(QIcon(""), tr("&Generate"), this);
543   generateSifAct->setShortcut(tr("Ctrl+G"));
544   generateSifAct->setStatusTip(tr("Genarete solver input file"));
545   connect(generateSifAct, SIGNAL(triggered()), this, SLOT(generateSifSlot()));
546 
547   // Edit -> Solver input file...
548   showsifAct = new QAction(QIcon(":/icons/document-properties.png"),
549                            tr("&Edit..."), this);
550   showsifAct->setShortcut(tr("Ctrl+S"));
551   showsifAct->setStatusTip(tr("Edit solver input file"));
552   connect(showsifAct, SIGNAL(triggered()), this, SLOT(showsifSlot()));
553 
554   // Mesh -> Control
555   meshcontrolAct =
556       new QAction(QIcon(":/icons/configure.png"), tr("&Configure..."), this);
557   meshcontrolAct->setShortcut(tr("Ctrl+C"));
558   meshcontrolAct->setStatusTip(tr("Configure mesh generators"));
559   connect(meshcontrolAct, SIGNAL(triggered()), this, SLOT(meshcontrolSlot()));
560 
561   // Mesh -> Remesh
562   remeshAct = new QAction(QIcon(":/icons/edit-redo.png"), tr("&Remesh"), this);
563   remeshAct->setShortcut(tr("Ctrl+R"));
564   remeshAct->setStatusTip(tr("Remesh"));
565   connect(remeshAct, SIGNAL(triggered()), this, SLOT(remeshSlot()));
566 
567   // Mesh -> Kill generator
568   stopMeshingAct = new QAction(QIcon(":/icons/window-close.png"),
569                                tr("&Terminate meshing"), this);
570   stopMeshingAct->setStatusTip(tr("Terminate mesh generator"));
571   connect(stopMeshingAct, SIGNAL(triggered()), this, SLOT(stopMeshingSlot()));
572   stopMeshingAct->setEnabled(false);
573 
574   // Mesh -> Divide surface
575   surfaceDivideAct =
576       new QAction(QIcon(":/icons/divide.png"), tr("&Divide surface..."), this);
577   surfaceDivideAct->setStatusTip(tr("Divide surface by sharp edges"));
578   connect(surfaceDivideAct, SIGNAL(triggered()), this,
579           SLOT(surfaceDivideSlot()));
580 
581   // Mesh -> Unify surface
582   surfaceUnifyAct =
583       new QAction(QIcon(":/icons/unify.png"), tr("&Unify surface"), this);
584   surfaceUnifyAct->setStatusTip(tr("Unify surface (merge selected)"));
585   connect(surfaceUnifyAct, SIGNAL(triggered()), this, SLOT(surfaceUnifySlot()));
586 
587   // Mesh -> Divide edge
588   edgeDivideAct = new QAction(QIcon(":/icons/divide-edge.png"),
589                               tr("&Divide edge..."), this);
590   edgeDivideAct->setStatusTip(tr("Divide edge by sharp points"));
591   connect(edgeDivideAct, SIGNAL(triggered()), this, SLOT(edgeDivideSlot()));
592 
593   // Mesh -> Unify edges
594   edgeUnifyAct =
595       new QAction(QIcon(":/icons/unify-edge.png"), tr("&Unify edge"), this);
596   edgeUnifyAct->setStatusTip(tr("Unify edge (merge selected)"));
597   connect(edgeUnifyAct, SIGNAL(triggered()), this, SLOT(edgeUnifySlot()));
598 
599   // Mesh -> Clean up
600   cleanHangingSharpEdgesAct = new QAction(QIcon(""), tr("Clean up"), this);
601   cleanHangingSharpEdgesAct->setStatusTip(
602       tr("Removes hanging/orphan sharp edges (for visualization)"));
603   connect(cleanHangingSharpEdgesAct, SIGNAL(triggered()), this,
604           SLOT(cleanHangingSharpEdgesSlot()));
605 
606   // View -> Full screen
607   viewFullScreenAct = new QAction(QIcon(), tr("Full screen"), this);
608   viewFullScreenAct->setShortcut(tr("Ctrl+L"));
609   viewFullScreenAct->setStatusTip(tr("Full screen mode"));
610   connect(viewFullScreenAct, SIGNAL(triggered()), this,
611           SLOT(viewFullScreenSlot()));
612   viewFullScreenAct->setCheckable(true);
613 
614   // View -> Show surface mesh
615   hidesurfacemeshAct = new QAction(QIcon(), tr("Surface mesh"), this);
616   hidesurfacemeshAct->setStatusTip(tr("Show/hide surface mesh "
617                                       "(do/do not outline surface elements)"));
618   connect(hidesurfacemeshAct, SIGNAL(triggered()), this,
619           SLOT(hidesurfacemeshSlot()));
620   hidesurfacemeshAct->setCheckable(true);
621 
622   // View -> Show volume mesh
623   hidevolumemeshAct = new QAction(QIcon(), tr("Volume mesh"), this);
624   hidevolumemeshAct->setStatusTip(tr("Show/hide volume mesh "
625                                      "(do/do not outline volume mesh edges)"));
626   connect(hidevolumemeshAct, SIGNAL(triggered()), this,
627           SLOT(hidevolumemeshSlot()));
628   hidevolumemeshAct->setCheckable(true);
629 
630   // View -> Show sharp edges
631   hidesharpedgesAct = new QAction(QIcon(), tr("Sharp edges"), this);
632   hidesharpedgesAct->setStatusTip(tr("Show/hide sharp edges"));
633   connect(hidesharpedgesAct, SIGNAL(triggered()), this,
634           SLOT(hidesharpedgesSlot()));
635   hidesharpedgesAct->setCheckable(true);
636 
637   // View -> Compass
638   viewCoordinatesAct = new QAction(QIcon(), tr("Compass"), this);
639   viewCoordinatesAct->setStatusTip(tr("View coordinates "
640                                       "(RGB=XYZ modulo translation)"));
641   connect(viewCoordinatesAct, SIGNAL(triggered()), this,
642           SLOT(viewCoordinatesSlot()));
643   viewCoordinatesAct->setCheckable(true);
644 
645   // View -> Select all surfaces
646   selectAllSurfacesAct = new QAction(QIcon(), tr("Select all surfaces"), this);
647   selectAllSurfacesAct->setStatusTip(tr("Select all surfaces"));
648   connect(selectAllSurfacesAct, SIGNAL(triggered()), this,
649           SLOT(selectAllSurfacesSlot()));
650 
651   // View -> Select all edges
652   selectAllEdgesAct = new QAction(QIcon(), tr("Select all edges"), this);
653   selectAllEdgesAct->setStatusTip(tr("Select all edges"));
654   connect(selectAllEdgesAct, SIGNAL(triggered()), this,
655           SLOT(selectAllEdgesSlot()));
656 
657   // View -> Select defined edges
658   selectDefinedEdgesAct =
659       new QAction(QIcon(), tr("Select defined edges"), this);
660   selectDefinedEdgesAct->setStatusTip(tr("Select defined edges"));
661   connect(selectDefinedEdgesAct, SIGNAL(triggered()), this,
662           SLOT(selectDefinedEdgesSlot()));
663 
664   // View -> Select defined surfaces
665   selectDefinedSurfacesAct =
666       new QAction(QIcon(), tr("Select defined surfaces"), this);
667   selectDefinedSurfacesAct->setStatusTip(tr("Select defined surfaces"));
668   connect(selectDefinedSurfacesAct, SIGNAL(triggered()), this,
669           SLOT(selectDefinedSurfacesSlot()));
670 
671   // View -> Hide/show selected
672   hideselectedAct = new QAction(QIcon(), tr("&Hide/show selected"), this);
673   hideselectedAct->setShortcut(tr("Ctrl+H"));
674   hideselectedAct->setStatusTip(tr("Show/hide selected objects"));
675   connect(hideselectedAct, SIGNAL(triggered()), this, SLOT(hideselectedSlot()));
676 
677   // View -> Show surface numbers
678   showSurfaceNumbersAct =
679       new QAction(QIcon(), tr("Surface element numbers"), this);
680   showSurfaceNumbersAct->setStatusTip(
681       tr("Show surface element numbers "
682          "(Show the surface element numbering)"));
683   connect(showSurfaceNumbersAct, SIGNAL(triggered()), this,
684           SLOT(showSurfaceNumbersSlot()));
685   showSurfaceNumbersAct->setCheckable(true);
686 
687   // View -> Show edge numbers
688   showEdgeNumbersAct = new QAction(QIcon(), tr("Edge element numbers"), this);
689   showEdgeNumbersAct->setStatusTip(tr("Show edge element numbers "
690                                       "(Show the node element numbering)"));
691   connect(showEdgeNumbersAct, SIGNAL(triggered()), this,
692           SLOT(showEdgeNumbersSlot()));
693   showEdgeNumbersAct->setCheckable(true);
694 
695   // View -> Show node numbers
696   showNodeNumbersAct = new QAction(QIcon(), tr("Node numbers"), this);
697   showNodeNumbersAct->setStatusTip(tr("Show node numbers "
698                                       "(Show the node numbers)"));
699   connect(showNodeNumbersAct, SIGNAL(triggered()), this,
700           SLOT(showNodeNumbersSlot()));
701   showNodeNumbersAct->setCheckable(true);
702 
703   // View -> Show boundray index
704   showBoundaryIndexAct = new QAction(QIcon(), tr("Boundary index"), this);
705   showBoundaryIndexAct->setStatusTip(tr("Show boundary index"));
706   connect(showBoundaryIndexAct, SIGNAL(triggered()), this,
707           SLOT(showBoundaryIndexSlot()));
708   showBoundaryIndexAct->setCheckable(true);
709 
710   // View -> Show body index
711   showBodyIndexAct = new QAction(QIcon(), tr("Body index"), this);
712   showBodyIndexAct->setStatusTip(tr("Show body index"));
713   connect(showBodyIndexAct, SIGNAL(triggered()), this,
714           SLOT(showBodyIndexSlot()));
715   showBodyIndexAct->setCheckable(true);
716 
717   // View -> Colors -> GL controls
718   glControlAct = new QAction(QIcon(), tr("GL controls..."), this);
719   glControlAct->setStatusTip(
720       tr("Control GL parameters for lights and materials"));
721   connect(glControlAct, SIGNAL(triggered()), this, SLOT(glControlSlot()));
722 
723   // View -> Colors -> Background
724   chooseBGColorAct = new QAction(QIcon(), tr("Background..."), this);
725   chooseBGColorAct->setStatusTip(tr("Set background color"));
726   connect(chooseBGColorAct, SIGNAL(triggered()), this,
727           SLOT(backgroundColorSlot()));
728 
729   // View -> Colors -> Surface elements
730   chooseSurfaceColorAct = new QAction(QIcon(), tr("Surface elements..."), this);
731   chooseSurfaceColorAct->setStatusTip(tr("Set surface color"));
732   connect(chooseSurfaceColorAct, SIGNAL(triggered()), this,
733           SLOT(surfaceColorSlot()));
734 
735   // View -> Colors -> Edge elements
736   chooseEdgeColorAct = new QAction(QIcon(), tr("Edge elements..."), this);
737   chooseEdgeColorAct->setStatusTip(tr("Set edge color"));
738   connect(chooseEdgeColorAct, SIGNAL(triggered()), this, SLOT(edgeColorSlot()));
739 
740   // View -> Colors -> Surface mesh
741   chooseSurfaceMeshColorAct = new QAction(QIcon(), tr("Surface mesh..."), this);
742   chooseSurfaceMeshColorAct->setStatusTip(tr("Set surface mesh color"));
743   connect(chooseSurfaceMeshColorAct, SIGNAL(triggered()), this,
744           SLOT(surfaceMeshColorSlot()));
745 
746   // View -> Colors -> Sharp edges
747   chooseSharpEdgeColorAct = new QAction(QIcon(), tr("Sharp edges..."), this);
748   chooseSharpEdgeColorAct->setStatusTip(tr("Set sharp edge color"));
749   connect(chooseSharpEdgeColorAct, SIGNAL(triggered()), this,
750           SLOT(sharpEdgeColorSlot()));
751 
752   // View -> Colors -> Boundaries
753   showBoundaryColorAct = new QAction(QIcon(), tr("Boundaries"), this);
754   showBoundaryColorAct->setStatusTip(
755       tr("Visualize different boundary parts with color patches"));
756   connect(showBoundaryColorAct, SIGNAL(triggered()), this,
757           SLOT(colorizeBoundarySlot()));
758   showBoundaryColorAct->setCheckable(true);
759 
760   // View -> Colors -> Bodies
761   showBodyColorAct = new QAction(QIcon(), tr("Bodies"), this);
762   showBodyColorAct->setStatusTip(
763       tr("Visualize different body with color patches"));
764   connect(showBodyColorAct, SIGNAL(triggered()), this,
765           SLOT(colorizeBodySlot()));
766   showBodyColorAct->setCheckable(true);
767 
768   // View -> Shade model -> Smooth
769   smoothShadeAct = new QAction(QIcon(), tr("Smooth"), this);
770   smoothShadeAct->setStatusTip(tr("Set shade model to smooth"));
771   connect(smoothShadeAct, SIGNAL(triggered()), this, SLOT(smoothShadeSlot()));
772   smoothShadeAct->setCheckable(true);
773 
774   // View -> Shade model -> Flat
775   flatShadeAct = new QAction(QIcon(), tr("Flat"), this);
776   flatShadeAct->setStatusTip(tr("Set shade model to flat"));
777   connect(flatShadeAct, SIGNAL(triggered()), this, SLOT(flatShadeSlot()));
778   flatShadeAct->setCheckable(true);
779 
780   // View -> Projection -> Orthogonal
781   orthoAct = new QAction(QIcon(), tr("Orthogonal"), this);
782   orthoAct->setStatusTip(tr("Set projection to orthogonal"));
783   connect(orthoAct, SIGNAL(triggered()), this, SLOT(orthoSlot()));
784   orthoAct->setCheckable(true);
785 
786   // View -> Projection -> Perspective
787   perspectiveAct = new QAction(QIcon(), tr("Perspective"), this);
788   perspectiveAct->setStatusTip(tr("Set projection to perspective"));
789   connect(perspectiveAct, SIGNAL(triggered()), this, SLOT(perspectiveSlot()));
790   perspectiveAct->setCheckable(true);
791 
792   // View -> Show all
793   showallAct = new QAction(QIcon(), tr("Show all"), this);
794   showallAct->setStatusTip(tr("Show all objects"));
795   connect(showallAct, SIGNAL(triggered()), this, SLOT(showallSlot()));
796 
797   // View -> Reset model view
798   resetAct = new QAction(QIcon(), tr("Reset model view"), this);
799   resetAct->setStatusTip(tr("Reset model view"));
800   connect(resetAct, SIGNAL(triggered()), this, SLOT(resetSlot()));
801 
802   // View -> Show cad model
803   showCadModelAct = new QAction(QIcon(), tr("Cad model..."), this);
804   showCadModelAct->setStatusTip(
805       tr("Displays the cad model in a separate window"));
806   connect(showCadModelAct, SIGNAL(triggered()), this, SLOT(showCadModelSlot()));
807 
808   // View -> Show 2d view
809   showTwodViewAct = new QAction(QIcon(), tr("2D modeler..."), this);
810   showTwodViewAct->setStatusTip(
811       tr("Displays the 2d geometry in a separate window"));
812   connect(showTwodViewAct, SIGNAL(triggered()), this, SLOT(showTwodViewSlot()));
813 
814   // View -> Show Object Browser
815   showObjectBrowserAct = new QAction(QIcon(), tr("Show Object Browser"), this);
816   showObjectBrowserAct->setStatusTip(tr("Show Object Browser"));
817   connect(showObjectBrowserAct, SIGNAL(triggered()), this,
818           SLOT(showObjectBrowserSlot()));
819   showObjectBrowserAct->setCheckable(true);
820 
821   // Solver -> Parallel settings
822   parallelSettingsAct = new QAction(QIcon(), tr("Parallel settings..."), this);
823   parallelSettingsAct->setStatusTip(
824       tr("Choose parameters and methods for parallel solution"));
825   connect(parallelSettingsAct, SIGNAL(triggered()), this,
826           SLOT(parallelSettingsSlot()));
827 
828   // Solver -> Run solver
829   runsolverAct =
830       new QAction(QIcon(":/icons/Solver.png"), tr("Start solver"), this);
831   runsolverAct->setStatusTip(tr("Run ElmerSolver"));
832   connect(runsolverAct, SIGNAL(triggered()), this, SLOT(runsolverSlot()));
833 
834   // Solver -> Kill solver
835   killsolverAct =
836       new QAction(QIcon(":/icons/window-close.png"), tr("Kill solver"), this);
837   killsolverAct->setStatusTip(tr("Kill ElmerSolver"));
838   connect(killsolverAct, SIGNAL(triggered()), this, SLOT(killsolverSlot()));
839   killsolverAct->setEnabled(false);
840 
841   // Solver -> Show convergence
842   showConvergenceAct = new QAction(QIcon(), tr("Show convergence"), this);
843   showConvergenceAct->setStatusTip(tr("Show/hide convergence plot"));
844   connect(showConvergenceAct, SIGNAL(triggered()), this,
845           SLOT(showConvergenceSlot()));
846   showConvergenceAct->setCheckable(true);
847 
848   // Solver -> Post process
849   resultsAct =
850       new QAction(QIcon(":/icons/Post.png"), tr("Start ElmerPost"), this);
851   resultsAct->setStatusTip(tr("Run ElmerPost for visualization"));
852   connect(resultsAct, SIGNAL(triggered()), this, SLOT(resultsSlot()));
853 
854   // Solver -> Kill post process
855   killresultsAct = new QAction(QIcon(":/icons/window-close.png"),
856                                tr("Kill ElmerPost"), this);
857   killresultsAct->setStatusTip(tr("Kill ElmerPost"));
858   connect(killresultsAct, SIGNAL(triggered()), this, SLOT(killresultsSlot()));
859   killresultsAct->setEnabled(false);
860 
861   // Solver -> Show Vtk postprocessor
862   showVtkPostAct = new QAction(QIcon(), tr("Start ElmerVTK"), this);
863   showVtkPostAct->setStatusTip(tr("Invokes VTK based ElmerGUI postprocessor"));
864   connect(showVtkPostAct, SIGNAL(triggered()), this, SLOT(showVtkPostSlot()));
865 
866   // Solver -> Show ParaView postprocessor
867   paraviewAct =
868       new QAction(QIcon(":/icons/Paraview.png"), tr("Start ParaView"), this);
869   paraviewAct->setStatusTip(tr("Invokes ParaView for visualization"));
870   connect(paraviewAct, SIGNAL(triggered()), this, SLOT(showParaViewSlot()));
871 
872   // Solver -> Compiler...
873   compileSolverAct = new QAction(QIcon(""), tr("Compiler..."), this);
874   compileSolverAct->setStatusTip(tr(
875       "Compile Elmer specific source code (f90) into a shared library (dll)"));
876   connect(compileSolverAct, SIGNAL(triggered()), this,
877           SLOT(compileSolverSlot()));
878 
879   // Help -> About
880   aboutAct = new QAction(QIcon(":/icons/help-about.png"), tr("About..."), this);
881   aboutAct->setStatusTip(tr("Information about the program"));
882   connect(aboutAct, SIGNAL(triggered()), this, SLOT(showaboutSlot()));
883 
884   generateAndSaveAndRunAct =
885       new QAction(QIcon(":/icons/arrow-right-double.png"),
886                   tr("&Generate, save and run"), this);
887   generateAndSaveAndRunAct->setStatusTip(
888       tr("Generate and save sif, save project, then run solver"));
889   connect(generateAndSaveAndRunAct, SIGNAL(triggered()), this,
890           SLOT(generateAndSaveAndRunSlot()));
891   ;
892 
893 #if WIN32
894 #else
895   compileSolverAct->setEnabled(false);
896 #endif
897 
898   if (egIni->isSet("bgimage"))
899     chooseBGColorAct->setEnabled(false);
900 
901   runPostProcessorAct = new QAction(QIcon(":/icons/Post.png"), tr("ElmerPost"), this);
902   runPostProcessorAct->setStatusTip(tr("Select ElmerPost as post-processor"));
903   connect(runPostProcessorAct, SIGNAL(triggered()), this, SLOT(resultsSlot()));
904 
905   selectElmerPostAct = new QAction(QIcon(":/icons/Post.png"), tr("ElmerPost"), this);
906   selectElmerPostAct->setStatusTip(tr("Select ElmerPost as post-processor"));
907   connect(selectElmerPostAct, SIGNAL(triggered()), this, SLOT(selectElmerPostSlot()));
908   selectElmerPostAct->setCheckable(true);
909 
910   selectVtkPostAct = new QAction(QIcon(":/icons/Mesh3D.png"), tr("ElmerVTK"), this);
911   selectVtkPostAct->setStatusTip(tr("Select ElmerVTK as post-processor"));
912   connect(selectVtkPostAct, SIGNAL(triggered()), this, SLOT(selectVtkPostSlot()));
913   selectVtkPostAct->setCheckable(true);
914 
915   selectParaViewAct = new QAction(QIcon(":/icons/Paraview.png"), tr("ParaView"), this);
916   selectParaViewAct->setStatusTip(tr("Select ParaView as post-processor"));
917   connect(selectParaViewAct, SIGNAL(triggered()), this, SLOT(selectParaViewSlot()));
918   selectParaViewAct->setCheckable(true);
919 }
920 
921 // Create menus...
922 //-----------------------------------------------------------------------------
createMenus()923 void MainWindow::createMenus() {
924   // File menu
925   fileMenu = menuBar()->addMenu(tr("&File"));
926   fileMenu->addAction(newProjectAct);
927   fileMenu->addAction(loadProjectAct);
928   recentProjectsMenu = fileMenu->addMenu(tr("&Recent projects"));
929   recentProjectsMenu->setEnabled(false);
930   fileMenu->addAction(saveProjectAct);
931   fileMenu->addAction(saveProjectAct);
932   fileMenu->addAction(saveProjectAsAct);
933   fileMenu->addSeparator();
934   fileMenu->addAction(openAct);
935   fileMenu->addAction(loadAct);
936   fileMenu->addAction(saveAct);
937   fileMenu->addAction(saveAsAct);
938   fileMenu->addSeparator();
939   fileMenu->addAction(editDefinitionsAct);
940   fileMenu->addSeparator();
941   fileMenu->addAction(savePictureAct);
942   fileMenu->addSeparator();
943   fileMenu->addAction(exitAct);
944 
945   // Mesh menu
946   meshMenu = menuBar()->addMenu(tr("&Mesh"));
947   meshMenu->addAction(meshcontrolAct);
948   meshMenu->addAction(remeshAct);
949   meshMenu->addAction(stopMeshingAct);
950   meshMenu->addSeparator();
951   meshMenu->addAction(surfaceDivideAct);
952   meshMenu->addAction(surfaceUnifyAct);
953   meshMenu->addSeparator();
954   meshMenu->addAction(edgeDivideAct);
955   meshMenu->addAction(edgeUnifyAct);
956   meshMenu->addSeparator();
957   meshMenu->addAction(cleanHangingSharpEdgesAct);
958 
959   // Model menu
960   modelMenu = menuBar()->addMenu(tr("&Model"));
961 
962   modelMenu->addAction(modelSetupAct);
963   modelMenu->addSeparator();
964 
965   equationMenu = modelMenu->addMenu(tr("Equation"));
966   equationMenu->addAction(addEquationAct);
967   equationMenu->addSeparator();
968   connect(equationMenu, SIGNAL(triggered(QAction *)), this,
969           SLOT(equationSelectedSlot(QAction *)));
970 
971   modelMenu->addSeparator();
972   materialMenu = modelMenu->addMenu(tr("Material"));
973   materialMenu->addAction(addMaterialAct);
974   materialMenu->addSeparator();
975   connect(materialMenu, SIGNAL(triggered(QAction *)), this,
976           SLOT(materialSelectedSlot(QAction *)));
977 
978   modelMenu->addSeparator();
979   bodyForceMenu = modelMenu->addMenu(tr("Body force"));
980   bodyForceMenu->addAction(addBodyForceAct);
981   bodyForceMenu->addSeparator();
982   connect(bodyForceMenu, SIGNAL(triggered(QAction *)), this,
983           SLOT(bodyForceSelectedSlot(QAction *)));
984 
985   modelMenu->addSeparator();
986   initialConditionMenu = modelMenu->addMenu(tr("Initial condition"));
987   initialConditionMenu->addAction(addInitialConditionAct);
988   initialConditionMenu->addSeparator();
989   connect(initialConditionMenu, SIGNAL(triggered(QAction *)), this,
990           SLOT(initialConditionSelectedSlot(QAction *)));
991 
992   modelMenu->addSeparator();
993   boundaryConditionMenu = modelMenu->addMenu(tr("Boundary condition"));
994   boundaryConditionMenu->addAction(addBoundaryConditionAct);
995   boundaryConditionMenu->addSeparator();
996   connect(boundaryConditionMenu, SIGNAL(triggered(QAction *)), this,
997           SLOT(boundaryConditionSelectedSlot(QAction *)));
998 
999   modelMenu->addSeparator();
1000   modelMenu->addAction(bodyEditAct);
1001   modelMenu->addAction(bcEditAct);
1002   modelMenu->addSeparator();
1003   modelMenu->addAction(modelSummaryAct);
1004   modelMenu->addSeparator();
1005   modelMenu->addAction(modelClearAct);
1006   modelMenu->addSeparator();
1007 
1008   // View menu
1009   viewMenu = menuBar()->addMenu(tr("&View"));
1010   viewMenu->addAction(viewFullScreenAct);
1011   viewMenu->addSeparator();
1012   viewMenu->addAction(hidesurfacemeshAct);
1013   viewMenu->addAction(hidevolumemeshAct);
1014   viewMenu->addAction(hidesharpedgesAct);
1015   viewMenu->addAction(viewCoordinatesAct);
1016   viewMenu->addSeparator();
1017   viewMenu->addAction(selectAllSurfacesAct);
1018   viewMenu->addAction(selectAllEdgesAct);
1019   // Momentarily disabled (see comment *** TODO *** below):
1020   // viewMenu->addSeparator();
1021   // viewMenu->addAction(selectDefinedEdgesAct);
1022   // viewMenu->addAction(selectDefinedSurfacesAct);
1023   viewMenu->addSeparator();
1024   viewMenu->addAction(hideselectedAct);
1025   viewMenu->addSeparator();
1026   shadeMenu = viewMenu->addMenu(tr("Shade model"));
1027   shadeMenu->addAction(flatShadeAct);
1028   shadeMenu->addAction(smoothShadeAct);
1029   viewMenu->addSeparator();
1030   projectionMenu = viewMenu->addMenu(tr("Projection"));
1031   projectionMenu->addAction(orthoAct);
1032   projectionMenu->addAction(perspectiveAct);
1033   viewMenu->addSeparator();
1034   numberingMenu = viewMenu->addMenu(tr("Numbering"));
1035   numberingMenu->addAction(showSurfaceNumbersAct);
1036   numberingMenu->addAction(showEdgeNumbersAct);
1037   numberingMenu->addAction(showNodeNumbersAct);
1038   numberingMenu->addSeparator();
1039   numberingMenu->addAction(showBoundaryIndexAct);
1040   numberingMenu->addAction(showBodyIndexAct);
1041   viewMenu->addSeparator();
1042   colorizeMenu = viewMenu->addMenu(tr("Lights and colors"));
1043   colorizeMenu->addAction(glControlAct);
1044   colorizeMenu->addSeparator();
1045   colorizeMenu->addAction(chooseBGColorAct);
1046   colorizeMenu->addSeparator();
1047   colorizeMenu->addAction(chooseSurfaceColorAct);
1048   colorizeMenu->addAction(chooseEdgeColorAct);
1049   colorizeMenu->addSeparator();
1050   colorizeMenu->addAction(chooseSurfaceMeshColorAct);
1051   colorizeMenu->addAction(chooseSharpEdgeColorAct);
1052   colorizeMenu->addSeparator();
1053   colorizeMenu->addAction(showBoundaryColorAct);
1054   colorizeMenu->addAction(showBodyColorAct);
1055   viewMenu->addSeparator();
1056   viewMenu->addAction(showallAct);
1057   viewMenu->addAction(resetAct);
1058 #ifdef EG_OCC
1059   viewMenu->addSeparator();
1060   viewMenu->addAction(showCadModelAct);
1061 #endif
1062   viewMenu->addAction(showTwodViewAct);
1063   viewMenu->addSeparator();
1064   viewMenu->addAction(showObjectBrowserAct);
1065 
1066   // Edit menu
1067   editMenu = menuBar()->addMenu(tr("&Sif"));
1068   editMenu->addAction(generateSifAct);
1069   editMenu->addSeparator();
1070   editMenu->addAction(showsifAct);
1071 
1072   //  SolverMenu
1073   solverMenu = menuBar()->addMenu(tr("&Run"));
1074   solverMenu->addAction(parallelSettingsAct);
1075   solverMenu->addSeparator();
1076   solverMenu->addAction(runsolverAct);
1077   solverMenu->addAction(killsolverAct);
1078 #ifdef EG_QWT
1079   solverMenu->addAction(showConvergenceAct);
1080 #endif
1081   solverMenu->addSeparator();
1082   solverMenu->addAction(resultsAct);
1083   solverMenu->addAction(killresultsAct);
1084 #ifdef EG_VTK
1085   solverMenu->addSeparator();
1086   solverMenu->addAction(showVtkPostAct);
1087 #endif
1088 #ifdef EG_PARAVIEW
1089   solverMenu->addSeparator();
1090   solverMenu->addAction(paraviewAct);
1091 #endif
1092   solverMenu->addSeparator();
1093   solverMenu->addAction(compileSolverAct);
1094 
1095   // Help menu
1096   helpMenu = menuBar()->addMenu(tr("&Help"));
1097   helpMenu->addAction(aboutAct);
1098 
1099   // Sys tray menu:
1100   sysTrayMenu = new QMenu;
1101   sysTrayMenu->addAction(modelSummaryAct);
1102   sysTrayMenu->addSeparator();
1103   sysTrayMenu->addAction(stopMeshingAct);
1104   sysTrayMenu->addSeparator();
1105   sysTrayMenu->addAction(killsolverAct);
1106   sysTrayMenu->addAction(killresultsAct);
1107   sysTrayMenu->addSeparator();
1108   sysTrayMenu->addAction(aboutAct);
1109   sysTrayMenu->addSeparator();
1110   sysTrayMenu->addAction(exitAct);
1111 
1112   // Context menu:
1113   contextMenu = new QMenu;
1114   contextMenu->addMenu(fileMenu);
1115   contextMenu->addMenu(meshMenu);
1116   contextMenu->addMenu(modelMenu);
1117   contextMenu->addMenu(viewMenu);
1118   contextMenu->addMenu(editMenu);
1119   contextMenu->addMenu(solverMenu);
1120   contextMenu->addMenu(helpMenu);
1121 
1122   selectPostMenu = new QMenu;
1123   selectPostMenu->addAction(selectElmerPostAct);
1124   selectPostMenu->addAction(selectVtkPostAct);
1125   selectPostMenu->addAction(selectParaViewAct);
1126 #ifndef EG_VTK
1127   selectVtkPostAct->setEnabled(false);
1128 #endif
1129 #ifndef EG_PARAVIEW
1130   selectParaViewAct->setEnabled(false);
1131 #endif
1132   // Disable unavailable external components:
1133   //------------------------------------------
1134   if (!egIni->isSet("checkexternalcomponents"))
1135     return;
1136 
1137   QProcess testProcess;
1138   QStringList args;
1139 
1140   cout << "Checking for ElmerSolver... ";
1141   updateSplash("Checking for ElmerSolver...");
1142   args << "-v";
1143   testProcess.start("ElmerSolver", args);
1144   if (!testProcess.waitForStarted()) {
1145     logMessage("no - disabling solver features");
1146     runsolverAct->setEnabled(false);
1147     showConvergenceAct->setEnabled(false);
1148     killsolverAct->setEnabled(false);
1149   } else {
1150     cout << "yes" << endl;
1151   }
1152   testProcess.waitForFinished(2000);
1153 
1154   cout << "Checking for ... ";
1155   updateSplash("Checking for ElmerPost...");
1156   args << "-v";
1157   testProcess.start("ElmerPost", args);
1158   if (!testProcess.waitForStarted()) {
1159     logMessage("no - disabling ElmerPost postprocessing features");
1160     resultsAct->setEnabled(false);
1161     killresultsAct->setEnabled(false);
1162   } else {
1163     cout << "yes" << endl;
1164   }
1165   testProcess.waitForFinished(2000);
1166 
1167   cout << "Checking for ElmerGrid... ";
1168   updateSplash("Checking for ElmerGrid...");
1169   testProcess.start("ElmerGrid");
1170   if (!testProcess.waitForStarted()) {
1171     logMessage("no - disabling parallel features");
1172     parallelSettingsAct->setEnabled(false);
1173   } else {
1174     cout << "yes" << endl;
1175   }
1176   testProcess.waitForFinished(2000);
1177 
1178   cout << "Checking for ElmerSolver_mpi... ";
1179   updateSplash("Checking for ElmerSolver_mpi...");
1180   args << "-v";
1181   testProcess.start("ElmerSolver_mpi", args);
1182   if (!testProcess.waitForStarted()) {
1183     logMessage("no - disabling parallel features");
1184     parallelSettingsAct->setEnabled(false);
1185   } else {
1186     cout << "yes" << endl;
1187   }
1188   testProcess.waitForFinished(2000);
1189 }
1190 
1191 // Create tool bars...
1192 //-----------------------------------------------------------------------------
createToolBars()1193 void MainWindow::createToolBars() {
1194   // File toolbar
1195   fileToolBar = addToolBar(tr("&File"));
1196   fileToolBar->addAction(newProjectAct);
1197   fileToolBar->addAction(loadProjectAct);
1198   fileToolBar->addAction(saveProjectAct);
1199   fileToolBar->addAction(saveProjectAsAct);
1200   fileToolBar->addSeparator();
1201   fileToolBar->addAction(openAct);
1202   fileToolBar->addAction(loadAct);
1203   fileToolBar->addAction(saveAct);
1204   fileToolBar->addAction(saveAsAct);
1205 
1206   fileToolBar->addSeparator();
1207   fileToolBar->addAction(savePictureAct);
1208 
1209   // Edit toolbar
1210   editToolBar = addToolBar(tr("&Edit"));
1211   editToolBar->addAction(showsifAct);
1212 
1213   // Mesh toolbar
1214   meshToolBar = addToolBar(tr("&Mesh"));
1215   meshToolBar->addAction(meshcontrolAct);
1216   meshToolBar->addAction(remeshAct);
1217   meshToolBar->addSeparator();
1218   meshToolBar->addAction(surfaceDivideAct);
1219   meshToolBar->addAction(surfaceUnifyAct);
1220   meshToolBar->addSeparator();
1221   meshToolBar->addAction(edgeDivideAct);
1222   meshToolBar->addAction(edgeUnifyAct);
1223   meshToolBar->addSeparator();
1224   meshToolBar->addAction(bodyEditAct);
1225   meshToolBar->addAction(bcEditAct);
1226 
1227   // Solver toolbar
1228   solverToolBar = addToolBar(tr("&Solver"));
1229   solverToolBar->addAction(runsolverAct);
1230   solverToolBar->addAction(runPostProcessorAct);
1231   solverToolBar->addAction(generateAndSaveAndRunAct);
1232 
1233   if (egIni->isSet("hidetoolbars")) {
1234     fileToolBar->hide();
1235     editToolBar->hide();
1236     meshToolBar->hide();
1237     solverToolBar->hide();
1238   }
1239 }
1240 
1241 // Create status bar...
1242 //-----------------------------------------------------------------------------
createStatusBar()1243 void MainWindow::createStatusBar() {
1244   progressBar = new QProgressBar;
1245   progressBar->setMaximumHeight(12);
1246   progressBar->setMaximumWidth(120);
1247   progressBar->setTextVisible(false);
1248   progressBar->hide();
1249 
1250   progressLabel = new QLabel;
1251   progressLabel->hide();
1252 
1253   statusBar()->addPermanentWidget(progressLabel);
1254   statusBar()->addPermanentWidget(progressBar);
1255 
1256   statusBar()->showMessage(tr("Ready"));
1257 
1258   connect(grabTimeLine, SIGNAL(frameChanged(int)), progressBar,
1259           SLOT(setValue(int)));
1260 }
1261 
1262 //*****************************************************************************
1263 //
1264 //                                File MENU
1265 //
1266 //*****************************************************************************
1267 
1268 // File -> Open...
1269 //-----------------------------------------------------------------------------
newProjectSlot()1270 void MainWindow::newProjectSlot() {
1271   NewProjectDialog dlg;
1272 
1273 #ifdef __APPLE__DONTGO_HERE_TODO
1274   QString extraDirpath = this->homePath + "/edf-extra";
1275 #else
1276   QString extraDirPath =
1277       QCoreApplication::applicationDirPath() + "/../share/ElmerGUI/edf-extra";
1278 
1279   QString elmerGuiHome = QString(getenv("ELMERGUI_HOME"));
1280 
1281   if (!elmerGuiHome.isEmpty())
1282     extraDirPath = elmerGuiHome + "/edf-extra";
1283 
1284   extraDirPath.replace('\\', '/');
1285 #endif
1286 
1287   QString defaultDir = getDefaultDirName();
1288   dlg.setDirectories(defaultDir, extraDirPath);
1289 
1290   if (dlg.exec() == QDialog::Accepted) {
1291 
1292     // re-initialize
1293     delete elmerDefs;
1294     elmerDefs = new QDomDocument;
1295     delete edfEditor;
1296     edfEditor = new EdfEditor;
1297     loadDefinitions();
1298     geometryInputFileName = "";
1299     currentProjectDirName = "";
1300     sifWindow->getTextEdit()->clear();
1301     sifWindow->hide();
1302     solverLogWindow->getTextEdit()->clear();
1303     solverLogWindow->hide();
1304     delete generalSetup;
1305     generalSetup = new GeneralSetup(this);
1306     summaryEditor->ui.summaryEdit->clear();
1307     delete twodView;
1308     twodView = new TwodView;
1309     meshControl->defaultControls();
1310     delete parallel;
1311     parallel = new Parallel(this);
1312 
1313 #ifdef EG_QWT
1314     convergenceView->removeData();
1315 #endif
1316 
1317 #ifdef EG_VTK
1318     settings_setValue("vtkPost/geometry", vtkPost->saveGeometry());
1319     delete vtkPost;
1320     vtkPost = new VtkPost(this);
1321     vtkPostMeshUnifierRunning = false;
1322     vtkPost->restoreGeometry(settings_value("vtkPost/geometry").toByteArray());
1323 #endif
1324 
1325 #ifdef EG_OCC
1326     settings_setValue("cadView/geometry", cadView->saveGeometry());
1327     delete cadView;
1328     cadView = new CadView();
1329     if (egIni->isPresent("deflection"))
1330       cadView->setDeflection(egIni->value("deflection").toDouble());
1331     cadView->restoreGeometry(settings_value("cadView/geometry").toByteArray());
1332 #endif
1333 
1334     // delete operations
1335     operation_t *p = operation.next;
1336     operation_t *q = NULL;
1337     while (p != NULL) {
1338       if (p->select_set != NULL)
1339         delete[] p->select_set;
1340       q = p->next;
1341       if (p != NULL)
1342         delete p;
1343       p = q;
1344     }
1345     operations = 0;
1346     operation.next = NULL;
1347 
1348     // reset mesh
1349     if (glWidget->hasMesh()) {
1350       glWidget->getMesh()->clear();
1351       glWidget->deleteMesh();
1352     }
1353     glWidget->newMesh();
1354     meshutils->findSurfaceElementEdges(glWidget->getMesh());
1355     meshutils->findSurfaceElementNormals(glWidget->getMesh());
1356     glWidget->rebuildLists();
1357 
1358     modelClearSlot();
1359 
1360     // load Elmer mesh/open geometry file
1361     bool bStartMeshing = false;
1362     if (dlg.ui.radioButton_elmerMesh->isChecked() &&
1363         !dlg.ui.label_meshDir->text().isEmpty()) {
1364       loadElmerMesh(dlg.ui.label_meshDir->text());
1365     } else if (dlg.ui.radioButton_geometryFile->isChecked() &&
1366                !dlg.ui.label_geometryFile->text().isEmpty()) {
1367       QString fileName = dlg.ui.label_geometryFile->text();
1368       geometryInputFileName = fileName;
1369       saveDirName = "";
1370       readInputFile(fileName);
1371       if (egIni->isSet("automesh"))
1372         bStartMeshing = true;
1373     }
1374 
1375     // save and load project
1376     saveProject(dlg.ui.label_projectDir->text());
1377     loadProject(dlg.ui.label_projectDir->text());
1378 
1379     // load extra solvers
1380     QString message;
1381     for (int i = 0; i < dlg.ui.listWidget_selectedSolvers->count(); i++) {
1382       message = "Load " + extraDirPath + "/" +
1383                 dlg.ui.listWidget_selectedSolvers->item(i)->text() + "... ";
1384 #if WITH_QT5
1385       cout << string(message.toLatin1());
1386       cout.flush();
1387 #else
1388       cout << string(message.toAscii());
1389       cout.flush();
1390 #endif
1391       edfEditor->appendFrom(extraDirPath + "/" +
1392                             dlg.ui.listWidget_selectedSolvers->item(i)->text());
1393       cout << " done" << endl;
1394     }
1395 
1396     if (bStartMeshing)
1397       remeshSlot();
1398   }
1399 }
1400 
parseCmdLine()1401 void MainWindow::parseCmdLine() {
1402   QStringList args = QCoreApplication::arguments();
1403 
1404   if (!args.contains("-nogui"))
1405     this->show();
1406 
1407   int input = args.indexOf("-i");
1408 
1409   if (input > 0) {
1410     QString fileName = args.at(input + 1);
1411 
1412     QFileInfo fileInfo(fileName);
1413 
1414     if (!fileInfo.exists()) {
1415 #if WITH_QT5
1416       cout << "Input file \"" << fileName.toLatin1().data()
1417            << "\" does not exist" << endl;
1418 #else
1419       cout << "Input file \"" << fileName.toAscii().data()
1420            << "\" does not exist" << endl;
1421 #endif
1422       QApplication::closeAllWindows();
1423       exit(0);
1424     }
1425 
1426     if (fileName.left(1) != "-") {
1427 #if WITH_QT5
1428       cout << "Reading input file " << fileName.toLatin1().data() << endl;
1429 #else
1430       cout << "Reading input file " << fileName.toAscii().data() << endl;
1431 #endif
1432       readInputFile(fileName);
1433       remeshSlot();
1434     }
1435   }
1436 }
1437 
1438 // File -> Open...
1439 //-----------------------------------------------------------------------------
openSlot()1440 void MainWindow::openSlot() {
1441   QString defaultDirName = getDefaultDirName();
1442 
1443   QString fileName = QFileDialog::getOpenFileName(
1444       this, tr("Open geometry input file"), defaultDirName);
1445 
1446   if (!fileName.isEmpty()) {
1447 
1448     QFileInfo fi(fileName);
1449     QString absolutePath = fi.absolutePath();
1450     QDir::setCurrent(absolutePath);
1451 
1452   } else {
1453 
1454     logMessage("Unable to open file: file name is empty");
1455     return;
1456   }
1457 
1458   geometryInputFileName = fileName;
1459 
1460   operation_t *p = operation.next;
1461   operation_t *q = NULL;
1462 
1463   while (p != NULL) {
1464     if (p->select_set != NULL)
1465       delete[] p->select_set;
1466 
1467     q = p->next;
1468 
1469     if (p != NULL)
1470       delete p;
1471 
1472     p = q;
1473   }
1474 
1475   operations = 0;
1476   operation.next = NULL;
1477 
1478   saveDirName = "";
1479   readInputFile(fileName);
1480 
1481   if (egIni->isSet("automesh"))
1482     remeshSlot();
1483 }
1484 
1485 // Read input file and populate mesh generator's input structures:
1486 //-----------------------------------------------------------------------------
readInputFile(QString fileName)1487 void MainWindow::readInputFile(QString fileName) {
1488   occInputOk = false;
1489 
1490   char cs[1024];
1491 
1492   QFileInfo fi(fileName);
1493   QString absolutePath = fi.absolutePath();
1494   QString baseName = fi.baseName();
1495   QString fileSuffix = fi.suffix();
1496   QString baseFileName = absolutePath + "/" + baseName;
1497 #if WITH_QT5
1498   sprintf(cs, "%s", baseFileName.toLatin1().data());
1499 #else
1500   sprintf(cs, "%s", baseFileName.toAscii().data());
1501 #endif
1502 
1503   activeGenerator = GEN_UNKNOWN;
1504   tetlibInputOk = false;
1505   nglibInputOk = false;
1506   ngDim = 3;
1507 
1508   // Choose generator according to fileSuffix:
1509   //------------------------------------------
1510   if ((fileSuffix == "smesh") || (fileSuffix == "poly")) {
1511 
1512     if (!tetlibPresent) {
1513       logMessage("unable to mesh - tetlib unavailable");
1514       return;
1515     }
1516 
1517     activeGenerator = GEN_TETLIB;
1518     cout << "Selected tetlib for smesh/poly-format" << endl;
1519 
1520     in->deinitialize();
1521     in->initialize();
1522     in->load_poly(cs);
1523 
1524     tetlibInputOk = true;
1525 
1526   } else if (fileSuffix == "off") {
1527 
1528     if (!tetlibPresent) {
1529       logMessage("unable to mesh - tetlib unavailable");
1530       return;
1531     }
1532 
1533     activeGenerator = GEN_TETLIB;
1534     cout << "Selected tetlib for off-format" << endl;
1535 
1536     in->deinitialize();
1537     in->initialize();
1538     in->load_off(cs);
1539 
1540     tetlibInputOk = true;
1541 
1542   } else if (fileSuffix == "ply") {
1543 
1544     if (!tetlibPresent) {
1545       logMessage("unable to mesh - tetlib unavailable");
1546       return;
1547     }
1548 
1549     activeGenerator = GEN_TETLIB;
1550     cout << "Selected tetlib for ply-format" << endl;
1551 
1552     in->deinitialize();
1553     in->initialize();
1554     in->load_ply(cs);
1555 
1556     tetlibInputOk = true;
1557 
1558   } else if (fileSuffix == "mesh") {
1559 
1560     if (!tetlibPresent) {
1561       logMessage("unable to mesh - tetlib unavailable");
1562       return;
1563     }
1564 
1565     activeGenerator = GEN_TETLIB;
1566     cout << "Selected tetlib for mesh-format" << endl;
1567 
1568     in->deinitialize();
1569     in->initialize();
1570     in->load_medit(cs, 1);
1571 
1572     tetlibInputOk = true;
1573 
1574   } else if (fileSuffix == "stl") {
1575 
1576     // for stl there are two alternative generators:
1577     if (meshControl->generatorType == GEN_NGLIB) {
1578 
1579       if (!nglibPresent) {
1580         logMessage("unable to mesh - nglib unavailable");
1581         return;
1582       }
1583 
1584       activeGenerator = GEN_NGLIB;
1585       cout << "Selected nglib for stl-format" << endl;
1586 
1587       stlFileName = fileName;
1588 
1589       nglibInputOk = true;
1590 
1591     } else {
1592 
1593       if (!tetlibPresent) {
1594         logMessage("unable to mesh - tetlib unavailable");
1595         return;
1596       }
1597 
1598       activeGenerator = GEN_TETLIB;
1599       cout << "Selected tetlib for stl-format" << endl;
1600 
1601       in->deinitialize();
1602       in->initialize();
1603       in->load_stl(cs);
1604 
1605       tetlibInputOk = true;
1606     }
1607 
1608   } else if ((fileSuffix == "grd") || (fileSuffix == "FDNEUT") ||
1609              (fileSuffix == "msh") || (fileSuffix == "mphtxt") ||
1610              (fileSuffix == "inp") || (fileSuffix == "unv") ||
1611              (fileSuffix == "plt")) {
1612 
1613     activeGenerator = GEN_ELMERGRID;
1614     cout << "Selected elmergrid" << endl;
1615 
1616 #if WITH_QT5
1617     int errstat = elmergridAPI->loadElmerMeshStructure(
1618         (const char *)(fileName.toLatin1()));
1619 #else
1620     int errstat = elmergridAPI->loadElmerMeshStructure(
1621         (const char *)(fileName.toAscii()));
1622 #endif
1623 
1624     if (errstat)
1625       logMessage("loadElmerMeshStructure failed!");
1626 
1627     return;
1628 
1629 #ifdef EG_OCC
1630 
1631   } else if ((fileSuffix.toLower() == "brep") ||
1632              (fileSuffix.toLower() == "step") ||
1633              (fileSuffix.toLower() == "stp") ||
1634              (fileSuffix.toLower() == "iges") ||
1635              (fileSuffix.toLower() == "igs")) {
1636 
1637     meshControl->ui.nglibRadioButton->setChecked(true);
1638     meshControl->generatorType = GEN_NGLIB;
1639     activeGenerator = meshControl->generatorType;
1640 
1641     if (egIni->isSet("autoview"))
1642       cadView->show();
1643 
1644     occInputOk = cadView->readFile(fileName);
1645 
1646     ngDim = cadView->getDim();
1647 
1648     if (!occInputOk) {
1649       logMessage("Cad import: error: Unable to proceed with input file");
1650       cadView->close();
1651       return;
1652     }
1653 
1654     nglibInputOk = true;
1655 
1656 #endif
1657 
1658   } else if ((fileSuffix.toLower() == "in2d")) {
1659 
1660     if (!nglibPresent) {
1661       logMessage("unable to mesh - nglib unavailable");
1662       return;
1663     }
1664 
1665     activeGenerator = GEN_NGLIB;
1666     cout << "Selected nglib for in2d-format" << endl;
1667 
1668     in2dFileName = fileName;
1669 
1670     nglibInputOk = true;
1671 
1672     ngDim = 2;
1673 
1674   } else {
1675 
1676     logMessage("Unable to open file: file type unknown");
1677     activeGenerator = GEN_UNKNOWN;
1678 
1679     return;
1680   }
1681 }
1682 
1683 // Populate elmer's mesh structure and make GL-lists (tetlib):
1684 //-----------------------------------------------------------------------------
makeElmerMeshFromTetlib()1685 void MainWindow::makeElmerMeshFromTetlib() {
1686   meshutils->clearMesh(glWidget->getMesh());
1687 
1688   glWidget->setMesh(tetlibAPI->createElmerMeshStructure());
1689 
1690   glWidget->rebuildLists();
1691 
1692   logMessage("Input file processed");
1693 }
1694 
1695 // Populate elmer's mesh structure and make GL-lists (nglib):
1696 //-----------------------------------------------------------------------------
makeElmerMeshFromNglib()1697 void MainWindow::makeElmerMeshFromNglib() {
1698   meshutils->clearMesh(glWidget->getMesh());
1699   nglibAPI->setDim(this->ngDim);
1700   nglibAPI->setNgmesh(ngmesh);
1701 
1702   glWidget->setMesh(nglibAPI->createElmerMeshStructure());
1703   glWidget->rebuildLists();
1704 
1705   logMessage("Input file processed");
1706 }
1707 
1708 // File -> Load mesh...
1709 //-----------------------------------------------------------------------------
loadSlot()1710 void MainWindow::loadSlot() {
1711   QString defaultDirName = getDefaultDirName();
1712 
1713   QString dirName = QFileDialog::getExistingDirectory(
1714       this, tr("Open mesh directory"), defaultDirName);
1715 
1716   if (!dirName.isEmpty()) {
1717 
1718     logMessage("Loading from directory " + dirName);
1719 
1720   } else {
1721 
1722     logMessage("Unable to load mesh: directory undefined");
1723     return;
1724   }
1725 
1726   loadElmerMesh(dirName);
1727 }
1728 
1729 // Import mesh files in elmer-format:
1730 //-----------------------------------------------------------------------------
loadElmerMesh(QString dirName)1731 void MainWindow::loadElmerMesh(QString dirName) {
1732   logMessage("Loading elmer mesh files");
1733 
1734   if (glWidget->hasMesh()) {
1735     glWidget->getMesh()->clear();
1736     glWidget->deleteMesh();
1737   }
1738 
1739   glWidget->newMesh();
1740 
1741 #if WITH_QT5
1742   bool success = glWidget->getMesh()->load(dirName.toLatin1().data());
1743 #else
1744   bool success = glWidget->getMesh()->load(dirName.toAscii().data());
1745 #endif
1746 
1747   if (!success) {
1748     glWidget->getMesh()->clear();
1749     glWidget->deleteMesh();
1750     logMessage("Failed loading mesh files");
1751     return;
1752   }
1753 
1754   meshutils->findSurfaceElementEdges(glWidget->getMesh());
1755   meshutils->findSurfaceElementNormals(glWidget->getMesh());
1756 
1757   glWidget->rebuildLists();
1758 
1759   QDir::setCurrent(dirName);
1760   saveDirName = dirName;
1761 
1762   logMessage("Ready");
1763 }
1764 
1765 // File -> Save...
1766 //-----------------------------------------------------------------------------
saveSlot()1767 void MainWindow::saveSlot() {
1768   if (!glWidget->hasMesh()) {
1769     logMessage("Unable to save mesh: no data");
1770     return;
1771   }
1772 
1773   if (!saveDirName.isEmpty()) {
1774     logMessage("Output directory " + saveDirName);
1775   } else {
1776     saveAsSlot();
1777     return;
1778   }
1779 
1780   saveElmerMesh(saveDirName);
1781 }
1782 
1783 // File -> Save as...
1784 //-----------------------------------------------------------------------------
saveAsSlot()1785 void MainWindow::saveAsSlot() {
1786   if (!glWidget->hasMesh()) {
1787     logMessage("Unable to save mesh: no data");
1788     return;
1789   }
1790 
1791   QString defaultDirName = getDefaultDirName();
1792 
1793   saveDirName = QFileDialog::getExistingDirectory(
1794       this, tr("Open directory to save mesh"), defaultDirName);
1795 
1796   if (!saveDirName.isEmpty()) {
1797     logMessage("Output directory " + saveDirName);
1798   } else {
1799     logMessage("Unable to save: directory undefined");
1800     return;
1801   }
1802 
1803   saveElmerMesh(saveDirName);
1804 }
1805 
1806 // File -> Save project
1807 //-----------------------------------------------------------------------------
saveProjectSlot()1808 void MainWindow::saveProjectSlot() {
1809   if (!glWidget->hasMesh()) {
1810     logMessage("Unable to save project: no mesh");
1811     return;
1812   }
1813 
1814   QString projectDirName = currentProjectDirName;
1815   if (!projectDirName.isEmpty()) {
1816     logMessage("Project directory " + projectDirName);
1817     saveProject(projectDirName);
1818   } else {
1819     saveProjectAsSlot();
1820   }
1821 }
1822 
1823 // File -> Save project as...
1824 //-----------------------------------------------------------------------------
saveProjectAsSlot()1825 void MainWindow::saveProjectAsSlot() {
1826   if (!glWidget->hasMesh()) {
1827     logMessage("Unable to save project: no mesh");
1828     return;
1829   }
1830 
1831   QString defaultDirName = getDefaultDirName();
1832 
1833   QString projectDirName = QFileDialog::getExistingDirectory(
1834       this, tr("Open directory to save project"), defaultDirName);
1835 
1836   if (!projectDirName.isEmpty()) {
1837     logMessage("Project directory " + projectDirName);
1838   } else {
1839     logMessage("Unable to save project: directory undefined");
1840     return;
1841   }
1842 
1843   saveProject(projectDirName);
1844 }
1845 
saveProject(QString projectDirName)1846 bool MainWindow::saveProject(QString projectDirName) {
1847   if (!glWidget->hasMesh()) {
1848     logMessage("Unable to save project: no mesh");
1849     return false;
1850   }
1851 
1852   progressBar->show();
1853   progressBar->setRange(0, 13);
1854 
1855   progressLabel->setText("Saving");
1856   progressLabel->show();
1857 
1858   // Create project document:
1859   //-------------------------
1860   progressBar->setValue(1);
1861 
1862   QDomDocument projectDoc("egproject");
1863   QDomElement contents = projectDoc.createElement("contents");
1864   projectDoc.appendChild(contents);
1865 
1866   //===========================================================================
1867   //                                  SAVE MESH
1868   //===========================================================================
1869   progressBar->setValue(2);
1870   logMessage("Saving mesh files...");
1871   saveElmerMesh(projectDirName);
1872 
1873   //===========================================================================
1874   //                        SAVE GEOMETRY INPUT FILE(S)
1875   //===========================================================================
1876   progressBar->setValue(3);
1877 
1878 #ifdef Q_OS_LINUX
1879   QFileInfo fileInfo(geometryInputFileName);
1880   QString pathName(fileInfo.absolutePath());
1881   QString baseName(fileInfo.baseName());
1882 
1883   // System copy command:
1884   QString cmd("cp -f " + pathName + "/" + baseName + ".* " + projectDirName);
1885 
1886   if (system(cmd.toLatin1().data()))
1887     logMessage("Geometry input file(s) not copied");
1888 
1889   QDomElement geomInput(projectDoc.createElement("geometryinputfile"));
1890   QDomText geomInputValue(projectDoc.createTextNode(fileInfo.fileName()));
1891   geomInput.appendChild(geomInputValue);
1892   contents.appendChild(geomInput);
1893 
1894 #else
1895   QFileInfo geometryInputFileInfo(geometryInputFileName);
1896   QString baseName(geometryInputFileInfo.baseName());
1897 
1898   QString srcPathName(geometryInputFileInfo.absolutePath());
1899   QString dstPathName(QDir(projectDirName).absolutePath());
1900 
1901   // Avoid copying file(s) into it self:
1902 
1903   if (srcPathName != dstPathName) {
1904     QDirIterator srcDirIterator(srcPathName);
1905 
1906     while (srcDirIterator.hasNext()) {
1907       QString srcFileName(srcDirIterator.next());
1908       QFileInfo srcFileInfo(srcDirIterator.fileInfo());
1909 
1910       if (srcFileInfo.baseName() == baseName) {
1911         logMessage("Copying: " + srcFileName);
1912 
1913         QFile src(srcFileName);
1914 
1915         if (!src.open(QFile::ReadOnly)) {
1916           logMessage("Unable to read: " + src.fileName());
1917           continue;
1918         }
1919 
1920         QFile dst(dstPathName + "/" + srcFileInfo.fileName());
1921 
1922         if (!dst.open(QFile::WriteOnly)) {
1923           logMessage("Unable to write: " + dst.fileName());
1924           src.close();
1925           continue;
1926         }
1927 
1928         QTextStream srcStream(&src);
1929         QTextStream dstStream(&dst);
1930         dstStream << srcStream.readAll();
1931 
1932         dst.close();
1933         src.close();
1934       }
1935     }
1936 
1937   } else {
1938     logMessage("Geometry input file(s) not copied");
1939   }
1940 
1941   QDomElement geomInput = projectDoc.createElement("geometryinputfile");
1942   QDomText geomInputValue =
1943       projectDoc.createTextNode(geometryInputFileInfo.fileName());
1944   geomInput.appendChild(geomInputValue);
1945   contents.appendChild(geomInput);
1946 #endif
1947 
1948   //===========================================================================
1949   //                               SAVE OPERATIONS
1950   //===========================================================================
1951   progressBar->setValue(4);
1952   QDomElement ops = projectDoc.createElement("operations");
1953   contents.appendChild(ops);
1954   operation.appendToProject(&projectDoc, &ops);
1955 
1956   //===========================================================================
1957   //                              SAVE GENERAL SETUP
1958   //===========================================================================
1959   progressBar->setValue(5);
1960   logMessage("Saving menu contents... ");
1961   QDomElement gsBlock = projectDoc.createElement("generalsetup");
1962   projectDoc.documentElement().appendChild(gsBlock);
1963   generalSetup->appendToProject(&projectDoc, &gsBlock);
1964 
1965   //===========================================================================
1966   //                            SAVE PARALLEL SETTINGS
1967   //===========================================================================
1968   progressBar->setValue(6);
1969   QDomElement paraBlock = projectDoc.createElement("parallelsettings");
1970   projectDoc.documentElement().appendChild(paraBlock);
1971   parallel->appendToProject(&projectDoc, &paraBlock);
1972 
1973   //===========================================================================
1974   //                            SAVE MESH PARAMETERS
1975   //===========================================================================
1976   progressBar->setValue(7);
1977   QDomElement meshParams = projectDoc.createElement("meshparameters");
1978   projectDoc.documentElement().appendChild(meshParams);
1979   meshControl->appendToProject(&projectDoc, &meshParams);
1980 
1981   //===========================================================================
1982   //                            SAVE SOLVER PARAMETERS
1983   //===========================================================================
1984   progressBar->setValue(8);
1985   QDomElement speBlock = projectDoc.createElement("solverparameters");
1986   projectDoc.documentElement().appendChild(speBlock);
1987 
1988   for (int index = 0; index < solverParameterEditor.size(); index++) {
1989     SolverParameterEditor *spe = solverParameterEditor[index];
1990 
1991     if (!spe)
1992       continue;
1993 
1994     QDomElement item = projectDoc.createElement("item");
1995     item.setAttribute("index", QString::number(index));
1996     item.setAttribute("name", spe->solverName);
1997     speBlock.appendChild(item);
1998     spe->appendToProject(&projectDoc, &item);
1999   }
2000 
2001   //===========================================================================
2002   //                          SAVE DYNAMIC MENU CONTENTS
2003   //===========================================================================
2004   progressBar->setValue(9);
2005   saveProjectContents(projectDoc, "equation", equationEditor);
2006   saveProjectContents(projectDoc, "material", materialEditor);
2007   saveProjectContents(projectDoc, "bodyforce", bodyForceEditor);
2008   saveProjectContents(projectDoc, "initialcondition", initialConditionEditor);
2009   saveProjectContents(projectDoc, "boundarycondition", boundaryConditionEditor);
2010 
2011   //===========================================================================
2012   //                          SAVE SOLVER SPECIFIC OPTIONS
2013   //===========================================================================
2014   progressBar->setValue(10);
2015   QDomElement solverOptionsBlock =
2016       projectDoc.createElement("solverspecificoptions");
2017   projectDoc.documentElement().appendChild(solverOptionsBlock);
2018 
2019   for (int index = 0; index < solverParameterEditor.size(); index++) {
2020     SolverParameterEditor *spe = solverParameterEditor[index];
2021 
2022     if (!spe)
2023       continue;
2024 
2025     DynamicEditor *dynEdit = spe->generalOptions;
2026 
2027     if (!dynEdit)
2028       continue;
2029 
2030     QDomElement item = projectDoc.createElement("item");
2031     item.setAttribute("index", QString::number(index));
2032     item.setAttribute("name", spe->solverName);
2033     item.setAttribute("id", QString::number(dynEdit->ID));
2034     solverOptionsBlock.appendChild(item);
2035 
2036     dynEdit->dumpHash(&projectDoc, &item);
2037   }
2038 
2039   //===========================================================================
2040   //                            SAVE BODY PROPERTIES
2041   //===========================================================================
2042   progressBar->setValue(11);
2043   QDomElement bodyBlock = projectDoc.createElement("bodyproperties");
2044   projectDoc.documentElement().appendChild(bodyBlock);
2045 
2046   for (int index = 0; index < bodyPropertyEditor.size(); index++) {
2047     BodyPropertyEditor *bpe = bodyPropertyEditor[index];
2048 
2049     if (!bpe)
2050       continue;
2051 
2052     QDomElement item = projectDoc.createElement("item");
2053     item.setAttribute("index", QString::number(index));
2054     bodyBlock.appendChild(item);
2055     bpe->appendToProject(&projectDoc, &item);
2056   }
2057 
2058   //===========================================================================
2059   //                          SAVE BOUNDARY PROPERTIES
2060   //===========================================================================
2061   progressBar->setValue(12);
2062   QDomElement boundaryBlock = projectDoc.createElement("boundaryproperties");
2063   projectDoc.documentElement().appendChild(boundaryBlock);
2064 
2065   for (int index = 0; index < boundaryPropertyEditor.size(); index++) {
2066     BoundaryPropertyEditor *bpe = boundaryPropertyEditor[index];
2067 
2068     if (!bpe)
2069       continue;
2070 
2071     QDomElement item = projectDoc.createElement("item");
2072     item.setAttribute("index", QString::number(index));
2073     boundaryBlock.appendChild(item);
2074     bpe->appendToProject(&projectDoc, &item);
2075   }
2076 
2077   //===========================================================================
2078   //                             SAVE PROJECT DOCUMENT
2079   //===========================================================================
2080   progressBar->setValue(13);
2081   const int indent = 3;
2082   QFile projectFile("egproject.xml");
2083   projectFile.open(QIODevice::WriteOnly);
2084   QTextStream projectTextStream(&projectFile);
2085   projectDoc.save(projectTextStream, indent);
2086 
2087   saveDirName = projectDirName;
2088   logMessage("Ready");
2089 
2090   progressBar->hide();
2091   progressLabel->hide();
2092 
2093   setWindowTitle(QString("ElmerGUI - ") + projectDirName);
2094   addRecentProject(projectDirName, true);
2095   currentProjectDirName = projectDirName;
2096 
2097   return true;
2098 }
2099 
2100 // Helper function for saveProject
2101 //-----------------------------------------------------------------------------
saveProjectContents(QDomDocument projectDoc,QString blockName,QVector<DynamicEditor * > & editor)2102 void MainWindow::saveProjectContents(QDomDocument projectDoc, QString blockName,
2103                                      QVector<DynamicEditor *> &editor) {
2104   int Nmax = editor.size();
2105 
2106   QDomElement editorBlock = projectDoc.createElement(blockName);
2107   projectDoc.documentElement().appendChild(editorBlock);
2108   int index = 0; // index excluding removed DynamicEditor instances
2109 
2110   for (int i = 0; i < Nmax; i++) {
2111     DynamicEditor *de = editor[i];
2112 
2113     if (de->menuAction == NULL)
2114       continue;
2115 
2116     // Menu item number:
2117     QDomElement item = projectDoc.createElement("item");
2118     item.setAttribute("index", QString::number(index++));
2119     editorBlock.appendChild(item);
2120 
2121     // Is active?
2122     QDomElement itemActive = projectDoc.createElement("active");
2123     QDomText itemActiveValue =
2124         projectDoc.createTextNode(QString::number(de->menuAction != NULL));
2125     itemActive.appendChild(itemActiveValue);
2126     item.appendChild(itemActive);
2127 
2128     // Name:
2129     if (de->menuAction != NULL) {
2130       QDomElement itemName = projectDoc.createElement("name");
2131       QDomText itemNameValue =
2132           projectDoc.createTextNode(de->nameEdit->text().trimmed());
2133       itemName.appendChild(itemNameValue);
2134       item.appendChild(itemName);
2135     }
2136 
2137     de->dumpHash(&projectDoc, &item);
2138   }
2139 }
2140 
2141 // File -> Load project...
2142 //-----------------------------------------------------------------------------
loadProjectSlot()2143 void MainWindow::loadProjectSlot() {
2144   QString defaultDirName = getDefaultDirName();
2145 
2146   QString projectDirName = QFileDialog::getExistingDirectory(
2147       this, tr("Open project directory"), defaultDirName);
2148 
2149   loadProject(projectDirName);
2150 }
2151 
loadProject(QString projectDirName)2152 void MainWindow::loadProject(QString projectDirName) {
2153   if (!projectDirName.isEmpty()) {
2154     logMessage("Project directory: " + projectDirName);
2155   } else {
2156     logMessage("Unable to load project: directory undefined");
2157     return;
2158   }
2159 
2160   QDir::setCurrent(projectDirName);
2161   saveDirName = projectDirName;
2162 
2163   progressBar->show();
2164   progressBar->setRange(0, 14);
2165 
2166   progressLabel->setText("Loading");
2167   progressLabel->show();
2168 
2169   // Clear previous data:
2170   //----------------------
2171   progressBar->setValue(1);
2172 
2173   logMessage("Clearing model data");
2174   modelClearSlot();
2175 
2176   // Re-initialize definitions and edfEditor
2177   delete elmerDefs;
2178   delete edfEditor;
2179   elmerDefs = new QDomDocument;
2180   edfEditor = new EdfEditor;
2181   loadDefinitions();
2182 
2183   // Load project doc:
2184   //-------------------
2185   progressBar->setValue(2);
2186 
2187   logMessage("Loading project document...");
2188   QDomDocument projectDoc;
2189   QString errStr;
2190   int errRow;
2191   int errCol;
2192   QFile projectFile("egproject.xml");
2193 
2194   if (!projectFile.exists()) {
2195     QMessageBox::information(window(), tr("Project loader"),
2196                              tr("Project file does not exist"));
2197 
2198     progressBar->hide();
2199     progressLabel->hide();
2200 
2201     return;
2202 
2203   } else {
2204 
2205     if (!projectDoc.setContent(&projectFile, true, &errStr, &errRow, &errCol)) {
2206       QMessageBox::information(window(), tr("Project loader"),
2207                                tr("Parse error at line %1, col %2:\n%3")
2208                                    .arg(errRow)
2209                                    .arg(errCol)
2210                                    .arg(errStr));
2211       projectFile.close();
2212 
2213       progressBar->hide();
2214       progressLabel->hide();
2215 
2216       return;
2217     }
2218   }
2219 
2220   projectFile.close();
2221 
2222   if (projectDoc.documentElement().tagName() != "contents") {
2223     QMessageBox::information(window(), tr("Project loader"),
2224                              tr("This is not a project file"));
2225 
2226     progressBar->hide();
2227     progressLabel->hide();
2228 
2229     return;
2230   }
2231 
2232   // load extra solvers from /edf-extra
2233   checkAndLoadExtraSolvers(&projectFile);
2234 
2235   setWindowTitle(QString("ElmerGUI - ") + projectDirName);
2236   addRecentProject(projectDirName, true);
2237   currentProjectDirName = projectDirName;
2238 
2239   QDomElement contents = projectDoc.documentElement();
2240 
2241   //===========================================================================
2242   //                                 LOAD MESH
2243   //===========================================================================
2244   progressBar->setValue(3);
2245   logMessage("Loading mesh files...");
2246   loadElmerMesh(projectDirName);
2247   resetSlot();
2248 
2249   //===========================================================================
2250   //                          LOAD GEOMETRY INPUT FILE
2251   //===========================================================================
2252   progressBar->setValue(4);
2253   cout << "Loading geometry input file" << endl;
2254   QDomElement geomInput = contents.firstChildElement("geometryinputfile");
2255   geometryInputFileName = projectDirName + "/" + geomInput.text().trimmed();
2256   logMessage("Geometry input file: " + geometryInputFileName);
2257   readInputFile(geometryInputFileName);
2258 
2259   //===========================================================================
2260   //                               LOAD OPERATIONS
2261   //===========================================================================
2262   logMessage("Loading operations...");
2263   progressBar->setValue(5);
2264   QDomElement ops = contents.firstChildElement("operations");
2265   operations = operation.readFromProject(&projectDoc, &ops);
2266 
2267   //===========================================================================
2268   //                            LOAD GENERAL SETUP
2269   //===========================================================================
2270   logMessage("Loading general setup...");
2271   progressBar->setValue(6);
2272   QDomElement gsBlock = contents.firstChildElement("generalsetup");
2273   generalSetup->readFromProject(&projectDoc, &gsBlock);
2274 
2275   //===========================================================================
2276   //                          LOAD PARALLEL SETTINGS
2277   //===========================================================================
2278   logMessage("Loading parallel settings...");
2279   progressBar->setValue(7);
2280   QDomElement paraBlock = contents.firstChildElement("parallelsettings");
2281   parallel->readFromProject(&projectDoc, &paraBlock);
2282 
2283   //===========================================================================
2284   //                            LOAD MESH PARAMETERS
2285   //===========================================================================
2286   logMessage("Loading mesh parameters...");
2287   progressBar->setValue(8);
2288   QDomElement meshParams = contents.firstChildElement("meshparameters");
2289   meshControl->readFromProject(&projectDoc, &meshParams);
2290 
2291   //===========================================================================
2292   //                          LOAD SOLVER PARAMETERS
2293   //===========================================================================
2294   logMessage("Loading solver parameters...");
2295   progressBar->setValue(9);
2296   QDomElement speBlock = contents.firstChildElement("solverparameters");
2297 
2298   QDomElement item = speBlock.firstChildElement("item");
2299   for (; !item.isNull(); item = item.nextSiblingElement()) {
2300     int index = item.attribute("index").toInt();
2301     QString name = item.attribute("name");
2302 
2303     if (name.trimmed().isEmpty())
2304       continue;
2305 
2306     // Find the real index for the current edf setup:
2307     int count = 0, realIndex = -1;
2308     QDomElement root = elmerDefs->documentElement();
2309     QDomElement elem = root.firstChildElement("PDE");
2310     while (!elem.isNull()) {
2311       QDomElement pdeName = elem.firstChildElement("Name");
2312       if (pdeName.text().trimmed() == name.trimmed())
2313         realIndex = count;
2314       elem = elem.nextSiblingElement();
2315       count++;
2316     }
2317 
2318     if (realIndex < 0) {
2319       cout << "ERROR: The current edf setup conflicts with the project. "
2320               "Aborting."
2321            << endl;
2322 
2323       progressBar->hide();
2324       progressLabel->hide();
2325 
2326       return;
2327     }
2328 
2329     index = realIndex - 1;
2330 
2331     if (index < 0) {
2332       logMessage("Load project: solver parameters: index out of bounds");
2333 
2334       progressBar->hide();
2335       progressLabel->hide();
2336 
2337       return;
2338     }
2339 
2340     if (index >= solverParameterEditor.size())
2341       solverParameterEditor.resize(index + 1);
2342 
2343     if (!solverParameterEditor[index])
2344       solverParameterEditor[index] = new SolverParameterEditor;
2345 
2346     SolverParameterEditor *spe = solverParameterEditor[index];
2347     spe->readFromProject(&projectDoc, &item);
2348   }
2349 
2350 #if 0
2351   // Changed the load order in 19 March 2009 for taking the "use as a body"
2352   // flags into account. The original boundary property loader is below.
2353   //
2354   // Changed back to original 23 March 2009. Todo...
2355   //===========================================================================
2356   //                          LOAD BOUNDARY PROPERTIES
2357   //===========================================================================
2358   progressBar->setValue(10);
2359   QDomElement boundaryBlock = contents.firstChildElement("boundaryproperties");
2360 
2361   item = boundaryBlock.firstChildElement("item");
2362   for( ; !item.isNull(); item = item.nextSiblingElement()) {
2363     int index = item.attribute("index").toInt();
2364 
2365     if(index < 0) {
2366       logMessage("Load project: boundary properties: index out of bounds");
2367 
2368       progressBar->hide();
2369       progressLabel->hide();
2370 
2371       return;
2372     }
2373 
2374     if(index >= boundaryPropertyEditor.size())
2375       boundaryPropertyEditor.resize(index + 1);
2376 
2377     if(!boundaryPropertyEditor[index])
2378       boundaryPropertyEditor[index] = new BoundaryPropertyEditor;
2379 
2380     BoundaryPropertyEditor *bpe = boundaryPropertyEditor[index];
2381 
2382     bpe->readFromProject(&projectDoc, &item);
2383 
2384     if(bpe->ui.boundaryAsABody->isChecked()) {
2385       connect(bpe, SIGNAL(BoundaryAsABodyChanged(BoundaryPropertyEditor*, int)),
2386 	      this, SLOT(boundaryAsABodyChanged(BoundaryPropertyEditor*, int)));
2387 
2388       populateBoundaryComboBoxes(bpe);
2389 
2390       bpe->ui.boundaryAsABody->toggle();
2391       bpe->ui.boundaryAsABody->toggle();
2392       bpe->ui.applyButton->click();
2393     }
2394   }
2395 #endif
2396 
2397   //===========================================================================
2398   //                        LOAD DYNAMIC EDITOR CONTENTS
2399   //===========================================================================
2400   logMessage("Loading dynamic editor contents...");
2401   progressBar->setValue(11);
2402   QDomElement element =
2403       projectDoc.documentElement().firstChildElement("equation");
2404   loadProjectContents(element, equationEditor, "Equation");
2405   element = projectDoc.documentElement().firstChildElement("material");
2406   loadProjectContents(element, materialEditor, "Material");
2407   element = projectDoc.documentElement().firstChildElement("bodyforce");
2408   loadProjectContents(element, bodyForceEditor, "BodyForce");
2409   element = projectDoc.documentElement().firstChildElement("initialcondition");
2410   loadProjectContents(element, initialConditionEditor, "InitialCondition");
2411   element = projectDoc.documentElement().firstChildElement("boundarycondition");
2412   loadProjectContents(element, boundaryConditionEditor, "BoundaryCondition");
2413 
2414   //===========================================================================
2415   //                          LOAD SOLVER SPECIFIC OPTIONS
2416   //===========================================================================
2417   logMessage("Loading solver specific options...");
2418   progressBar->setValue(12);
2419   QDomElement solverOptionsBlock =
2420       contents.firstChildElement("solverspecificoptions");
2421 
2422   for (item = solverOptionsBlock.firstChildElement("item"); !item.isNull();
2423        item = item.nextSiblingElement()) {
2424 
2425     int index = item.attribute("index").toInt();
2426     QString name = item.attribute("name");
2427     int id = item.attribute("id").toInt();
2428 
2429     if (name.trimmed().isEmpty())
2430       continue;
2431 
2432     // Find the real index for the current edf setup:
2433     int count = 0, realIndex = -1;
2434     QDomElement root = elmerDefs->documentElement();
2435     QDomElement elem = root.firstChildElement("PDE");
2436     while (!elem.isNull()) {
2437       QDomElement pdeName = elem.firstChildElement("Name");
2438       if (pdeName.text().trimmed() == name.trimmed())
2439         realIndex = count;
2440       elem = elem.nextSiblingElement();
2441       count++;
2442     }
2443 
2444     if (realIndex < 0) {
2445       cout << "ERROR: The current edf setup conflicts with the project. "
2446               "Aborting."
2447            << endl;
2448 
2449       progressBar->hide();
2450       progressLabel->hide();
2451 
2452       return;
2453     }
2454 
2455     index = realIndex - 1;
2456 
2457     if (index < 0) {
2458       logMessage("Load project: solver specific options: index out of bounds");
2459 
2460       progressBar->hide();
2461       progressLabel->hide();
2462 
2463       return;
2464     }
2465 
2466     if (index >= solverParameterEditor.size())
2467       solverParameterEditor.resize(index + 1);
2468 
2469     if (!solverParameterEditor[index])
2470       solverParameterEditor[index] = new SolverParameterEditor;
2471 
2472     SolverParameterEditor *spe = solverParameterEditor[index];
2473     spe->solverName = name;
2474 
2475     if (spe->generalOptions == NULL) {
2476       spe->generalOptions = new DynamicEditor;
2477 
2478       // following 3 lines were moved into if() block to avoid doubled "Solver
2479       // specific options" tabs (Nov 2019 by TS)
2480       spe->generalOptions->setupTabs(elmerDefs, "Solver", id);
2481       spe->generalOptions->populateHash(&item);
2482       spe->ui.solverControlTabs->insertTab(
2483           0, spe->generalOptions->tabWidget->widget(id),
2484           "Solver specific options");
2485     }
2486   }
2487 
2488   //===========================================================================
2489   //                           LOAD BODY PROPERTIES
2490   //===========================================================================
2491   logMessage("Loading body properties...");
2492   progressBar->setValue(13);
2493   QDomElement bodyBlock = contents.firstChildElement("bodyproperties");
2494 
2495   item = bodyBlock.firstChildElement("item");
2496   for (; !item.isNull(); item = item.nextSiblingElement()) {
2497     int index = item.attribute("index").toInt();
2498 
2499     if (index < 0) {
2500       logMessage("Load project: body properties: index out of bounds");
2501 
2502       progressBar->hide();
2503       progressLabel->hide();
2504 
2505       return;
2506     }
2507 
2508     if (index >= bodyPropertyEditor.size())
2509       bodyPropertyEditor.resize(index + 1);
2510 
2511     if (!bodyPropertyEditor[index])
2512       bodyPropertyEditor[index] = new BodyPropertyEditor;
2513 
2514     BodyPropertyEditor *bpe = bodyPropertyEditor[index];
2515     bpe->readFromProject(&projectDoc, &item);
2516   }
2517 
2518   //===========================================================================
2519   //                          LOAD BOUNDARY PROPERTIES
2520   //===========================================================================
2521   logMessage("Loading boundary properties...");
2522   progressBar->setValue(13);
2523   QDomElement boundaryBlock = contents.firstChildElement("boundaryproperties");
2524 
2525   item = boundaryBlock.firstChildElement("item");
2526   for (; !item.isNull(); item = item.nextSiblingElement()) {
2527     int index = item.attribute("index").toInt();
2528 
2529     if (index < 0) {
2530       logMessage("Load project: boundary properties: index out of bounds");
2531 
2532       progressBar->hide();
2533       progressLabel->hide();
2534 
2535       return;
2536     }
2537 
2538     if (index >= boundaryPropertyEditor.size())
2539       boundaryPropertyEditor.resize(index + 1);
2540 
2541     if (!boundaryPropertyEditor[index])
2542       boundaryPropertyEditor[index] = new BoundaryPropertyEditor;
2543 
2544     BoundaryPropertyEditor *bpe = boundaryPropertyEditor[index];
2545     bpe->readFromProject(&projectDoc, &item);
2546   }
2547 
2548   //===========================================================================
2549   //                              LOAD SIF
2550   //===========================================================================
2551   progressBar->setValue(14);
2552   if (glWidget->hasMesh()) {
2553       QFile file;
2554       QString sifName = generalSetup->ui.solverInputFileEdit->text().trimmed();
2555       file.setFileName(sifName);
2556       if (file.open(QIODevice::ReadOnly)) {
2557         QTextStream inputStream(&file);
2558         QString line = inputStream.readAll();
2559         file.close();
2560         sifWindow->getTextEdit()->clear();
2561         sifWindow->getTextEdit()->append(line);
2562         sifWindow->setFirstTime(true);
2563         sifWindow->setFound(false);
2564         logMessage(sifName + " loaded.");
2565       } else {
2566         logMessage(" failed to open " + sifName);
2567       }
2568   }
2569 
2570   logMessage("Ready");
2571 
2572   progressBar->hide();
2573   progressLabel->hide();
2574 }
2575 
2576 // Helper function for load project
2577 //--------------------------------------------------------------------------------------------
loadProjectContents(QDomElement projectElement,QVector<DynamicEditor * > & editor,QString Mname)2578 void MainWindow::loadProjectContents(QDomElement projectElement,
2579                                      QVector<DynamicEditor *> &editor,
2580                                      QString Mname) {
2581   int Nmax = editor.size();
2582 
2583   QDomElement item = projectElement.firstChildElement("item");
2584 
2585   for (; !item.isNull(); item = item.nextSiblingElement()) {
2586     int index = item.attribute("index").toInt();
2587 
2588     if (index < 0) {
2589       logMessage("Project loader: index out of bounds (dynamic editor)");
2590       return;
2591     }
2592 
2593     if (index >= editor.size())
2594       editor.resize(index + 1);
2595 
2596     if (!editor[index])
2597       editor[index] = new DynamicEditor;
2598 
2599     DynamicEditor *de = editor[index];
2600 
2601     bool active = (item.firstChildElement("active").text().toInt() > 0);
2602 
2603     if (!active)
2604       continue;
2605 
2606     // Set up dynamic editor and connect:
2607     //------------------------------------
2608     QString itemName = item.firstChildElement("name").text().trimmed();
2609 
2610     de->setupTabs(elmerDefs, Mname, index);
2611     de->nameEdit->setText(itemName);
2612     de->applyButton->setText("Update");
2613     de->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
2614     de->discardButton->setText("Remove");
2615     de->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
2616 
2617     const QString &tmpName = itemName;
2618     QAction *act = new QAction(tmpName, this);
2619 
2620     if (Mname == "Equation") {
2621       connect(de, SIGNAL(dynamicEditorReady(int, int)), this,
2622               SLOT(pdeEditorFinishedSlot(int, int)));
2623       de->spareButton->setText("Edit Solver Settings");
2624       de->spareButton->show();
2625       de->spareButton->setIcon(QIcon(":/icons/tools-wizard.png"));
2626       connect(de, SIGNAL(dynamicEditorSpareButtonClicked(int, int)), this,
2627               SLOT(editNumericalMethods(int, int)));
2628       equationMenu->addAction(act);
2629     }
2630 
2631     if (Mname == "Material") {
2632       connect(de, SIGNAL(dynamicEditorReady(int, int)), this,
2633               SLOT(matEditorFinishedSlot(int, int)));
2634       de->spareButton->setText("Material library");
2635       de->spareButton->show();
2636       de->spareButton->setIcon(QIcon(":/icons/tools-wizard.png"));
2637       connect(de, SIGNAL(dynamicEditorSpareButtonClicked(int, int)), this,
2638               SLOT(showMaterialLibrary(int, int)));
2639       materialMenu->addAction(act);
2640     }
2641 
2642     if (Mname == "BodyForce") {
2643       connect(de, SIGNAL(dynamicEditorReady(int, int)), this,
2644               SLOT(bodyForceEditorFinishedSlot(int, int)));
2645       bodyForceMenu->addAction(act);
2646     }
2647 
2648     if (Mname == "InitialCondition") {
2649       connect(de, SIGNAL(dynamicEditorReady(int, int)), this,
2650               SLOT(initialConditionEditorFinishedSlot(int, int)));
2651       initialConditionMenu->addAction(act);
2652     }
2653 
2654     if (Mname == "BoundaryCondition") {
2655       connect(de, SIGNAL(dynamicEditorReady(int, int)), this,
2656               SLOT(boundaryConditionEditorFinishedSlot(int, int)));
2657       boundaryConditionMenu->addAction(act);
2658     }
2659 
2660     de->menuAction = act;
2661 
2662     if (Mname == "Equation")
2663       createBodyCheckBoxes(BODY_EQUATION, de);
2664 
2665     if (Mname == "Material")
2666       createBodyCheckBoxes(BODY_MATERIAL, de);
2667 
2668     if (Mname == "BodyForce")
2669       createBodyCheckBoxes(BODY_FORCE, de);
2670 
2671     if (Mname == "InitialCondition")
2672       createBodyCheckBoxes(BODY_INITIAL, de);
2673 
2674     if (Mname == "BoundaryCondition")
2675       createBoundaryCheckBoxes(de);
2676 
2677     de->populateHash(&item);
2678   }
2679 }
2680 
2681 // Export mesh files in elmer-format:
2682 //-----------------------------------------------------------------------------
saveElmerMesh(QString dirName)2683 void MainWindow::saveElmerMesh(QString dirName) {
2684   logMessage("Saving elmer mesh files");
2685 
2686   QDir dir(dirName);
2687 
2688   if (!dir.exists())
2689     dir.mkdir(dirName);
2690 
2691   dir.setCurrent(dirName);
2692 
2693   // Save mesh files:
2694   //------------------
2695 #if WITH_QT5
2696   glWidget->getMesh()->save(dirName.toLatin1().data());
2697 #else
2698   glWidget->getMesh()->save(dirName.toAscii().data());
2699 #endif
2700 
2701   // Save solver input file:
2702   //-------------------------
2703   QFile file;
2704   QString sifName = generalSetup->ui.solverInputFileEdit->text().trimmed();
2705   file.setFileName(sifName);
2706   file.open(QIODevice::WriteOnly);
2707   QTextStream sif(&file);
2708 
2709   QApplication::setOverrideCursor(Qt::WaitCursor);
2710   sif << sifWindow->getTextEdit()->toPlainText();
2711   QApplication::restoreOverrideCursor();
2712 
2713   file.close();
2714 
2715   // Save ELMERSOLVER_STARTINFO:
2716   //-----------------------------
2717   file.setFileName("ELMERSOLVER_STARTINFO");
2718   file.open(QIODevice::WriteOnly);
2719   QTextStream startinfo(&file);
2720 
2721 #if WITH_QT5
2722   startinfo << sifName.toLatin1() << endl << "1" << endl;
2723 #else
2724   startinfo << sifName.toAscii() << endl << "1" << endl;
2725 #endif
2726 
2727   file.close();
2728 
2729   logMessage("Ready");
2730 }
2731 
2732 // File -> Exit
2733 //-----------------------------------------------------------------------------
closeMainWindowSlot()2734 void MainWindow::closeMainWindowSlot() {
2735   saveSlot();
2736   QApplication::closeAllWindows();
2737   // close();
2738 }
2739 
2740 // File -> Save picture as...
2741 //-----------------------------------------------------------------------------
savePictureSlot()2742 void MainWindow::savePictureSlot() {
2743   QString defaultDirName(getDefaultDirName());
2744 
2745   pictureFileName = QFileDialog::getSaveFileName(
2746       this, tr("Save picture"), defaultDirName,
2747       tr("Picture files (*.bmp *.jpg *.png *.pbm *.pgm *.ppm)"));
2748 
2749   if (pictureFileName.isEmpty()) {
2750     logMessage("File name is empty");
2751     return;
2752   }
2753 
2754   int delay = egIni->value("screenshotdelay").toInt();
2755 
2756   grabTimeLine->stop();
2757   grabTimeLine->setDuration(delay);
2758   grabTimeLine->setCurveShape(QTimeLine::LinearCurve);
2759   grabTimeLine->setDirection(QTimeLine::Backward);
2760   grabTimeLine->setFrameRange(0, 10);
2761   progressLabel->setText("Delay screen shot");
2762   progressLabel->show();
2763   progressBar->setRange(0, 10);
2764   progressBar->show();
2765   grabTimeLine->start();
2766 }
2767 
grabFrameSlot()2768 void MainWindow::grabFrameSlot() {
2769   progressLabel->hide();
2770   progressBar->hide();
2771 
2772   if (pictureFileName.isEmpty()) {
2773     logMessage("Unable to take screen shot - file name is empty");
2774     return;
2775   }
2776 
2777   QFileInfo fi(pictureFileName);
2778   QString suffix(fi.suffix());
2779   suffix.toUpper();
2780 
2781   int imageQuality(egIni->value("defaultimagequality").toInt());
2782 
2783   bool withAlpha(false);
2784 
2785   glWidget->updateGL();
2786   glReadBuffer(GL_BACK);
2787 
2788   QImage image(glWidget->grabFrameBuffer(withAlpha));
2789 
2790 #if WITH_QT5
2791   bool success(image.save(pictureFileName, suffix.toLatin1(), imageQuality));
2792 #else
2793   bool success(image.save(pictureFileName, suffix.toAscii(), imageQuality));
2794 #endif
2795 
2796   if (!success)
2797     logMessage("Failed writing picture file");
2798 }
2799 
2800 //*****************************************************************************
2801 //
2802 //                                Model MENU
2803 //
2804 //*****************************************************************************
2805 
2806 // Model -> Setup...
2807 //-----------------------------------------------------------------------------
modelSetupSlot()2808 void MainWindow::modelSetupSlot() { generalSetup->show(); }
2809 
2810 //-----------------------------------------------------------------------------
createBodyCheckBoxes(int which,DynamicEditor * pe)2811 void MainWindow::createBodyCheckBoxes(int which, DynamicEditor *pe) {
2812   if (!glWidget->hasMesh())
2813     return;
2814 
2815   if (pe->spareScroll->widget())
2816     delete pe->spareScroll->widget();
2817 
2818   QGridLayout *slayout = new QGridLayout;
2819   QLabel *l = new QLabel(tr("Apply to bodies:"));
2820 
2821   int count = 0, even = 0;
2822 
2823   slayout->addWidget(l, count, 0);
2824   count++;
2825 
2826   QMapIterator<int, int> itr(glWidget->bodyMap);
2827   while (itr.hasNext()) {
2828     itr.next();
2829     int n = itr.key();
2830     if (n >= 0) {
2831       int m = itr.value();
2832 
2833       if (m >= bodyPropertyEditor.size())
2834         bodyPropertyEditor.resize(m + 1);
2835 
2836       if (!bodyPropertyEditor[m])
2837         bodyPropertyEditor[m] = new BodyPropertyEditor;
2838 
2839       BodyPropertyEditor *body = bodyPropertyEditor[m];
2840 
2841       populateBodyComboBoxes(body);
2842 
2843       QString title = body->ui.nameEdit->text().trimmed();
2844       QCheckBox *a;
2845 
2846       if (title.isEmpty())
2847         // a = new QCheckBox("Body " + QString::number(n));
2848         a = new QCheckBox("Body Property " + QString::number(n));
2849       else
2850         a = new QCheckBox(title);
2851 
2852       DynamicEditor *p = NULL;
2853 
2854       switch (which) {
2855       case BODY_MATERIAL:
2856         p = body->material;
2857         connect(a, SIGNAL(stateChanged(int)), this,
2858                 SLOT(materialBodyChanged(int)));
2859         break;
2860       case BODY_INITIAL:
2861         p = body->initial;
2862         connect(a, SIGNAL(stateChanged(int)), this,
2863                 SLOT(initialBodyChanged(int)));
2864         break;
2865       case BODY_FORCE:
2866         p = body->force;
2867         connect(a, SIGNAL(stateChanged(int)), this,
2868                 SLOT(forceBodyChanged(int)));
2869         break;
2870       case BODY_EQUATION:
2871         p = body->equation;
2872         connect(a, SIGNAL(stateChanged(int)), this,
2873                 SLOT(equationBodyChanged(int)));
2874         break;
2875       }
2876 
2877       a->setProperty("body", (qulonglong)body);
2878       a->setProperty("editor", (qulonglong)pe);
2879 
2880       if (p == pe)
2881         a->setChecked(true);
2882       else if (p != NULL)
2883         a->setEnabled(false);
2884       else
2885         a->setChecked(false);
2886 
2887       slayout->addWidget(a, count, even);
2888       even = 1 - even;
2889       if (!even)
2890         count++;
2891     }
2892   }
2893 
2894   for (int i = 0; i < boundaryPropertyEditor.size(); i++) {
2895     BoundaryPropertyEditor *boundary = boundaryPropertyEditor[i];
2896 
2897     if (!boundary)
2898       continue;
2899 
2900     if (boundary->bodyProperties) {
2901       BodyPropertyEditor *body = boundary->bodyProperties;
2902       populateBodyComboBoxes(body);
2903 
2904       QString title = body->ui.nameEdit->text().trimmed();
2905       QCheckBox *a;
2906 
2907       if (title.isEmpty())
2908         a = new QCheckBox("Body{Boundary " + QString::number(i) + "}");
2909       else
2910         a = new QCheckBox(title);
2911 
2912       DynamicEditor *p = NULL;
2913 
2914       switch (which) {
2915       case BODY_MATERIAL:
2916         p = body->material;
2917         connect(a, SIGNAL(stateChanged(int)), this,
2918                 SLOT(materialBodyChanged(int)));
2919         break;
2920       case BODY_INITIAL:
2921         p = body->initial;
2922         connect(a, SIGNAL(stateChanged(int)), this,
2923                 SLOT(initialBodyChanged(int)));
2924         break;
2925       case BODY_FORCE:
2926         p = body->force;
2927         connect(a, SIGNAL(stateChanged(int)), this,
2928                 SLOT(forceBodyChanged(int)));
2929         break;
2930       case BODY_EQUATION:
2931         p = body->equation;
2932         connect(a, SIGNAL(stateChanged(int)), this,
2933                 SLOT(equationBodyChanged(int)));
2934         break;
2935       }
2936 
2937       a->setProperty("body", (qulonglong)body);
2938       a->setProperty("editor", (qulonglong)pe);
2939 
2940       if (p == pe)
2941         a->setChecked(true);
2942       else if (p != NULL)
2943         a->setEnabled(false);
2944 
2945       slayout->addWidget(a, count, even);
2946       even = 1 - even;
2947       if (!even)
2948         count++;
2949     }
2950   }
2951 
2952   QGroupBox *box = new QGroupBox;
2953   box->setLayout(slayout);
2954 
2955   pe->spareScroll->setWidget(box);
2956   pe->spareScroll->setMinimumHeight(80);
2957   pe->spareScroll->show();
2958 }
2959 
2960 //-----------------------------------------------------------------------------
2961 
2962 //*****************************************************************************
2963 
2964 // Model -> Equation -> Add...
2965 //-----------------------------------------------------------------------------
addEquationSlot()2966 void MainWindow::addEquationSlot() {
2967   DynamicEditor *pe = new DynamicEditor;
2968   equationEditor.append(pe);
2969   int current = equationEditor.size() - 1;
2970 
2971   pe->setupTabs(elmerDefs, "Equation", current);
2972 
2973   pe->applyButton->setText("Add");
2974   pe->applyButton->setIcon(QIcon(":/icons/list-add.png"));
2975   pe->discardButton->setText("Cancel");
2976   pe->discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
2977   pe->show();
2978 
2979   connect(pe, SIGNAL(dynamicEditorReady(int, int)), this,
2980           SLOT(pdeEditorFinishedSlot(int, int)));
2981 
2982   // Use "spareButton" to invoke solver parameter editor:
2983   pe->spareButton->setText("Edit Solver Settings");
2984   pe->spareButton->show();
2985   pe->spareButton->setIcon(QIcon(":/icons/tools-wizard.png"));
2986   connect(pe, SIGNAL(dynamicEditorSpareButtonClicked(int, int)), this,
2987           SLOT(editNumericalMethods(int, int)));
2988 
2989   // Equation is new - add to menu:
2990   const QString &equationName = pe->nameEdit->text().trimmed();
2991   QAction *act = new QAction(equationName, this);
2992   equationMenu->addAction(act);
2993   pe->menuAction = act;
2994 
2995   connect(pe->nameEdit, SIGNAL(textChanged(QString)), this,
2996           SLOT(dynamicEditorNameChange(QString)));
2997 
2998   createBodyCheckBoxes(BODY_EQUATION, pe);
2999 }
3000 
3001 // signal (int, int) emitted by dynamic editor when "spare button" clicked:
3002 //-----------------------------------------------------------------------------
editNumericalMethods(int current,int id)3003 void MainWindow::editNumericalMethods(int current, int id) {
3004   QString title = "";
3005 
3006   for (int i = 0; i < equationEditor.size(); i++) {
3007     // ** 23/04/09 **
3008     if (equationEditor[i]->ID == id) {
3009       title = equationEditor[i]->tabWidget->tabText(current);
3010       break;
3011     }
3012   }
3013 
3014   if (title == "General") {
3015     logMessage("No solver controls for 'General' equation options");
3016     return;
3017   }
3018 
3019   if (current >= solverParameterEditor.size())
3020     solverParameterEditor.resize(current + 1);
3021 
3022   if (!solverParameterEditor[current])
3023     solverParameterEditor[current] = new SolverParameterEditor;
3024 
3025   SolverParameterEditor *spe = solverParameterEditor[current];
3026 
3027   spe->setWindowTitle("Solver control for " + title);
3028 
3029   spe->solverName = title;
3030 
3031   if (spe->generalOptions == NULL) {
3032     spe->generalOptions = new DynamicEditor(spe);
3033     spe->generalOptions->setupTabs(elmerDefs, "Solver", current);
3034     spe->ui.solverControlTabs->insertTab(
3035         0, spe->generalOptions->tabWidget->widget(current),
3036         "Solver specific options");
3037 
3038 #if 0
3039     for( int i=0; i < spe->generalOptions->tabWidget->count(); i++ )
3040       {
3041 	if ( spe->generalOptions->tabWidget->tabText(i) == title )
3042 	  {
3043 	    spe->ui.solverControlTabs->insertTab(0, spe->generalOptions->tabWidget->widget(i),
3044 						 "Solver specific options");
3045 	    break;
3046 	  }
3047       }
3048 #endif
3049   }
3050 
3051   spe->show();
3052   spe->raise();
3053 }
3054 
dynamicEditorNameChange(QString t)3055 void MainWindow::dynamicEditorNameChange(QString t) {
3056   for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3057     if (!bodyPropertyEditor[i])
3058       continue;
3059 
3060     if (bodyPropertyEditor[i]->touched)
3061       populateBodyComboBoxes(bodyPropertyEditor[i]);
3062   }
3063 
3064   for (int i = 0; i < boundaryPropertyEditor.size(); i++) {
3065     if (!boundaryPropertyEditor[i])
3066       continue;
3067 
3068     if (boundaryPropertyEditor[i]->touched)
3069       populateBoundaryComboBoxes(boundaryPropertyEditor[i]);
3070   }
3071 }
3072 
3073 // signal (int,int) emitted by equation editor when ready:
3074 //-----------------------------------------------------------------------------
pdeEditorFinishedSlot(int signal,int id)3075 void MainWindow::pdeEditorFinishedSlot(int signal, int id) {
3076   DynamicEditor *pe = equationEditor[id];
3077 
3078   const QString &equationName = pe->nameEdit->text().trimmed();
3079 
3080   bool signalOK = signal == MAT_OK || signal == MAT_APPLY;
3081 
3082   if ((equationName.isEmpty()) && signalOK) {
3083     logMessage("Refusing to add/update equation without name");
3084     return;
3085   }
3086 
3087   if (signalOK) {
3088     if (pe->menuAction != NULL) {
3089       pe->menuAction->setText(equationName);
3090       logMessage("Equation updated");
3091       if (signal == MAT_OK)
3092         pe->close();
3093     }
3094   } else if (signal == MAT_NEW) {
3095     addEquationSlot();
3096 
3097   } else if (signal == MAT_DELETE) {
3098 
3099     for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3100       BodyPropertyEditor *body = bodyPropertyEditor[i];
3101 
3102       if (!body)
3103         continue;
3104 
3105       if (body->equation == pe) {
3106         body->equation = NULL;
3107         body->ui.equationCombo->setCurrentIndex(0);
3108         body->touched = true;
3109       }
3110     }
3111 
3112     // Equation is not in menu:
3113     if (pe->menuAction == NULL) {
3114       logMessage("Ready");
3115       pe->close();
3116       return;
3117     }
3118 
3119     // Delete from menu:
3120     delete pe->menuAction;
3121     pe->menuAction = NULL;
3122     pe->close();
3123 
3124     pe->ID = -100;
3125     pe->nameEdit->setText("");
3126 
3127     logMessage("Equation deleted");
3128   }
3129 }
3130 
3131 // signal (QAction*) emitted by equationMenu when an item has been selected:
3132 //-----------------------------------------------------------------------------
equationSelectedSlot(QAction * act)3133 void MainWindow::equationSelectedSlot(QAction *act) {
3134   // Edit the selected material:
3135   for (int i = 0; i < equationEditor.size(); i++) {
3136     DynamicEditor *pe = equationEditor[i];
3137     if (pe->menuAction == act) {
3138       pe->applyButton->setText("Update");
3139       pe->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
3140       pe->discardButton->setText("Remove");
3141       pe->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
3142       createBodyCheckBoxes(BODY_EQUATION, pe);
3143       pe->show();
3144       pe->raise();
3145     }
3146   }
3147 }
3148 
3149 //-----------------------------------------------------------------------------
equationBodyChanged(int state)3150 void MainWindow::equationBodyChanged(int state) {
3151   QWidget *a = (QWidget *)QObject::sender();
3152   if (glWidget->getMesh()) {
3153     BodyPropertyEditor *body =
3154         (BodyPropertyEditor *)a->property("body").toULongLong();
3155     populateBodyComboBoxes(body);
3156     if (state) {
3157       DynamicEditor *mat = (DynamicEditor *)a->property("editor").toULongLong();
3158       QString mat_name = mat->nameEdit->text().trimmed();
3159       int ind = body->ui.equationCombo->findText(mat_name);
3160       body->touched = true;
3161       body->equation = mat;
3162       body->ui.equationCombo->setCurrentIndex(ind);
3163     } else {
3164       body->equation = NULL;
3165       body->ui.equationCombo->setCurrentIndex(-1);
3166     }
3167   }
3168 }
3169 
3170 //*****************************************************************************
3171 
3172 // Model -> Material -> Add...
3173 //-----------------------------------------------------------------------------
addMaterialSlot()3174 void MainWindow::addMaterialSlot() {
3175   DynamicEditor *pe = new DynamicEditor;
3176   materialEditor.append(pe);
3177   int current = materialEditor.size() - 1;
3178 
3179   pe->setupTabs(elmerDefs, "Material", current);
3180   pe->applyButton->setText("Add");
3181   pe->applyButton->setIcon(QIcon(":/icons/list-add.png"));
3182   pe->discardButton->setText("Cancel");
3183   pe->discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
3184 
3185   connect(pe, SIGNAL(dynamicEditorReady(int, int)), this,
3186           SLOT(matEditorFinishedSlot(int, int)));
3187 
3188   // Use "spareButton" to invoke material library:
3189   pe->spareButton->setText("Material library");
3190   pe->spareButton->show();
3191   pe->spareButton->setIcon(QIcon(":/icons/tools-wizard.png"));
3192   connect(pe, SIGNAL(dynamicEditorSpareButtonClicked(int, int)), this,
3193           SLOT(showMaterialLibrary(int, int)));
3194 
3195   connect(pe->nameEdit, SIGNAL(textChanged(QString)), this,
3196           SLOT(dynamicEditorNameChange(QString)));
3197 
3198   // Material is new - add to menu:
3199   const QString &materialName = pe->nameEdit->text().trimmed();
3200   QAction *act = new QAction(materialName, this);
3201   materialMenu->addAction(act);
3202   pe->menuAction = act;
3203 
3204   createBodyCheckBoxes(BODY_MATERIAL, pe);
3205   pe->show();
3206   pe->raise();
3207 }
3208 
showMaterialLibrary(int tab,int ID)3209 void MainWindow::showMaterialLibrary(int tab, int ID) {
3210   materialLibrary->editor = materialEditor[ID];
3211   materialLibrary->elmerDefs = this->elmerDefs;
3212   materialLibrary->show();
3213 }
3214 
3215 // signal (int,int) emitted by material editor when ready:
3216 //-----------------------------------------------------------------------------
matEditorFinishedSlot(int signal,int id)3217 void MainWindow::matEditorFinishedSlot(int signal, int id) {
3218   DynamicEditor *pe = materialEditor[id];
3219 
3220   const QString &materialName = pe->nameEdit->text().trimmed();
3221 
3222   bool signalOK = signal == MAT_OK || signal == MAT_APPLY;
3223   if (materialName.isEmpty() && signalOK) {
3224     logMessage("Refusing to add/update material with no name");
3225     return;
3226   }
3227 
3228   if (signalOK) {
3229     if (pe->menuAction != NULL) {
3230       pe->menuAction->setText(materialName);
3231       logMessage("Material updated");
3232       if (signal == MAT_OK)
3233         pe->close();
3234       return;
3235     }
3236   } else if (signal == MAT_NEW) {
3237 
3238     addMaterialSlot();
3239 
3240   } else if (signal == MAT_DELETE) {
3241 
3242     for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3243       BodyPropertyEditor *body = bodyPropertyEditor[i];
3244 
3245       if (!body)
3246         continue;
3247 
3248       if (body->material == pe) {
3249         body->material = NULL;
3250         body->ui.materialCombo->setCurrentIndex(0);
3251         body->touched = true;
3252       }
3253     }
3254 
3255     // Material is not in menu:
3256     if (pe->menuAction == NULL) {
3257       logMessage("Ready");
3258       pe->close();
3259       return;
3260     }
3261 
3262     // Delete from menu:
3263     delete pe->menuAction;
3264     pe->menuAction = NULL;
3265     pe->close();
3266 
3267     pe->ID = -100;
3268     pe->nameEdit->setText("");
3269 
3270     logMessage("Material deleted");
3271 
3272   } else {
3273     cout << "Matedit: unknown signal" << endl;
3274   }
3275 }
3276 
3277 // signal (QAction*) emitted by materialMenu when an item has been selected:
3278 //-----------------------------------------------------------------------------
materialSelectedSlot(QAction * act)3279 void MainWindow::materialSelectedSlot(QAction *act) {
3280   // Edit the selected material:
3281   for (int i = 0; i < materialEditor.size(); i++) {
3282     DynamicEditor *pe = materialEditor[i];
3283 
3284     if (pe->menuAction == act) {
3285       pe->applyButton->setText("Update");
3286       pe->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
3287       pe->discardButton->setText("Remove");
3288       pe->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
3289       createBodyCheckBoxes(BODY_MATERIAL, pe);
3290       pe->show();
3291       pe->raise();
3292     }
3293   }
3294 }
3295 
materialBodyChanged(int state)3296 void MainWindow::materialBodyChanged(int state) {
3297   QWidget *a = (QWidget *)QObject::sender();
3298   if (glWidget->hasMesh()) {
3299     BodyPropertyEditor *body =
3300         (BodyPropertyEditor *)a->property("body").toULongLong();
3301     populateBodyComboBoxes(body);
3302 
3303     if (state > 0) {
3304       DynamicEditor *mat = (DynamicEditor *)a->property("editor").toULongLong();
3305       QString mat_name = mat->nameEdit->text().trimmed();
3306       int ind = body->ui.materialCombo->findText(mat_name);
3307 
3308       body->touched = true;
3309       body->material = mat;
3310       body->ui.materialCombo->setCurrentIndex(ind);
3311     } else {
3312       body->material = NULL;
3313       body->ui.materialCombo->setCurrentIndex(-1);
3314     }
3315   }
3316 }
3317 
3318 //*****************************************************************************
3319 
3320 // Model -> Body force -> Add...
3321 //-----------------------------------------------------------------------------
addBodyForceSlot()3322 void MainWindow::addBodyForceSlot() {
3323   DynamicEditor *pe = new DynamicEditor;
3324   bodyForceEditor.append(pe);
3325   int current = bodyForceEditor.size() - 1;
3326 
3327   pe->setupTabs(elmerDefs, "BodyForce", current);
3328 
3329   pe->applyButton->setText("Add");
3330   pe->applyButton->setIcon(QIcon(":/icons/list-add.png"));
3331   pe->discardButton->setText("Cancel");
3332   pe->discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
3333 
3334   connect(pe, SIGNAL(dynamicEditorReady(int, int)), this,
3335           SLOT(bodyForceEditorFinishedSlot(int, int)));
3336 
3337   // Body force is new - add to menu:
3338   const QString &bodyForceName = pe->nameEdit->text().trimmed();
3339   QAction *act = new QAction(bodyForceName, this);
3340   bodyForceMenu->addAction(act);
3341   pe->menuAction = act;
3342 
3343   connect(pe->nameEdit, SIGNAL(textChanged(QString)), this,
3344           SLOT(dynamicEditorNameChange(QString)));
3345 
3346   createBodyCheckBoxes(BODY_FORCE, pe);
3347   pe->show();
3348   pe->raise();
3349 }
3350 
3351 // signal (int,int) emitted by body force editor when ready:
3352 //-----------------------------------------------------------------------------
bodyForceEditorFinishedSlot(int signal,int id)3353 void MainWindow::bodyForceEditorFinishedSlot(int signal, int id) {
3354   DynamicEditor *pe = bodyForceEditor[id];
3355 
3356   const QString &bodyForceName = pe->nameEdit->text().trimmed();
3357 
3358   bool signalOK = signal == MAT_OK || signal == MAT_APPLY;
3359 
3360   if ((bodyForceName.isEmpty()) && signalOK) {
3361     logMessage("Refusing to add/update body force with no name");
3362     return;
3363   }
3364 
3365   if (signalOK) {
3366     if (pe->menuAction != NULL) {
3367       pe->menuAction->setText(bodyForceName);
3368       logMessage("Body force updated");
3369       if (signal == MAT_OK)
3370         pe->close();
3371     }
3372 
3373   } else if (signal == MAT_NEW) {
3374     addBodyForceSlot();
3375 
3376   } else if (signal == MAT_DELETE) {
3377     for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3378       BodyPropertyEditor *body = bodyPropertyEditor[i];
3379 
3380       if (!body)
3381         continue;
3382 
3383       if (body->force == pe) {
3384         body->force = NULL;
3385         body->ui.bodyForceCombo->setCurrentIndex(0);
3386         body->touched = true;
3387       }
3388     }
3389 
3390     if (pe->menuAction == NULL) {
3391       logMessage("Ready");
3392       pe->close();
3393       return;
3394     }
3395 
3396     // Delete from menu:
3397     delete pe->menuAction;
3398     pe->menuAction = NULL;
3399     pe->close();
3400 
3401     pe->ID = -100;
3402     pe->nameEdit->setText("");
3403 
3404     logMessage("Body force deleted");
3405   }
3406 }
3407 
3408 // signal (QAction*) emitted by bodyForceMenu when an item has been selected:
3409 //-----------------------------------------------------------------------------
bodyForceSelectedSlot(QAction * act)3410 void MainWindow::bodyForceSelectedSlot(QAction *act) {
3411   // Edit the selected body force:
3412   for (int i = 0; i < bodyForceEditor.size(); i++) {
3413     DynamicEditor *pe = bodyForceEditor[i];
3414     if (pe->menuAction == act) {
3415       pe->applyButton->setText("Update");
3416       pe->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
3417       pe->discardButton->setText("Remove");
3418       pe->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
3419       createBodyCheckBoxes(BODY_FORCE, pe);
3420       pe->show();
3421       pe->raise();
3422     }
3423   }
3424 }
3425 
3426 //-----------------------------------------------------------------------------
forceBodyChanged(int state)3427 void MainWindow::forceBodyChanged(int state) {
3428   QWidget *a = (QWidget *)QObject::sender();
3429   if (glWidget->hasMesh()) {
3430     BodyPropertyEditor *body =
3431         (BodyPropertyEditor *)a->property("body").toULongLong();
3432     populateBodyComboBoxes(body);
3433 
3434     if (state) {
3435       DynamicEditor *mat = (DynamicEditor *)a->property("editor").toULongLong();
3436       QString mat_name = mat->nameEdit->text().trimmed();
3437       int ind = body->ui.bodyForceCombo->findText(mat_name);
3438 
3439       body->touched = true;
3440       body->force = mat;
3441       body->ui.bodyForceCombo->setCurrentIndex(ind);
3442     } else {
3443       body->force = NULL;
3444       body->ui.bodyForceCombo->setCurrentIndex(-1);
3445     }
3446   }
3447 }
3448 
3449 //*****************************************************************************
3450 
3451 // Model -> Initial condition -> Add...
3452 //-----------------------------------------------------------------------------
addInitialConditionSlot()3453 void MainWindow::addInitialConditionSlot() {
3454   DynamicEditor *pe = new DynamicEditor;
3455   initialConditionEditor.append(pe);
3456   int current = initialConditionEditor.size() - 1;
3457 
3458   pe->setupTabs(elmerDefs, "InitialCondition", current);
3459 
3460   pe->applyButton->setText("Add");
3461   pe->applyButton->setIcon(QIcon(":/icons/list-add.png"));
3462   pe->discardButton->setText("Cancel");
3463   pe->discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
3464 
3465   connect(pe, SIGNAL(dynamicEditorReady(int, int)), this,
3466           SLOT(initialConditionEditorFinishedSlot(int, int)));
3467 
3468   // Initial condition is new - add to menu:
3469   const QString &initialConditionName = pe->nameEdit->text().trimmed();
3470   QAction *act = new QAction(initialConditionName, this);
3471   initialConditionMenu->addAction(act);
3472   pe->menuAction = act;
3473 
3474   connect(pe->nameEdit, SIGNAL(textChanged(QString)), this,
3475           SLOT(dynamicEditorNameChange(QString)));
3476 
3477   createBodyCheckBoxes(BODY_INITIAL, pe);
3478   pe->show();
3479   pe->raise();
3480 }
3481 
3482 // signal (int,int) emitted by initial condition editor when ready:
3483 //-----------------------------------------------------------------------------
initialConditionEditorFinishedSlot(int signal,int id)3484 void MainWindow::initialConditionEditorFinishedSlot(int signal, int id) {
3485   DynamicEditor *pe = initialConditionEditor[id];
3486 
3487   const QString &initialConditionName = pe->nameEdit->text().trimmed();
3488 
3489   bool signalOK = signal == MAT_OK || signal == MAT_APPLY;
3490   if ((initialConditionName.isEmpty()) && signalOK) {
3491     logMessage("Refusing to add/update initial condition with no name");
3492     return;
3493   }
3494 
3495   if (signalOK) {
3496     if (pe->menuAction != NULL) {
3497       pe->menuAction->setText(initialConditionName);
3498       logMessage("Initial condition updated");
3499       if (signal == MAT_OK)
3500         pe->close();
3501     }
3502   } else if (signal == MAT_NEW) {
3503     addInitialConditionSlot();
3504 
3505   } else if (signal == MAT_DELETE) {
3506 
3507     for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3508       BodyPropertyEditor *body = bodyPropertyEditor[i];
3509 
3510       if (!body)
3511         continue;
3512 
3513       if (body->initial == pe) {
3514         body->initial = NULL;
3515         body->ui.initialConditionCombo->setCurrentIndex(0);
3516         body->touched = true;
3517       }
3518     }
3519 
3520     // Initial condition is not in menu:
3521     if (pe->menuAction == NULL) {
3522       logMessage("Ready");
3523       pe->close();
3524       return;
3525     }
3526 
3527     // Delete from menu:
3528     delete pe->menuAction;
3529     pe->menuAction = NULL;
3530     pe->close();
3531 
3532     pe->ID = -100;
3533     pe->nameEdit->setText("");
3534 
3535     logMessage("Initial condition deleted");
3536   }
3537 }
3538 
3539 // signal (QAction*) emitted by initialConditionMenu when item selected:
3540 //-----------------------------------------------------------------------------
initialConditionSelectedSlot(QAction * act)3541 void MainWindow::initialConditionSelectedSlot(QAction *act) {
3542   // Edit the selected initial condition:
3543   for (int i = 0; i < initialConditionEditor.size(); i++) {
3544     DynamicEditor *pe = initialConditionEditor[i];
3545     if (pe->menuAction == act) {
3546       pe->applyButton->setText("Update");
3547       pe->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
3548       pe->discardButton->setText("Remove");
3549       pe->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
3550       createBodyCheckBoxes(BODY_INITIAL, pe);
3551       pe->show();
3552       pe->raise();
3553     }
3554   }
3555 }
3556 
3557 //-----------------------------------------------------------------------------
initialBodyChanged(int state)3558 void MainWindow::initialBodyChanged(int state) {
3559   QWidget *a = (QWidget *)QObject::sender();
3560   if (glWidget->hasMesh()) {
3561     BodyPropertyEditor *body =
3562         (BodyPropertyEditor *)a->property("body").toULongLong();
3563     populateBodyComboBoxes(body);
3564 
3565     if (state) {
3566       DynamicEditor *mat = (DynamicEditor *)a->property("editor").toULongLong();
3567       QString mat_name = mat->nameEdit->text().trimmed();
3568       int ind = body->ui.initialConditionCombo->findText(mat_name);
3569       body->touched = true;
3570       body->initial = mat;
3571       body->ui.initialConditionCombo->setCurrentIndex(ind);
3572     } else {
3573       body->initial = NULL;
3574       body->ui.initialConditionCombo->setCurrentIndex(-1);
3575     }
3576   }
3577 }
3578 
3579 //*****************************************************************************
3580 //-----------------------------------------------------------------------------
createBoundaryCheckBoxes(DynamicEditor * pe)3581 void MainWindow::createBoundaryCheckBoxes(DynamicEditor *pe) {
3582   if (!glWidget->hasMesh())
3583     return;
3584 
3585   if (pe->spareScroll->widget()) {
3586     delete pe->spareScroll->widget();
3587   }
3588 
3589   QGridLayout *slayout = new QGridLayout;
3590   QLabel *l = new QLabel(tr("Apply to boundaries:"));
3591   int count = 0, even = 0;
3592 
3593   slayout->addWidget(l, count, 0);
3594   count++;
3595 
3596   QMapIterator<int, int> itr(glWidget->boundaryMap);
3597   while (itr.hasNext()) {
3598     itr.next();
3599     int n = itr.key();
3600     if (n >= 0) {
3601       int m = itr.value();
3602 
3603       if (m >= boundaryPropertyEditor.size())
3604         boundaryPropertyEditor.resize(m + 1);
3605 
3606       if (!boundaryPropertyEditor[m])
3607         boundaryPropertyEditor[m] = new BoundaryPropertyEditor;
3608 
3609       BoundaryPropertyEditor *boundary = boundaryPropertyEditor[m];
3610 
3611       populateBoundaryComboBoxes(boundary);
3612 
3613       // TODO: check this
3614       QString title = ""; // boundary->ui.nameEdit->text().trimmed();
3615       QCheckBox *a;
3616 
3617       if (title.isEmpty())
3618         a = new QCheckBox("Boundary " + QString::number(n));
3619       else
3620         a = new QCheckBox(title);
3621 
3622       if (glWidget->stateBcColors) {
3623         int c[3];
3624         QPixmap pm(16, 16);
3625 
3626         GLWidget::indexColors(c, n);
3627         pm.fill(qRgb(c[0], c[1], c[2]));
3628         a->setIcon(QIcon(pm));
3629       }
3630 
3631       DynamicEditor *p = NULL;
3632 
3633       p = boundary->condition;
3634       connect(a, SIGNAL(stateChanged(int)), this, SLOT(bcBoundaryChanged(int)));
3635 
3636       a->setProperty("boundary", (qulonglong)boundary);
3637       a->setProperty("condition", (qulonglong)pe);
3638 
3639       if (p == pe)
3640         a->setChecked(true);
3641       else if (p != NULL)
3642         a->setEnabled(false);
3643 
3644       slayout->addWidget(a, count, even);
3645       even = 1 - even;
3646       if (!even)
3647         count++;
3648     }
3649   }
3650 
3651   QGroupBox *box = new QGroupBox;
3652   box->setLayout(slayout);
3653 
3654   pe->spareScroll->setWidget(box);
3655   pe->spareScroll->setMinimumHeight(80);
3656   pe->spareScroll->show();
3657 }
3658 
3659 //-----------------------------------------------------------------------------
3660 
3661 // Model -> Boundary condition -> Add...
3662 //-----------------------------------------------------------------------------
addBoundaryConditionSlot()3663 void MainWindow::addBoundaryConditionSlot() {
3664   DynamicEditor *pe = new DynamicEditor;
3665   boundaryConditionEditor.append(pe);
3666   int current = boundaryConditionEditor.size() - 1;
3667 
3668   pe->setupTabs(elmerDefs, "BoundaryCondition", current);
3669 
3670   pe->applyButton->setText("Add");
3671   pe->applyButton->setIcon(QIcon(":/icons/list-add.png"));
3672   pe->discardButton->setText("Cancel");
3673   pe->discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
3674   pe->show();
3675 
3676   connect(pe, SIGNAL(dynamicEditorReady(int, int)), this,
3677           SLOT(boundaryConditionEditorFinishedSlot(int, int)));
3678 
3679   // Boundary condition is new - add to menu:
3680   const QString &boundaryConditionName = pe->nameEdit->text().trimmed();
3681   QAction *act = new QAction(boundaryConditionName, this);
3682   boundaryConditionMenu->addAction(act);
3683   pe->menuAction = act;
3684 
3685   connect(pe->nameEdit, SIGNAL(textChanged(QString)), this,
3686           SLOT(dynamicEditorNameChange(QString)));
3687 
3688   createBoundaryCheckBoxes(pe);
3689 }
3690 
3691 // signal (int,int) emitted by boundary condition editor when ready:
3692 //-----------------------------------------------------------------------------
boundaryConditionEditorFinishedSlot(int signal,int id)3693 void MainWindow::boundaryConditionEditorFinishedSlot(int signal, int id) {
3694   DynamicEditor *pe = boundaryConditionEditor[id];
3695 
3696   const QString &boundaryConditionName = pe->nameEdit->text().trimmed();
3697 
3698   bool signalOK = signal == MAT_OK || signal == MAT_APPLY;
3699 
3700   if ((boundaryConditionName.isEmpty()) && signalOK) {
3701     logMessage("Refusing to add/update boundary condition with no name");
3702     return;
3703   }
3704 
3705   if (signalOK) {
3706     if (pe->menuAction != NULL) {
3707       pe->menuAction->setText(boundaryConditionName);
3708       logMessage("Boundary condition updated");
3709       if (signal == MAT_OK)
3710         pe->close();
3711     }
3712   } else if (signal == MAT_NEW) {
3713     addBoundaryConditionSlot();
3714 
3715   } else if (signal == MAT_DELETE) {
3716 
3717     pe->nameEdit->setText(QString());
3718 
3719     for (int i = 0; i < boundaryPropertyEditor.size(); i++) {
3720       BoundaryPropertyEditor *bndry = boundaryPropertyEditor[i];
3721 
3722       if (!bndry)
3723         continue;
3724 
3725       if (bndry->condition == pe) {
3726         bndry->condition = NULL;
3727         bndry->ui.boundaryConditionCombo->setCurrentIndex(0);
3728         bndry->touched = true;
3729       }
3730     }
3731 
3732     // Boundary condition is not in menu:
3733     if (pe->menuAction == NULL) {
3734       logMessage("Ready");
3735       pe->close();
3736       return;
3737     }
3738 
3739     // Delete from menu:
3740     delete pe->menuAction;
3741     pe->menuAction = NULL;
3742     pe->close();
3743 
3744     pe->ID = -100;
3745     pe->nameEdit->setText("");
3746 
3747     logMessage("Boundary condition deleted");
3748   }
3749 }
3750 
3751 // signal (QAction*) emitted by boundaryConditionMenu when item selected:
3752 //-----------------------------------------------------------------------------
boundaryConditionSelectedSlot(QAction * act)3753 void MainWindow::boundaryConditionSelectedSlot(QAction *act) {
3754   // Edit the selected boundary condition:
3755   for (int i = 0; i < boundaryConditionEditor.size(); i++) {
3756     DynamicEditor *pe = boundaryConditionEditor[i];
3757     if (pe->menuAction == act) {
3758       pe->applyButton->setText("Update");
3759       pe->applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
3760       pe->discardButton->setText("Remove");
3761       pe->discardButton->setIcon(QIcon(":/icons/list-remove.png"));
3762       createBoundaryCheckBoxes(pe);
3763       pe->show();
3764       pe->raise();
3765     }
3766   }
3767 }
3768 
3769 //-----------------------------------------------------------------------------
bcBoundaryChanged(int state)3770 void MainWindow::bcBoundaryChanged(int state) {
3771   QWidget *a = (QWidget *)QObject::sender();
3772   if (glWidget->hasMesh()) {
3773     BoundaryPropertyEditor *boundary =
3774         (BoundaryPropertyEditor *)a->property("boundary").toULongLong();
3775     populateBoundaryComboBoxes(boundary);
3776 
3777     if (state) {
3778       DynamicEditor *mat =
3779           (DynamicEditor *)a->property("condition").toULongLong();
3780       QString mat_name = mat->nameEdit->text().trimmed();
3781       int ind = boundary->ui.boundaryConditionCombo->findText(mat_name);
3782       boundary->touched = true;
3783       boundary->condition = mat;
3784       boundary->ui.boundaryConditionCombo->setCurrentIndex(ind);
3785     } else {
3786       boundary->condition = NULL;
3787       boundary->ui.boundaryConditionCombo->setCurrentIndex(-1);
3788     }
3789   }
3790 }
3791 
3792 // Model -> Set body properties
3793 //-----------------------------------------------------------------------------
bodyEditSlot()3794 void MainWindow::bodyEditSlot() {
3795   if (!glWidget->hasMesh()) {
3796     logMessage("Unable to open body editor - no mesh");
3797     bodyEditActive = false;
3798     synchronizeMenuToState();
3799     return;
3800   }
3801 
3802   bodyEditActive = !bodyEditActive;
3803   glWidget->bodyEditActive = bodyEditActive;
3804 
3805   if (bodyEditActive)
3806     bcEditActive = false;
3807 
3808   synchronizeMenuToState();
3809 
3810   if (bodyEditActive)
3811     logMessage("Double click a boundary to edit body properties");
3812 }
3813 
3814 // Model -> Set boundary conditions
3815 //-----------------------------------------------------------------------------
bcEditSlot()3816 void MainWindow::bcEditSlot() {
3817   if (!glWidget->hasMesh()) {
3818     logMessage("Unable to open BC editor - no mesh");
3819     bcEditActive = false;
3820     synchronizeMenuToState();
3821     return;
3822   }
3823 
3824   bcEditActive = !bcEditActive;
3825 
3826   if (bcEditActive)
3827     bodyEditActive = false;
3828 
3829   synchronizeMenuToState();
3830 
3831   if (bcEditActive)
3832     logMessage("Double click a boundary to edit BCs");
3833 }
3834 
3835 // Model -> Summary...
3836 //-----------------------------------------------------------------------------
modelSummarySlot()3837 void MainWindow::modelSummarySlot() {
3838   mesh_t *mesh = glWidget->getMesh();
3839   QTextEdit *te = summaryEditor->ui.summaryEdit;
3840   te->clear();
3841   summaryEditor->show();
3842 
3843   if (mesh == NULL) {
3844     te->append("No mesh");
3845     return;
3846   }
3847 
3848   te->append("FINITE ELEMENT MESH");
3849   te->append("Mesh dimension: " + QString::number(mesh->getCdim()));
3850   te->append("Leading element dimension: " + QString::number(mesh->getDim()));
3851   te->append("Nodes: " + QString::number(mesh->getNodes()));
3852   te->append("Volume elements: " + QString::number(mesh->getElements()));
3853   te->append("Surface elements: " + QString::number(mesh->getSurfaces()));
3854   te->append("Edge elements: " + QString::number(mesh->getEdges()));
3855   te->append("Point elements: " + QString::number(mesh->getPoints()));
3856   te->append("");
3857 
3858   // This is almost duplicate info with the above, they might be fused in some
3859   // way...
3860   te->append("ELEMENT TYPES");
3861   int *elementtypes = new int[828];
3862   for (int i = 0; i <= 827; i++)
3863     elementtypes[i] = 0;
3864   for (int i = 0; i < mesh->getElements(); i++)
3865     elementtypes[mesh->getElement(i)->getCode()] += 1;
3866   for (int i = 0; i < mesh->getSurfaces(); i++)
3867     elementtypes[mesh->getSurface(i)->getCode()] += 1;
3868   for (int i = 0; i < mesh->getEdges(); i++)
3869     elementtypes[mesh->getEdge(i)->getCode()] += 1;
3870   for (int i = 0; i < mesh->getPoints(); i++)
3871     elementtypes[mesh->getPoint(i)->getCode()] += 1;
3872   for (int i = 827; i > 0; i--)
3873     if (elementtypes[i])
3874       te->append(QString::number(i) + ": " + QString::number(elementtypes[i]));
3875   te->append("");
3876   delete[] elementtypes;
3877 
3878   te->append("BOUNDING BOX");
3879   QString coordnames = "XYZ";
3880   for (int j = 0; j < 3; j++) {
3881     double mincoord, maxcoord, coord;
3882     mincoord = maxcoord = mesh->getNode(0)->getX(j);
3883     for (int i = 0; i < mesh->getNodes(); i++) {
3884       coord = mesh->getNode(i)->getX(j);
3885       if (mincoord > coord)
3886         mincoord = coord;
3887       if (maxcoord < coord)
3888         maxcoord = coord;
3889     }
3890     te->append(coordnames[j] + "-coordinate: [ " + QString::number(mincoord) +
3891                " ,  " + QString::number(maxcoord) + " ]");
3892   }
3893   te->append("");
3894 
3895   // Check equations:
3896   int count = 0;
3897   for (int i = 0; i < equationEditor.size(); i++) {
3898     if (equationEditor[i]->menuAction != NULL)
3899       count++;
3900   }
3901   te->append("GENERAL");
3902   te->append("Equations: " + QString::number(count));
3903 
3904   // Check materials:
3905   count = 0;
3906   for (int i = 0; i < materialEditor.size(); i++) {
3907     if (materialEditor[i]->menuAction != NULL)
3908       count++;
3909   }
3910   te->append("Materials: " + QString::number(count));
3911 
3912   // Check boundary conditions:
3913   count = 0;
3914   for (int i = 0; i < boundaryConditionEditor.size(); i++) {
3915     if (boundaryConditionEditor[i]->touched)
3916       count++;
3917   }
3918   te->append("Boundary conditions: " + QString::number(count));
3919 
3920   // Check body properties:
3921   count = 0;
3922   for (int i = 0; i < bodyPropertyEditor.size(); i++) {
3923 
3924     if (!bodyPropertyEditor[i])
3925       continue;
3926 
3927     if (bodyPropertyEditor[i]->touched)
3928       count++;
3929   }
3930 
3931   te->append("Body properties: " + QString::number(count));
3932   te->append("");
3933 
3934   // Count volume bodies:
3935   //---------------------
3936   int undetermined = 0;
3937   int *tmp = new int[mesh->getElements()];
3938   for (int i = 0; i < mesh->getElements(); i++)
3939     tmp[i] = 0;
3940 
3941   for (int i = 0; i < mesh->getElements(); i++) {
3942     element_t *e = mesh->getElement(i);
3943     if (e->getNature() == PDE_BULK) {
3944       if (e->getIndex() >= 0)
3945         tmp[e->getIndex()]++;
3946       else
3947         undetermined++;
3948     }
3949   }
3950 
3951   te->append("VOLUME BODIES");
3952   count = 0;
3953   for (int i = 0; i < mesh->getElements(); i++) {
3954     if (tmp[i] > 0) {
3955       count++;
3956       QString qs = "Body " + QString::number(i) + ": " +
3957                    QString::number(tmp[i]) + " volume elements";
3958 
3959       element_t *e = mesh->getElement(i);
3960       int j = e->getIndex();
3961 
3962       if ((j >= 0) && (j < bodyPropertyEditor.size()))
3963         if (bodyPropertyEditor[j] && bodyPropertyEditor[j]->touched)
3964           qs.append(" (Body property set)");
3965 
3966       te->append(qs);
3967     }
3968   }
3969   te->append("Undetermined: " + QString::number(undetermined));
3970   te->append("Total: " + QString::number(count) + " volume bodies");
3971   te->append("");
3972 
3973   delete[] tmp;
3974 
3975   // Count surface bodies:
3976   //---------------------
3977   undetermined = 0;
3978   tmp = new int[mesh->getSurfaces()];
3979   for (int i = 0; i < mesh->getSurfaces(); i++)
3980     tmp[i] = 0;
3981 
3982   for (int i = 0; i < mesh->getSurfaces(); i++) {
3983     surface_t *s = mesh->getSurface(i);
3984     if (s->getNature() == PDE_BULK) {
3985       if (s->getIndex() >= 0)
3986         tmp[s->getIndex()]++;
3987       else
3988         undetermined++;
3989     }
3990   }
3991 
3992   te->append("SURFACE BODIES");
3993   count = 0;
3994   for (int i = 0; i < mesh->getSurfaces(); i++) {
3995     if (tmp[i] > 0) {
3996       count++;
3997       QString qs = "Body " + QString::number(i) + ": " +
3998                    QString::number(tmp[i]) + " surface elements";
3999 
4000       surface_t *s = mesh->getSurface(i);
4001       int j = s->getIndex();
4002 
4003       if ((j >= 0) && (j < bodyPropertyEditor.size()))
4004         if (bodyPropertyEditor[j] && bodyPropertyEditor[j]->touched)
4005           qs.append(" (Body property set)");
4006 
4007       te->append(qs);
4008     }
4009   }
4010   te->append("Undetermined: " + QString::number(undetermined));
4011   te->append("Total: " + QString::number(count) + " surface bodies");
4012   te->append("");
4013 
4014   delete[] tmp;
4015 
4016   // Count edge bodies:
4017   //---------------------
4018   undetermined = 0;
4019   tmp = new int[mesh->getEdges()];
4020   for (int i = 0; i < mesh->getEdges(); i++)
4021     tmp[i] = 0;
4022 
4023   for (int i = 0; i < mesh->getEdges(); i++) {
4024     edge_t *e = mesh->getEdge(i);
4025     if (e->getNature() == PDE_BULK) {
4026       if (e->getIndex() >= 0)
4027         tmp[e->getIndex()]++;
4028       else
4029         undetermined++;
4030     }
4031   }
4032 
4033   te->append("EDGE BODIES");
4034   count = 0;
4035   for (int i = 0; i < mesh->getEdges(); i++) {
4036     if (tmp[i] > 0) {
4037       count++;
4038       QString qs = "Body " + QString::number(i) + ": " +
4039                    QString::number(tmp[i]) + " edge elements";
4040 
4041       edge_t *e = mesh->getEdge(i);
4042       int j = e->getIndex();
4043 
4044       if ((j >= 0) && (j < bodyPropertyEditor.size()))
4045         if (bodyPropertyEditor[j] && bodyPropertyEditor[j]->touched)
4046           qs.append(" (Body property set)");
4047 
4048       te->append(qs);
4049     }
4050   }
4051   te->append("Undetermined: " + QString::number(undetermined));
4052   te->append("Total: " + QString::number(count) + " edge bodies");
4053   te->append("");
4054 
4055   delete[] tmp;
4056 
4057   // Count surface boundaries:
4058   //--------------------------
4059   undetermined = 0;
4060   tmp = new int[mesh->getSurfaces()];
4061   for (int i = 0; i < mesh->getSurfaces(); i++)
4062     tmp[i] = 0;
4063 
4064   for (int i = 0; i < mesh->getSurfaces(); i++) {
4065     surface_t *s = mesh->getSurface(i);
4066     if (s->getNature() == PDE_BOUNDARY) {
4067       if (s->getIndex() >= 0)
4068         tmp[s->getIndex()]++;
4069       else
4070         undetermined++;
4071     }
4072   }
4073 
4074   te->append("SURFACE BOUNDARIES");
4075   count = 0;
4076   for (int i = 0; i < mesh->getSurfaces(); i++) {
4077     if (tmp[i] > 0) {
4078       count++;
4079       QString qs = "Boundary " + QString::number(i) + ": " +
4080                    QString::number(tmp[i]) + " surface elements";
4081 
4082       surface_t *s = mesh->getSurface(i);
4083       int j = s->getIndex();
4084       if ((j >= 0) && (j < boundaryConditionEditor.size()))
4085         if (boundaryConditionEditor[j]->touched)
4086           qs.append(" (BC set)");
4087 
4088       te->append(qs);
4089     }
4090   }
4091   te->append("Undetermined: " + QString::number(undetermined));
4092   te->append("Total: " + QString::number(count) + " surface boundaries");
4093   te->append("");
4094 
4095   delete[] tmp;
4096 
4097   // Count edge boundaries:
4098   //--------------------------
4099   undetermined = 0;
4100   tmp = new int[mesh->getEdges()];
4101   for (int i = 0; i < mesh->getEdges(); i++)
4102     tmp[i] = 0;
4103 
4104   for (int i = 0; i < mesh->getEdges(); i++) {
4105     edge_t *e = mesh->getEdge(i);
4106     if (e->getNature() == PDE_BOUNDARY) {
4107       if (e->getIndex() >= 0)
4108         tmp[e->getIndex()]++;
4109       else
4110         undetermined++;
4111     }
4112   }
4113 
4114   te->append("EDGE BOUNDARIES");
4115   count = 0;
4116   for (int i = 0; i < mesh->getEdges(); i++) {
4117     if (tmp[i] > 0) {
4118       count++;
4119       QString qs = "Boundary " + QString::number(i) + ": " +
4120                    QString::number(tmp[i]) + " edge elements";
4121 
4122       edge_t *e = mesh->getEdge(i);
4123       int j = e->getIndex();
4124       if ((j >= 0) && (j < boundaryConditionEditor.size()))
4125         if (boundaryConditionEditor[j]->touched)
4126           qs.append(" (BC set)");
4127 
4128       te->append(qs);
4129     }
4130   }
4131   te->append("Undetermined: " + QString::number(undetermined));
4132   te->append("Total: " + QString::number(count) + " edge boundaries");
4133   te->append("");
4134 
4135   delete[] tmp;
4136 }
4137 
4138 // Model -> Clear
4139 //-----------------------------------------------------------------------------
modelClearSlot()4140 void MainWindow::modelClearSlot() {
4141   // clear equations:
4142   for (int i = 0; i < equationEditor.size(); i++) {
4143     DynamicEditor *pe = equationEditor[i];
4144     if (pe->menuAction != NULL)
4145       delete pe->menuAction;
4146   }
4147 
4148   for (int i = 0; i < equationEditor.size(); i++)
4149     delete equationEditor[i];
4150 
4151   equationEditor.clear();
4152 
4153   // clear materials:
4154   for (int i = 0; i < materialEditor.size(); i++) {
4155     DynamicEditor *de = materialEditor[i];
4156     if (de->menuAction != NULL)
4157       delete de->menuAction;
4158   }
4159 
4160   for (int i = 0; i < materialEditor.size(); i++)
4161     delete materialEditor[i];
4162 
4163   materialEditor.clear();
4164 
4165   // clear body forces:
4166   for (int i = 0; i < bodyForceEditor.size(); i++) {
4167     DynamicEditor *de = bodyForceEditor[i];
4168     if (de->menuAction != NULL)
4169       delete de->menuAction;
4170   }
4171 
4172   for (int i = 0; i < bodyForceEditor.size(); i++)
4173     delete bodyForceEditor[i];
4174 
4175   bodyForceEditor.clear();
4176 
4177   // clear initial conditions:
4178   for (int i = 0; i < initialConditionEditor.size(); i++) {
4179     DynamicEditor *de = initialConditionEditor[i];
4180     if (de->menuAction != NULL)
4181       delete de->menuAction;
4182   }
4183 
4184   for (int i = 0; i < initialConditionEditor.size(); i++)
4185     delete initialConditionEditor[i];
4186 
4187   initialConditionEditor.clear();
4188 
4189   // clear boundary conditions:
4190   for (int i = 0; i < boundaryConditionEditor.size(); i++) {
4191     DynamicEditor *de = boundaryConditionEditor[i];
4192     if (de->menuAction != NULL)
4193       delete de->menuAction;
4194   }
4195 
4196   for (int i = 0; i < boundaryConditionEditor.size(); i++)
4197     if (boundaryConditionEditor[i])
4198       delete boundaryConditionEditor[i];
4199 
4200   boundaryConditionEditor.clear();
4201 
4202   // clear boundary setting:
4203   for (int i = 0; i < boundaryPropertyEditor.size(); i++)
4204     if (boundaryPropertyEditor[i])
4205       delete boundaryPropertyEditor[i];
4206 
4207   boundaryPropertyEditor.clear();
4208 
4209   // clear body settings:
4210   for (int i = 0; i < bodyPropertyEditor.size(); i++)
4211     if (bodyPropertyEditor[i])
4212       delete bodyPropertyEditor[i];
4213 
4214   bodyPropertyEditor.clear();
4215 
4216   // clear solver specific settings:
4217   for (int i = 0; i < solverParameterEditor.size(); i++)
4218     if (solverParameterEditor[i])
4219       delete solverParameterEditor[i];
4220 
4221   solverParameterEditor.clear();
4222 }
4223 
4224 //*****************************************************************************
4225 //
4226 //                                View MENU
4227 //
4228 //*****************************************************************************
4229 
4230 // View -> Full screen
4231 //-----------------------------------------------------------------------------
viewFullScreenSlot()4232 void MainWindow::viewFullScreenSlot() {
4233   if (!isFullScreen()) {
4234     cout << "Switching to full screen mode" << endl;
4235     menuBar()->hide();
4236     statusBar()->hide();
4237     fileToolBar->hide();
4238     editToolBar->hide();
4239     meshToolBar->hide();
4240     solverToolBar->hide();
4241     this->showFullScreen();
4242   } else {
4243     viewNormalModeSlot();
4244   }
4245   synchronizeMenuToState();
4246 }
4247 
4248 // Return to normal mode (GLWidget emits (void) when esc is pressed)...
4249 //-----------------------------------------------------------------------------
viewNormalModeSlot()4250 void MainWindow::viewNormalModeSlot() {
4251   if (isFullScreen()) {
4252     cout << "Switching to normal window mode" << endl;
4253     this->showNormal();
4254     menuBar()->show();
4255     statusBar()->show();
4256     if (!egIni->isSet("hidetoolbars")) {
4257       fileToolBar->show();
4258       editToolBar->show();
4259       meshToolBar->show();
4260       solverToolBar->show();
4261     }
4262   }
4263   synchronizeMenuToState();
4264   statusBar()->showMessage(tr("Ready"));
4265 }
4266 
4267 // Context menu event (usually mouse has been right clicked)...
4268 //-----------------------------------------------------------------------------
contextMenuEvent(QContextMenuEvent * event)4269 void MainWindow::contextMenuEvent(QContextMenuEvent *event) {
4270   // if(isFullScreen())
4271   contextMenu->popup(event->globalPos());
4272 }
4273 
4274 // View -> Surface mesh
4275 //-----------------------------------------------------------------------------
hidesurfacemeshSlot()4276 void MainWindow::hidesurfacemeshSlot() {
4277   mesh_t *mesh = glWidget->getMesh();
4278   int lists = glWidget->getLists();
4279 
4280   if (mesh == NULL) {
4281     logMessage("There is no surface mesh to hide/show");
4282     return;
4283   }
4284 
4285   glWidget->stateDrawSurfaceMesh = !glWidget->stateDrawSurfaceMesh;
4286 
4287   for (int i = 0; i < lists; i++) {
4288     list_t *l = glWidget->getList(i);
4289     if (l->getType() == SURFACEMESHLIST) {
4290       l->setVisible(glWidget->stateDrawSurfaceMesh);
4291 
4292       // do not set visible if the parent surface list is hidden
4293       int p = l->getParent();
4294       if (p >= 0) {
4295         list_t *lp = glWidget->getList(p);
4296         if (!lp->isVisible())
4297           l->setVisible(false);
4298       }
4299     }
4300   }
4301 
4302   synchronizeMenuToState();
4303 
4304   if (!glWidget->stateDrawSurfaceMesh)
4305     logMessage("Surface mesh hidden");
4306   else
4307     logMessage("Surface mesh shown");
4308 }
4309 
4310 // View -> Volume mesh
4311 //-----------------------------------------------------------------------------
hidevolumemeshSlot()4312 void MainWindow::hidevolumemeshSlot() {
4313   mesh_t *mesh = glWidget->getMesh();
4314   int lists = glWidget->getLists();
4315 
4316   if (mesh == NULL) {
4317     logMessage("There is no volume mesh to hide/show");
4318     return;
4319   }
4320 
4321   glWidget->stateDrawVolumeMesh = !glWidget->stateDrawVolumeMesh;
4322 
4323   for (int i = 0; i < lists; i++) {
4324     list_t *l = glWidget->getList(i);
4325     if (l->getType() == VOLUMEMESHLIST)
4326       l->setVisible(glWidget->stateDrawVolumeMesh);
4327   }
4328 
4329   synchronizeMenuToState();
4330 
4331   if (!glWidget->stateDrawVolumeMesh)
4332     logMessage("Volume mesh hidden");
4333   else
4334     logMessage("Volume mesh shown");
4335 }
4336 
4337 // View -> Sharp edges
4338 //-----------------------------------------------------------------------------
hidesharpedgesSlot()4339 void MainWindow::hidesharpedgesSlot() {
4340   mesh_t *mesh = glWidget->getMesh();
4341   int lists = glWidget->getLists();
4342 
4343   if (mesh == NULL) {
4344     logMessage("There are no sharp edges to hide/show");
4345     return;
4346   }
4347 
4348   glWidget->stateDrawSharpEdges = !glWidget->stateDrawSharpEdges;
4349 
4350   for (int i = 0; i < lists; i++) {
4351     list_t *l = glWidget->getList(i);
4352     if (l->getType() == SHARPEDGELIST)
4353       l->setVisible(glWidget->stateDrawSharpEdges);
4354   }
4355 
4356   synchronizeMenuToState();
4357 
4358   if (!glWidget->stateDrawSharpEdges)
4359     logMessage("Sharp edges hidden");
4360   else
4361     logMessage("Sharp edges shown");
4362 }
4363 
4364 // View -> Coordinates
4365 //-----------------------------------------------------------------------------
viewCoordinatesSlot()4366 void MainWindow::viewCoordinatesSlot() {
4367   if (glWidget->toggleCoordinates())
4368     logMessage("Coordinates shown");
4369   else
4370     logMessage("Coordinates hidden");
4371 
4372   synchronizeMenuToState();
4373 }
4374 
4375 // View -> Select defined edges
4376 //-----------------------------------------------------------------------------
selectDefinedEdgesSlot()4377 void MainWindow::selectDefinedEdgesSlot() {
4378   mesh_t *mesh = glWidget->getMesh();
4379   int lists = glWidget->getLists();
4380 
4381   if (mesh == NULL) {
4382     logMessage("There are no entities from which to select");
4383     return;
4384   }
4385 
4386   // At the moment only edges are included in search:
4387   int nmax = 0;
4388   for (int i = 0; i < glWidget->boundaryMap.count(); i++) {
4389     int n = glWidget->boundaryMap.key(i);
4390     if (n > nmax)
4391       nmax = n;
4392   }
4393 
4394   bool *activeboundary = new bool[nmax + 1];
4395   for (int i = 0; i <= nmax; i++)
4396     activeboundary[i] = false;
4397 
4398   for (int i = 0; i < glWidget->boundaryMap.count(); i++) {
4399     int n = glWidget->boundaryMap.key(i);
4400     if (n >= 0) {
4401       int m = glWidget->boundaryMap.value(n);
4402 
4403       if (m >= boundaryPropertyEditor.size())
4404         boundaryPropertyEditor.resize(m + 1);
4405 
4406       if (!boundaryPropertyEditor[m])
4407         boundaryPropertyEditor[m] = new BoundaryPropertyEditor;
4408 
4409       BoundaryPropertyEditor *boundary = boundaryPropertyEditor[m];
4410       activeboundary[n] = boundary->condition;
4411     }
4412   }
4413 
4414   for (int i = 0; i < lists; i++) {
4415     list_t *l = glWidget->getList(i);
4416     if (l->getType() == EDGELIST) {
4417       int j = l->getIndex();
4418       if (j < 0)
4419         continue;
4420 
4421       // *** TODO ***
4422       //
4423       // This is wrong: Comparing body indices with boundary indices
4424       if (activeboundary[j])
4425         l->setSelected(true);
4426     }
4427   }
4428 
4429   for (int i = 0; i < mesh->getEdges(); i++) {
4430     edge_t *edge = mesh->getEdge(i);
4431     if (edge->getNature() == PDE_BOUNDARY) {
4432       int j = edge->getIndex();
4433       if (j < 0)
4434         continue;
4435       if (activeboundary[j])
4436         edge->setSelected(true);
4437     }
4438   }
4439   delete[] activeboundary;
4440 
4441   glWidget->rebuildEdgeLists();
4442   glWidget->updateGL();
4443 
4444   logMessage("Defined edges selected");
4445 }
4446 
4447 // View -> Select defined surfaces
4448 //-----------------------------------------------------------------------------
selectDefinedSurfacesSlot()4449 void MainWindow::selectDefinedSurfacesSlot() {
4450   mesh_t *mesh = glWidget->getMesh();
4451   int lists = glWidget->getLists();
4452 
4453   if (mesh == NULL) {
4454     logMessage("There are no entities from which to select");
4455     return;
4456   }
4457 
4458   // At the moment only surfaces are included in search:
4459   int nmax = 0;
4460   for (int i = 0; i < glWidget->bodyMap.count(); i++) {
4461     int n = glWidget->bodyMap.key(i);
4462     if (n > nmax)
4463       nmax = n;
4464   }
4465 
4466   bool *activebody = new bool[nmax + 1];
4467   for (int i = 0; i <= nmax; i++)
4468     activebody[i] = false;
4469 
4470   for (int i = 0; i < glWidget->bodyMap.count(); i++) {
4471     int n = glWidget->bodyMap.key(i);
4472     if (n >= 0) {
4473       int m = glWidget->bodyMap.value(n);
4474 
4475       BodyPropertyEditor *body = bodyPropertyEditor[m];
4476 
4477       if (!body) {
4478         cout << "MainWindow: Body index out of bounds" << endl;
4479         continue;
4480       }
4481 
4482       activebody[n] = body->material && body->equation;
4483     }
4484   }
4485 
4486   for (int i = 0; i < lists; i++) {
4487     list_t *l = glWidget->getList(i);
4488     if (l->getType() == SURFACELIST) {
4489       int j = l->getIndex();
4490       if (j < 0)
4491         continue;
4492 
4493       // *** TODO ***
4494       //
4495       // This is wrong: Comparing body indices with boundary indexes
4496       if (activebody[j])
4497         l->setSelected(true);
4498     }
4499   }
4500 
4501   for (int i = 0; i < mesh->getSurfaces(); i++) {
4502     surface_t *surface = mesh->getSurface(i);
4503     if (surface->getNature() == PDE_BULK) {
4504       int j = surface->getIndex();
4505       if (j < 0)
4506         continue;
4507       if (activebody[j])
4508         surface->setSelected(true);
4509     }
4510   }
4511   delete[] activebody;
4512 
4513   glWidget->rebuildSurfaceLists();
4514   glWidget->updateGL();
4515 
4516   logMessage("Defined surfaces selected");
4517 }
4518 
4519 // View -> Select all surfaces
4520 //-----------------------------------------------------------------------------
selectAllSurfacesSlot()4521 void MainWindow::selectAllSurfacesSlot() {
4522   mesh_t *mesh = glWidget->getMesh();
4523   int lists = glWidget->getLists();
4524 
4525   if (mesh == NULL) {
4526     logMessage("There are no surfaces to select");
4527     return;
4528   }
4529 
4530   for (int i = 0; i < lists; i++) {
4531     list_t *l = glWidget->getList(i);
4532     if (l->getType() == SURFACELIST) {
4533       l->setSelected(true);
4534       for (int j = 0; j < mesh->getSurfaces(); j++) {
4535         surface_t *surf = mesh->getSurface(j);
4536         if (l->getIndex() == surf->getIndex())
4537           surf->setSelected(l->isSelected());
4538       }
4539     }
4540   }
4541 
4542   glWidget->rebuildSurfaceLists();
4543   glWidget->updateGL();
4544 
4545   logMessage("All surfaces selected");
4546 }
4547 
4548 // View -> Select all edges
4549 //-----------------------------------------------------------------------------
selectAllEdgesSlot()4550 void MainWindow::selectAllEdgesSlot() {
4551   mesh_t *mesh = glWidget->getMesh();
4552   int lists = glWidget->getLists();
4553 
4554   if (mesh == NULL) {
4555     logMessage("There are no edges to select");
4556     return;
4557   }
4558 
4559   for (int i = 0; i < lists; i++) {
4560     list_t *l = glWidget->getList(i);
4561 
4562     if (l->getType() == EDGELIST)
4563       l->setSelected(true);
4564 
4565     for (int j = 0; j < mesh->getEdges(); j++) {
4566       edge_t *edge = mesh->getEdge(j);
4567       if (l->getIndex() == edge->getIndex())
4568         edge->setSelected(l->isSelected());
4569     }
4570   }
4571 
4572   glWidget->rebuildEdgeLists();
4573   glWidget->updateGL();
4574 
4575   logMessage("All edges selected");
4576 }
4577 
4578 // View -> Hide/Show selected
4579 //-----------------------------------------------------------------------------
hideselectedSlot()4580 void MainWindow::hideselectedSlot() {
4581   mesh_t *mesh = glWidget->getMesh();
4582   int lists = glWidget->getLists();
4583 
4584   if (mesh == NULL) {
4585     logMessage("There is nothing to hide/show");
4586     return;
4587   }
4588 
4589   bool something_selected = false;
4590   for (int i = 0; i < lists; i++) {
4591     list_t *l = glWidget->getList(i);
4592     something_selected |= l->isSelected();
4593   }
4594 
4595   if (!something_selected) {
4596     logMessage("Nothing selected");
4597     return;
4598   }
4599 
4600   bool vis = false;
4601   for (int i = 0; i < lists; i++) {
4602     list_t *l = glWidget->getList(i);
4603     if (l->isSelected()) {
4604       l->setVisible(!l->isVisible());
4605       if (l->isVisible())
4606         vis = true;
4607 
4608       // hide the child surface edge list if parent is hidden
4609       int c = l->getChild();
4610       if (c >= 0) {
4611         list_t *lc = glWidget->getList(c);
4612         lc->setVisible(l->isVisible());
4613         if (!glWidget->stateDrawSurfaceMesh)
4614           lc->setVisible(false);
4615       }
4616     }
4617   }
4618   glWidget->updateGL();
4619 
4620   if (!vis)
4621     logMessage("Selected objects hidden");
4622   else
4623     logMessage("Selected objects shown");
4624 }
4625 
4626 // View -> Show all
4627 //-----------------------------------------------------------------------------
showallSlot()4628 void MainWindow::showallSlot() {
4629   int lists = glWidget->getLists();
4630 
4631   glWidget->stateDrawSurfaceMesh = true;
4632 #ifndef WIN32
4633   glWidget->stateDrawSharpEdges = true;
4634 #endif
4635   glWidget->stateDrawSurfaceElements = true;
4636   glWidget->stateDrawEdgeElements = true;
4637 
4638   synchronizeMenuToState();
4639 
4640   for (int i = 0; i < lists; i++) {
4641     list_t *l = glWidget->getList(i);
4642     l->setVisible(true);
4643   }
4644 
4645   logMessage("All objects visible");
4646 }
4647 
4648 // View -> Reset model view
4649 //-----------------------------------------------------------------------------
resetSlot()4650 void MainWindow::resetSlot() {
4651   mesh_t *mesh = glWidget->getMesh();
4652   int lists = glWidget->getLists();
4653 
4654   if (mesh == NULL) {
4655     logMessage("There is nothing to reset");
4656     return;
4657   }
4658 
4659   glWidget->stateFlatShade = true;
4660   glWidget->stateDrawSurfaceMesh = true;
4661 #ifndef WIN32
4662   glWidget->stateDrawSharpEdges = true;
4663 #endif
4664   glWidget->stateDrawSurfaceElements = true;
4665   glWidget->stateDrawEdgeElements = true;
4666   glWidget->stateDrawSurfaceNumbers = false;
4667   glWidget->stateDrawEdgeNumbers = false;
4668   glWidget->stateDrawNodeNumbers = false;
4669 
4670   for (int i = 0; i < lists; i++) {
4671     list_t *l = glWidget->getList(i);
4672     l->setVisible(true);
4673     l->setSelected(false);
4674 
4675     for (int j = 0; j < mesh->getSurfaces(); j++) {
4676       surface_t *surf = mesh->getSurface(j);
4677       if (l->getIndex() == surf->getIndex())
4678         surf->setSelected(l->isSelected());
4679     }
4680     for (int j = 0; j < mesh->getEdges(); j++) {
4681       edge_t *edge = mesh->getEdge(j);
4682       if (l->getIndex() == edge->getIndex())
4683         edge->setSelected(l->isSelected());
4684     }
4685   }
4686 
4687   glWidget->stateBcColors = false;
4688   glWidget->stateBodyColors = false;
4689 
4690   glLoadIdentity();
4691   glWidget->rebuildLists();
4692   glWidget->updateGL();
4693 
4694   synchronizeMenuToState();
4695   logMessage("Reset model view");
4696 }
4697 
4698 // View -> Shade model -> Flat
4699 //-----------------------------------------------------------------------------
flatShadeSlot()4700 void MainWindow::flatShadeSlot() {
4701   if (!glWidget->hasMesh()) {
4702     logMessage("Refusing to change shade model when mesh is empty");
4703     return;
4704   }
4705 
4706   glWidget->stateFlatShade = true;
4707   glWidget->rebuildSurfaceLists();
4708   glWidget->updateGL();
4709 
4710   synchronizeMenuToState();
4711   logMessage("Shade model: flat");
4712 }
4713 
4714 // View -> Shade model -> Smooth
4715 //-----------------------------------------------------------------------------
smoothShadeSlot()4716 void MainWindow::smoothShadeSlot() {
4717   if (!glWidget->hasMesh()) {
4718     logMessage("Refusing to change shade model when mesh is empty");
4719     return;
4720   }
4721 
4722   glWidget->stateFlatShade = false;
4723   glWidget->rebuildSurfaceLists();
4724   glWidget->updateGL();
4725 
4726   synchronizeMenuToState();
4727   logMessage("Shade model: smooth");
4728 }
4729 
4730 // View -> Projection -> Orthogonal
4731 //-----------------------------------------------------------------------------
orthoSlot()4732 void MainWindow::orthoSlot() {
4733   if (!glWidget->hasMesh()) {
4734     logMessage("Refusing to change projection when mesh is empty");
4735     return;
4736   }
4737 
4738   glWidget->stateOrtho = true;
4739   glWidget->changeProjection();
4740   glWidget->updateGL();
4741 
4742   synchronizeMenuToState();
4743   logMessage("Projection: orthogonal");
4744 }
4745 
4746 // View -> Projection -> Perspective
4747 //-----------------------------------------------------------------------------
perspectiveSlot()4748 void MainWindow::perspectiveSlot() {
4749   if (!glWidget->hasMesh()) {
4750     logMessage("Refusing to change projection when mesh is empty");
4751     return;
4752   }
4753 
4754   glWidget->stateOrtho = false;
4755   glWidget->changeProjection();
4756   glWidget->updateGL();
4757 
4758   synchronizeMenuToState();
4759   logMessage("Projection: perspective");
4760 }
4761 
4762 // View -> Show numbering -> Surface numbering
4763 //-----------------------------------------------------------------------------
showSurfaceNumbersSlot()4764 void MainWindow::showSurfaceNumbersSlot() {
4765   if (!glWidget->hasMesh()) {
4766     logMessage("Refusing to show surface element numbering when mesh is empty");
4767     return;
4768   }
4769   glWidget->stateDrawSurfaceNumbers = !glWidget->stateDrawSurfaceNumbers;
4770   glWidget->updateGL();
4771   synchronizeMenuToState();
4772 
4773   if (glWidget->stateDrawSurfaceNumbers)
4774     logMessage("Surface element numbering turned on");
4775   else
4776     logMessage("Surface element numbering turned off");
4777 }
4778 
4779 // View -> Show numbering -> Edge numbering
4780 //-----------------------------------------------------------------------------
showEdgeNumbersSlot()4781 void MainWindow::showEdgeNumbersSlot() {
4782   if (!glWidget->hasMesh()) {
4783     logMessage("Refusing to show edge element numbering when mesh is empty");
4784     return;
4785   }
4786   glWidget->stateDrawEdgeNumbers = !glWidget->stateDrawEdgeNumbers;
4787   glWidget->updateGL();
4788   synchronizeMenuToState();
4789 
4790   if (glWidget->stateDrawEdgeNumbers)
4791     logMessage("Edge element numbering turned on");
4792   else
4793     logMessage("Edge element numbering turned off");
4794 }
4795 
4796 // View -> Numbering -> Node numbers
4797 //-----------------------------------------------------------------------------
showNodeNumbersSlot()4798 void MainWindow::showNodeNumbersSlot() {
4799   if (!glWidget->hasMesh()) {
4800     logMessage("Refusing to show node numbering when mesh is empty");
4801     return;
4802   }
4803   glWidget->stateDrawNodeNumbers = !glWidget->stateDrawNodeNumbers;
4804   glWidget->updateGL();
4805   synchronizeMenuToState();
4806 
4807   if (glWidget->stateDrawNodeNumbers)
4808     logMessage("Node numbering turned on");
4809   else
4810     logMessage("Node numbering turned off");
4811 }
4812 
4813 // View -> Numbering -> Boundary index
4814 //-----------------------------------------------------------------------------
showBoundaryIndexSlot()4815 void MainWindow::showBoundaryIndexSlot() {
4816   if (!glWidget->hasMesh()) {
4817     logMessage("Refusing to show boundary indices when mesh is empty");
4818     return;
4819   }
4820   glWidget->stateDrawBoundaryIndex = !glWidget->stateDrawBoundaryIndex;
4821   glWidget->updateGL();
4822   synchronizeMenuToState();
4823 
4824   if (glWidget->stateDrawBoundaryIndex)
4825     logMessage("Boundary indices visible");
4826   else
4827     logMessage("Boundary indices hidden");
4828 }
4829 
4830 // View -> Numbering -> Body index
4831 //-----------------------------------------------------------------------------
showBodyIndexSlot()4832 void MainWindow::showBodyIndexSlot() {
4833   if (!glWidget->hasMesh()) {
4834     logMessage("Refusing to show body indices when mesh is empty");
4835     return;
4836   }
4837 
4838   glWidget->stateDrawBodyIndex = !glWidget->stateDrawBodyIndex;
4839   glWidget->updateGL();
4840   synchronizeMenuToState();
4841 
4842   if (glWidget->stateDrawBodyIndex)
4843     logMessage("Body indices visible");
4844   else
4845     logMessage("Body indices hidden");
4846 }
4847 
4848 // View -> Colors -> GL controls
4849 //-----------------------------------------------------------------------------
glControlSlot()4850 void MainWindow::glControlSlot() {
4851   if (!glWidget->hasMesh()) {
4852     logMessage("No mesh - unable to set GL parameters when the mesh is empty");
4853     return;
4854   }
4855 
4856   glControl->glWidget = this->glWidget;
4857   glControl->show();
4858 }
4859 
4860 // View -> Colors -> Boundaries
4861 //-----------------------------------------------------------------------------
colorizeBoundarySlot()4862 void MainWindow::colorizeBoundarySlot() {
4863   if (!glWidget->hasMesh()) {
4864     logMessage("No mesh - unable to colorize boundaries");
4865     return;
4866   }
4867 
4868   glWidget->stateBcColors = !glWidget->stateBcColors;
4869 
4870   if (glWidget->stateBcColors)
4871     glWidget->stateBodyColors = false;
4872 
4873   glWidget->rebuildLists();
4874   synchronizeMenuToState();
4875 }
4876 
4877 // View -> Colors -> Bodies
4878 //-----------------------------------------------------------------------------
colorizeBodySlot()4879 void MainWindow::colorizeBodySlot() {
4880   if (!glWidget->hasMesh()) {
4881     logMessage("No mesh - unable to colorize bodies");
4882     return;
4883   }
4884 
4885   glWidget->stateBodyColors = !glWidget->stateBodyColors;
4886 
4887   if (glWidget->stateBodyColors)
4888     glWidget->stateBcColors = false;
4889 
4890   glWidget->rebuildLists();
4891   synchronizeMenuToState();
4892 }
4893 
4894 // View -> Colors -> Background
4895 //-----------------------------------------------------------------------------
backgroundColorSlot()4896 void MainWindow::backgroundColorSlot() {
4897   QColor newColor = QColorDialog::getColor(glWidget->backgroundColor, this);
4898   glWidget->qglClearColor(newColor);
4899   glWidget->backgroundColor = newColor;
4900 }
4901 
4902 // View -> Colors -> Surface
4903 //-----------------------------------------------------------------------------
surfaceColorSlot()4904 void MainWindow::surfaceColorSlot() {
4905   if (!glWidget->hasMesh()) {
4906     logMessage("Unable to change surface color when the mesh is empty");
4907     return;
4908   }
4909 
4910   QColor newColor = QColorDialog::getColor(glWidget->surfaceColor, this);
4911   glWidget->surfaceColor = newColor;
4912   glWidget->rebuildLists();
4913 }
4914 
4915 // View -> Colors -> Edge
4916 //-----------------------------------------------------------------------------
edgeColorSlot()4917 void MainWindow::edgeColorSlot() {
4918   if (!glWidget->hasMesh()) {
4919     logMessage("Unable to change edge color when the mesh is empty");
4920     return;
4921   }
4922 
4923   QColor newColor = QColorDialog::getColor(glWidget->edgeColor, this);
4924   glWidget->edgeColor = newColor;
4925   glWidget->rebuildLists();
4926 }
4927 
4928 // View -> Colors -> Surface mesh
4929 //-----------------------------------------------------------------------------
surfaceMeshColorSlot()4930 void MainWindow::surfaceMeshColorSlot() {
4931   if (!glWidget->hasMesh()) {
4932     logMessage("Unable to change surface mesh color when the mesh is empty");
4933     return;
4934   }
4935 
4936   QColor newColor = QColorDialog::getColor(glWidget->surfaceMeshColor, this);
4937   glWidget->surfaceMeshColor = newColor;
4938   glWidget->rebuildLists();
4939 }
4940 
4941 // View -> Colors -> Sharp edges
4942 //-----------------------------------------------------------------------------
sharpEdgeColorSlot()4943 void MainWindow::sharpEdgeColorSlot() {
4944   if (!glWidget->hasMesh()) {
4945     logMessage("Unable to change sharp edge colors when the mesh is empty");
4946     return;
4947   }
4948 
4949   QColor newColor = QColorDialog::getColor(glWidget->sharpEdgeColor, this);
4950   glWidget->sharpEdgeColor = newColor;
4951   glWidget->rebuildLists();
4952 }
4953 
4954 // View -> Cad model...
4955 //-----------------------------------------------------------------------------
showCadModelSlot()4956 void MainWindow::showCadModelSlot() {
4957 #ifdef EG_OCC
4958   cadView->show();
4959 #endif
4960 }
4961 
4962 // View -> Twod model...
4963 //-----------------------------------------------------------------------------
showTwodViewSlot()4964 void MainWindow::showTwodViewSlot() { twodView->show(); }
4965 
4966 // View -> VTK post...
4967 //-----------------------------------------------------------------------------
showVtkPostSlot()4968 void MainWindow::showVtkPostSlot() {
4969 #ifdef EG_VTK
4970   QString postFileName =
4971       saveDirName + "/" + generalSetup->ui.postFileEdit->text().trimmed();
4972   // Parallel solution:
4973   //====================
4974   Ui::parallelDialog ui = parallel->ui;
4975   bool parallelActive = ui.parallelActiveCheckBox->isChecked();
4976 
4977   if (parallelActive) {
4978 
4979     // unify mesh:
4980     if (meshUnifier->state() == QProcess::Running) {
4981       logMessage("Mesh unifier is already running - aborted");
4982       return;
4983     }
4984 
4985     if (saveDirName.isEmpty()) {
4986       logMessage("saveDirName is empty - unable to locate result files");
4987       return;
4988     }
4989 
4990     // Set up log window:
4991     solverLogWindow->setWindowTitle(tr("ElmerGrid log"));
4992     solverLogWindow->getTextEdit()->clear();
4993     solverLogWindow->setFound(false);
4994     solverLogWindow->show();
4995 
4996     QString postName = generalSetup->ui.postFileEdit->text().trimmed();
4997     QStringList postNameSplitted = postName.split(".");
4998     int nofProcessors = ui.nofProcessorsSpinBox->value();
4999 
5000     QString unifyingCommand = ui.mergeLineEdit->text().trimmed();
5001     unifyingCommand.replace(QString("%ep"), postNameSplitted.at(0).trimmed());
5002     unifyingCommand.replace(QString("%n"), QString::number(nofProcessors));
5003 
5004     logMessage("Executing: " + unifyingCommand);
5005 
5006     meshUnifier->start(unifyingCommand);
5007 
5008     if (!meshUnifier->waitForStarted()) {
5009       solverLogWindow->getTextEdit()->append(
5010           "Unable to start ElmerGrid for mesh unification - aborted");
5011       logMessage("Unable to start ElmerGrid for mesh unification - aborted");
5012       vtkPostMeshUnifierRunning = false;
5013       return;
5014     }
5015 
5016     // The rest is done in meshUnifierFinishedSlot:
5017     vtkPostMeshUnifierRunning = true;
5018     return;
5019   }
5020 
5021   // Scalar solution:
5022   //-----------------
5023   vtkPost->show();
5024 
5025   QFileInfo info(postFileName);
5026   QDir dir = info.dir();
5027   if(postFileName.endsWith(".vtu", Qt::CaseInsensitive)){
5028     if(!parallelActive){
5029       QString vtuFileName = postFileName;
5030 		  vtuFileName.insert(vtuFileName.length()-4, "_t0001");
5031       if(!vtkPost->ReadSingleVtuFile(vtuFileName)){
5032         vtuFileName = postFileName;
5033 		    vtuFileName.insert(vtuFileName.length()-4, "0001");
5034 		    vtkPost->ReadSingleVtuFile(vtuFileName);
5035       }
5036     }
5037   }else{
5038     vtkPost->ReadPostFile(postFileName);
5039   }
5040 #endif
5041 }
5042 
5043 // View -> Paraview
5044 //-----------------------------------------------------------------------------
showParaViewSlot()5045 void MainWindow::showParaViewSlot() {
5046 #ifdef EG_PARAVIEW
5047 
5048   if (paraview->state() == QProcess::Running) {
5049     logMessage("ParaView is already running");
5050     return;
5051   }
5052 
5053   QString postFileName = generalSetup->ui.postFileEdit->text().trimmed();
5054   QFileInfo pvFile(postFileName);
5055 
5056   Ui::parallelDialog ui = parallel->ui;
5057   bool parallelActive = ui.parallelActiveCheckBox->isChecked();
5058 
5059   QDir currentDir;
5060   QStringList args;
5061   QString secondName;
5062 
5063   currentDir = QDir(saveDirName);
5064 
5065   // Paraview can deal with case..vtu kind of arguments which however,
5066   // fail if there is only one file. Use dirty check to see that there
5067   // are more than one file.
5068 
5069   if (!parallelActive) {
5070     secondName = pvFile.baseName() + "_t0002.vtu";
5071   } else {
5072     secondName = pvFile.baseName() + "_t0002.pvtu";
5073   }
5074 
5075   QFile secondFile(secondName);
5076 
5077   // Serial solution
5078   //================
5079   if (!parallelActive) {
5080     if (secondFile.exists())
5081       args << pvFile.baseName() + "_t..vtu";
5082     else
5083       args << pvFile.baseName() + "_t0001.vtu";
5084   }
5085 
5086   // Parallel solution
5087   //==================
5088   if (parallelActive) {
5089     if (secondFile.exists())
5090       args << pvFile.baseName() + "_t..pvtu";
5091     else
5092       args << pvFile.baseName() + "_t0001.pvtu";
5093   }
5094 
5095   // Launch ParaView
5096   //================
5097   paraview->start("paraview", args);
5098 
5099   if (!paraview->waitForStarted()) {
5100     logMessage("Unable to start ParaView");
5101     return;
5102   }
5103 
5104   logMessage("ParaView started");
5105 
5106   updateSysTrayIcon("ParaView started",
5107                     "");
5108 
5109 #endif
5110 }
5111 
5112 //*****************************************************************************
5113 //
5114 //                                Mesh MENU
5115 //
5116 //*****************************************************************************
5117 
5118 // Mesh -> Control...
5119 //-----------------------------------------------------------------------------
meshcontrolSlot()5120 void MainWindow::meshcontrolSlot() {
5121   meshControl->tetlibPresent = this->tetlibPresent;
5122   meshControl->nglibPresent = this->nglibPresent;
5123 
5124   if (!tetlibPresent) {
5125     meshControl->tetlibPresent = false;
5126     meshControl->ui.nglibRadioButton->setChecked(true);
5127     meshControl->ui.tetlibRadioButton->setEnabled(false);
5128     meshControl->ui.tetlibStringEdit->setEnabled(false);
5129   }
5130 
5131   if (!nglibPresent) {
5132     meshControl->nglibPresent = false;
5133     meshControl->ui.tetlibRadioButton->setChecked(true);
5134     meshControl->ui.nglibRadioButton->setEnabled(false);
5135     meshControl->ui.nglibMaxHEdit->setEnabled(false);
5136     meshControl->ui.nglibFinenessEdit->setEnabled(false);
5137     meshControl->ui.nglibBgmeshEdit->setEnabled(false);
5138   }
5139 
5140   if (!tetlibPresent && !nglibPresent)
5141     meshControl->ui.elmerGridRadioButton->setChecked(true);
5142 
5143   meshControl->show();
5144 }
5145 
5146 // Mesh -> Remesh
5147 //-----------------------------------------------------------------------------
remeshSlot()5148 void MainWindow::remeshSlot() {
5149   if (activeGenerator == GEN_UNKNOWN) {
5150     logMessage("Unable to (re)mesh: no input data or mesh generator (please "
5151                "make sure that your input file suffix is in lower case)");
5152     return;
5153   }
5154 
5155   // ***** ELMERGRID *****
5156 
5157   if (activeGenerator == GEN_ELMERGRID) {
5158 
5159     meshutils->clearMesh(glWidget->getMesh());
5160     glWidget->newMesh();
5161     mesh_t *mesh = glWidget->getMesh();
5162 
5163 #if WITH_QT5
5164     elmergridAPI->createElmerMeshStructure(
5165         mesh, meshControl->elmerGridControlString.toLatin1());
5166 #else
5167     elmergridAPI->createElmerMeshStructure(
5168         mesh, meshControl->elmerGridControlString.toAscii());
5169 #endif
5170 
5171     if (mesh->getSurfaces() == 0)
5172       meshutils->findSurfaceElements(mesh);
5173 
5174     for (int i = 0; i < mesh->getSurfaces(); i++) {
5175       surface_t *surface = mesh->getSurface(i);
5176 
5177       surface->setEdges((int)(surface->getCode() / 100));
5178       surface->newEdgeIndexes(surface->getEdges());
5179       for (int j = 0; j < surface->getEdges(); j++)
5180         surface->setEdgeIndex(j, -1);
5181     }
5182 
5183     meshutils->findSurfaceElementEdges(mesh);
5184     meshutils->findSurfaceElementNormals(mesh);
5185 
5186     glWidget->rebuildLists();
5187     applyOperations();
5188 
5189     return;
5190   }
5191 
5192   // ***** Threaded generators *****
5193 
5194   if (!remeshAct->isEnabled()) {
5195     logMessage("Meshing thread is already running - aborting");
5196     return;
5197   }
5198 
5199   if (activeGenerator == GEN_TETLIB) {
5200 
5201     if (!tetlibPresent) {
5202       logMessage("tetlib functionality unavailable");
5203       return;
5204     }
5205 
5206     if (!tetlibInputOk) {
5207       logMessage("Remesh: error: no input data for tetlib");
5208       return;
5209     }
5210 
5211     // Usually "J" should be included in the control string:
5212     tetlibControlString = meshControl->tetlibControlString;
5213 
5214   } else if (activeGenerator == GEN_NGLIB) {
5215 
5216     if (!nglibPresent) {
5217       logMessage("nglib functionality unavailable");
5218       return;
5219     }
5220 
5221     if (!nglibInputOk) {
5222       logMessage("Remesh: error: no input data for nglib");
5223       return;
5224     }
5225 
5226     // Init & set mesh params.:
5227     //--------------------------
5228     cout << "Initializing nglib" << endl;
5229     nglib::Ng_Init();
5230 
5231     char backgroundmesh[1024];
5232 #if WITH_QT5
5233     sprintf(backgroundmesh, "%s",
5234             meshControl->nglibBackgroundmesh.toLatin1().data());
5235 #else
5236     sprintf(backgroundmesh, "%s",
5237             meshControl->nglibBackgroundmesh.toAscii().data());
5238 #endif
5239 
5240     mp.maxh = meshControl->nglibMaxH.toDouble();
5241     mp.fineness = meshControl->nglibFineness.toDouble();
5242     mp.secondorder = 0;
5243     mp.meshsize_filename = backgroundmesh;
5244 
5245     if (ngDim == 3) {
5246 
5247       // STL (3D):
5248       //-----------
5249       cout << "Start meshing..." << endl;
5250 
5251       nggeom = nglib::Ng_STL_NewGeometry();
5252 
5253       ngmesh = nglib::Ng_NewMesh();
5254 
5255       if (!occInputOk) {
5256 
5257         // STL: regenerate structures for nglib:
5258         //--------------------------------------
5259 #if WITH_QT5
5260         nggeom = nglib::Ng_STL_LoadGeometry(stlFileName.toLatin1().data(), 0);
5261 #else
5262         nggeom = nglib::Ng_STL_LoadGeometry(stlFileName.toAscii().data(), 0);
5263 #endif
5264 
5265         if (!nggeom) {
5266           logMessage("Ng_STL_LoadGeometry failed");
5267           return;
5268         }
5269 
5270         nglib::Ng_STL_InitSTLGeometry(nggeom);
5271 
5272         nglib::Ng_STL_MakeEdges(nggeom, ngmesh, &mp);
5273 
5274         double maxMeshSize = mp.maxh;
5275 
5276         if (maxMeshSize <= 0)
5277           maxMeshSize = 10000000;
5278 
5279         nglib::Ng_RestrictMeshSizeGlobal(ngmesh, maxMeshSize);
5280 
5281 #ifdef EG_OCC
5282       } else {
5283 
5284         // OCC: (re)generate STL for nglib:
5285         //----------------------------------
5286         cadView->setMesh(ngmesh);
5287         cadView->setGeom(nggeom);
5288         cadView->setMp(&mp);
5289         cadView->generateSTL();
5290 #endif
5291       }
5292 
5293     } else if (ngDim == 2) {
5294 
5295       // IN2D (2D):
5296       //------------
5297       cout << "Start 2D meshing..." << endl;
5298 
5299       if (!occInputOk) {
5300 
5301         // Native 2D geometry input for Ng:
5302         //----------------------------------
5303         if (in2dFileName.isEmpty()) {
5304           logMessage("File name is empty - aborting");
5305           return;
5306         }
5307 
5308         ngmesh = nglib::Ng_NewMesh();
5309 
5310 #if WITH_QT5
5311         nggeom2d = nglib::Ng_LoadGeometry_2D(in2dFileName.toLatin1().data());
5312 #else
5313         nggeom2d = nglib::Ng_LoadGeometry_2D(in2dFileName.toAscii().data());
5314 #endif
5315 
5316         if (!nggeom2d) {
5317           logMessage("Ng_LoadGeometry_2D failed");
5318           return;
5319         }
5320 
5321         nglibAPI->setNggeom2D(nggeom2d);
5322 
5323         double maxMeshSize = mp.maxh;
5324 
5325         if (maxMeshSize <= 0)
5326           maxMeshSize = 10000000;
5327 
5328         nglib::Ng_RestrictMeshSizeGlobal(ngmesh, maxMeshSize);
5329 
5330 #ifdef EG_OCC
5331       } else {
5332 
5333         // Model originates from a 2D cad file:
5334         //--------------------------------------
5335         cadView->generateIn2dFile();
5336 
5337         ngmesh = nglib::Ng_NewMesh();
5338 
5339         nggeom2d = nglib::Ng_LoadGeometry_2D("iges2ng.in2d");
5340 
5341         if (!nggeom2d) {
5342           logMessage("Ng_LoadGeometry_2D failed");
5343           return;
5344         }
5345 
5346         nglibAPI->setNggeom2D(nggeom2d);
5347 
5348         double maxMeshSize = mp.maxh;
5349 
5350         if (maxMeshSize <= 0)
5351           maxMeshSize = 10000000;
5352 
5353         nglib::Ng_RestrictMeshSizeGlobal(ngmesh, maxMeshSize);
5354 #endif
5355       }
5356 
5357     } else {
5358 
5359       // Unknown spatial dimension:
5360       //----------------------------
5361       cout << "Unknown spatial dimension" << endl;
5362       return;
5363     }
5364 
5365   } else {
5366 
5367     logMessage("Remesh: unknown generator type");
5368     return;
5369   }
5370 
5371   // ***** Start meshing thread *****
5372 
5373   logMessage("Sending start request to mesh generator...");
5374 
5375   meshutils->clearMesh(glWidget->getMesh());
5376   glWidget->newMesh();
5377   mesh_t *mesh = glWidget->getMesh();
5378 
5379   // Re-enable when finished() or terminated() signal is received:
5380   remeshAct->setEnabled(false);
5381   stopMeshingAct->setEnabled(true);
5382 
5383   if (activeGenerator == GEN_NGLIB)
5384     stopMeshingAct->setEnabled(false);
5385 
5386   meshingThread->generate(activeGenerator, tetlibControlString, tetlibAPI,
5387                           ngmesh, nggeom, nggeom2d, ngDim, &mp);
5388 }
5389 
5390 // Mesh -> Kill generator
5391 //-----------------------------------------------------------------------------
stopMeshingSlot()5392 void MainWindow::stopMeshingSlot() {
5393   if (remeshAct->isEnabled()) {
5394     logMessage("Mesh generator is not running");
5395     return;
5396   }
5397 
5398   logMessage("Sending termination request to mesh generator...");
5399   meshingThread->stopMeshing();
5400 }
5401 
5402 // Meshing has started (signaled by meshingThread):
5403 //-----------------------------------------------------------------------------
meshingStartedSlot()5404 void MainWindow::meshingStartedSlot() {
5405   logMessage("Mesh generator started");
5406 
5407   updateSysTrayIcon("Mesh generator started",
5408                     "Use Mesh->Terminate to stop processing");
5409 
5410   statusBar()->showMessage(tr("Mesh generator started"));
5411 
5412   progressBar->show();
5413   progressBar->setRange(0, 0);
5414 
5415   progressLabel->show();
5416   progressLabel->setText("Meshing");
5417 }
5418 
5419 // Meshing has been terminated (signaled by meshingThread):
5420 //-----------------------------------------------------------------------------
meshingTerminatedSlot()5421 void MainWindow::meshingTerminatedSlot() {
5422   logMessage("Mesh generator terminated");
5423 
5424   progressBar->hide();
5425   progressBar->setRange(0, 100);
5426 
5427   progressLabel->hide();
5428 
5429   stopMeshingAct->setEnabled(true);
5430 
5431   updateSysTrayIcon("Mesh generator terminated", "Use Mesh->Remesh to restart");
5432 
5433   statusBar()->showMessage(tr("Ready"));
5434 
5435   // clean up:
5436   if (activeGenerator == GEN_TETLIB) {
5437     cout << "Cleaning up...";
5438     out->deinitialize();
5439     cout << "done" << endl;
5440     cout.flush();
5441   }
5442 
5443   if (activeGenerator == GEN_NGLIB) {
5444     nglib::Ng_DeleteMesh(ngmesh);
5445     nglib::Ng_Exit();
5446   }
5447 
5448   remeshAct->setEnabled(true);
5449   stopMeshingAct->setEnabled(false);
5450 }
5451 
5452 // Mesh is ready (signaled by meshingThread):
5453 //-----------------------------------------------------------------------------
meshingFinishedSlot()5454 void MainWindow::meshingFinishedSlot() {
5455   logMessage("Mesh generation ready");
5456 
5457   progressBar->hide();
5458   progressBar->setRange(0, 100);
5459 
5460   progressLabel->hide();
5461 
5462   if (activeGenerator == GEN_TETLIB) {
5463 
5464     makeElmerMeshFromTetlib();
5465 
5466   } else if (activeGenerator == GEN_NGLIB) {
5467 
5468     this->ngmesh = meshingThread->getNgMesh();
5469 
5470     makeElmerMeshFromNglib();
5471 
5472     nglib::Ng_DeleteMesh(ngmesh);
5473     nglib::Ng_Exit();
5474 
5475   } else {
5476 
5477     logMessage("MeshOk: error: unknown mesh generator");
5478   }
5479 
5480   applyOperations();
5481 
5482   statusBar()->showMessage(tr("Ready"));
5483 
5484   updateSysTrayIcon("Mesh generator has finished",
5485                     "Select Model->Summary for statistics");
5486 
5487   remeshAct->setEnabled(true);
5488   stopMeshingAct->setEnabled(false);
5489 
5490   // Check cmd line arguments:
5491   //---------------------------
5492   QStringList args = QCoreApplication::arguments();
5493 
5494   int output = args.indexOf("-o");
5495 
5496   if (output > 0) {
5497     QString dirName = args.at(output + 1);
5498 
5499     if (dirName.left(1) != "-") {
5500       cout << "Saving mesh files" << endl;
5501       saveElmerMesh(dirName);
5502     }
5503   }
5504 
5505   if (args.contains("-e") || args.contains("-nogui")) {
5506     cout << "Exiting" << endl;
5507     QApplication::closeAllWindows();
5508     exit(0);
5509   }
5510 
5511   resetSlot();
5512 }
5513 
5514 // Mesh -> Divide surface...
5515 //-----------------------------------------------------------------------------
surfaceDivideSlot()5516 void MainWindow::surfaceDivideSlot() {
5517   if (!glWidget->hasMesh()) {
5518     logMessage("There is nothing to divide - mesh is empty");
5519     return;
5520   }
5521 
5522   boundaryDivide->target = TARGET_SURFACES;
5523   boundaryDivide->show();
5524 }
5525 
5526 // Make surface division by sharp edges (signalled by boundaryDivide)...
5527 //-----------------------------------------------------------------------------
doDivideSurfaceSlot(double angle)5528 void MainWindow::doDivideSurfaceSlot(double angle) {
5529   mesh_t *mesh = glWidget->getMesh();
5530   int lists = glWidget->getLists();
5531 
5532   if (mesh == NULL) {
5533     logMessage("No mesh to divide");
5534     return;
5535   }
5536 
5537   operations++;
5538   operation_t *p = new operation_t;
5539   operation_t *q = NULL;
5540 
5541   for (q = &operation; q->next; q = q->next)
5542     ;
5543   q->next = p;
5544   p->next = NULL;
5545 
5546   p->type = OP_DIVIDE_SURFACE;
5547   p->angle = angle;
5548 
5549   int selected = 0;
5550 
5551   for (int i = 0; i < lists; i++) {
5552     list_t *l = glWidget->getList(i);
5553 
5554     if (l->isSelected() && (l->getType() == SURFACELIST) &&
5555         (l->getNature() == PDE_BOUNDARY))
5556       selected++;
5557   }
5558   p->selected = selected;
5559   p->select_set = new int[selected];
5560   selected = 0;
5561 
5562   for (int i = 0; i < lists; i++) {
5563     list_t *l = glWidget->getList(i);
5564     if (l->isSelected() && (l->getType() == SURFACELIST) &&
5565         (l->getNature() == PDE_BOUNDARY))
5566       p->select_set[selected++] = i;
5567   }
5568 
5569   meshutils->findSharpEdges(mesh, angle);
5570   int parts = meshutils->divideSurfaceBySharpEdges(mesh);
5571 
5572   QString qs = "Surface divided into " + QString::number(parts) + " parts";
5573   statusBar()->showMessage(qs);
5574 
5575   synchronizeMenuToState();
5576   glWidget->rebuildLists();
5577   glWidget->updateGL();
5578 
5579   // Added 05 September 2009
5580   boundaryPropertyEditor.clear();
5581   boundaryPropertyEditor.resize(parts);
5582   for (int i = 0; i < parts; i++)
5583     boundaryPropertyEditor[i] = new BoundaryPropertyEditor;
5584 }
5585 
5586 // Mesh -> Unify surface
5587 //-----------------------------------------------------------------------------
surfaceUnifySlot()5588 void MainWindow::surfaceUnifySlot() {
5589   mesh_t *mesh = glWidget->getMesh();
5590   int lists = glWidget->getLists();
5591 
5592   if (mesh == NULL) {
5593     logMessage("No surfaces to unify");
5594     return;
5595   }
5596 
5597   int targetindex = -1, selected = 0;
5598   QVector<BoundaryPropertyEditor *> unusedBoundary;
5599   for (int i = 0; i < lists; i++) {
5600     list_t *l = glWidget->getList(i);
5601     if (l->isSelected() && (l->getType() == SURFACELIST) &&
5602         (l->getNature() == PDE_BOUNDARY)) {
5603       selected++;
5604       if (targetindex < 0)
5605         targetindex = l->getIndex();
5606       else {
5607         int v = glWidget->boundaryMap.value(l->getIndex());
5608         if (v >= 0 && v < boundaryPropertyEditor.size() &&
5609             boundaryPropertyEditor[v] != NULL) {
5610           unusedBoundary.append(boundaryPropertyEditor[v]);
5611         }
5612       }
5613     }
5614   }
5615 
5616   if (targetindex < 0) {
5617     logMessage("No surfaces selected");
5618     return;
5619   }
5620 
5621   operations++;
5622   operation_t *p = new operation_t, *q;
5623   for (q = &operation; q->next; q = q->next)
5624     ;
5625   q->next = p;
5626   p->next = NULL;
5627   p->type = OP_UNIFY_SURFACE;
5628   p->selected = selected;
5629   p->select_set = new int[selected];
5630 
5631   selected = 0;
5632   for (int i = 0; i < lists; i++) {
5633     list_t *l = glWidget->getList(i);
5634     if (l->isSelected() && (l->getType() == SURFACELIST) &&
5635         (l->getNature() == PDE_BOUNDARY)) {
5636       p->select_set[selected++] = i;
5637       for (int j = 0; j < mesh->getSurfaces(); j++) {
5638         surface_t *s = mesh->getSurface(j);
5639         if ((s->getIndex() == l->getIndex()) &&
5640             (s->getNature() == PDE_BOUNDARY))
5641           s->setIndex(targetindex);
5642       }
5643     }
5644   }
5645 
5646   for (int i = 0; i < unusedBoundary.size(); i++) {
5647     boundaryPropertyEditor.remove(
5648         boundaryPropertyEditor.indexOf(unusedBoundary[i]));
5649     delete unusedBoundary[i];
5650   }
5651 
5652   cout << "Selected surfaces marked with index " << targetindex << endl;
5653   cout.flush();
5654 
5655   glWidget->rebuildLists();
5656 
5657   logMessage("Selected surfaces unified");
5658 }
5659 
applyOperations()5660 void MainWindow::applyOperations() {
5661   mesh_t *mesh = glWidget->getMesh();
5662 
5663   cout << "Apply " << operations << " operations" << endl;
5664   cout.flush();
5665 
5666   operation_t *p = operation.next;
5667   for (; p; p = p->next) {
5668     int lists = glWidget->getLists();
5669 
5670     for (int i = 0; i < lists; i++)
5671       glWidget->getList(i)->setSelected(false);
5672 
5673     for (int j = 0; j < mesh->getSurfaces(); j++)
5674       mesh->getSurface(j)->setSelected(false);
5675 
5676     for (int j = 0; j < mesh->getEdges(); j++)
5677       mesh->getEdge(j)->setSelected(false);
5678 
5679     for (int i = 0; i < p->selected; i++) {
5680       list_t *l = glWidget->getList(p->select_set[i]);
5681 
5682       l->setSelected(true);
5683       if (p->type < OP_UNIFY_EDGE) {
5684         for (int j = 0; j < mesh->getSurfaces(); j++) {
5685           surface_t *surf = mesh->getSurface(j);
5686           if (l->getIndex() == surf->getIndex())
5687             surf->setSelected(l->isSelected());
5688         }
5689       } else {
5690         for (int j = 0; j < mesh->getEdges(); j++) {
5691           edge_t *edge = mesh->getEdge(j);
5692           if (l->getIndex() == edge->getIndex())
5693             edge->setSelected(l->isSelected());
5694         }
5695       }
5696     }
5697 
5698     if (p->type == OP_DIVIDE_SURFACE) {
5699       meshutils->findSharpEdges(mesh, p->angle);
5700       int parts = meshutils->divideSurfaceBySharpEdges(mesh);
5701       QString qs = "Surface divided into " + QString::number(parts) + " parts";
5702       statusBar()->showMessage(qs);
5703 
5704     } else if (p->type == OP_DIVIDE_EDGE) {
5705       meshutils->findEdgeElementPoints(mesh);
5706       meshutils->findSharpPoints(mesh, p->angle);
5707       int parts = meshutils->divideEdgeBySharpPoints(mesh);
5708       QString qs = "Edges divided into " + QString::number(parts) + " parts";
5709       statusBar()->showMessage(qs);
5710 
5711     } else if (p->type == OP_UNIFY_SURFACE) {
5712       int targetindex = -1;
5713 
5714       for (int i = 0; i < lists; i++) {
5715         list_t *l = glWidget->getList(i);
5716         if (l->isSelected() && (l->getType() == SURFACELIST) &&
5717             (l->getNature() == PDE_BOUNDARY)) {
5718           if (targetindex < 0) {
5719             targetindex = l->getIndex();
5720             break;
5721           }
5722         }
5723       }
5724       for (int i = 0; i < lists; i++) {
5725         list_t *l = glWidget->getList(i);
5726         if (l->isSelected() && (l->getType() == SURFACELIST) &&
5727             (l->getNature() == PDE_BOUNDARY)) {
5728           for (int j = 0; j < mesh->getSurfaces(); j++) {
5729             surface_t *s = mesh->getSurface(j);
5730             if ((s->getIndex() == l->getIndex()) &&
5731                 (s->getNature() == PDE_BOUNDARY))
5732               s->setIndex(targetindex);
5733           }
5734         }
5735       }
5736       cout << "Selected surfaces marked with index " << targetindex << endl;
5737       cout.flush();
5738 
5739     } else if (p->type == OP_UNIFY_EDGE) {
5740       int targetindex = -1;
5741       for (int i = 0; i < lists; i++) {
5742         list_t *l = glWidget->getList(i);
5743         if (l->isSelected() && l->getType() == EDGELIST &&
5744             l->getNature() == PDE_BOUNDARY) {
5745           if (targetindex < 0) {
5746             targetindex = l->getIndex();
5747             break;
5748           }
5749         }
5750       }
5751       for (int i = 0; i < lists; i++) {
5752         list_t *l = glWidget->getList(i);
5753         if (l->isSelected() && l->getType() == EDGELIST &&
5754             l->getNature() == PDE_BOUNDARY) {
5755           for (int j = 0; j < mesh->getEdges(); j++) {
5756             edge_t *e = mesh->getEdge(j);
5757             if (e->getIndex() == l->getIndex() &&
5758                 e->getNature() == PDE_BOUNDARY)
5759               e->setIndex(targetindex);
5760           }
5761         }
5762       }
5763       cout << "Selected edges marked with index " << targetindex << endl;
5764       cout.flush();
5765     }
5766     glWidget->rebuildLists();
5767   }
5768 
5769   synchronizeMenuToState();
5770   glWidget->updateGL();
5771 
5772   // Added 05 September 2009
5773   boundaryPropertyEditor.clear();
5774   int parts = glWidget->getLists();
5775   boundaryPropertyEditor.resize(parts);
5776   for (int i = 0; i < parts; i++)
5777     boundaryPropertyEditor[i] = new BoundaryPropertyEditor;
5778 }
5779 
5780 // Mesh -> Divide edge...
5781 //-----------------------------------------------------------------------------
edgeDivideSlot()5782 void MainWindow::edgeDivideSlot() {
5783   if (!glWidget->hasMesh()) {
5784     logMessage("There is nothing to divide - mesh is empty");
5785     return;
5786   }
5787 
5788   boundaryDivide->target = TARGET_EDGES;
5789   boundaryDivide->show();
5790 }
5791 
5792 // Make edge division by sharp points (signalled by boundaryDivide)...
5793 //-----------------------------------------------------------------------------
doDivideEdgeSlot(double angle)5794 void MainWindow::doDivideEdgeSlot(double angle) {
5795   mesh_t *mesh = glWidget->getMesh();
5796   int lists = glWidget->getLists();
5797 
5798   if (mesh == NULL) {
5799     logMessage("No mesh to divide");
5800     return;
5801   }
5802 
5803   operations++;
5804   operation_t *p = new operation_t, *q;
5805   for (q = &operation; q->next; q = q->next)
5806     ;
5807   q->next = p;
5808   p->next = NULL;
5809 
5810   p->type = OP_DIVIDE_EDGE;
5811   p->angle = angle;
5812 
5813   int selected = 0;
5814   for (int i = 0; i < lists; i++) {
5815     list_t *l = glWidget->getList(i);
5816     if (l->isSelected() && l->getType() == EDGELIST &&
5817         l->getNature() == PDE_BOUNDARY)
5818       selected++;
5819   }
5820   p->selected = selected;
5821   p->select_set = new int[selected];
5822   selected = 0;
5823 
5824   for (int i = 0; i < lists; i++) {
5825     list_t *l = glWidget->getList(i);
5826     if (l->isSelected() && l->getType() == EDGELIST &&
5827         l->getNature() == PDE_BOUNDARY)
5828       p->select_set[selected++] = i;
5829   }
5830 
5831   meshutils->findEdgeElementPoints(mesh);
5832   meshutils->findSharpPoints(mesh, angle);
5833   int parts = meshutils->divideEdgeBySharpPoints(mesh);
5834 
5835   QString qs = "Edge divided into " + QString::number(parts) + " parts";
5836   statusBar()->showMessage(qs);
5837 
5838   synchronizeMenuToState();
5839   glWidget->rebuildLists();
5840   glWidget->updateGL();
5841 
5842   // Added 05 September 2009
5843   boundaryPropertyEditor.clear();
5844   boundaryPropertyEditor.resize(parts);
5845   for (int i = 0; i < parts; i++)
5846     boundaryPropertyEditor[i] = new BoundaryPropertyEditor;
5847 }
5848 
5849 // Mesh -> Unify edge
5850 //-----------------------------------------------------------------------------
edgeUnifySlot()5851 void MainWindow::edgeUnifySlot() {
5852   mesh_t *mesh = glWidget->getMesh();
5853   int lists = glWidget->getLists();
5854 
5855   if (mesh == NULL) {
5856     logMessage("No edges to unify");
5857     return;
5858   }
5859 
5860   int targetindex = -1, selected = 0;
5861   QVector<BoundaryPropertyEditor *> unusedBoundary;
5862   for (int i = 0; i < lists; i++) {
5863     list_t *l = glWidget->getList(i);
5864     if (l->isSelected() && l->getType() == EDGELIST &&
5865         l->getNature() == PDE_BOUNDARY) {
5866       selected++;
5867       if (targetindex < 0)
5868         targetindex = l->getIndex();
5869       else {
5870         int v = glWidget->boundaryMap.value(l->getIndex());
5871         if (v >= 0 && v < boundaryPropertyEditor.size() &&
5872             boundaryPropertyEditor[v] != NULL) {
5873           unusedBoundary.append(boundaryPropertyEditor[v]);
5874         }
5875       }
5876     }
5877   }
5878 
5879   if (targetindex < 0) {
5880     logMessage("No edges selected");
5881     return;
5882   }
5883 
5884   operations++;
5885   operation_t *p = new operation_t, *q;
5886   for (q = &operation; q->next; q = q->next)
5887     ;
5888   q->next = p;
5889   p->next = NULL;
5890   p->type = OP_UNIFY_EDGE;
5891   p->selected = selected;
5892   p->select_set = new int[selected];
5893 
5894   selected = 0;
5895 
5896   for (int i = 0; i < lists; i++) {
5897     list_t *l = glWidget->getList(i);
5898     if (l->isSelected() && l->getType() == EDGELIST &&
5899         l->getNature() == PDE_BOUNDARY) {
5900       p->select_set[selected++] = i;
5901       for (int j = 0; j < mesh->getEdges(); j++) {
5902         edge_t *e = mesh->getEdge(j);
5903         if (e->getIndex() == l->getIndex() && e->getNature() == PDE_BOUNDARY)
5904           e->setIndex(targetindex);
5905       }
5906     }
5907   }
5908 
5909   for (int i = 0; i < unusedBoundary.size(); i++) {
5910     boundaryPropertyEditor.remove(
5911         boundaryPropertyEditor.indexOf(unusedBoundary[i]));
5912     delete unusedBoundary[i];
5913   }
5914 
5915   cout << "Selected edges marked with index " << targetindex << endl;
5916   cout.flush();
5917 
5918   glWidget->rebuildLists();
5919 
5920   logMessage("Selected edges unified");
5921 }
5922 
5923 // Mesh -> Clean up
5924 //-----------------------------------------------------------------------------
cleanHangingSharpEdgesSlot()5925 void MainWindow::cleanHangingSharpEdgesSlot() {
5926   mesh_t *mesh = glWidget->getMesh();
5927 
5928   if (mesh == NULL)
5929     return;
5930 
5931   int count = meshutils->cleanHangingSharpEdges(mesh);
5932 
5933   cout << "Removed " << count << " hanging sharp edges" << endl;
5934   cout.flush();
5935 
5936   glWidget->rebuildLists();
5937 }
5938 
5939 //*****************************************************************************
5940 //
5941 //                                Edit MENU
5942 //
5943 //*****************************************************************************
5944 
5945 // Edit -> Sif...
5946 //-----------------------------------------------------------------------------
showsifSlot()5947 void MainWindow::showsifSlot() {
5948   // QFont sansFont("Courier", 10);
5949   // sifWindow->getTextEdit()->setCurrentFont(sansFont);
5950   if(sifWindow->windowState() & Qt::WindowMinimized){
5951     sifWindow->showNormal();
5952   }
5953   else sifWindow->show();
5954 }
5955 
5956 // Edit -> Generate sif
5957 //-----------------------------------------------------------------------------
generateSifSlot()5958 void MainWindow::generateSifSlot() {
5959   mesh_t *mesh = glWidget->getMesh();
5960 
5961   if (mesh == NULL) {
5962     logMessage("Unable to create SIF: no mesh");
5963     return;
5964   }
5965 
5966   if ((mesh->getDim() < 1) || (mesh->getCdim() < 1)) {
5967     logMessage("Model dimension inconsistent with SIF syntax");
5968     return;
5969   }
5970 
5971   // Clear SIF text editor:
5972   //------------------------
5973   sifWindow->getTextEdit()->clear();
5974   sifWindow->setFirstTime(true);
5975   sifWindow->setFound(false);
5976   // QFont sansFont("Courier", 10);
5977   // sifWindow->getTextEdit()->setCurrentFont(sansFont);
5978 
5979   // Set up SIF generator:
5980   //-----------------------
5981   sifGenerator->setMesh(mesh);
5982   sifGenerator->setTextEdit(sifWindow->getTextEdit());
5983   sifGenerator->setDim(mesh->getDim());
5984   sifGenerator->setCdim(mesh->getCdim());
5985   sifGenerator->setGeneralSetup(generalSetup);
5986 
5987   sifGenerator->setEquationEditor(equationEditor);
5988   sifGenerator->setMaterialEditor(materialEditor);
5989   sifGenerator->setBodyForceEditor(bodyForceEditor);
5990   sifGenerator->setInitialConditionEditor(initialConditionEditor);
5991   sifGenerator->setBoundaryConditionEditor(boundaryConditionEditor);
5992   sifGenerator->setSolverParameterEditor(solverParameterEditor);
5993   sifGenerator->setBoundaryPropertyEditor(boundaryPropertyEditor);
5994   sifGenerator->setBodyPropertyEditor(bodyPropertyEditor);
5995   sifGenerator->setMeshControl(meshControl);
5996   sifGenerator->setElmerDefs(elmerDefs);
5997   sifGenerator->bodyMap = glWidget->bodyMap;
5998   sifGenerator->boundaryMap = glWidget->boundaryMap;
5999 
6000   // Make SIF:
6001   //----------
6002   sifGenerator->makeHeaderBlock();
6003   sifGenerator->makeSimulationBlock();
6004   sifGenerator->makeConstantsBlock();
6005   sifGenerator->makeBodyBlocks();
6006   sifGenerator->makeEquationBlocks();
6007   sifGenerator->makeMaterialBlocks();
6008   sifGenerator->makeBodyForceBlocks();
6009   sifGenerator->makeInitialConditionBlocks();
6010   sifGenerator->makeBoundaryBlocks();
6011 }
6012 
6013 // Boundary selected by double clicking (signaled by glWidget::select):
6014 //-----------------------------------------------------------------------------
boundarySelectedSlot(list_t * l)6015 void MainWindow::boundarySelectedSlot(list_t *l) {
6016   QString qs;
6017 
6018   if (l->getIndex() < 0) {
6019     statusBar()->showMessage("Ready");
6020     return;
6021   }
6022 
6023   if (l->isSelected()) {
6024     if (l->getType() == SURFACELIST) {
6025       qs = "Selected surface " + QString::number(l->getIndex());
6026     } else if (l->getType() == EDGELIST) {
6027       qs = "Selected edge " + QString::number(l->getIndex());
6028     } else {
6029       qs = "Selected object " + QString::number(l->getIndex()) +
6030            " (type unknown)";
6031     }
6032   } else {
6033     if (l->getType() == SURFACELIST) {
6034       qs = "Unselected surface " + QString::number(l->getIndex());
6035     } else if (l->getType() == EDGELIST) {
6036       qs = "Unselected edge " + QString::number(l->getIndex());
6037     } else {
6038       qs = "Unselected object " + QString::number(l->getIndex()) +
6039            " (type unknown)";
6040     }
6041   }
6042 
6043   logMessage(qs);
6044 
6045   // Open bc property sheet for selected boundary:
6046   //-----------------------------------------------
6047   if (l->isSelected() && (glWidget->altPressed || bcEditActive)) {
6048     glWidget->ctrlPressed = false;
6049     glWidget->shiftPressed = false;
6050     glWidget->altPressed = false;
6051 
6052     if (l->getNature() != PDE_BOUNDARY) {
6053       /*Ignore when double clicking a body of 2D geometry under boundary
6054        * selection mode*/
6055       raise();
6056       return;
6057     }
6058 
6059     // renumbering:
6060     int n = glWidget->boundaryMap.value(l->getIndex());
6061 
6062     if (n >= boundaryPropertyEditor.size()) {
6063       logMessage("Error: Boundary index mismatch");
6064       return;
6065     }
6066 
6067     BoundaryPropertyEditor *boundaryEdit = boundaryPropertyEditor[n];
6068     populateBoundaryComboBoxes(boundaryEdit);
6069 
6070     connect(boundaryEdit,
6071             SIGNAL(BoundaryAsABodyChanged(BoundaryPropertyEditor *, int)), this,
6072             SLOT(boundaryAsABodyChanged(BoundaryPropertyEditor *, int)));
6073 
6074     if (boundaryEdit->touched) {
6075       boundaryEdit->ui.applyButton->setText("Update");
6076       // boundaryEdit->ui.discardButton->setText("Remove");
6077       boundaryEdit->ui.discardButton->setText("Cancel");
6078       boundaryEdit->ui.applyButton->setIcon(
6079           QIcon(":/icons/dialog-ok-apply.png"));
6080       // boundaryEdit->ui.discardButton->setIcon(QIcon(":/icons/list-remove.png"));
6081       boundaryEdit->ui.discardButton->setIcon(
6082           QIcon(":/icons/dialog-close.png"));
6083     } else {
6084       boundaryEdit->ui.applyButton->setText("Add");
6085       boundaryEdit->ui.discardButton->setText("Cancel");
6086       boundaryEdit->ui.applyButton->setIcon(QIcon(":/icons/list-add.png"));
6087       boundaryEdit->ui.discardButton->setIcon(
6088           QIcon(":/icons/dialog-close.png"));
6089     }
6090 
6091     boundaryEdit->setWindowTitle("Properties for boundary " +
6092                                  QString::number(l->getIndex()));
6093     boundaryEdit->show();
6094     boundaryEdit->raise();
6095   }
6096 
6097   BodyPropertyEditor *bodyEdit = NULL;
6098   int current = -1, n = -1;
6099 
6100   // boundary as a body treatment
6101   // ----------------------------
6102   if (l->isSelected() && glWidget->ctrlPressed) {
6103 
6104     // renumbering:
6105     int n = glWidget->boundaryMap.value(l->getIndex());
6106 
6107     if (n >= boundaryPropertyEditor.size()) {
6108       logMessage("Error: Boundary index mismatch");
6109       return;
6110     }
6111 
6112     BoundaryPropertyEditor *boundaryEdit = boundaryPropertyEditor[n];
6113 
6114     if (!boundaryEdit) {
6115       cout << "MainWindow: Boundary index out of bounds" << endl;
6116       return;
6117     }
6118 
6119     bodyEdit = boundaryEdit->bodyProperties;
6120 
6121     if (bodyEdit) {
6122       glWidget->ctrlPressed = false;
6123       glWidget->shiftPressed = false;
6124       glWidget->altPressed = false;
6125 
6126       bodyEdit->setWindowTitle("Properties for body " +
6127                                QString::number(current));
6128 
6129       // if(bodyEdit->ui.nameEdit->text().trimmed().isEmpty())
6130       // bodyEdit->ui.nameEdit->setText("Body Property{Boundary " +
6131       // QString::number(n+1) +  "}");
6132       bodyEdit->ui.nameEdit->setText("Body {Boundary " +
6133                                      QString::number(n + 1) + "}");
6134     }
6135   }
6136 
6137   // Open body property sheet for selected body:
6138   //---------------------------------------------
6139   if ((glWidget->currentlySelectedBody >= 0) &&
6140       (glWidget->shiftPressed || bodyEditActive)) {
6141 
6142     glWidget->ctrlPressed = false;
6143     glWidget->shiftPressed = false;
6144     glWidget->altPressed = false;
6145 
6146     current = glWidget->currentlySelectedBody;
6147 
6148     cout << "Current selection uniquely determines body: " << current << endl;
6149     cout.flush();
6150 
6151     // renumbering:
6152     n = glWidget->bodyMap.value(current);
6153 
6154     if (n >= bodyPropertyEditor.size()) {
6155       logMessage("MainWindow: Body index out of bounds)");
6156       return;
6157     }
6158 
6159     bodyEdit = bodyPropertyEditor[n];
6160 
6161     if (!bodyEdit)
6162       cout << "MainWindow: Undetermined body index" << endl;
6163 
6164     bodyEdit->setWindowTitle("Properties for body " + QString::number(current));
6165 
6166     if (bodyEdit->ui.nameEdit->text().trimmed().isEmpty())
6167       bodyEdit->ui.nameEdit->setText("Body Property " + QString::number(n + 1));
6168   }
6169 
6170   if (bodyEdit) {
6171     populateBodyComboBoxes(bodyEdit);
6172 
6173     if (bodyEdit->touched) {
6174       bodyEdit->ui.applyButton->setText("Update");
6175       // bodyEdit->ui.discardButton->setText("Remove");
6176       bodyEdit->ui.discardButton->setText("Cancel");
6177       bodyEdit->ui.applyButton->setIcon(QIcon(":/icons/dialog-ok-apply.png"));
6178       // bodyEdit->ui.discardButton->setIcon(QIcon(":/icons/list-remove.png"));
6179       bodyEdit->ui.discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
6180     } else {
6181       bodyEdit->ui.applyButton->setText("Add");
6182       bodyEdit->ui.discardButton->setText("Cancel");
6183       bodyEdit->ui.applyButton->setIcon(QIcon(":/icons/list-add.png"));
6184       bodyEdit->ui.discardButton->setIcon(QIcon(":/icons/dialog-close.png"));
6185     }
6186 
6187     bodyEdit->show();
6188     bodyEdit->raise();
6189   }
6190 }
6191 
6192 // Populate boundary editor's comboboxes:
6193 //---------------------------------------
populateBoundaryComboBoxes(BoundaryPropertyEditor * boundary)6194 void MainWindow::populateBoundaryComboBoxes(BoundaryPropertyEditor *boundary) {
6195   boundary->disconnect(
6196       SIGNAL(BoundaryComboChanged(BoundaryPropertyEditor *, QString)));
6197   while (boundary && boundary->ui.boundaryConditionCombo &&
6198          boundary->ui.boundaryConditionCombo->count() > 0)
6199     boundary->ui.boundaryConditionCombo->removeItem(0);
6200 
6201   int takethis = 1; //-1;
6202   int count = 1;
6203   boundary->ui.boundaryConditionCombo->insertItem(count++, "");
6204 
6205   for (int i = 0; i < boundaryConditionEditor.size(); i++) {
6206     DynamicEditor *bcEdit = boundaryConditionEditor[i];
6207     if (bcEdit->menuAction != NULL) {
6208       const QString &name = bcEdit->nameEdit->text().trimmed();
6209       boundary->ui.boundaryConditionCombo->insertItem(count, name);
6210       if (boundary->condition == bcEdit)
6211         takethis = count;
6212       count++;
6213     }
6214   }
6215   connect(boundary,
6216           SIGNAL(BoundaryComboChanged(BoundaryPropertyEditor *, QString)), this,
6217           SLOT(boundaryComboChanged(BoundaryPropertyEditor *, QString)));
6218 
6219   boundary->ui.boundaryConditionCombo->setCurrentIndex(takethis - 1);
6220 }
6221 
6222 //-----------------------------------------------------------------------------
boundaryComboChanged(BoundaryPropertyEditor * b,QString text)6223 void MainWindow::boundaryComboChanged(BoundaryPropertyEditor *b, QString text) {
6224   b->condition = 0;
6225   b->touched = false;
6226 
6227   for (int i = 0; i < boundaryConditionEditor.size(); i++) {
6228     DynamicEditor *bc = boundaryConditionEditor[i];
6229     if (bc->ID >= 0) {
6230       if (bc->nameEdit->text().trimmed() == text) {
6231         b->condition = bc;
6232         b->touched = true;
6233         break;
6234       }
6235     }
6236   }
6237 }
6238 
6239 //-----------------------------------------------------------------------------
boundaryAsABodyChanged(BoundaryPropertyEditor * b,int status)6240 void MainWindow::boundaryAsABodyChanged(BoundaryPropertyEditor *b, int status) {
6241   int indx = glWidget->bodyMap.count();
6242 
6243   if (status) {
6244     for (int i = 0; i < boundaryPropertyEditor.size(); i++)
6245       if (boundaryPropertyEditor[i] &&
6246           boundaryPropertyEditor[i]->bodyProperties)
6247         indx++;
6248 
6249     if (indx >= bodyPropertyEditor.size()) {
6250       cout << "MainWindow: Body index out of bounds" << endl;
6251       return;
6252     }
6253 
6254     if (bodyPropertyEditor[indx])
6255       b->bodyProperties = bodyPropertyEditor[indx];
6256 
6257   } else {
6258 
6259     b->bodyProperties = NULL;
6260   }
6261 }
6262 
6263 // Populate body editor's comboboxes:
6264 //-----------------------------------
populateBodyComboBoxes(BodyPropertyEditor * bodyEdit)6265 void MainWindow::populateBodyComboBoxes(BodyPropertyEditor *bodyEdit) {
6266   // Equation:
6267   // =========
6268   bodyEdit->disconnect(
6269       SIGNAL(BodyEquationComboChanged(BodyPropertyEditor *, QString)));
6270   bodyEdit->ui.equationCombo->clear();
6271 
6272   int count = 1;
6273   int takethis = 1; // -1
6274   bodyEdit->ui.equationCombo->insertItem(count++, "");
6275 
6276   for (int i = 0; i < equationEditor.size(); i++) {
6277     DynamicEditor *eqEdit = equationEditor[i];
6278     if (eqEdit->menuAction != NULL) {
6279       const QString &name = eqEdit->nameEdit->text().trimmed();
6280       bodyEdit->ui.equationCombo->insertItem(count, name);
6281       if (bodyEdit->equation == eqEdit)
6282         takethis = count;
6283       count++;
6284     }
6285   }
6286   connect(bodyEdit,
6287           SIGNAL(BodyEquationComboChanged(BodyPropertyEditor *, QString)), this,
6288           SLOT(equationComboChanged(BodyPropertyEditor *, QString)));
6289   bodyEdit->ui.equationCombo->setCurrentIndex(takethis - 1);
6290 
6291   // Material
6292   // =========
6293   bodyEdit->disconnect(
6294       SIGNAL(BodyMaterialComboChanged(BodyPropertyEditor *, QString)));
6295   bodyEdit->ui.materialCombo->clear();
6296 
6297   count = 1;
6298   takethis = 1;
6299   bodyEdit->ui.materialCombo->insertItem(count, "");
6300   count++;
6301 
6302   for (int i = 0; i < materialEditor.size(); i++) {
6303     DynamicEditor *matEdit = materialEditor[i];
6304 
6305     if (matEdit->menuAction != NULL) {
6306       const QString &name = matEdit->nameEdit->text().trimmed();
6307       bodyEdit->ui.materialCombo->insertItem(count, name);
6308       if (bodyEdit->material == matEdit)
6309         takethis = count;
6310       count++;
6311     }
6312   }
6313 
6314   connect(bodyEdit,
6315           SIGNAL(BodyMaterialComboChanged(BodyPropertyEditor *, QString)), this,
6316           SLOT(materialComboChanged(BodyPropertyEditor *, QString)));
6317 
6318   bodyEdit->ui.materialCombo->setCurrentIndex(takethis - 1);
6319 
6320   // Bodyforce:
6321   //===========
6322   bodyEdit->disconnect(
6323       SIGNAL(BodyForceComboChanged(BodyPropertyEditor *, QString)));
6324   bodyEdit->ui.bodyForceCombo->clear();
6325 
6326   count = 1;
6327   takethis = 1; // -1
6328   bodyEdit->ui.bodyForceCombo->insertItem(count++, "");
6329 
6330   for (int i = 0; i < bodyForceEditor.size(); i++) {
6331     DynamicEditor *bodyForceEdit = bodyForceEditor[i];
6332     if (bodyForceEdit->menuAction != NULL) {
6333       const QString &name = bodyForceEdit->nameEdit->text().trimmed();
6334       bodyEdit->ui.bodyForceCombo->insertItem(count, name);
6335       if (bodyEdit->force == bodyForceEdit)
6336         takethis = count;
6337       count++;
6338     }
6339   }
6340   connect(bodyEdit,
6341           SIGNAL(BodyForceComboChanged(BodyPropertyEditor *, QString)), this,
6342           SLOT(forceComboChanged(BodyPropertyEditor *, QString)));
6343   bodyEdit->ui.bodyForceCombo->setCurrentIndex(takethis - 1);
6344 
6345   // Initial Condition:
6346   //====================
6347   bodyEdit->disconnect(
6348       SIGNAL(BodyInitialComboChanged(BodyPropertyEditor *, QString)));
6349   bodyEdit->ui.initialConditionCombo->clear();
6350 
6351   count = 1;
6352   takethis = 1; // -1
6353   bodyEdit->ui.initialConditionCombo->insertItem(count++, "");
6354 
6355   for (int i = 0; i < initialConditionEditor.size(); i++) {
6356     DynamicEditor *initialConditionEdit = initialConditionEditor[i];
6357     if (initialConditionEdit->menuAction != NULL) {
6358       const QString &name = initialConditionEdit->nameEdit->text().trimmed();
6359       bodyEdit->ui.initialConditionCombo->insertItem(count, name);
6360       if (bodyEdit->initial == initialConditionEdit)
6361         takethis = count;
6362       count++;
6363     }
6364   }
6365   connect(bodyEdit,
6366           SIGNAL(BodyInitialComboChanged(BodyPropertyEditor *, QString)), this,
6367           SLOT(initialComboChanged(BodyPropertyEditor *, QString)));
6368   bodyEdit->ui.initialConditionCombo->setCurrentIndex(takethis - 1);
6369 }
6370 
6371 //-----------------------------------------------------------------------------
materialComboChanged(BodyPropertyEditor * b,QString text)6372 void MainWindow::materialComboChanged(BodyPropertyEditor *b, QString text) {
6373   b->material = 0;
6374   b->touched = false;
6375   for (int i = 0; i < materialEditor.size(); i++) {
6376     DynamicEditor *mat = materialEditor[i];
6377 
6378     if (mat->ID >= 0) {
6379       if (mat->nameEdit->text().trimmed() == text) {
6380         b->material = mat;
6381         b->touched = true;
6382         break;
6383       }
6384     }
6385   }
6386 }
6387 
6388 //-----------------------------------------------------------------------------
initialComboChanged(BodyPropertyEditor * b,QString text)6389 void MainWindow::initialComboChanged(BodyPropertyEditor *b, QString text) {
6390   b->initial = 0;
6391   b->touched = false;
6392 
6393   for (int i = 0; i < initialConditionEditor.size(); i++) {
6394     DynamicEditor *ic = initialConditionEditor[i];
6395     if (ic->ID >= 0) {
6396       if (ic->nameEdit->text().trimmed() == text) {
6397         b->initial = ic;
6398         b->touched = true;
6399         break;
6400       }
6401     }
6402   }
6403 }
6404 
6405 //-----------------------------------------------------------------------------
forceComboChanged(BodyPropertyEditor * b,QString text)6406 void MainWindow::forceComboChanged(BodyPropertyEditor *b, QString text) {
6407   b->force = 0;
6408   b->touched = false;
6409 
6410   for (int i = 0; i < bodyForceEditor.size(); i++) {
6411     DynamicEditor *bf = bodyForceEditor[i];
6412     if (bf->ID >= 0) {
6413       if (bf->nameEdit->text().trimmed() == text) {
6414         b->force = bf;
6415         b->touched = true;
6416         break;
6417       }
6418     }
6419   }
6420 }
6421 
6422 //-----------------------------------------------------------------------------
equationComboChanged(BodyPropertyEditor * b,QString text)6423 void MainWindow::equationComboChanged(BodyPropertyEditor *b, QString text) {
6424   b->equation = 0;
6425   b->touched = false;
6426 
6427   for (int i = 0; i < equationEditor.size(); i++) {
6428     DynamicEditor *equ = equationEditor[i];
6429     if (equ->ID >= 0) {
6430       if (equ->nameEdit->text().trimmed() == text) {
6431         b->equation = equ;
6432         b->touched = true;
6433         break;
6434       }
6435     }
6436   }
6437 }
6438 
6439 // Edit -> Definitions...
6440 //-----------------------------------------------------------------------------
editDefinitionsSlot()6441 void MainWindow::editDefinitionsSlot() {
6442   if (elmerDefs == NULL)
6443     return;
6444 
6445   edfEditor->show();
6446 }
6447 
6448 //*****************************************************************************
6449 //
6450 //                                Solver MENU
6451 //
6452 //*****************************************************************************
6453 
6454 // Solver -> Parallel settings
6455 //-----------------------------------------------------------------------------
parallelSettingsSlot()6456 void MainWindow::parallelSettingsSlot() { parallel->show(); }
6457 
6458 // Solver -> Run solver
6459 //-----------------------------------------------------------------------------
runsolverSlot()6460 void MainWindow::runsolverSlot() {
6461   if (!glWidget->hasMesh()) {
6462     logMessage("No mesh - unable to start solver");
6463     return;
6464   }
6465 
6466   if (solver->state() == QProcess::Running) {
6467     logMessage("Solver is already running - returning");
6468     return;
6469   }
6470 
6471   // Parallel solution:
6472   //====================
6473   Ui::parallelDialog ui = parallel->ui;
6474   bool parallelActive = ui.parallelActiveCheckBox->isChecked();
6475   bool partitioningActive = !ui.skipPartitioningCheckBox->isChecked();
6476   int nofProcessors = ui.nofProcessorsSpinBox->value();
6477 
6478   if (parallelActive) {
6479 
6480     // Set up log window:
6481     solverLogWindow->setWindowTitle(tr("Solver log"));
6482     solverLogWindow->getTextEdit()->clear();
6483     solverLogWindow->setFound(false);
6484     solverLogWindow->show();
6485 
6486     if (!partitioningActive) {
6487 
6488       // skip splitting:
6489       meshSplitterFinishedSlot(0);
6490 
6491     } else {
6492 
6493       // split mesh:
6494       if (meshSplitter->state() == QProcess::Running) {
6495         logMessage("Mesh partitioner is already running - aborted");
6496         return;
6497       }
6498 
6499       if (saveDirName.isEmpty()) {
6500         logMessage("Please save the mesh before running the parallel solver - "
6501                    "aborted");
6502         return;
6503       }
6504 
6505       QString partitioningCommand = ui.divideLineEdit->text().trimmed();
6506       partitioningCommand.replace(QString("%msh"), saveDirName);
6507       partitioningCommand.replace(QString("%n"),
6508                                   QString::number(nofProcessors));
6509 
6510       logMessage("Executing: " + partitioningCommand);
6511 
6512       meshSplitter->start(partitioningCommand);
6513 
6514       if (!meshSplitter->waitForStarted()) {
6515         logMessage("Unable to start ElmerGrid for mesh partitioning - aborted");
6516         return;
6517       }
6518     }
6519 
6520     // the rest is done in meshSplitterFinishedSlot:
6521     return;
6522   }
6523 
6524   // Scalar solution:
6525   //==================
6526   solver->start("ElmerSolver");
6527   killsolverAct->setEnabled(true);
6528 
6529   if (!solver->waitForStarted()) {
6530     logMessage("Unable to start solver");
6531     return;
6532   }
6533 
6534   solverLogWindow->setWindowTitle(tr("Solver log"));
6535   solverLogWindow->getTextEdit()->clear();
6536   solverLogWindow->setFound(false);
6537   solverLogWindow->show();
6538 
6539   // convergence plot:
6540 #ifdef EG_QWT
6541   convergenceView->removeData();
6542   convergenceView->title = "Convergence history";
6543 #endif
6544 
6545   logMessage("Solver started");
6546 
6547   runsolverAct->setIcon(QIcon(":/icons/Solver-red.png"));
6548 
6549   updateSysTrayIcon("ElmerSolver started",
6550                     "Use Run->Kill solver to stop processing");
6551 }
6552 
6553 // meshSplitter emits (int) when ready...
6554 //-----------------------------------------------------------------------------
meshSplitterFinishedSlot(int exitCode)6555 void MainWindow::meshSplitterFinishedSlot(int exitCode) {
6556   if (exitCode != 0) {
6557     solverLogWindow->getTextEdit()->append("MeshSplitter failed - aborting");
6558     logMessage("MeshSplitter failed - aborting");
6559     return;
6560   }
6561 
6562   logMessage("MeshSplitter ready");
6563 
6564   // Prepare for parallel solution:
6565   Ui::parallelDialog ui = parallel->ui;
6566 
6567   int nofProcessors = ui.nofProcessorsSpinBox->value();
6568 
6569   QString parallelExec = ui.parallelExecLineEdit->text().trimmed();
6570 #ifdef WIN32
6571   parallelExec = "\"" + parallelExec + "\"";
6572 #endif
6573 
6574   QString parallelArgs = ui.parallelArgsLineEdit->text().trimmed();
6575   parallelArgs.replace(QString("%n"), QString::number(nofProcessors));
6576 
6577   QString parallelCmd = parallelExec + " " + parallelArgs;
6578 
6579   logMessage("Executing: " + parallelCmd);
6580 
6581   solver->start(parallelCmd);
6582   killsolverAct->setEnabled(true);
6583 
6584   if (!solver->waitForStarted()) {
6585     solverLogWindow->getTextEdit()->append("Unable to start parallel solver");
6586     logMessage("Unable to start parallel solver");
6587     return;
6588   }
6589 
6590   // Set up convergence plot:
6591 #ifdef EG_QWT
6592   convergenceView->removeData();
6593   convergenceView->title = "Convergence history";
6594 #endif
6595   logMessage("Parallel solver started");
6596   runsolverAct->setIcon(QIcon(":/icons/Solver-red.png"));
6597 
6598   updateSysTrayIcon("ElmerSolver started",
6599                     "Use Run->Kill solver to stop processing");
6600 }
6601 
6602 // meshSplitter emits (void) when there is something to read from stdout:
6603 //-----------------------------------------------------------------------------
meshSplitterStdoutSlot()6604 void MainWindow::meshSplitterStdoutSlot() {
6605   QString qs = meshSplitter->readAllStandardOutput();
6606 
6607   while (qs.at(qs.size() - 1).unicode() == '\n')
6608     qs.chop(1);
6609 
6610   solverLogWindow->getTextEdit()->append(qs);
6611 }
6612 
6613 // meshSplitter emits (void) when there is something to read from stderr:
6614 //-----------------------------------------------------------------------------
meshSplitterStderrSlot()6615 void MainWindow::meshSplitterStderrSlot() {
6616   QString qs = meshSplitter->readAllStandardError();
6617 
6618   while (qs.at(qs.size() - 1).unicode() == '\n')
6619     qs.chop(1);
6620 
6621   solverLogWindow->getTextEdit()->append(qs);
6622 }
6623 
6624 // meshUnifier emits (int) when ready...
6625 //-----------------------------------------------------------------------------
meshUnifierFinishedSlot(int exitCode)6626 void MainWindow::meshUnifierFinishedSlot(int exitCode) {
6627   QStringList args;
6628 
6629   if (exitCode != 0) {
6630     solverLogWindow->getTextEdit()->append("MeshUnifier failed - aborting");
6631     logMessage("MeshUnifier failed - aborting");
6632     vtkPostMeshUnifierRunning = false;
6633     return;
6634   }
6635 
6636   logMessage("MeshUnifier ready");
6637 
6638   // Prepare for post processing parallel reults:
6639   //----------------------------------------------
6640   QString postName = generalSetup->ui.postFileEdit->text().trimmed();
6641 
6642   // VtkPost:
6643   //---------
6644 #ifdef EG_VTK
6645   if (vtkPostMeshUnifierRunning) {
6646     vtkPost->show();
6647 
6648     vtkPost->ReadPostFile(postName);
6649     // if(!vtkPost->ReadPostFile(postName)) vtkPost->readEpFileSlot();
6650 
6651     vtkPostMeshUnifierRunning = false;
6652     return;
6653   }
6654 #endif
6655 
6656   QFile file(postName);
6657   if (!file.exists()) {
6658     solverLogWindow->getTextEdit()->append(
6659         "Elmerpost input file does not exist.");
6660     logMessage("Elmerpost input file does not exist.");
6661     vtkPostMeshUnifierRunning = false;
6662     return;
6663   }
6664 
6665   file.open(QIODevice::ReadOnly);
6666   QTextStream header(&file);
6667 
6668   int nn, ne, nt, nf;
6669   QString type, name, tstep;
6670 
6671   header >> nn >> ne >> nf >> nt >> type >> name;
6672   if (type == "vector:")
6673     name = name + "_abs";
6674 
6675   file.close();
6676 
6677   QString simtype =
6678       generalSetup->ui.simulationTypeCombo->currentText().trimmed();
6679   if (simtype.toLower() == "transient") {
6680     tstep = QString::number(1);
6681   } else {
6682     tstep = QString::number(nt);
6683   }
6684 
6685   args << "readfile " + postName + " " + tstep + " " + tstep +
6686               "1; "
6687               "set ColorScaleY -0.85; "
6688               "set ColorScaleEntries  4;"
6689               "set ColorScaleDecimals 2;"
6690               "set ColorScaleColor " +
6691               name +
6692               ";"
6693               "set DisplayStyle(ColorScale) 1; "
6694               "set MeshStyle 1; "
6695               "set MeshColor " +
6696               name +
6697               ";"
6698               "set DisplayStyle(ColorMesh) 1; "
6699               "translate -y 0.2; "
6700               "UpdateObject; ";
6701 
6702   post->start("ElmerPost", args);
6703   killresultsAct->setEnabled(true);
6704 
6705   if (!post->waitForStarted()) {
6706     logMessage("Unable to start post processor");
6707     return;
6708   }
6709 
6710   resultsAct->setIcon(QIcon(":/icons/Post-red.png"));
6711 
6712   logMessage("Post processor started");
6713 
6714   updateSysTrayIcon("ElmerPost started",
6715                     "Use Run->Kill ElmerPost to stop processing");
6716 }
6717 
6718 // meshUnifier emits (void) when there is something to read from stdout:
6719 //-----------------------------------------------------------------------------
meshUnifierStdoutSlot()6720 void MainWindow::meshUnifierStdoutSlot() {
6721   QString qs = meshUnifier->readAllStandardOutput();
6722 
6723   while (qs.at(qs.size() - 1).unicode() == '\n')
6724     qs.chop(1);
6725 
6726   solverLogWindow->getTextEdit()->append(qs);
6727 }
6728 
6729 // meshUnifier emits (void) when there is something to read from stderr:
6730 //-----------------------------------------------------------------------------
meshUnifierStderrSlot()6731 void MainWindow::meshUnifierStderrSlot() {
6732   QString qs = meshUnifier->readAllStandardError();
6733 
6734   while (qs.at(qs.size() - 1).unicode() == '\n')
6735     qs.chop(1);
6736 
6737   solverLogWindow->getTextEdit()->append(qs);
6738 }
6739 
6740 // solver process emits (void) when there is something to read from stdout:
6741 //-----------------------------------------------------------------------------
solverStdoutSlot()6742 void MainWindow::solverStdoutSlot() {
6743   static QString qs_save = "";
6744 
6745   QString qs = qs_save + solver->readAllStandardOutput();
6746 
6747   int n = qs.lastIndexOf('\n');
6748 
6749   if ((n > 0) && (n < qs.size() - 1)) {
6750     qs_save = qs.mid(n + 1);
6751     qs = qs.mid(0, n);
6752 
6753   } else if (n == 0) {
6754     if (qs.size() == 1) {
6755       qs_save = "";
6756       return;
6757     }
6758     qs_save = qs.mid(1);
6759     return;
6760 
6761   } else if (n < 0) {
6762     qs_save = qs;
6763     return;
6764 
6765   } else
6766     qs_save = "";
6767 
6768   while (qs.at(qs.size() - 1).unicode() == '\n')
6769     qs.chop(1);
6770 
6771   if (qs.isEmpty())
6772     return;
6773 
6774   solverLogWindow->getTextEdit()->append(qs);
6775 
6776 #ifdef EG_QWT
6777   if (!showConvergence) {
6778 
6779     // hide convergence plot
6780     //----------------------
6781     convergenceView->hide();
6782 
6783   } else {
6784 
6785     // show convergence plot
6786     //----------------------
6787     if (!convergenceView->isVisible())
6788       convergenceView->show();
6789   }
6790 #endif
6791 
6792   QStringList qsl = qs.split("\n");
6793   for (int i = 0; i < qsl.count(); i++) {
6794     QString tmp = qsl.at(i).trimmed();
6795 
6796     if (tmp.contains("Time:")) {
6797       QStringList tmpSplitted = tmp.split(" ");
6798       int last = tmpSplitted.count() - 1;
6799       QString timeString = tmpSplitted.at(last);
6800       double timeDouble = timeString.toDouble();
6801 #ifdef EG_QWT
6802       convergenceView->title =
6803           "Convergence history (time=" + QString::number(timeDouble) + ")";
6804 #endif
6805     }
6806 
6807     if (tmp.contains("ComputeChange")) { // && tmp.contains("NS")) {
6808       QString copyOfTmp = tmp;
6809 
6810       // check solver name:
6811       QStringList tmpSplitted = tmp.split(":");
6812       int last = tmpSplitted.count() - 1;
6813       QString name = tmpSplitted.at(last).trimmed();
6814 
6815       // parse rest of the line:
6816       double res1 = 0.0;
6817       double res2 = 0.0;
6818       int n = tmp.indexOf("NRM,RELC");
6819       tmp = tmp.mid(n);
6820       tmpSplitted = tmp.split("(");
6821 
6822       if (tmpSplitted.count() >= 2) {
6823         QString tmp2 = tmpSplitted.at(1).trimmed();
6824         QStringList tmp2Splitted = tmp2.split(" ");
6825         QString qs1 = tmp2Splitted.at(0).trimmed();
6826         res1 = qs1.toDouble();
6827         int pos = 1;
6828         // while(tmp2Splitted.at(pos).trimmed() == "") {
6829         while (tmp2Splitted.at(pos).trimmed().isEmpty()) {
6830           pos++;
6831           if (pos > tmp2Splitted.count())
6832             break;
6833         }
6834         QString qs2 = tmp2Splitted.at(pos).trimmed();
6835         res2 = max(qs2.toDouble(), 1.0e-16);
6836       }
6837 
6838       // res1 = norm, res2 = relative change
6839 #ifdef EG_QWT
6840       if (copyOfTmp.contains("NS"))
6841         convergenceView->appendData(res2, "NS/" + name);
6842 
6843       if (copyOfTmp.contains("SS"))
6844         convergenceView->appendData(res2, "SS/" + name);
6845 #endif
6846     }
6847   }
6848 }
6849 
6850 // solver process emits (void) when there is something to read from stderr:
6851 //-----------------------------------------------------------------------------
solverStderrSlot()6852 void MainWindow::solverStderrSlot() {
6853   QString qs = solver->readAllStandardError();
6854 
6855   while (qs.at(qs.size() - 1).unicode() == '\n')
6856     qs.chop(1);
6857 
6858   solverLogWindow->getTextEdit()->append(qs);
6859 }
6860 
6861 // solver process emits (int) when ready...
6862 //-----------------------------------------------------------------------------
solverFinishedSlot(int)6863 void MainWindow::solverFinishedSlot(int) {
6864   logMessage("Solver ready");
6865   runsolverAct->setIcon(QIcon(":/icons/Solver.png"));
6866   updateSysTrayIcon(
6867       "ElmerSolver has finished",
6868       "Use Run->Start ElmerPost, ElmerVTK or Paraview to view results");
6869   killsolverAct->setEnabled(false);
6870 }
6871 
6872 // solver process emits (QProcess::ProcessError) when error occurs...
6873 //-----------------------------------------------------------------------------
solverErrorSlot(QProcess::ProcessError error)6874 void MainWindow::solverErrorSlot(QProcess::ProcessError error) {
6875   logMessage("Solver emitted error signal: " + QString::number(error));
6876   solver->kill();
6877   runsolverAct->setIcon(QIcon(":/icons/Solver.png"));
6878 }
6879 
6880 // solver process emits (QProcess::ProcessState) when state changed...
6881 //-----------------------------------------------------------------------------
solverStateChangedSlot(QProcess::ProcessState state)6882 void MainWindow::solverStateChangedSlot(QProcess::ProcessState state) {
6883   logMessage("Solver emitted signal: QProcess::ProcessState: " +
6884              QString::number(state));
6885   // solver->kill();
6886   // runsolverAct->setIcon(QIcon(":/icons/Solver.png"));
6887 }
6888 
6889 // Solver -> Kill solver
6890 //-----------------------------------------------------------------------------
killsolverSlot()6891 void MainWindow::killsolverSlot() {
6892   solver->kill();
6893 
6894   logMessage("Solver killed");
6895   runsolverAct->setIcon(QIcon(":/icons/Solver.png"));
6896 }
6897 
6898 // Solver -> Show convergence
6899 //-----------------------------------------------------------------------------
showConvergenceSlot()6900 void MainWindow::showConvergenceSlot() {
6901   showConvergence = !showConvergence;
6902   synchronizeMenuToState();
6903 }
6904 
6905 // Solver -> Run post process
6906 //-----------------------------------------------------------------------------
resultsSlot()6907 void MainWindow::resultsSlot() {
6908   QStringList args;
6909 
6910   if (post->state() == QProcess::Running) {
6911     logMessage("Post processor is already running");
6912     return;
6913   }
6914 
6915   // Parallel solution:
6916   //====================
6917   Ui::parallelDialog ui = parallel->ui;
6918   bool parallelActive = ui.parallelActiveCheckBox->isChecked();
6919 
6920   if (parallelActive) {
6921 
6922     // unify mesh:
6923     if (meshUnifier->state() == QProcess::Running) {
6924       logMessage("Mesh unifier is already running - aborted");
6925       return;
6926     }
6927 
6928     if (saveDirName.isEmpty()) {
6929       logMessage("saveDirName is empty - unable to locate result files");
6930       return;
6931     }
6932 
6933     // Set up log window:
6934     solverLogWindow->setWindowTitle(tr("ElmerGrid log"));
6935     solverLogWindow->getTextEdit()->clear();
6936     solverLogWindow->setFound(false);
6937     solverLogWindow->show();
6938 
6939     QString postName = generalSetup->ui.postFileEdit->text().trimmed();
6940     QStringList postNameSplitted = postName.split(".");
6941     int nofProcessors = ui.nofProcessorsSpinBox->value();
6942 
6943     QString unifyingCommand = ui.mergeLineEdit->text().trimmed();
6944     unifyingCommand.replace(QString("%ep"), postNameSplitted.at(0).trimmed());
6945     unifyingCommand.replace(QString("%n"), QString::number(nofProcessors));
6946 
6947     logMessage("Executing: " + unifyingCommand);
6948 
6949     meshUnifier->start(unifyingCommand);
6950 
6951     if (!meshUnifier->waitForStarted()) {
6952       solverLogWindow->getTextEdit()->append(
6953           "Unable to start ElmerGrid for mesh unification - aborted");
6954       logMessage("Unable to start ElmerGrid for mesh unification - aborted");
6955       return;
6956     }
6957 
6958     // The rest is done in meshUnifierFinishedSlot:
6959     return;
6960   }
6961 
6962   // Scalar solution:
6963   //==================
6964   QString postName = generalSetup->ui.postFileEdit->text().trimmed();
6965   QFile file(postName);
6966   if (!file.exists()) {
6967     logMessage("Elmerpost input file does not exist.");
6968     /*Even though input file does not exist, lauch ElmerPost*/
6969     post->start("ElmerPost");
6970     killresultsAct->setEnabled(true);
6971     if (!post->waitForStarted()) {
6972       logMessage("Unable to start ElmerPost");
6973       return;
6974     }
6975     resultsAct->setIcon(QIcon(":/icons/Post-red.png"));
6976 
6977     logMessage("ElmerPost started");
6978 
6979     updateSysTrayIcon("ElmerPost started",
6980                       "Elmerpost input file does not exist.");
6981     return;
6982   }
6983 
6984   file.open(QIODevice::ReadOnly);
6985   QTextStream header(&file);
6986 
6987   int nn, ne, nt, nf;
6988   QString type, name, tstep;
6989 
6990   header >> nn >> ne >> nf >> nt >> type >> name;
6991   if (type == "vector:")
6992     name = name + "_abs";
6993 
6994   file.close();
6995 
6996   QString simtype =
6997       generalSetup->ui.simulationTypeCombo->currentText().trimmed();
6998   if (simtype.toLower() == "transient") {
6999     tstep = QString::number(1);
7000   } else {
7001     tstep = QString::number(nt);
7002   }
7003 
7004   args << "readfile " + postName + " " + tstep + " " + tstep +
7005               " 1; "
7006               "set ColorScaleY -0.85; "
7007               "set ColorScaleEntries  4;"
7008               "set ColorScaleDecimals 2;"
7009               "set ColorScaleColor " +
7010               name +
7011               ";"
7012               "set DisplayStyle(ColorScale) 1; "
7013               "set MeshStyle 1; "
7014               "set MeshColor " +
7015               name +
7016               ";"
7017               "set DisplayStyle(ColorMesh) 1; "
7018               "translate -y 0.2; "
7019               "UpdateObject; ";
7020 
7021   post->start("ElmerPost", args);
7022   killresultsAct->setEnabled(true);
7023 
7024   if (!post->waitForStarted()) {
7025     logMessage("Unable to start ElmerPost");
7026     return;
7027   }
7028 
7029   resultsAct->setIcon(QIcon(":/icons/Post-red.png"));
7030 
7031   logMessage("ElmerPost started");
7032 
7033   updateSysTrayIcon("ElmerPost started",
7034                     "Use Run->Kill ElmerPost to stop processing");
7035 }
7036 
7037 // Signal (int) emitted by postProcess when finished:
7038 //-----------------------------------------------------------------------------
postProcessFinishedSlot(int)7039 void MainWindow::postProcessFinishedSlot(int) {
7040   logMessage("ElmerPost finished");
7041   resultsAct->setIcon(QIcon(":/icons/Post.png"));
7042   updateSysTrayIcon("ElmerPost has finished",
7043                     "Use Run->Start ElmerPost to restart");
7044   killresultsAct->setEnabled(false);
7045 }
7046 
7047 // Signal (int) emitted by paraview when finished:
7048 //-----------------------------------------------------------------------------
paraviewProcessFinishedSlot(int)7049 void MainWindow::paraviewProcessFinishedSlot(int) {
7050   logMessage("ParaView finished");
7051   updateSysTrayIcon("ParaView has finished",
7052                     "Use Run->Start ParaView to restart");
7053 }
7054 
7055 // Solver -> Kill post process
7056 //-----------------------------------------------------------------------------
killresultsSlot()7057 void MainWindow::killresultsSlot() {
7058   post->kill();
7059 
7060   logMessage("Post process killed");
7061   resultsAct->setIcon(QIcon(":/icons/Post.png"));
7062 }
7063 
7064 // Solver -> Compile...
7065 //-----------------------------------------------------------------------------
compileSolverSlot()7066 void MainWindow::compileSolverSlot() {
7067   QString defaultDirName = getDefaultDirName();
7068 
7069   QString fileName = QFileDialog::getOpenFileName(
7070       this, tr("Open source file"), defaultDirName, tr("F90 files (*.f90)"));
7071 
7072   if (!fileName.isEmpty()) {
7073     QFileInfo fi(fileName);
7074     QString absolutePath = fi.absolutePath();
7075     QDir::setCurrent(absolutePath);
7076   } else {
7077     logMessage("Unable to open file: file name is empty");
7078     return;
7079   }
7080 
7081   if (compiler->state() == QProcess::Running) {
7082     logMessage("Compiler is currently running");
7083     return;
7084   }
7085 
7086   QStringList args;
7087 #ifdef WIN32
7088   args << "/C";
7089   args << "" + QString(qgetenv("ELMER_HOME")) + "\\bin\\elmerf90.bat";
7090   QString dllFileName;
7091   dllFileName = fileName.left(fileName.lastIndexOf(".")) + ".dll";
7092   args << "-o";
7093   args << dllFileName;
7094 #endif
7095   args << fileName;
7096 
7097 #ifdef WIN32
7098   compiler->start("cmd.exe", args);
7099 #else
7100   logMessage("Run->compiler is currently not implemented on this platform");
7101   return;
7102 #endif
7103 
7104   if (!compiler->waitForStarted()) {
7105     logMessage("Unable to start compiler");
7106     return;
7107   }
7108 
7109   solverLogWindow->setWindowTitle(tr("Compiler log"));
7110   solverLogWindow->getTextEdit()->clear();
7111   solverLogWindow->setFound(false);
7112   solverLogWindow->show();
7113   solverLogWindow->statusBar()->showMessage("Compiling...");
7114 
7115   logMessage("Compiling...");
7116 }
7117 
7118 // compiler process emits (void) when there is something to read from stdout:
7119 //-----------------------------------------------------------------------------
compilerStdoutSlot()7120 void MainWindow::compilerStdoutSlot() {
7121   QString qs = compiler->readAllStandardOutput();
7122 
7123   while (qs.at(qs.size() - 1).unicode() == '\n')
7124     qs.chop(1);
7125 
7126   solverLogWindow->getTextEdit()->append(qs);
7127 }
7128 
7129 // compiler process emits (void) when there is something to read from stderr:
7130 //-----------------------------------------------------------------------------
compilerStderrSlot()7131 void MainWindow::compilerStderrSlot() {
7132   QString qs = compiler->readAllStandardError();
7133 
7134   while (qs.at(qs.size() - 1).unicode() == '\n')
7135     qs.chop(1);
7136 
7137   solverLogWindow->getTextEdit()->append(qs);
7138 }
7139 
7140 // Signal (int) emitted by compiler when finished:
7141 //-----------------------------------------------------------------------------
compilerFinishedSlot(int)7142 void MainWindow::compilerFinishedSlot(int) {
7143   logMessage("Ready");
7144   solverLogWindow->statusBar()->showMessage("Ready");
7145   solverLogWindow->getTextEdit()->append("Ready");
7146 }
7147 
7148 //*****************************************************************************
7149 //
7150 //                                Help MENU
7151 //
7152 //*****************************************************************************
7153 
7154 // About dialog...
7155 //-----------------------------------------------------------------------------
showaboutSlot()7156 void MainWindow::showaboutSlot() {
7157   QMessageBox::about(
7158       this, tr("Information about ElmerGUI"),
7159       tr("ElmerGUI is a preprocessor for two and "
7160          "three dimensional modeling with Elmer "
7161          "finite element software. The program "
7162          "uses elmergrid, nglib, and optionally tetlib, "
7163          "as finite element mesh generators:\n\n"
7164          "http://www.csc.fi/elmer/\n"
7165          "https://ngsolve.org/\n"
7166          "http://tetgen.berlios.de/\n\n"
7167          "ElmerGUI uses the Qt Cross-Platform "
7168          "Application Framework by The Qt Company:\n\n"
7169          "http://www.qt.io/\n\n"
7170 #ifdef EG_VTK
7171          "This version of ElmerGUI contains a built-in "
7172          "postprocessor based on the Visualization Toolkit "
7173          "(VTK):\n\n"
7174          "http://www.vtk.org/\n\n"
7175 #endif
7176 
7177 #ifdef EG_PARAVIEW
7178          "This version of ElmerGUI has been linked "
7179          "against ParaView visualization software."
7180          "\n\n"
7181          "http://www.paraview.org\n\n"
7182 #endif
7183 
7184 #ifdef EG_OCC
7185          "This version of ElmerGUI has been compiled with "
7186          "the OpenCascade solids modeling library:\n\n"
7187          "http://www.opencascade.org/\n\n"
7188 #endif
7189 
7190 #ifdef MPICH2
7191          "The parallel solver of this package has been linked "
7192          "against the MPICH2 library v. 1.0.7 from Argonne "
7193          "national laboratory. In order to use the parallel "
7194          "solver, the MPICH2 runtime environment should be "
7195          "installed and configured on your system. For more "
7196          "details, see:\n\n"
7197          "http://www.mcs.anl.gov/research/projects/mpich2/\n\n"
7198 #endif
7199          "The GPL-licensed source code of ElmerGUI is available "
7200          "from the git repository\n\n"
7201          "https://github.com/ElmerCSC/elmerfem/\n\n"
7202          "Written by Mikko Lyly, Juha Ruokolainen, Saeki Takayuki,\n"
7203          "Peter Raback and Sampo Sillanpaa 2008-2020"));
7204 }
7205 
7206 //*****************************************************************************
7207 //
7208 //                           Auxiliary non-menu items
7209 //
7210 //*****************************************************************************
7211 
7212 // Log message...
7213 //-----------------------------------------------------------------------------
logMessage(QString message)7214 void MainWindow::logMessage(QString message) {
7215 #if WITH_QT5
7216   cout << string(message.toLatin1()) << endl;
7217 #else
7218   cout << string(message.toAscii()) << endl;
7219 #endif
7220   statusBar()->showMessage(message, 0);
7221   cout.flush();
7222 }
7223 
7224 // Synchronize menu to GL glwidget state variables:
7225 //-----------------------------------------------------------------------------
synchronizeMenuToState()7226 void MainWindow::synchronizeMenuToState() {
7227   // glwidget state variables:
7228   if (glWidget->stateDrawSurfaceMesh)
7229     hidesurfacemeshAct->setChecked(true);
7230   else
7231     hidesurfacemeshAct->setChecked(false);
7232 
7233   if (glWidget->stateDrawVolumeMesh)
7234     hidevolumemeshAct->setChecked(true);
7235   else
7236     hidevolumemeshAct->setChecked(false);
7237 
7238   if (glWidget->stateDrawSharpEdges)
7239     hidesharpedgesAct->setChecked(true);
7240   else
7241     hidesharpedgesAct->setChecked(false);
7242 
7243   if (glWidget->stateFlatShade) {
7244     flatShadeAct->setChecked(true);
7245     smoothShadeAct->setChecked(false);
7246   } else {
7247     flatShadeAct->setChecked(false);
7248     smoothShadeAct->setChecked(true);
7249   }
7250 
7251   if (glWidget->stateOrtho) {
7252     orthoAct->setChecked(true);
7253     perspectiveAct->setChecked(false);
7254   } else {
7255     orthoAct->setChecked(false);
7256     perspectiveAct->setChecked(true);
7257   }
7258 
7259   if (glWidget->stateDrawSurfaceNumbers)
7260     showSurfaceNumbersAct->setChecked(true);
7261   else
7262     showSurfaceNumbersAct->setChecked(false);
7263 
7264   if (glWidget->stateDrawEdgeNumbers)
7265     showEdgeNumbersAct->setChecked(true);
7266   else
7267     showEdgeNumbersAct->setChecked(false);
7268 
7269   if (glWidget->stateDrawNodeNumbers)
7270     showNodeNumbersAct->setChecked(true);
7271   else
7272     showNodeNumbersAct->setChecked(false);
7273 
7274   if (glWidget->stateDrawBoundaryIndex)
7275     showBoundaryIndexAct->setChecked(true);
7276   else
7277     showBoundaryIndexAct->setChecked(false);
7278 
7279   if (glWidget->stateDrawBodyIndex)
7280     showBodyIndexAct->setChecked(true);
7281   else
7282     showBodyIndexAct->setChecked(false);
7283 
7284   if (glWidget->stateDrawCoordinates)
7285     viewCoordinatesAct->setChecked(true);
7286   else
7287     viewCoordinatesAct->setChecked(false);
7288 
7289   if (bodyEditActive)
7290     bodyEditAct->setChecked(true);
7291   else
7292     bodyEditAct->setChecked(false);
7293 
7294   if (bcEditActive)
7295     bcEditAct->setChecked(true);
7296   else
7297     bcEditAct->setChecked(false);
7298 
7299   if (showConvergence)
7300     showConvergenceAct->setChecked(true);
7301   else
7302     showConvergenceAct->setChecked(false);
7303 
7304   if (glWidget->stateBcColors)
7305     showBoundaryColorAct->setChecked(true);
7306   else
7307     showBoundaryColorAct->setChecked(false);
7308 
7309   if (glWidget->stateBodyColors)
7310     showBodyColorAct->setChecked(true);
7311   else
7312     showBodyColorAct->setChecked(false);
7313 
7314   if (isFullScreen())
7315     viewFullScreenAct->setChecked(true);
7316   else
7317     viewFullScreenAct->setChecked(false);
7318 }
7319 
7320 // Load definitions...
7321 //-----------------------------------------------------------------------------
loadDefinitions()7322 void MainWindow::loadDefinitions() {
7323   // Determine edf-file location and name:
7324   //--------------------------------------
7325   QString elmerGuiHome;
7326 
7327 #ifdef __APPLE__DONTGO_HERE_TODO
7328   QString generalDefs = this->homePath + "/edf/edf.xml";
7329 #else
7330   QString generalDefs =
7331       QCoreApplication::applicationDirPath() +
7332       "/../share/ElmerGUI/edf/edf.xml"; // @TODO: fix path to share/ElmerGUI/edf
7333 
7334   elmerGuiHome = QString(getenv("ELMERGUI_HOME"));
7335 
7336   if (!elmerGuiHome.isEmpty())
7337     generalDefs = elmerGuiHome + "/edf/edf.xml";
7338 
7339   // ML 5. August 2010
7340   generalDefs.replace('\\', '/');
7341 #endif
7342 
7343   // Load general definitions file:
7344   //--------------------------------
7345 #if WITH_QT5
7346   cout << "Load " << string(generalDefs.toLatin1()) << "... ";
7347 #else
7348   cout << "Load " << string(generalDefs.toAscii()) << "... ";
7349 #endif
7350   cout.flush();
7351   updateSplash("Loading general definitions...");
7352 
7353   QFile file(generalDefs);
7354 
7355   QString errStr;
7356   int errRow;
7357   int errCol;
7358 
7359   if (!file.exists()) {
7360 
7361     elmerDefs = NULL;
7362     QMessageBox::information(window(), tr("Edf loader: ") + generalDefs,
7363                              tr("Definitions file does not exist"));
7364     return;
7365 
7366   } else {
7367 
7368     if (!elmerDefs->setContent(&file, true, &errStr, &errRow, &errCol)) {
7369       QMessageBox::information(window(), tr("Edf loader: ") + generalDefs,
7370                                tr("Parse error at line %1, col %2:\n%3")
7371                                    .arg(errRow)
7372                                    .arg(errCol)
7373                                    .arg(errStr));
7374       file.close();
7375       return;
7376 
7377     } else {
7378 
7379       if (elmerDefs->documentElement().tagName() != "edf") {
7380         QMessageBox::information(window(), tr("Edf loader: ") + generalDefs,
7381                                  tr("This is not an edf file"));
7382         delete elmerDefs;
7383         file.close();
7384         return;
7385       }
7386     }
7387   }
7388 
7389   edfEditor->setupEditor(elmerDefs);
7390   file.close();
7391 
7392   cout << "done" << endl;
7393   cout.flush();
7394 
7395   // load additional definitions:
7396   //-----------------------------
7397 #ifdef __APPLE__DONTGO_HERE_TODO
7398   QDirIterator iterator(homePath + "/edf", QDirIterator::Subdirectories);
7399 #else
7400   QString additionalEdfs =
7401       QCoreApplication::applicationDirPath() +
7402       "/../share/ElmerGUI/edf"; // @TODO: fix path to share/ElmerGUI/edf
7403 
7404   if (!elmerGuiHome.isEmpty())
7405     additionalEdfs = elmerGuiHome + "/edf";
7406 
7407   QDirIterator iterator(additionalEdfs, QDirIterator::Subdirectories);
7408 #endif
7409 
7410   while (iterator.hasNext()) {
7411     QString fileName = iterator.next();
7412 
7413     // ML 5. August 2010
7414     fileName.replace('\\', '/');
7415 
7416     QFileInfo fileInfo(fileName);
7417     QString fileSuffix = fileInfo.suffix();
7418 
7419     // The names "egini" and "egmaterials" are reserved, skip them:
7420     if (fileInfo.completeBaseName() == "egini")
7421       continue;
7422 
7423     if (fileInfo.completeBaseName() == "egmaterials")
7424       continue;
7425 
7426     if ((fileSuffix == "xml") && (fileName != generalDefs)) {
7427 
7428 #if WITH_QT5
7429       cout << "Load " << string(fileName.toLatin1()) << "... ";
7430 #else
7431       cout << "Load " << string(fileName.toAscii()) << "... ";
7432 #endif
7433       cout.flush();
7434 
7435       updateSplash("Loading " + fileName + "...");
7436 
7437       file.setFileName(fileName);
7438 
7439       QDomDocument tmpDoc;
7440       tmpDoc.clear();
7441 
7442       if (!tmpDoc.setContent(&file, true, &errStr, &errRow, &errCol)) {
7443         QMessageBox::information(window(), tr("Edf loader: ") + fileName,
7444                                  tr("Parse error at line %1, col %2:\n%3")
7445                                      .arg(errRow)
7446                                      .arg(errCol)
7447                                      .arg(errStr));
7448         file.close();
7449         return;
7450 
7451       } else {
7452 
7453         if (tmpDoc.documentElement().tagName() != "edf") {
7454           QMessageBox::information(window(), tr("Edf loader: ") + fileName,
7455                                    tr("This is not an edf file"));
7456           file.close();
7457           return;
7458         }
7459       }
7460 
7461       // add new elements to the document
7462       QDomElement root = elmerDefs->documentElement();
7463       QDomElement tmpRoot = tmpDoc.documentElement();
7464       QDomElement element = tmpRoot.firstChildElement();
7465 
7466       while (!element.isNull()) {
7467         root.appendChild(element);
7468         element = tmpRoot.firstChildElement();
7469       }
7470 
7471       edfEditor->setupEditor(elmerDefs);
7472 
7473       file.close();
7474 
7475       cout << "done" << endl;
7476       cout.flush();
7477     }
7478   }
7479 
7480   // Load qss:
7481   //-----------
7482   QString qssFileName = QCoreApplication::applicationDirPath() +
7483                         "/elmergui.qss"; // @TODO: fix path to share/ElmerGUI
7484 
7485 #ifdef __APPLE__
7486   qssFileName = homePath + "/elmergui.qss";
7487 #else
7488   if (!elmerGuiHome.isEmpty())
7489     qssFileName = elmerGuiHome + "/elmergui.qss";
7490 #endif
7491 
7492   QFile qssFile(qssFileName);
7493 
7494   if (qssFile.exists()) {
7495     cout << "Loading QSS style sheet... ";
7496     qssFile.open(QFile::ReadOnly);
7497     QString styleSheet = QLatin1String(qssFile.readAll());
7498     qssFile.close();
7499     qApp->setStyleSheet(styleSheet);
7500     cout << "done" << endl;
7501   }
7502 }
7503 
7504 // Setup splash...
7505 //-----------------------------------------------------------------------------
setupSplash()7506 void MainWindow::setupSplash() {
7507   QStringList args = QCoreApplication::arguments();
7508 
7509   if (args.contains("-nogui"))
7510     return;
7511 
7512   if (egIni->isSet("splashscreen")) {
7513     pixmap.load(":/images/splash.png");
7514     splash.setPixmap(pixmap);
7515     splash.show();
7516     qApp->processEvents();
7517   }
7518 }
7519 
7520 // Update splash...
7521 //-----------------------------------------------------------------------------
updateSplash(QString text)7522 void MainWindow::updateSplash(QString text) {
7523   QStringList args = QCoreApplication::arguments();
7524 
7525   if (args.contains("-nogui"))
7526     return;
7527 
7528   if (!egIni->isSet("splashscreen"))
7529     return;
7530 
7531   if (splash.isVisible()) {
7532     splash.showMessage(text, Qt::AlignBottom);
7533     qApp->processEvents();
7534   }
7535 }
7536 
7537 // Finalize splash...
7538 //-----------------------------------------------------------------------------
finalizeSplash()7539 void MainWindow::finalizeSplash() {
7540   if (!egIni->isSet("splashscreen"))
7541     return;
7542 
7543   if (splash.isVisible())
7544     splash.finish(this);
7545 }
7546 
7547 // Setup system tray icon...
7548 //-----------------------------------------------------------------------------
setupSysTrayIcon()7549 void MainWindow::setupSysTrayIcon() {
7550   sysTrayIcon = NULL;
7551 
7552   QStringList args = QCoreApplication::arguments();
7553 
7554   if (args.contains("-nogui"))
7555     return;
7556 
7557   if (!egIni->isSet("systrayicon"))
7558     return;
7559 
7560   if (QSystemTrayIcon::isSystemTrayAvailable()) {
7561     sysTrayIcon = new QSystemTrayIcon(this);
7562     sysTrayIcon->setIcon(QIcon(":/icons/Mesh3D.png"));
7563     sysTrayIcon->setVisible(true);
7564     sysTrayIcon->setContextMenu(sysTrayMenu);
7565   }
7566 }
7567 
7568 // Update system tray icon...
7569 //-----------------------------------------------------------------------------
updateSysTrayIcon(QString label,QString msg)7570 void MainWindow::updateSysTrayIcon(QString label, QString msg) {
7571   int duration = 3000;
7572 
7573   QStringList args = QCoreApplication::arguments();
7574 
7575   if (args.contains("-nogui"))
7576     return;
7577 
7578   if (!sysTrayIcon)
7579     return;
7580 
7581   if (!egIni->isSet("systraymessages"))
7582     return;
7583 
7584   if (isFullScreen())
7585     return;
7586 
7587   if (egIni->isPresent("systraymsgduration"))
7588     duration = egIni->value("systraymsgduration").toInt();
7589 
7590   if (sysTrayIcon->supportsMessages())
7591     sysTrayIcon->showMessage(label, msg, QSystemTrayIcon::Information,
7592                              duration);
7593 }
7594 
7595 // Finalize system tray icon...
7596 //-----------------------------------------------------------------------------
finalizeSysTrayIcon()7597 void MainWindow::finalizeSysTrayIcon() {}
7598 
7599 // Get default open/save directory
7600 //-----------------------------------------------------------------------------
getDefaultDirName()7601 QString MainWindow::getDefaultDirName() {
7602   QString defaultDirName = "";
7603 
7604 #ifdef WIN32
7605   defaultDirName = egIni->value("win32defaultdir");
7606 #else
7607 #ifdef __APPLE__
7608   defaultDirName = egIni->value("macxdefaultdir");
7609 #else
7610   defaultDirName = egIni->value("unixdefaultdir");
7611 #endif
7612 #endif
7613 
7614   if (!saveDirName.isEmpty())
7615     defaultDirName = saveDirName;
7616 
7617   return defaultDirName;
7618 }
7619 
7620 // Load settings
7621 //-----------------------------------------------------------------------------
loadSettings()7622 void MainWindow::loadSettings() {
7623   restoreGeometry(settings_value("mainWindow/geometry").toByteArray());
7624   sifWindow->restoreGeometry(
7625       settings_value("sifWindow/geometry").toByteArray());
7626   solverLogWindow->restoreGeometry(
7627       settings_value("solverLogWindow/geometry").toByteArray());
7628 
7629 #ifdef EG_QWT
7630   convergenceView->restoreGeometry(
7631       settings_value("convergenceView/geometry").toByteArray());
7632 #endif
7633 
7634 #ifdef EG_OCC
7635   cadView->restoreGeometry(settings_value("cadView/geometry").toByteArray());
7636 #endif
7637 
7638 #ifdef EG_VTK
7639   vtkPost->restoreGeometry(settings_value("vtkPost/geometry").toByteArray());
7640 #endif
7641 
7642   int n = settings_value("recentProject/n", 0).toInt();
7643   QString key = "recentProject/";
7644   char num[] = "01234";
7645   QString path;
7646   for (int i = n - 1; i >= 0; i--) {
7647     path = settings_value(key + num[i], "$").toString();
7648     if (path != "$")
7649       addRecentProject(path, false);
7650   }
7651 
7652   if (settings_value("objectBrowser/show", true).toBool()) {
7653     objectBrowser = new ObjectBrowser(this);
7654     showObjectBrowserAct->setChecked(true);
7655   } else {
7656     objectBrowser = NULL;
7657   }
7658 
7659   switch (settings_value("postProcessor/i", 0).toInt()){
7660     case 0: selectElmerPostSlot(); break;
7661     case 1: selectVtkPostSlot(); break;
7662     case 2: selectParaViewSlot(); break;
7663   }
7664 }
7665 
7666 // Save settings
7667 //-----------------------------------------------------------------------------
saveSettings()7668 void MainWindow::saveSettings() {
7669   settings_setValue("mainWindow/geometry", saveGeometry());
7670   settings_setValue("sifWindow/geometry", sifWindow->saveGeometry());
7671   settings_setValue("solverLogWindow/geometry",
7672                     solverLogWindow->saveGeometry());
7673 
7674 #ifdef EG_QWT
7675   settings_setValue("convergenceView/geometry",
7676                     convergenceView->saveGeometry());
7677 #endif
7678 
7679 #ifdef EG_OCC
7680   settings_setValue("cadView/geometry", cadView->saveGeometry());
7681 #endif
7682 
7683 #ifdef EG_VTK
7684   settings_setValue("vtkPost/geometry", vtkPost->saveGeometry());
7685 #endif
7686 
7687   if (showObjectBrowserAct->isChecked() && objectBrowser != NULL) {
7688     settings_setValue("objectBrowser/show", true);
7689   } else {
7690     settings_setValue("objectBrowser/show", false);
7691   }
7692 
7693   if(selectElmerPostAct->isChecked()){
7694     settings_setValue("postProcessor/i", 0);
7695   }else if(selectVtkPostAct->isChecked()){
7696     settings_setValue("postProcessor/i", 1);
7697   }else if(selectParaViewAct->isChecked()){
7698     settings_setValue("postProcessor/i", 2);
7699   }
7700 }
7701 
addRecentProject(QString dir,bool bSaveToIni)7702 void MainWindow::addRecentProject(QString dir, bool bSaveToIni) {
7703   if (recentProject.indexOf(dir) != -1) {
7704     recentProject.removeAt(recentProject.indexOf(dir));
7705   }
7706   recentProject.prepend(dir);
7707 
7708   int i = 0;
7709 
7710   recentProjectsMenu->removeAction(recentProject0Act);
7711   recentProjectsMenu->removeAction(recentProject1Act);
7712   recentProjectsMenu->removeAction(recentProject2Act);
7713   recentProjectsMenu->removeAction(recentProject3Act);
7714   recentProjectsMenu->removeAction(recentProject4Act);
7715   recentProjectsMenu->clear(); // just in case
7716 
7717   if (i < 5 && i < recentProject.size()) {
7718     recentProject0Act->setText(recentProject.at(i));
7719     recentProjectsMenu->addAction(recentProject0Act);
7720   }
7721   i++;
7722   if (i < 5 && i < recentProject.size()) {
7723     recentProject1Act->setText(recentProject.at(i));
7724     recentProjectsMenu->addAction(recentProject1Act);
7725   }
7726   i++;
7727   if (i < 5 && i < recentProject.size()) {
7728     recentProject2Act->setText(recentProject.at(i));
7729     recentProjectsMenu->addAction(recentProject2Act);
7730   }
7731   i++;
7732   if (i < 5 && i < recentProject.size()) {
7733     recentProject3Act->setText(recentProject.at(i));
7734     recentProjectsMenu->addAction(recentProject3Act);
7735   }
7736   i++;
7737   if (i < 5 && i < recentProject.size()) {
7738     recentProject4Act->setText(recentProject.at(i));
7739     recentProjectsMenu->addAction(recentProject4Act);
7740   }
7741   recentProjectsMenu->setEnabled(recentProject.size() > 0);
7742 
7743   if (bSaveToIni) {
7744     int n = recentProject.size();
7745     if (n > 5)
7746       n = 5;
7747     settings_setValue("recentProject/n", n);
7748     QString key = "recentProject/";
7749     char num[] = "01234";
7750     for (int i = 0; i < n; i++) {
7751       settings_setValue(key + num[i], recentProject.at(i));
7752     }
7753   }
7754 }
7755 
loadRecentProject0Slot()7756 void MainWindow::loadRecentProject0Slot() { loadProject(recentProject.at(0)); }
7757 
loadRecentProject1Slot()7758 void MainWindow::loadRecentProject1Slot() { loadProject(recentProject.at(1)); }
7759 
loadRecentProject2Slot()7760 void MainWindow::loadRecentProject2Slot() { loadProject(recentProject.at(2)); }
7761 
loadRecentProject3Slot()7762 void MainWindow::loadRecentProject3Slot() { loadProject(recentProject.at(3)); }
7763 
loadRecentProject4Slot()7764 void MainWindow::loadRecentProject4Slot() { loadProject(recentProject.at(4)); }
7765 
loadExtraSolver(QString solverName)7766 bool MainWindow::loadExtraSolver(QString solverName) {
7767 
7768 #ifdef __APPLE__DONTGO_HERE_TODO
7769   QString extraDirpath = this->homePath + "/edf-extra";
7770 #else
7771   QString extraDirPath =
7772       QCoreApplication::applicationDirPath() + "/../share/ElmerGUI/edf-extra";
7773 
7774   QString elmerGuiHome = QString(getenv("ELMERGUI_HOME"));
7775 
7776   if (!elmerGuiHome.isEmpty())
7777     extraDirPath = elmerGuiHome + "/edf-extra";
7778 
7779   extraDirPath.replace('\\', '/');
7780 #endif
7781 
7782   QString name;
7783   QDir extraDir(extraDirPath);
7784   QStringList nameFilters;
7785   nameFilters << "*.xml";
7786   QString message;
7787   QStringList fileNameList =
7788       extraDir.entryList(nameFilters, QDir::Files | QDir::Readable);
7789   for (int i = 0; i < fileNameList.size(); i++) {
7790     QFile file(extraDirPath + "/" + fileNameList.at(i));
7791     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
7792       QTextStream in(&file);
7793       while (!in.atEnd()) {
7794         QString line = in.readLine();
7795         if (line.indexOf("<PDE Name=") > 0) {
7796           line = in.readLine();
7797           while (line.indexOf("<Name>") == -1 && !in.atEnd())
7798             line = in.readLine();
7799           int i0 = line.indexOf("<Name>");
7800           if (i0 >= 0) {
7801             int i1 = line.indexOf("</Name>", i0 + 6);
7802             if (i1 > 0) {
7803               name = line.mid(i0 + 6, i1 - i0 - 6).trimmed();
7804               if (solverName.trimmed() == name) {
7805                 file.close();
7806 
7807                 message =
7808                     "Load " + extraDirPath + "/" + fileNameList.at(i) + "... ";
7809 #if WITH_QT5
7810                 cout << string(message.toLatin1());
7811                 cout.flush();
7812 #else
7813                 cout << string(message.toAscii());
7814                 cout.flush();
7815 #endif
7816 
7817                 edfEditor->appendFrom(extraDirPath + "/" + fileNameList.at(i));
7818 
7819                 cout << "done" << endl;
7820 
7821                 return true;
7822               }
7823             }
7824           }
7825         }
7826       }
7827 
7828       file.close();
7829     } else {
7830       logMessage(" failed to open " + fileNameList.at(i));
7831       return false;
7832     }
7833   }
7834   logMessage(" Extra solver " + solverName + " not found");
7835   return false;
7836 }
7837 
checkAndLoadExtraSolvers(QFile * file)7838 void MainWindow::checkAndLoadExtraSolvers(QFile *file) {
7839   QStringList loadedSolverName;
7840   QStringList unloadedSolverName;
7841   QDomElement root = elmerDefs->documentElement();
7842   QDomElement elem = root.firstChildElement("PDE");
7843   while (!elem.isNull()) {
7844     QDomElement pdeName = elem.firstChildElement("Name");
7845     loadedSolverName.append(pdeName.text().trimmed());
7846     elem = elem.nextSiblingElement();
7847   }
7848 
7849   QString name;
7850   if (file->open(QIODevice::ReadOnly | QIODevice::Text)) {
7851     QTextStream in(file);
7852     while (!in.atEnd()) {
7853       QString line = in.readLine();
7854       int i0, i1;
7855       i0 = line.indexOf("<key>/");
7856       if (i0 >= 0) {
7857         i1 = line.indexOf("/", i0 + 7);
7858         if (i1 > 0) {
7859           name = line.mid(i0 + 6, i1 - i0 - 6);
7860           if (!loadedSolverName.contains(name)) {
7861             loadExtraSolver(name);
7862 
7863             // update list (to avoid doubled loading - one solver file can
7864             // generate multiple tabs)
7865             loadedSolverName.clear();
7866             QDomElement root = elmerDefs->documentElement();
7867             QDomElement elem = root.firstChildElement("PDE");
7868             while (!elem.isNull()) {
7869               QDomElement pdeName = elem.firstChildElement("Name");
7870               loadedSolverName.append(pdeName.text().trimmed());
7871               elem = elem.nextSiblingElement();
7872             }
7873           }
7874         }
7875       }
7876     }
7877   } else {
7878     logMessage(" failed to open project file" + name);
7879   }
7880 
7881   /*
7882     QString name;
7883     if (file->open(QIODevice::ReadOnly | QIODevice::Text)){
7884       QTextStream in(file);
7885       while (!in.atEnd()) {
7886         QString line = in.readLine();
7887         int i0, i1;
7888         i0=line.indexOf("<key>/");
7889         if( i0 >= 0){
7890           i1 = line.indexOf("/", i0+7);
7891           if(i1 > 0){
7892             name = line.mid(i0+6, i1-i0-6);
7893             if(!loadedSolverName.contains(name) &&
7894     !unloadedSolverName.contains(name) ){ unloadedSolverName.append(name);
7895               loadExtraSolver(name);
7896             }
7897           }
7898         }
7899       }
7900     }else{
7901       logMessage(" failed to open project file" + name);
7902     }
7903     */
7904 }
7905 
settings_value(const QString & key,const QVariant & defaultValue) const7906 QVariant MainWindow::settings_value(const QString &key,
7907                                     const QVariant &defaultValue) const {
7908   QString oldElmerGuiIniFilePath =
7909       QCoreApplication::applicationDirPath() + "/ElmerGUI.ini";
7910   QString elmerGuiIniFilePath = QDir::homePath() + "/.elmergui";
7911   if (!QFile::exists(elmerGuiIniFilePath) &&
7912       QFile::exists(oldElmerGuiIniFilePath)) {
7913     elmerGuiIniFilePath = oldElmerGuiIniFilePath;
7914   }
7915   QSettings settings(elmerGuiIniFilePath, QSettings::IniFormat);
7916   return settings.value(key, defaultValue);
7917 }
7918 
settings_setValue(const QString & key,const QVariant & value)7919 void MainWindow::settings_setValue(const QString &key, const QVariant &value) {
7920   QString elmerGuiIniFilePath = QDir::homePath() + "/.elmergui";
7921   QSettings settings(elmerGuiIniFilePath, QSettings::IniFormat);
7922   settings.setValue(key, value);
7923 }
7924 
saveAndRun(bool generateSif)7925 void MainWindow::saveAndRun(bool generateSif) {
7926 
7927   //------- Save project -------//
7928   if (!glWidget->hasMesh()) {
7929     logMessage("Unable to save project: no mesh");
7930     return;
7931   }
7932 
7933   QString projectDirName = currentProjectDirName;
7934   if (projectDirName.isEmpty()) {
7935     QString defaultDirName = getDefaultDirName();
7936     projectDirName = QFileDialog::getExistingDirectory(
7937         this, tr("Open directory to save project"), defaultDirName);
7938 
7939     if (!projectDirName.isEmpty()) {
7940       logMessage("Project directory " + projectDirName);
7941     } else {
7942       logMessage("Unable to save project: directory undefined");
7943       return;
7944     }
7945   }
7946 
7947   if(generateSif){ generateSifSlot();}
7948 
7949   bool ret = saveProject(projectDirName);
7950 
7951   //------- Run solver -------//
7952   if (ret) {
7953     runsolverSlot();
7954   }
7955 }
7956 
generateAndSaveAndRunSlot()7957 void MainWindow::generateAndSaveAndRunSlot() { saveAndRun(true); }
7958 
closeEvent(QCloseEvent * event)7959 void MainWindow::closeEvent(QCloseEvent *event) {
7960   saveSettings();
7961   delete objectBrowser;
7962 }
7963 
showObjectBrowserSlot()7964 void MainWindow::showObjectBrowserSlot() {
7965   if (showObjectBrowserAct->isChecked()) {
7966     delete objectBrowser; // just in case
7967     objectBrowser = new ObjectBrowser(this);
7968   } else {
7969     delete objectBrowser;
7970     objectBrowser = NULL;
7971   }
7972 }
7973 
selectElmerPostSlot()7974 void MainWindow::selectElmerPostSlot(){
7975   runPostProcessorAct->setText(tr("Start ElmerPost"));
7976   runPostProcessorAct->setIcon(QIcon(":/icons/Post.png"));
7977   runPostProcessorAct->setStatusTip(tr("Run ElmerPost for visualization"));
7978   runPostProcessorAct->disconnect();
7979   connect(runPostProcessorAct, SIGNAL(triggered()), this, SLOT(resultsSlot()));
7980   selectElmerPostAct->setChecked(true);
7981   selectVtkPostAct->setChecked(false);
7982   selectParaViewAct->setChecked(false);
7983 }
selectVtkPostSlot()7984 void MainWindow::selectVtkPostSlot(){
7985   runPostProcessorAct->setText(tr("Start ElmerVTK"));
7986   runPostProcessorAct->setIcon(QIcon(":/icons/Mesh3D.png"));
7987   runPostProcessorAct->setStatusTip(tr("Invokes VTK based ElmerGUI postprocessor"));
7988   runPostProcessorAct->disconnect();
7989   connect(runPostProcessorAct, SIGNAL(triggered()), this, SLOT(showVtkPostSlot()));
7990   selectElmerPostAct->setChecked(false);
7991   selectVtkPostAct->setChecked(true);
7992   selectParaViewAct->setChecked(false);
7993 }
selectParaViewSlot()7994 void MainWindow::selectParaViewSlot(){
7995   runPostProcessorAct->setText(tr("Start ParaView"));
7996   runPostProcessorAct->setIcon(QIcon(":/icons/Paraview.png"));
7997   runPostProcessorAct->setStatusTip(tr("Invokes ParaView for visualization"));
7998   runPostProcessorAct->disconnect();
7999   connect(runPostProcessorAct, SIGNAL(triggered()), this, SLOT(showParaViewSlot()));
8000   selectElmerPostAct->setChecked(false);
8001   selectVtkPostAct->setChecked(false);
8002   selectParaViewAct->setChecked(true);
8003 }
8004