Forked from
gmsh / gmsh
12797 commits behind the upstream repository.
-
Christophe Geuzaine authored
- allow changing some gear options during computation - new option to not merge results at all
Christophe Geuzaine authored- allow changing some gear options during computation - new option to not merge results at all
onelabWindow.cpp 36.61 KiB
// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.
#include <FL/Fl.H>
#include "GmshConfig.h"
#include "GmshMessage.h"
#if defined(HAVE_ONELAB)
#include "onelab.h"
#endif
#if defined(HAVE_ONELAB) && (FL_MAJOR_VERSION == 1) && (FL_MINOR_VERSION == 3)
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Input_Choice.H>
#include <FL/fl_ask.H>
#include "inputRange.h"
#include "Context.h"
#include "GModel.h"
#include "GmshDefines.h"
#include "Options.h"
#include "OS.h"
#include "StringUtils.h"
#include "OpenFile.h"
#include "CreateFile.h"
#include "drawContext.h"
#include "PView.h"
#include "PViewData.h"
#include "PViewOptions.h"
#include "FlGui.h"
#include "paletteWindow.h"
#include "menuWindow.h"
#include "fileDialogs.h"
#include "onelabWindow.h"
// This file contains the Gmsh/FLTK specific parts of the OneLab
// interface. You'll need to reimplement this if you plan to build
// a different OneLab server.
class onelabGmshServer : public GmshServer{
private:
onelab::localNetworkClient *_client;
public:
onelabGmshServer(onelab::localNetworkClient *client)
: GmshServer(), _client(client) {}
~onelabGmshServer(){}
int SystemCall(const char *str){ return ::SystemCall(str); }
int NonBlockingWait(int socket, double waitint, double timeout)
{
double start = GetTimeInSeconds();
while(1){
if(timeout > 0 && GetTimeInSeconds() - start > timeout)
return 2; // timout
if(_client->getPid() < 0 || (_client->getCommandLine().empty() &&
!CTX::instance()->solver.listen))
return 1; // process has been killed or we stopped listening
// check if there is data (call select with a zero timeout to
// return immediately, i.e., do polling)
int ret = Select(0, 0, socket);
if(ret == 0){
// nothing available: wait at most waitint seconds, and in the
// meantime respond to FLTK events
FlGui::instance()->wait(waitint);
}
else if(ret > 0){
return 0; // data is there!
}
else{
// an error happened
_client->setPid(-1);
_client->setGmshServer(0);
return 1;
}
}
}
};
bool onelab::localNetworkClient::run()
{
new_connection:
_pid = 0;
_gmshServer = 0;
onelabGmshServer *server = new onelabGmshServer(this);
std::string sockname;
std::ostringstream tmp;
if(!strstr(CTX::instance()->solver.socketName.c_str(), ":")){
// Unix socket
tmp << CTX::instance()->homeDir << CTX::instance()->solver.socketName << getId();
sockname = FixWindowsPath(tmp.str());
}
else{
// TCP/IP socket
if(CTX::instance()->solver.socketName.size() &&
CTX::instance()->solver.socketName[0] == ':')
tmp << GetHostName(); // prepend hostname if only the port number is given
tmp << CTX::instance()->solver.socketName << getId();
sockname = tmp.str();
}
std::string command = FixWindowsPath(_commandLine);
if(command.size()){
// complete the command line if "UseCommandLine" is set in the database
std::vector<onelab::number> n;
get(n, getName() + "/UseCommandLine");
if(n.size() && n[0].getValue()){
std::vector<onelab::string> ps;
get(ps, getName() + "/Action");
std::string action = (ps.empty() ? "" : ps[0].getValue());
get(ps, getName() + "/1ModelName");
std::string modelName = (ps.empty() ? "" : ps[0].getValue());
get(ps, getName() + "/9CheckCommand");
std::string checkCommand = (ps.empty() ? "" : ps[0].getValue());
get(ps, getName() + "/9ComputeCommand");
std::string computeCommand = (ps.empty() ? "" : ps[0].getValue());
if(action == "check")
command += " " + modelName + " " + checkCommand;
else if(action == "compute")
command += " " + modelName + " " + computeCommand;
}
// append "-onelab" command line argument
command += " " + _socketSwitch + " \"" + getName() + "\"";
}
else{
Msg::Info("Listening on socket '%s'", sockname.c_str());
}
int sock;
try{
sock = server->Start(command.c_str(), sockname.c_str(),
CTX::instance()->solver.timeout);
}
catch(const char *err){
Msg::Error("%s (on socket '%s')", err, sockname.c_str());
sock = -1;
}
if(sock < 0){
server->Shutdown();
delete server;
return false;
}
Msg::StatusBar(2, true, "Running '%s'...", _name.c_str());
while(1) {
if(_pid < 0 || (command.empty() && !CTX::instance()->solver.listen))
break;
int stop = server->NonBlockingWait(sock, 0.001, 0.);
if(stop || _pid < 0 || (command.empty() && !CTX::instance()->solver.listen))
break;
double timer = GetTimeInSeconds();
int type, length, swap;
if(!server->ReceiveHeader(&type, &length, &swap)){
Msg::Error("Did not receive message header: stopping server");
break;
}
std::string message(length, ' ');
if(!server->ReceiveMessage(length, &message[0])){
Msg::Error("Did not receive message body: stopping server");
break;
}
switch (type) {
case GmshSocket::GMSH_START:
_pid = atoi(message.c_str());
_gmshServer = server;
break;
case GmshSocket::GMSH_STOP:
_pid = -1;
_gmshServer = 0;
break;
case GmshSocket::GMSH_PARAMETER:
{
std::string version, type, name;
onelab::parameter::getInfoFromChar(message, version, type, name);
if(type == "number"){
onelab::number p;
p.fromChar(message);
set(p);
}
else if(type == "string"){
onelab::string p;
p.fromChar(message);
set(p);
}
else
Msg::Error("FIXME not done for this parameter type: %s", type.c_str());
}
break;
case GmshSocket::GMSH_PARAMETER_QUERY:
{
std::string version, type, name, reply;
onelab::parameter::getInfoFromChar(message, version, type, name);
if(type == "number"){
std::vector<onelab::number> par;
get(par, name);
if(par.size() == 1) reply = par[0].toChar();
}
else if(type == "string"){
std::vector<onelab::string> par;
get(par, name);
if(par.size() == 1) reply = par[0].toChar();
}
else
Msg::Error("FIXME not done for this parameter type: %s", type.c_str());
if(reply.size()){
server->SendMessage(GmshSocket::GMSH_PARAMETER, reply.size(), &reply[0]);
}
else{
reply = "Parameter '" + name + "' not found";
server->SendMessage(GmshSocket::GMSH_INFO, reply.size(), &reply[0]);
}
}
break;
case GmshSocket::GMSH_PARAM_QUERY_ALL:
{
std::string version, type, name, reply;
onelab::parameter::getInfoFromChar(message, version, type, name);
if(type == "number"){
std::vector<onelab::number> numbers;
get(numbers);
for(std::vector<onelab::number>::iterator it = numbers.begin();
it != numbers.end(); it++){
reply = (*it).toChar();
server->SendMessage(GmshSocket::GMSH_PARAM_QUERY_ALL, reply.size(), &reply[0]);
}
reply = "OneLab: sent all numbers";
server->SendMessage(GmshSocket::GMSH_PARAM_QUERY_END, reply.size(), &reply[0]);
}
else if(type == "string"){
std::vector<onelab::string> strings;
get(strings);
for(std::vector<onelab::string>::iterator it = strings.begin();
it != strings.end(); it++){
reply = (*it).toChar();
server->SendMessage(GmshSocket::GMSH_PARAM_QUERY_ALL, reply.size(), &reply[0]);
}
reply = "OneLab: sent all strings";
server->SendMessage(GmshSocket::GMSH_PARAM_QUERY_END, reply.size(), &reply[0]);
}
else
Msg::Error("FIXME query not done for this parameter type: %s", type.c_str());
}
break;
case GmshSocket::GMSH_PROGRESS:
Msg::StatusBar(2, false, "%s %s", _name.c_str(), message.c_str());
break;
case GmshSocket::GMSH_INFO:
Msg::Direct("%-8.8s: %s", _name.c_str(), message.c_str());
break;
case GmshSocket::GMSH_WARNING:
Msg::Direct(2, "%-8.8s: %s", _name.c_str(), message.c_str());
break;
case GmshSocket::GMSH_ERROR:
Msg::Direct(1, "%-8.8s: %s", _name.c_str(), message.c_str());
break;
case GmshSocket::GMSH_MERGE_FILE:
{
if(!FlGui::instance()->onelab->mergeAuto()) break;
int n = PView::list.size();
for(int i = 0; i < n; i++){
if(PView::list[i]->getData()->getFileName().substr(0, 6) != "OneLab")
PView::list[i]->getOptions()->visible = 0;
}
MergeFile(message);
if(FlGui::instance()->onelab->hideNewViews()){
for(int i = n; i < PView::list.size(); i++){
if(PView::list[i]->getData()->getFileName().substr(0, 6) != "OneLab")
PView::list[i]->getOptions()->visible = 0;
}
}
drawContext::global()->draw();
if(n != (int)PView::list.size())
FlGui::instance()->menu->setContext(menu_post, 0);
}
break;
case GmshSocket::GMSH_PARSE_STRING:
ParseString(message);
drawContext::global()->draw();
break;
case GmshSocket::GMSH_SPEED_TEST:
Msg::Info("got %d Mb message in %g seconds",
length / 1024 / 1024, GetTimeInSeconds() - timer);
break;
case GmshSocket::GMSH_VERTEX_ARRAY:
{
int n = PView::list.size();
PView::fillVertexArray(this, length, &message[0], swap);
FlGui::instance()->updateViews(n != (int)PView::list.size());
drawContext::global()->draw();
}
break;
default:
Msg::Warning("Received unknown message type (%d)", type);
break;
}
}
_gmshServer = 0;
server->Shutdown();
delete server;
Msg::StatusBar(2, true, "Done running '%s'", _name.c_str());
if(command.empty()){
Msg::Info("Client disconnected: starting new connection");
goto new_connection;
}
return true;
}
bool onelab::localNetworkClient::kill()
{
if(_pid > 0) {
if(KillProcess(_pid)){
Msg::Info("Killed '%s' (pid %d)", _name.c_str(), _pid);
_pid = -1;
return true;
}
}
_pid = -1;
return false;
}
static std::string getMshFileName(onelab::client *c)
{
std::vector<onelab::string> ps;
c->get(ps, "Gmsh/MshFileName");
if(ps.size()){
return ps[0].getValue();
}
else{
std::string name = CTX::instance()->outputFileName;
if(name.empty()){
if(CTX::instance()->mesh.fileFormat == FORMAT_AUTO)
name = GetDefaultFileName(FORMAT_MSH);
else
name = GetDefaultFileName(CTX::instance()->mesh.fileFormat);
}
onelab::string o("Gmsh/MshFileName", name, "Mesh name");
o.setKind("file");
c->set(o);
return name;
}
}
static void guessModelName(onelab::client *c)
{
std::vector<onelab::string> ps;
c->get(ps, c->getName() + "/1ModelName");
if(ps.empty()){
std::vector<std::string> split = SplitFileName(GModel::current()->getFileName());
std::string ext = "";
onelab::server::instance()->get(ps, c->getName() + "/FileExtension");
if(ps.size()) ext = ps[0].getValue();
std::string name(split[0] + split[1] + ext);
onelab::string o(c->getName() + "/1ModelName", name, "Model name");
o.setKind("file");
c->set(o);
}
}
static void initializeLoop(std::string level)
{
bool changed = false;
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers);
for(unsigned int i = 0; i < numbers.size(); i++){
if(numbers[i].getAttribute("Loop") == level){
if(numbers[i].getChoices().size() > 1){
numbers[i].setValue(numbers[i].getChoices()[0]);
onelab::server::instance()->set(numbers[i]);
changed = true;
}
else if(numbers[i].getMin() != -onelab::parameter::maxNumber() &&
numbers[i].getStep()){
numbers[i].setValue(numbers[i].getMin());
onelab::server::instance()->set(numbers[i]);
changed = true;
}
}
}
// force this to make sure that we remesh, even if a mesh exists and
// we did not actually change a Gmsh parameter
if(changed)
onelab::server::instance()->setChanged(true, "Gmsh");
}
static bool incrementLoop(std::string level)
{
bool recompute = false, loop = false;
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers);
for(unsigned int i = 0; i < numbers.size(); i++){
if(numbers[i].getAttribute("Loop") == level){
loop = true;
if(numbers[i].getChoices().size() > 1){
// FIXME should store loopVariable attribute in the parameter
// -- the following test will loop forever if 2 values are
// identical in the list of choices
std::vector<double> choices(numbers[i].getChoices());
for(unsigned int j = 0; j < choices.size() - 1; j++){
if(numbers[i].getValue() == choices[j]){
numbers[i].setValue(choices[j + 1]);
onelab::server::instance()->set(numbers[i]);
Msg::Info("Recomputing with new choice %s=%g",
numbers[i].getName().c_str(), numbers[i].getValue());
recompute = true;
break;
}
}
}
else if(numbers[i].getMax() != onelab::parameter::maxNumber() &&
numbers[i].getValue() < numbers[i].getMax() &&
numbers[i].getStep()){
numbers[i].setValue(numbers[i].getValue() + numbers[i].getStep());
onelab::server::instance()->set(numbers[i]);
Msg::Info("Recomputing with new step %s=%g",
numbers[i].getName().c_str(), numbers[i].getValue());
recompute = true;
}
}
}
if(loop && !recompute) // end of this loop level
initializeLoop(level);
return recompute;
}
static void initializeLoop()
{
initializeLoop("1");
initializeLoop("2");
initializeLoop("3");
if(onelab::server::instance()->getChanged())
FlGui::instance()->onelab->rebuildTree();
}
static bool incrementLoop()
{
bool ret = false;
if(incrementLoop("3")) ret = true;
else if(incrementLoop("2")) ret = true;
else if(incrementLoop("1")) ret = true;
if(onelab::server::instance()->getChanged())
FlGui::instance()->onelab->rebuildTree();
return ret;
}
static std::vector<double> getRange(onelab::number &p)
{
std::vector<double> v;
if(p.getChoices().size()){
v = p.getChoices();
}
else if(p.getMin() != -onelab::parameter::maxNumber() &&
p.getMax() != onelab::parameter::maxNumber() && p.getStep()){
for(double d = p.getMin(); d <= p.getMax(); d += p.getStep())
v.push_back(d);
}
return v;
}
static bool updateOnelabGraph(int num)
{
bool changed = false;
for(unsigned int i = 0; i < PView::list.size(); i++){
if(PView::list[i]->getData()->getFileName() == "OneLab" + num){
delete PView::list[i];
changed = true;
break;
}
}
std::vector<double> x, y;
std::string xName, yName;
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers);
for(unsigned int i = 0; i < numbers.size(); i++){
std::string v = numbers[i].getAttribute("Graph");
v.resize(8, '0');
if(v[2 * num] == '1'){
x = getRange(numbers[i]);
xName = numbers[i].getShortName();
}
if(v[2 * num + 1] == '1'){
y = getRange(numbers[i]);
yName = numbers[i].getShortName();
}
}
if(x.empty()){
xName.clear();
for(unsigned int i = 0; i < y.size(); i++) x.push_back(i);
}
if(x.size() && y.size()){
PView *v = new PView(xName, yName, x, y);
v->getData()->setFileName("OneLab" + num);
v->getOptions()->intervalsType = PViewOptions::Discrete;
changed = true;
}
if(changed) FlGui::instance()->updateViews();
return changed;
}
static void updateOnelabGraphs()
{
bool redraw0 = updateOnelabGraph(0);
bool redraw1 = updateOnelabGraph(1);
bool redraw2 = updateOnelabGraph(2);
bool redraw3 = updateOnelabGraph(3);
if(redraw0 || redraw1 || redraw2 || redraw3)
drawContext::global()->draw();
}
static void importPhysicalGroups(onelab::client *c, GModel *m)
{
std::map<int, std::vector<GEntity*> > groups[4];
m->getPhysicalGroups(groups);
// create "read-only" onelab groups
}
static void runGmshClient(const std::string &action)
{
onelab::server::citer it = onelab::server::instance()->findClient("Gmsh");
if(it == onelab::server::instance()->lastClient()) return;
onelab::client *c = it->second;
std::string mshFileName = getMshFileName(c);
if(action == "initialize") return;
static std::string modelName = "";
if(modelName.empty()){
// first pass is special to prevent model reload, as well as
// remeshing if a mesh file already exists on disk
modelName = GModel::current()->getName();
if(!StatFile(mshFileName))
onelab::server::instance()->setChanged(false, "Gmsh");
}
if(action == "check"){
if(onelab::server::instance()->getChanged("Gmsh") ||
modelName != GModel::current()->getName()){
// reload geometry if Gmsh parameters have been modified or if
// the model name has changed
modelName = GModel::current()->getName();
geometry_reload_cb(0, 0);
importPhysicalGroups(c, GModel::current());
}
}
else if(action == "compute"){
if(onelab::server::instance()->getChanged("Gmsh") ||
modelName != GModel::current()->getName()){
// reload the geometry, mesh it and save the mesh if Gmsh
// parameters have been modified or if the model name has
// changed
modelName = GModel::current()->getName();
geometry_reload_cb(0, 0);
importPhysicalGroups(c, GModel::current());
if(FlGui::instance()->onelab->meshAuto()){
mesh_3d_cb(0, 0);
CreateOutputFile(mshFileName, CTX::instance()->mesh.fileFormat);
}
}
else if(StatFile(mshFileName)){
// mesh+save if the mesh file does not exist
if(FlGui::instance()->onelab->meshAuto()){
mesh_3d_cb(0, 0);
CreateOutputFile(mshFileName, CTX::instance()->mesh.fileFormat);
}
}
onelab::server::instance()->setChanged(false, "Gmsh");
}
}
void onelab_cb(Fl_Widget *w, void *data)
{
if(!data) return;
std::string action((const char*)data);
if(action == "refresh"){
FlGui::instance()->onelab->setButtonMode("", "stop");
updateOnelabGraphs();
FlGui::instance()->onelab->rebuildTree();
FlGui::instance()->onelab->setButtonMode("refresh", "stop");
return;
}
if(action == "stop"){
FlGui::instance()->onelab->stop(true);
FlGui::instance()->onelab->setButtonMode("", "kill");
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++){
onelab::string o(it->second->getName() + "/Action", "stop");
o.setVisible(false);
onelab::server::instance()->set(o);
}
return;
}
if(action == "kill"){
FlGui::instance()->onelab->stop(true);
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++)
it->second->kill();
return;
}
if(action == "dump"){
std::string db = onelab::server::instance()->toChar();
Msg::Direct("OneLab database dump:");
int start = 0;
for(unsigned int i = 0; i < db.size(); i++){
if(db[i] == onelab::parameter::charSep()) db[i] = '|';
if(db[i] == '\n'){
Msg::Direct("%s", db.substr(start, i - start).c_str());
start = i + 1;
}
}
return;
}
if(action == "reset"){
// clear everything except model names and command line setup
std::vector<onelab::string> modelNames;
std::vector<onelab::number> useCommandLines;
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++){
onelab::client *c = it->second;
std::vector<onelab::string> ps;
c->get(ps, c->getName() + "/1ModelName");
if(ps.size()) modelNames.push_back(ps[0]);
std::vector<onelab::number> ps2;
c->get(ps2, c->getName() + "/UseCommandLine");
if(ps2.size()) useCommandLines.push_back(ps2[0]);
}
onelab::server::instance()->clear();
if(onelab::server::instance()->findClient("Gmsh") !=
onelab::server::instance()->lastClient())
geometry_reload_cb(0, 0);
for(unsigned int i = 0; i < modelNames.size(); i++)
onelab::server::instance()->set(modelNames[i]);
for(unsigned int i = 0; i < useCommandLines.size(); i++)
onelab::server::instance()->set(useCommandLines[i]);
action = "check";
}
if(action == "compute")
FlGui::instance()->onelab->setButtonMode("refresh", "stop");
else
FlGui::instance()->onelab->setButtonMode("", "stop");
if(action == "compute") initializeLoop();
do{ // enter loop
// the Gmsh client is special: it always gets executed first. (The
// meta-model will allow more flexibility: but in the simple GUI
// we can assume this)
runGmshClient(action);
if(action == "compute")
FlGui::instance()->onelab->checkForErrors("Gmsh");
if(FlGui::instance()->onelab->stop()) break;
// iterate over all other clients (there should narmally only be one)
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++){
onelab::client *c = it->second;
if(c->getName() == "Gmsh" || // local Gmsh client
c->getName() == "Listen" || // unknown client connecting through "-listen"
c->getName() == "GmshRemote") // distant post-processing Gmsh client
continue;
if(action != "initialize") guessModelName(c);
onelab::string o(c->getName() + "/Action", action);
o.setVisible(false);
onelab::server::instance()->set(o);
c->run();
if(action == "compute")
FlGui::instance()->onelab->checkForErrors(c->getName());
if(FlGui::instance()->onelab->stop()) break;
}
if(action != "initialize"){
updateOnelabGraphs();
FlGui::instance()->onelab->rebuildTree();
}
} while(action == "compute" && !FlGui::instance()->onelab->stop() &&
incrementLoop());
FlGui::instance()->onelab->stop(false);
FlGui::instance()->onelab->setButtonMode("check", "compute");
if(action != "initialize") FlGui::instance()->onelab->show();
}
static void onelab_check_button_cb(Fl_Widget *w, void *data)
{
if(!data) return;
std::string name = FlGui::instance()->onelab->getPath((Fl_Tree_Item*)data);
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers, name);
if(numbers.size()){
Fl_Check_Button *o = (Fl_Check_Button*)w;
numbers[0].setValue(o->value());
onelab::server::instance()->set(numbers[0]);
}
}
static void onelab_input_range_cb(Fl_Widget *w, void *data)
{
if(!data) return;
std::string name = FlGui::instance()->onelab->getPath((Fl_Tree_Item*)data);
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers, name);
if(numbers.size()){
inputRange *o = (inputRange*)w;
if(o->doCallbackOnValues()){
numbers[0].setValue(o->value());
numbers[0].setMin(o->minimum());
numbers[0].setMax(o->maximum());
numbers[0].setStep(o->step());
numbers[0].setChoices(o->choices());
}
o->doCallbackOnValues(true);
numbers[0].setAttribute("Loop", o->loop());
numbers[0].setAttribute("Graph", o->graph());
onelab::server::instance()->set(numbers[0]);
updateOnelabGraphs();
}
}
static void onelab_input_choice_cb(Fl_Widget *w, void *data)
{
if(!data) return;
std::string name = FlGui::instance()->onelab->getPath((Fl_Tree_Item*)data);
std::vector<onelab::string> strings;
onelab::server::instance()->get(strings, name);
if(strings.size()){
Fl_Input_Choice *o = (Fl_Input_Choice*)w;
strings[0].setValue(o->value());
onelab::server::instance()->set(strings[0]);
}
}
static void onelab_input_choice_file_chooser_cb(Fl_Widget *w, void *data)
{
Fl_Input_Choice *but = (Fl_Input_Choice*)w->parent();
if(fileChooser(FILE_CHOOSER_SINGLE, "Choose", "", but->value())){
but->value(fileChooserGetName(1).c_str());
but->do_callback(but, data);
}
}
static void onelab_input_choice_file_edit_cb(Fl_Widget *w, void *data)
{
Fl_Input_Choice *but = (Fl_Input_Choice*)w->parent();
std::string prog = FixWindowsPath(CTX::instance()->editor);
std::string file = FixWindowsPath(but->value());
SystemCall(ReplaceSubString("%s", file, prog));
}
static void onelab_choose_executable_cb(Fl_Widget *w, void *data)
{
onelab::localNetworkClient *c = (onelab::localNetworkClient*)data;
std::string pattern = "*";
#if defined(WIN32)
pattern += ".exe";
#endif
const char *old = 0;
if(!c->getCommandLine().empty()) old = c->getCommandLine().c_str();
if(fileChooser(FILE_CHOOSER_SINGLE, "Choose executable", pattern.c_str(), old)){
std::string exe = fileChooserGetName(1);
c->setCommandLine(exe);
if(c->getIndex() >= 0 && c->getIndex() < 5)
opt_solver_executable(c->getIndex(), GMSH_SET, exe);
}
}
static void onelab_remove_solver_cb(Fl_Widget *w, void *data)
{
onelab::client *c = (onelab::client*)data;
FlGui::instance()->onelab->removeSolver(c->getName());
}
static void onelab_add_solver_cb(Fl_Widget *w, void *data)
{
for(int i = 0; i < 5; i++){
if(opt_solver_name(i, GMSH_GET, "").empty()){
const char *name = fl_input("Client name:", "");
if(name){
FlGui::instance()->onelab->addSolver(name, "", i);
}
return;
}
}
}
onelabWindow::onelabWindow(int deltaFontSize)
: _deltaFontSize(deltaFontSize), _stop(false)
{
FL_NORMAL_SIZE -= _deltaFontSize;
int width = 29 * FL_NORMAL_SIZE;
int height = 15 * BH + 3 * WB;
_win = new paletteWindow
(width, height, CTX::instance()->nonModalWindows ? true : false, "OneLab");
_win->box(GMSH_WINDOW_BOX);
_tree = new Fl_Tree(WB, WB, width - 2 * WB, height - 3 * WB - BH);
_tree->connectorstyle(FL_TREE_CONNECTOR_SOLID);
_tree->showroot(0);
_butt[0] = new Fl_Button(width - 2*WB - 2*BB, height - WB - BH, BB, BH, "Check");
_butt[0]->callback(onelab_cb, (void*)"check");
_butt[1] = new Fl_Button(width - WB - BB, height - WB - BH, BB, BH, "Compute");
_butt[1]->callback(onelab_cb, (void*)"compute");
_gear = new Fl_Menu_Button
(_butt[0]->x() - WB - BB/2, _butt[0]->y(), BB/2, BH, "@-1gmsh_gear");
_gear->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
_gear->add("Reset database", 0, onelab_cb, (void*)"reset");
_gear->add("_Print database", 0, onelab_cb, (void*)"dump");
_gear->add("Remesh automatically", 0, 0, 0, FL_MENU_TOGGLE);
_gear->add("Merge results automatically", 0, 0, 0, FL_MENU_TOGGLE);
_gear->add("_Hide new views", 0, 0, 0, FL_MENU_TOGGLE);
((Fl_Menu_Item*)_gear->menu())[2].set();
((Fl_Menu_Item*)_gear->menu())[3].set();
((Fl_Menu_Item*)_gear->menu())[4].clear();
_gearFrozenMenuSize = _gear->menu()->size();
Fl_Box *resbox = new Fl_Box(WB, WB,
width - 2 * BB - BB / 2 - 4 * WB,
height - 3 * WB - BH);
_win->resizable(resbox);
_win->size_range(2 * BB + BB / 2 + 4 * WB + 1, 2 * BH + 3 * WB);
_win->position
(CTX::instance()->solverPosition[0], CTX::instance()->solverPosition[1]);
_win->end();
FL_NORMAL_SIZE += _deltaFontSize;
}
void onelabWindow::rebuildTree()
{
FL_NORMAL_SIZE -= _deltaFontSize;
int width = (int)(0.5 * _tree->w());
_tree->clear();
_tree->sortorder(FL_TREE_SORT_ASCENDING);
_tree->selectmode(FL_TREE_SELECT_NONE);
for(unsigned int i = 0; i < _treeWidgets.size(); i++)
Fl::delete_widget(_treeWidgets[i]);
_treeWidgets.clear();
for(unsigned int i = 0; i < _treeStrings.size(); i++)
free(_treeStrings[i]);
_treeStrings.clear();
std::vector<onelab::number> numbers;
onelab::server::instance()->get(numbers);
for(unsigned int i = 0; i < numbers.size(); i++){
if(!numbers[i].getVisible()) continue;
Fl_Tree_Item *n = _tree->add(numbers[i].getName().c_str());
n->labelsize(FL_NORMAL_SIZE + 5);
std::string label = numbers[i].getShortName();
_tree->begin();
if(numbers[i].getChoices().size() == 2 &&
numbers[i].getChoices()[0] == 0 && numbers[i].getChoices()[1] == 1){
Fl_Check_Button *but = new Fl_Check_Button(1, 1, width, 1);
_treeWidgets.push_back(but);
but->copy_label(label.c_str());
but->value(numbers[i].getValue());
but->callback(onelab_check_button_cb, (void*)n);
n->widget(but);
}
else{
inputRange *but = new inputRange
(1, 1, width, 1, onelab::parameter::maxNumber());
_treeWidgets.push_back(but);
but->copy_label(label.c_str());
but->value(numbers[i].getValue());
but->minimum(numbers[i].getMin());
but->maximum(numbers[i].getMax());
but->step(numbers[i].getStep());
but->choices(numbers[i].getChoices());
but->loop(numbers[i].getAttribute("Loop"));
but->graph(numbers[i].getAttribute("Graph"));
but->align(FL_ALIGN_RIGHT);
but->callback(onelab_input_range_cb, (void*)n);
but->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
n->widget(but);
}
_tree->end();
}
std::vector<onelab::string> strings;
onelab::server::instance()->get(strings);
for(unsigned int i = 0; i < strings.size(); i++){
if(!strings[i].getVisible()) continue;
Fl_Tree_Item *n = _tree->add(strings[i].getName().c_str());
n->labelsize(FL_NORMAL_SIZE + 5);
std::string label = strings[i].getShortName();
_tree->begin();
Fl_Input_Choice *but = new Fl_Input_Choice(1, 1, width, 1);
_treeWidgets.push_back(but);
but->copy_label(label.c_str());
std::vector<Fl_Menu_Item> menu;
for(unsigned int j = 0; j < strings[i].getChoices().size(); j++){
// need to manually manage the label strings
char *str = strdup(strings[i].getChoices()[j].c_str());
_treeStrings.push_back(str);
bool divider = (strings[i].getKind() == "file" &&
j == strings[i].getChoices().size() - 1);
Fl_Menu_Item it = {str, 0, 0, 0, divider ? FL_MENU_DIVIDER : 0};
menu.push_back(it);
}
if(strings[i].getKind() == "file"){
Fl_Menu_Item it = {"Choose...", 0, onelab_input_choice_file_chooser_cb, (void*)n};
menu.push_back(it);
Fl_Menu_Item it2 = {"Edit...", 0, onelab_input_choice_file_edit_cb, (void*)n};
menu.push_back(it2);
}
Fl_Menu_Item it = {0};
menu.push_back(it);
but->menubutton()->copy(&menu[0]);
but->value(strings[i].getValue().c_str());
but->align(FL_ALIGN_RIGHT);
but->callback(onelab_input_choice_cb, (void*)n);
but->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
n->widget(but);
_tree->end();
}
for(Fl_Tree_Item *n = _tree->first(); n; n = n->next()){
if(n->has_children()){
_tree->begin();
Fl_Box *but = new Fl_Box(1, 1, width, 1);
but->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
_treeWidgets.push_back(but);
onelab::string o(n->label());
but->copy_label(o.getShortName().c_str());
n->widget(but);
_tree->end();
}
}
_tree->redraw();
FL_NORMAL_SIZE += _deltaFontSize;
}
void onelabWindow::checkForErrors(const std::string &client)
{
if(Msg::GetErrorCount() > 0 && !CTX::instance()->expertMode){
Msg::ResetErrorCounter();
std::string msg
(client + " reported an error: do you really want to continue?\n\n"
"(To disable this warning in the future, select `Enable expert mode'\n"
"in the option dialog.)");
if(Msg::GetAnswer(msg.c_str(), 1, "Stop", "Continue") == 0)
_stop = true;
}
}
void onelabWindow::setButtonMode(const std::string &butt0, const std::string &butt1)
{
if(butt0 == "check"){
_butt[0]->activate();
_butt[0]->label("Check");
_butt[0]->callback(onelab_cb, (void*)"check");
}
else if(butt0 == "refresh"){
_butt[0]->activate();
_butt[0]->label("Refresh");
_butt[0]->callback(onelab_cb, (void*)"refresh");
}
else{
_butt[0]->deactivate();
}
if(butt1 == "compute"){
_butt[1]->activate();
_butt[1]->label("Compute");
_butt[1]->callback(onelab_cb, (void*)"compute");
for(int i = 0; i < _gear->menu()->size(); i++)
((Fl_Menu_Item*)_gear->menu())[i].activate();
}
else if(butt1 == "stop"){
_butt[1]->activate();
_butt[1]->label("Stop");
_butt[1]->callback(onelab_cb, (void*)"stop");
for(int i = 0; i < _gear->menu()->size(); i++)
if(i < 1 || i > 4) ((Fl_Menu_Item*)_gear->menu())[i].deactivate();
}
else if(butt1 == "kill"){
_butt[1]->activate();
_butt[1]->label("Kill");
_butt[1]->callback(onelab_cb, (void*)"kill");
for(int i = 0; i < _gear->menu()->size(); i++)
if(i < 1 || i > 4) ((Fl_Menu_Item*)_gear->menu())[i].deactivate();
}
else{
_butt[1]->deactivate();
for(int i = 0; i < _gear->menu()->size(); i++)
if(i < 1 || i > 4) ((Fl_Menu_Item*)_gear->menu())[i].deactivate();
}
}
void onelabWindow::rebuildSolverList()
{
// update OneLab window title and gear menu
_title = "OneLab";
for(int i = _gear->menu()->size(); i >= _gearFrozenMenuSize - 1; i--)
_gear->remove(i);
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++){
if(it == onelab::server::instance()->firstClient()) _title += " -";
if(it->second->isNetworkClient()){
onelab::localNetworkClient *c = (onelab::localNetworkClient*)it->second;
char tmp[256];
sprintf(tmp, "%s/Choose executable", c->getName().c_str());
_gear->add(tmp, 0, onelab_choose_executable_cb, (void*)c);
sprintf(tmp, "%s/Remove", c->getName().c_str());
_gear->add(tmp, 0, onelab_remove_solver_cb, (void*)c);
}
_title += " " + it->second->getName();
}
_gear->add("Add new client...", 0, onelab_add_solver_cb);
_win->label(_title.c_str());
// update Gmsh solver menu
std::vector<std::string> names, exes;
for(int i = 0; i < 5; i++){
if(opt_solver_name(i, GMSH_GET, "").size()){
names.push_back(opt_solver_name(i, GMSH_GET, ""));
exes.push_back(opt_solver_executable(i, GMSH_GET, ""));
}
}
for(int i = 0; i < 5; i++){
if(i < names.size()){
onelab::server::citer it = onelab::server::instance()->findClient(names[i]);
if(it != onelab::server::instance()->lastClient())
it->second->setIndex(i);
opt_solver_name(i, GMSH_SET, names[i]);
opt_solver_executable(i, GMSH_SET, exes[i]);
}
else{
opt_solver_name(i, GMSH_SET, "");
opt_solver_executable(i, GMSH_SET, "");
}
}
FlGui::instance()->menu->setContext(menu_solver, 0);
}
void onelabWindow::addSolver(const std::string &name, const std::string &commandLine,
int index)
{
if(onelab::server::instance()->findClient(name) !=
onelab::server::instance()->lastClient()) return; // solver already exists
// unregister the other non-local clients so we keep only the new one
std::vector<onelab::client*> networkClients;
for(onelab::server::citer it = onelab::server::instance()->firstClient();
it != onelab::server::instance()->lastClient(); it++)
if(it->second->isNetworkClient())
networkClients.push_back(it->second);
for(unsigned int i = 0; i < networkClients.size(); i++){
onelab::server::instance()->unregisterClient(networkClients[i]);
delete networkClients[i];
}
// create and register the new client
onelab::localNetworkClient *c = new onelab::localNetworkClient(name, commandLine);
c->setIndex(index);
opt_solver_name(index, GMSH_SET, name);
if(commandLine.empty())
onelab_choose_executable_cb(0, (void *)c);
FlGui::instance()->onelab->rebuildSolverList();
// initialize the client
onelab_cb(0, (void*)"initialize");
}
void onelabWindow::removeSolver(const std::string &name)
{
onelab::server::citer it = onelab::server::instance()->findClient(name);
if(it != onelab::server::instance()->lastClient()){
onelab::client *c = it->second;
if(c->isNetworkClient()){
onelab::server::instance()->unregisterClient(c);
if(c->getIndex() >= 0 && c->getIndex() < 5){
opt_solver_name(c->getIndex(), GMSH_SET, "");
opt_solver_executable(c->getIndex(), GMSH_SET, "");
}
delete c;
}
}
FlGui::instance()->onelab->rebuildSolverList();
}
void solver_cb(Fl_Widget *w, void *data)
{
int num = (intptr_t)data;
if(num >= 0){
std::string name = opt_solver_name(num, GMSH_GET, "");
std::string exe = opt_solver_executable(num, GMSH_GET, "");
FlGui::instance()->onelab->addSolver(name, exe, num);
}
else
FlGui::instance()->onelab->rebuildSolverList();
onelab_cb(0, (void*)"check");
}
#else
#if defined(HAVE_ONELAB)
bool onelab::localNetworkClient::run()
{
Msg::Error("The solver interface requires OneLab and FLTK 1.3");
return false;
}
bool onelab::localNetworkClient::kill()
{
Msg::Error("The solver interface requires OneLab and FLTK 1.3");
return false;
}
#endif
void solver_cb(Fl_Widget *w, void *data)
{
Msg::Error("The solver interface requires OneLab and FLTK 1.3");
}
#endif