Salome HOME
Merge branch 'V9_7_BR'
[modules/med.git] / src / MEDCalc / gui / DatasourceController.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // Author : Guillaume Boulant (EDF)
21
22 #include "DatasourceController.hxx"
23 #include <MEDCalcConstants.hxx>
24
25 #include <SalomeApp_Application.h>
26 #include <SalomeApp_Study.h>
27 #include <SalomeApp_DataObject.h>
28
29 #include <SALOME_ListIO.hxx>
30 #include <LightApp_SelectionMgr.h>
31
32 #include <SALOME_LifeCycleCORBA.hxx>
33 #include <SALOMEDS_SObject.hxx>
34 #include <SALOMEDS_Study.hxx>
35
36 #include "MEDFactoryClient.hxx"
37 #include "MEDModule.hxx"
38 #include "QtHelper.hxx"
39
40 #include CORBA_CLIENT_HEADER(SALOMEDS)
41 #include CORBA_CLIENT_HEADER(SALOMEDS_Attributes)
42 #include <SUIT_FileDlg.h>
43 #include <SUIT_Desktop.h>
44
45 #include <QStringList>
46 #include <QString>
47 #include <QMessageBox>
48 #include <QFileDialog>
49
50 #include "DlgAlias.hxx"
51
52 //
53 // ==============================================================
54 // Datasource controller
55 // ==============================================================
56 //
57 DatasourceController::DatasourceController(MEDModule* salomeModule)
58 {
59   STDLOG("Creating a DatasourceController");
60   _salomeModule = salomeModule;
61   _studyEditor = _salomeModule->getStudyEditor();
62 }
63
64 DatasourceController::~DatasourceController() {
65   STDLOG("Deleting the DatasourceController");
66 }
67
68 void DatasourceController::createActions() {
69   int toolbarId = _salomeModule->createTool("Datasource", "DatasourceToolbar");
70
71   //
72   // Main actions (toolbar and menubar)
73   //
74   QString label   = tr("LAB_ADD_DATA_SOURCE");
75   QString tooltip = tr("TIP_ADD_DATA_SOURCE");
76   QString icon    = tr("ICO_DATASOURCE_ADD");
77   int actionId;
78   actionId = _salomeModule->createStandardAction(label, this, SLOT(OnAddDatasource()),
79                                                  icon, tooltip, FIELDSOp::OpAddDataSource);
80   _salomeModule->createTool(actionId, toolbarId);
81
82   // This action has to be placed in the general file menu with the label "Import MED file"
83   int menuId = _salomeModule->createMenu( tr( "MEN_FILE" ), -1,  1 );
84   _salomeModule->action(actionId)->setIconVisibleInMenu(true);
85   _salomeModule->createMenu(actionId, menuId, 10);
86
87   label   = tr("LAB_ADD_IMAGE_SOURCE");
88   tooltip = tr("TIP_ADD_IMAGE_SOURCE");
89   icon    = tr("ICO_IMAGE_ADD");
90   actionId = _salomeModule->createStandardAction(label,this, SLOT(OnAddImagesource()),icon,tooltip);
91   _salomeModule->createTool(actionId, toolbarId);
92   _salomeModule->action(actionId)->setIconVisibleInMenu(true);
93   _salomeModule->createMenu(actionId, menuId, 20);
94
95   //
96   // Actions for popup menu only
97   //
98   // Expand field timeseries
99   label = tr("LAB_EXPAND_FIELD");
100   icon  = tr("ICO_DATASOURCE_EXPAND_FIELD");
101   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnExpandField()),icon);
102   _salomeModule->addActionInPopupMenu(actionId);
103
104   // Use in workspace
105   label = tr("LAB_USE_IN_WORKSPACE");
106   icon  = tr("ICO_DATASOURCE_USE");
107   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnUseInWorkspace()),icon);
108   _salomeModule->addActionInPopupMenu(actionId);
109
110 }
111
112 /**
113  * This function adds the specified MED file as a datasource in the
114  * dataspace. Technically speaking, the engine loads the
115  * meta-information concerning med data from the file, gives this
116  * information to the GUI, and the GUI creates a tree view of these
117  * data in the study object browser.
118  */
119 // This function emits a signal that will be caught by workspace to delegate command (datasource creation) to python console.
120 void
121 DatasourceController::addDatasource(const char* filename)
122 {
123   DatasourceEvent* event = new DatasourceEvent();
124   event->eventtype = DatasourceEvent::EVENT_ADD_DATASOURCE;
125   event->objectalias = filename;
126   emit datasourceSignal(event);  // --> WorkspaceController::processDatasourceEvent()
127 //#ifdef MED_WITH_QTTESTING
128 //  _dirtyAddDataSource = true;
129 //  while(_dirtyAddDataSource)
130 //    QApplication::processEvents();
131 //#endif
132 }
133
134 // After above data source creation, python console emits a signal, forwarded by workspace, to update the GUI
135 void
136 DatasourceController::updateTreeViewWithNewDatasource(const MEDCALC::DatasourceHandler* datasourceHandler)
137 {
138   if (!datasourceHandler) {
139     return;
140   }
141
142   _salomeModule->engine()->addDatasourceToStudy(*datasourceHandler);
143
144   // update Object browser
145   _salomeModule->getApp()->updateObjectBrowser(true);
146
147 //#ifdef MED_WITH_QTTESTING
148 //  _dirtyAddDataSource = false;
149 //#endif
150 }
151
152 void DatasourceController::OnAddDatasource()
153 {
154   // Dialog to get the filename where the input data are read from
155   QStringList filter;
156   filter.append(tr("FILE_FILTER_MED"));
157
158   QString anInitialPath = "";
159   if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
160     anInitialPath = QDir::currentPath();
161
162 //  QStringList filenames = SUIT_FileDlg::getOpenFileNames( _salomeModule->getApp()->desktop(),
163 //                                                          anInitialPath,
164 //                                                          filter,
165 //                                                          tr("IMPORT_MED_FIELDS") );
166   // [ABN] the below to be compatible with QtTesting:
167   QStringList filenames = QFileDialog::getOpenFileNames( _salomeModule->getApp()->desktop(),
168                                                           tr("IMPORT_MED_FIELDS"),
169                                                           anInitialPath,
170                                                           tr("FILE_FILTER_MED") );
171
172   if ( filenames.count() <= 0 ) return;
173   for ( QStringList::ConstIterator itFile = filenames.begin();
174         itFile != filenames.end(); ++itFile ) {
175     QString filename = *itFile;
176     this->addDatasource(QCHARSTAR(filename));
177     _salomeModule->updateObjBrowser(true);
178   }
179 }
180
181 #include "DlgImageToMed.hxx"
182 void DatasourceController::OnAddImagesource()
183 {
184
185   DlgImageToMed dialog;
186   dialog.setAutoLoaded(true);
187   int choice = dialog.exec();
188   if ( choice == QDialog::Rejected ) {
189     // The user decides to cancel the operation
190     return;
191   }
192
193   QString imageFilename = dialog.getImageFilepath();
194   /*
195   QString medFilename   = dialog.getMedFilepath();
196   bool autoLoad         = dialog.isAutoLoaded();
197
198   std::string ROOT_DIR(getenv("FIELDS_ROOT_DIR"));
199   std::string command(ROOT_DIR+"/bin/salome/fields/image2med.py");
200   command += " -i "+QS2S(imageFilename);
201   command += " -m "+QS2S(medFilename);
202   int error = system(command.c_str());
203   if ( error != 0 ) {
204     QMessageBox::critical(_salomeModule->getApp()->desktop(),
205            tr("Operation failed"),
206            tr("The creation of med data from the image file failed"));
207     return;
208   }
209
210   if ( autoLoad ) {
211     this->addDatasource(QCHARSTAR(medFilename));
212     _salomeModule->updateObjBrowser(true);
213   }
214   */
215
216   DatasourceEvent* event = new DatasourceEvent();
217   event->eventtype = DatasourceEvent::EVENT_ADD_IMAGE_AS_DATASOURCE;
218   event->objectalias = imageFilename;
219   emit datasourceSignal(event); // --> WorkspaceController::processDatasourceEvent()
220 }
221
222 void DatasourceController::OnExpandField()
223 {
224   SALOMEDS::Study_var aStudy = KERNEL::getStudyServant();
225   // check if reference to study is valid
226   if (CORBA::is_nil(aStudy)) 
227     return;
228
229   // check if reference to use case builder is valid
230   SALOMEDS::UseCaseBuilder_var useCaseBuilder = aStudy->GetUseCaseBuilder();
231   if (CORBA::is_nil(useCaseBuilder)) 
232     return;
233
234
235   // Get the selected objects in the study (SObject)
236   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
237   for (int i=0; i<(int)listOfSObject->size(); i++) {
238     SALOMEDS::SObject_var soFieldseries = listOfSObject->at(i);
239     std::string name(_studyEditor->getName(soFieldseries));
240     if (soFieldseries->_is_nil() || name == "MEDCalc")
241       return;
242
243     // First retrieve the fieldseries id associated to this study object
244     SALOMEDS::GenericAttribute_var anAttr;
245     SALOMEDS::AttributeParameter_var aParam;
246     if ( soFieldseries->FindAttribute(anAttr,"AttributeParameter") ) {
247       aParam = SALOMEDS::AttributeParameter::_narrow(anAttr);
248       if (! aParam->IsSet(FIELD_SERIES_ID, PT_INTEGER))
249         return;
250     }
251     long fieldseriesId = aParam->GetInt(FIELD_SERIES_ID);
252     STDLOG("Expand the field timeseries "<<fieldseriesId);
253
254     // If fieldseriesId equals -1, then it means that it is not a
255     // fieldseries managed by the MED module, and we stop this
256     // function process.
257     if ( fieldseriesId < 0 )
258       continue;
259     // _GBO_ A better correction should be to no display the
260     // contextual menu if the selected object is not conform
261
262     // Then retrieve the list of fields in this timeseries
263     MEDCALC::FieldHandlerList* fieldHandlerList =
264       MEDFactoryClient::getDataManager()->getFieldListInFieldseries(fieldseriesId);
265
266     // Finally, create an entry for each of the field
267     for(CORBA::ULong iField=0; iField<fieldHandlerList->length(); iField++) {
268       MEDCALC::FieldHandler fieldHandler = (*fieldHandlerList)[iField];
269       SALOMEDS::SObject_var soField = _studyEditor->newObject(soFieldseries);
270       std::string label("it="); label += ToString(fieldHandler.iteration);
271       _studyEditor->setName(soField,label.c_str());
272       _studyEditor->setParameterInt(soField, FIELD_ID, fieldHandler.id);
273       _studyEditor->setParameterBool(soField,IS_IN_WORKSPACE,false);
274       useCaseBuilder->AppendTo(soField->GetFather(), soField);
275     }
276   }
277   _salomeModule->updateObjBrowser(true);
278 }
279
280 void DatasourceController::OnUseInWorkspace() {
281   // Get the selected objects in the study (SObject)
282   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
283   if ( listOfSObject->size() == 1 ) {
284     // In this case we ask the name of the variable for the python
285     // console
286
287     // >>>
288     // _GBO_ Note that it works only for a single field but the
289     // XmedDataObject will be improved to deal with mesh, timeseries
290     // and single field in a futur version. We suppose here that a
291     // single field has been selected.
292     // <<<
293
294     SALOMEDS::SObject_var soField = listOfSObject->at(0);
295     std::string name(_studyEditor->getName(soField));
296     if (soField->_is_nil() || name == "MEDCalc")
297       return;
298     SALOMEDS::GenericAttribute_var anAttr;
299     SALOMEDS::AttributeParameter_var aParam;
300     if ( soField->FindAttribute(anAttr,"AttributeParameter") ) {
301       aParam = SALOMEDS::AttributeParameter::_narrow(anAttr);
302       if (! aParam->IsSet(IS_IN_WORKSPACE, PT_BOOLEAN))
303         return;
304     }
305     bool isInWorkspace = aParam->GetBool(IS_IN_WORKSPACE);
306     if ( isInWorkspace ) {
307       QMessageBox::warning(_salomeModule->getApp()->desktop(),
308          tr("Operation not allowed"),
309          tr("This field is already defined in the workspace"));
310       return;
311     }
312
313     if (! aParam->IsSet(FIELD_ID, PT_INTEGER))
314       return;
315     int fieldId = aParam->GetInt(FIELD_ID);
316
317     // If fieldId equals -1, then it means that it is not a field
318     // managed by the MED module, and we stop this function process.
319     if ( fieldId < 0 ) {
320       QMessageBox::warning(_salomeModule->getApp()->desktop(),
321          tr("Operation not allowed"),
322          tr("This element is not a field object"));
323       return;
324     }
325
326     MEDCALC::FieldHandler* fieldHandler =
327       MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
328
329     if (! fieldHandler) {
330       QMessageBox::warning(_salomeModule->getApp()->desktop(),
331          tr("Operation not allowed"),
332          tr("No field is defined"));
333       return;
334     }
335
336     QString alias(fieldHandler->fieldname);
337     DlgAlias dialog;
338     dialog.setAlias(alias);
339     int choice = dialog.exec();
340     if ( choice == QDialog::Rejected ) {
341       // The user decides to cancel the operation
342       return;
343     }
344     alias = dialog.getAlias();
345
346     DatasourceEvent* event = new DatasourceEvent();
347     event->eventtype = DatasourceEvent::EVENT_USE_OBJECT;
348     XmedDataObject* dataObject = new XmedDataObject();
349     dataObject->setFieldHandler(*fieldHandler);
350     event->objectdata  = dataObject;
351     event->objectalias = alias;
352     emit datasourceSignal(event);  // --> WorkspaceController::processDatasourceEvent()
353     // Tag the item to prevent double import
354     //    _studyEditor->setParameterBool(soField,IS_IN_WORKSPACE,true);
355     // Tag the field as persistent on the server. It means that a
356     // saving of the workspace will save at least this field (maybe it
357     // should be an option?)
358     MEDFactoryClient::getDataManager()->markAsPersistent(fieldId, true);
359   }
360   else {
361     // In this case, we don't ask the user to specify an alias for
362     // each item, we just import the whole set of items.
363     for (int i=0; i<(int)listOfSObject->size(); i++) {
364       SALOMEDS::SObject_var soField = listOfSObject->at(i);
365       if (soField->_is_nil())
366         continue;
367       SALOMEDS::GenericAttribute_var anAttr;
368       SALOMEDS::AttributeParameter_var aParam;
369       if ( soField->FindAttribute(anAttr,"AttributeParameter") ) {
370         aParam = SALOMEDS::AttributeParameter::_narrow(anAttr);
371         if (! aParam->IsSet(IS_IN_WORKSPACE, PT_BOOLEAN))
372           return;
373       }
374       bool isInWorkspace = aParam->GetBool(IS_IN_WORKSPACE);
375       if ( !isInWorkspace ) {
376         if (! aParam->IsSet(FIELD_ID, PT_INTEGER))
377           continue;
378         int fieldId = aParam->GetInt(FIELD_ID);
379         MEDCALC::FieldHandler* fieldHandler =
380           MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
381         DatasourceEvent* event = new DatasourceEvent();
382         event->eventtype = DatasourceEvent::EVENT_IMPORT_OBJECT;
383         XmedDataObject* dataObject = new XmedDataObject();
384         dataObject->setFieldHandler(*fieldHandler);
385         event->objectdata  = dataObject;
386         emit datasourceSignal(event); // --> WorkspaceController::processDatasourceEvent()
387         // Note that this signal is processed by the WorkspaceController
388
389         // Tag the item to prevent double import
390         //        _studyEditor->setParameterBool(soField,IS_IN_WORKSPACE,true);
391         // Tag the field as persistent on the server. It means that a
392         // saving of the workspace will save at least this field (maybe it
393         // should be an option?)
394         MEDFactoryClient::getDataManager()->markAsPersistent(fieldId, true);
395       }
396       else {
397         STDLOG("The field "<<_studyEditor->getName(soField)<<
398                " is already defined in the workspace");
399       }
400     }
401   }
402 }
403
404 void
405 DatasourceController::processWorkspaceEvent(const MEDCALC::MedEvent* event)
406 {
407   if ( event->type == MEDCALC::EVENT_ADD_DATASOURCE ) {
408     MEDCALC::DatasourceHandler* datasourceHandler = MEDFactoryClient::getDataManager()->getDatasourceHandler(event->filename);
409     this->updateTreeViewWithNewDatasource(datasourceHandler);
410   }
411 }