1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5
6 #include <stdlib.h>
7 #include <FL/Fl_Box.H>
8 #include "GModel.h"
9 #include "Context.h"
10 #include "StringUtils.h"
11 #include "FlGui.h"
12 #include "paletteWindow.h"
13 #include "onelab.h"
14 #include "onelabGroup.h"
15 #include "onelabContextWindow.h"
16 #include "drawContext.h"
17
handle(int event)18 int contextWindow::handle(int event)
19 {
20 if(event == FL_HIDE) {
21 GModel::current()->setSelection(0);
22 if(_redraw) drawContext::global()->draw();
23 }
24 return paletteWindow::handle(event);
25 }
26
getDimName(int dim)27 static std::string getDimName(int dim)
28 {
29 switch(dim) {
30 case 0: return "Point"; break;
31 case 1: return "Curve"; break;
32 case 2: return "Surface"; break;
33 case 3: return "Volume"; break;
34 }
35 return "";
36 }
37
replaceTemplateString(onelab::number & n,const std::string & in,const std::string & out)38 static void replaceTemplateString(onelab::number &n,
39 const std::string &in,
40 const std::string &out)
41 {
42 // for number parameter replace the template only in attributes
43 auto attr = n.getAttributes();
44 for(auto &a : attr) { ReplaceSubStringInPlace(in, out, a.second); }
45 n.setAttributes(attr);
46 }
47
replaceTemplateString(onelab::string & n,const std::string & in,const std::string & out)48 static void replaceTemplateString(onelab::string &n,
49 const std::string &in,
50 const std::string &out)
51 {
52 // for string parameters replace the template in the values, the attributes
53 // and the choices
54 auto vals = n.getValues();
55 for(auto &v : vals) { ReplaceSubStringInPlace(in, out, v); }
56 n.setValues(vals);
57
58 auto choices = n.getChoices();
59 for(auto &c : choices) { ReplaceSubStringInPlace(in, out, c); }
60 n.setChoices(choices);
61
62 auto attr = n.getAttributes();
63 for(auto &a : attr) { ReplaceSubStringInPlace(in, out, a.second); }
64 n.setAttributes(attr);
65 }
66
67 template <typename T>
_addOnelabWidget(T & p,const std::string & pattern,std::set<std::pair<std::string,Fl_Widget * >> & widgets)68 void onelabContextWindow::_addOnelabWidget(
69 T &p, const std::string &pattern,
70 std::set<std::pair<std::string, Fl_Widget *> > &widgets)
71 {
72 if(p.getName().find(pattern) == std::string::npos) return;
73 // does the parameter exist?
74 std::string in = getDimName(_dim) + " Template";
75 std::string out;
76 if(_choice->value() == 0) // elementary
77 out = getDimName(_dim) + " " + std::to_string(_tag);
78 else // physical
79 out = "Physical " + getDimName(_dim) + " " +
80 std::to_string(_physicalGroups[_choice->value() - 1].first);
81 std::string name = ReplaceSubString(in, out, p.getName());
82 std::vector<T> pn;
83 onelab::server::instance()->get(pn, name);
84 T n;
85 if(pn.empty()) {
86 n = p;
87 n.setName(name);
88 // replace the template string in the parameter so that e.g. the attributes
89 // can be used in entity-dependent server actions, the value can be used in
90 // a user action, etc.
91 replaceTemplateString(n, in, out);
92 onelab::server::instance()->set(n);
93 }
94 else
95 n = pn[0];
96
97 if(n.getVisible()) {
98 bool highlight = false;
99 Fl_Color c;
100 if(getParameterColor(n.getAttribute("Highlight"), c)) highlight = true;
101
102 int xx = 0, yy = 0, ww = _width / 2;
103 std::string aspect = n.getAttribute("Aspect");
104 if(aspect.find("Left") != std::string::npos) {
105 xx = 1;
106 yy = 1;
107 ww = _width / 3;
108 }
109 if(aspect.find("Middle") != std::string::npos) {
110 xx = 2;
111 yy = 1;
112 ww = _width / 3;
113 }
114 if(aspect.find("Right") != std::string::npos) {
115 xx = 3;
116 yy = 1;
117 ww = _width / 3;
118 }
119 Fl_Widget *w = addParameterWidget(n, xx, yy, ww, BH, 1., n.getName(),
120 highlight, c, win->color(), _toFree);
121 w->copy_label(n.getShortName().c_str());
122 std::string help = n.getHelp();
123 if(help.empty()) help = n.getLabel();
124 if(help.empty()) help = n.getShortName();
125 w->copy_tooltip(help.c_str());
126 widgets.insert(std::make_pair(n.getName(), w));
127 _onelabWidgets.push_back(w);
128 }
129 }
130
choice_cb(Fl_Widget * w,void * data)131 static void choice_cb(Fl_Widget *w, void *data)
132 {
133 FlGui::instance()->onelabContext->rebuild(false);
134 FlGui::instance()->onelabContext->highlightSelection();
135 // allow the solver code to react to the selection of the elementary or
136 // physical group
137 if(CTX::instance()->solver.autoCheck)
138 onelab_cb(nullptr, (void *)"check");
139 }
140
onelabContextWindow(int deltaFontSize)141 onelabContextWindow::onelabContextWindow(int deltaFontSize)
142 {
143 FL_NORMAL_SIZE -= deltaFontSize;
144
145 _dim = _tag = -1;
146 _entity = nullptr;
147
148 _width = 26 * FL_NORMAL_SIZE;
149 _height = 2 * WB + 1 * BH;
150
151 win = new contextWindow(_width, _height,
152 CTX::instance()->nonModalWindows ? true : false,
153 "Parameters");
154 win->box(GMSH_WINDOW_BOX);
155
156 _choice = new Fl_Choice(WB, WB, _width - 2 * WB, BH);
157 _choice->callback(choice_cb);
158
159 win->position(CTX::instance()->ctxPosition[0],
160 CTX::instance()->ctxPosition[1]);
161 win->end();
162
163 FL_NORMAL_SIZE += deltaFontSize;
164 }
165
~onelabContextWindow()166 onelabContextWindow::~onelabContextWindow() { Fl::delete_widget(win); }
167
show(int dim,int tag)168 void onelabContextWindow::show(int dim, int tag)
169 {
170 FlGui::instance()->lastContextWindow = 4;
171
172 _dim = dim;
173 _tag = tag;
174 _name = GModel::current()->getElementaryName(dim, tag);
175 _entity = GModel::current()->getEntityByTag(dim, tag);
176 _physicalGroups.clear();
177 _physicalGroupEntities.clear();
178 if(_entity) {
179 std::map<int, std::vector<GEntity *> > groups;
180 GModel::current()->getPhysicalGroups(_dim, groups);
181 for(auto &p : _entity->physicals) {
182 int n = std::abs(p); // can be < 0 to switch orientation
183 _physicalGroups.push_back(
184 std::make_pair(n, GModel::current()->getPhysicalName(dim, n)));
185 _physicalGroupEntities.push_back(groups[n]);
186 }
187 }
188
189 // create menu with possible entities (elementary and all associated
190 // physicals)
191 std::vector<Fl_Menu_Item> menu;
192 static std::vector<char *> toFree;
193 for(std::size_t i = 0; i < toFree.size(); i++) free(toFree[i]);
194 toFree.clear();
195 {
196 std::string label = getDimName(_dim) + " " + std::to_string(_tag);
197 if(_name.size()) label += ": " + _name;
198 char *str = strdup(label.c_str());
199 Fl_Menu_Item item = {str, 0, nullptr, nullptr, 0};
200 toFree.push_back(str);
201 menu.push_back(item);
202 }
203 for(auto &p : _physicalGroups) {
204 std::string label =
205 "Physical " + getDimName(_dim) + " " + std::to_string(p.first);
206 if(p.second.size()) label += ": " + p.second;
207 char *str = strdup(label.c_str());
208 Fl_Menu_Item item = {str, 0, nullptr, nullptr, 0};
209 toFree.push_back(str);
210 menu.push_back(item);
211 }
212 Fl_Menu_Item item = {nullptr};
213 menu.push_back(item);
214 _choice->copy(&menu[0]);
215
216 if(menu.size() > 1) _choice->value(menu.size() - 2); // last physical
217
218 rebuild(true);
219 highlightSelection();
220
221 if(!win->shown()) win->show();
222 win->enableRedraw();
223 }
224
hide()225 void onelabContextWindow::hide() { win->hide(); }
226
rebuild(bool deleteWidgets)227 void onelabContextWindow::rebuild(bool deleteWidgets)
228 {
229 if(!_entity) return; // no entity selected
230
231 // hide old widgets and record if one had the focus
232 std::string focus;
233 for(auto &w : _onelabWidgets) {
234 if(w == Fl::focus()) focus = w->label();
235 w->hide();
236 }
237
238 // delete widgets if requested (as this is called every time the main ONELAB
239 // tree is rebuilt)
240 if(deleteWidgets) {
241 for(auto &w : _onelabWidgets) Fl::delete_widget(w);
242 _onelabWidgets.clear();
243 for(std::size_t i = 0; i < _toFree.size(); i++) free(_toFree[i]);
244 _toFree.clear();
245 }
246
247 // get all ONELAB parameters
248 std::vector<onelab::number> pn, fpn;
249 onelab::server::instance()->get(pn);
250 std::vector<onelab::string> ps, fps;
251 onelab::server::instance()->get(ps);
252
253 // for parameters with names containing "ONELAB Context/... Template/", check
254 // if the corresponding parameter exists; if not, create it and add it to the
255 // server; then create the widget
256 std::string pat = "ONELAB Context/" + getDimName(_dim) + " Template/";
257 std::set<std::pair<std::string, Fl_Widget *> > wset;
258 for(auto &p : pn) _addOnelabWidget(p, pat, wset);
259 for(auto &p : ps) _addOnelabWidget(p, pat, wset);
260 std::vector<std::pair<std::string, Fl_Widget *> > wvec(wset.begin(),
261 wset.end());
262 int h = _height;
263 int w = win->w();
264 if(wvec.empty()) {
265 Fl_Box *b = new Fl_Box(WB, h, w - 2 * WB, BH, "No parameters");
266 _onelabWidgets.push_back(b);
267 win->add(b);
268 h += BH;
269 }
270 else {
271 for(std::size_t i = 0; i < wvec.size(); i++) {
272 // x, y of widgets used to decide how to position them
273 int ww = wvec[i].second->w();
274 int xx =
275 (wvec[i].second->x() == 3) ? _width - ww - WB : // right
276 (wvec[i].second->x() == 2) ? _width - 2 * ww - 2 * WB : // middle
277 (wvec[i].second->x() == 1) ? _width - 3 * ww - 3 * WB : // left
278 WB; // left
279 int yy =
280 (wvec[i].second->y() == 1) ? h + 2 * WB : // vertical padding
281 h; // none
282 if((i < wvec.size() - 1) && (wvec[i].second->x() > 0) &&
283 (wvec[i + 1].second->x() == (wvec[i].second->x() + 1))) {
284 // next button to the right, don't increment horizontal position
285 }
286 else if(wvec[i].second->y() == 1) {
287 // padding
288 h += wvec[i].second->h() + 2 * WB;
289 }
290 else { // default
291 h += wvec[i].second->h();
292 }
293 wvec[i].second->position(xx, yy);
294 win->add(wvec[i].second);
295 }
296 }
297 h += WB;
298
299 // resize the window and restore the focus
300 win->resize(win->x(), win->y(), win->w(), h);
301 win->size_range(_width, win->h(), 0, win->h());
302
303 for(auto w : _onelabWidgets) {
304 if(w->label() && focus == std::string(w->label())) { w->take_focus(); }
305 }
306
307 // TODO: we should add a "Check" button if Solver.AutoCheck is not set (as in
308 // the main ONELAB tree)
309 }
310
highlightSelection()311 void onelabContextWindow::highlightSelection()
312 {
313 if(!_entity) return;
314
315 GModel::current()->setSelection(0);
316 if(_choice->value() == 0) { // elementary
317 _entity->setVisibility(1);
318 _entity->setSelection(2);
319 }
320 else { // physical
321 if(_choice->value() - 1 < (int)_physicalGroupEntities.size()) {
322 for(auto e : _physicalGroupEntities[_choice->value() - 1]) {
323 e->setVisibility(1);
324 e->setSelection(2);
325 }
326 }
327 }
328 drawContext::global()->draw();
329 }
330
disableRedraw()331 void onelabContextWindow::disableRedraw() { win->disableRedraw(); }
332