fw4spl
DynamicView.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "guiQt/editor/DynamicView.hpp"
8 
9 #include <fwCom/Slot.hpp>
10 #include <fwCom/Slot.hxx>
11 #include <fwCom/Slots.hpp>
12 #include <fwCom/Slots.hxx>
13 
14 #include <fwCore/base.hpp>
15 
16 #include <fwData/Boolean.hpp>
17 #include <fwData/Composite.hpp>
18 #include <fwData/String.hpp>
19 
20 #include <fwDataCamp/getObject.hpp>
21 
22 #include <fwGui/dialog/MessageDialog.hpp>
23 #include <fwGui/GuiRegistry.hpp>
24 
25 #include <fwRuntime/ConfigurationElement.hpp>
26 #include <fwRuntime/operations.hpp>
27 
28 #include <fwServices/macros.hpp>
29 #include <fwServices/registry/AppConfig.hpp>
30 
31 #include <boost/foreach.hpp>
32 
33 #include <QBoxLayout>
34 #include <QTabWidget>
35 #include <QtGui>
36 
37 namespace guiQt
38 {
39 namespace editor
40 {
41 
42 static const ::fwCom::Slots::SlotKeyType s_CREATE_TAB_SLOT = "createTab";
43 
44 fwServicesRegisterMacro( ::fwGui::view::IView, ::guiQt::editor::DynamicView);
45 
46 AppConfig::AppConfig(const ConfigType& config) :
47  id(config.get<std::string>("<xmlattr>.id")),
48  title(config.get<std::string>("<xmlattr>.title"))
49 {
50  std::string closableStr = config.get_optional<std::string>("<xmlattr>.closable").get_value_or("true");
51  closable = (closableStr == "true");
52 
53  tabInfo = config.get_optional<std::string>("<xmlattr>.tabinfo").get_value_or("");
54 
55  if(config.count("parameters") == 1 )
56  {
57  const ConfigType& configParameters = config.get_child("parameters");
58  BOOST_FOREACH( const ConfigType::value_type& v, configParameters.equal_range("parameter") )
59  {
60  ParameterType parameter( v.second );
61  parameters.push_back( parameter );
62  }
63  }
64  OSLM_ASSERT("At most 1 <parameters> tag is allowed", config.count("parameters") < 2);
65 }
66 
67 //------------------------------------------------------------------------------
68 
69 DynamicView::DynamicView() noexcept
70 {
71  m_dynamicConfigStartStop = false;
72 
73  newSlot(s_CREATE_TAB_SLOT, &DynamicView::createTab, this);
74 }
75 
76 //------------------------------------------------------------------------------
77 
78 DynamicView::~DynamicView() noexcept
79 {
80 }
81 
82 //------------------------------------------------------------------------------
83 
84 void DynamicView::starting()
85 {
86  FW_DEPRECATED_MSG("The service '::guiQt::editor::DynamicView' is deprecated, use '::guiQt::editor::SDynamicView' "
87  "instead", "18.0");
88 
90 
91  ::fwGuiQt::container::QtContainer::sptr parentContainer
92  = ::fwGuiQt::container::QtContainer::dynamicCast( this->getContainer() );
93 
94  m_tabWidget = new QTabWidget();
95  m_tabWidget->setTabsClosable( true );
96  m_tabWidget->setDocumentMode( true );
97  m_tabWidget->setMovable( true );
98 
99  QObject::connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTabSignal(int)));
100  QObject::connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(changedTab(int)));
101 
102  QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
103  layout->addWidget( m_tabWidget );
104 
105  parentContainer->setLayout(layout);
106 
107  m_currentWidget = 0;
108 
109  if(!m_appConfig.id.empty())
110  {
111  DynamicViewInfo info = this->buildDynamicViewInfo(m_appConfig);
112  this->launchTab(info);
113  }
114 }
115 
116 //------------------------------------------------------------------------------
117 
118 void DynamicView::stopping()
119 {
120  SLM_TRACE_FUNC();
121  while(m_tabWidget->count())
122  {
123  this->closeTab(0, true);
124  }
125  m_tabWidget->clear();
126 
127  this->destroy();
128  m_tabWidget = 0;
129 }
130 
131 //------------------------------------------------------------------------------
132 
133 void DynamicView::configuring()
134 {
136 
137  if(this->getConfigTree().count("config") > 0)
138  {
139  SLM_ASSERT("There must be one (and only one) <config/> element.",
140  this->getConfigTree().count("config") == 1 );
141  const ::fwServices::IService::ConfigType srvconfig = this->getConfigTree();
142  const ::fwServices::IService::ConfigType& config = srvconfig.get_child("config");
143 
144  const std::string dynamicConfig =
145  config.get_optional<std::string>("<xmlattr>.dynamicConfigStartStop").get_value_or("false");
146  m_dynamicConfigStartStop = (dynamicConfig == "true");
147 
148  if(config.count("appConfig") == 1 )
149  {
150  const ::fwServices::IService::ConfigType& appConfig = config.get_child("appConfig");
151  m_appConfig = AppConfig(appConfig);
152  }
153  OSLM_ASSERT("At most 1 <appConfig> tag is allowed", config.count("appConfig") < 2);
154  }
155 }
156 
157 //------------------------------------------------------------------------------
158 
159 DynamicView::DynamicViewInfo DynamicView::buildDynamicViewInfo(const AppConfig& appConfig)
160 {
161  DynamicViewInfo info;
162  info.tabID = "TABID_" + this->getID();
163 
164  if(appConfig.tabInfo.empty())
165  {
166  info.title = appConfig.title;
167  }
168  else
169  {
170  info.title = appConfig.tabInfo;
171  }
172  info.viewConfigID = appConfig.id;
173  info.closable = appConfig.closable;
174 
175  ::fwData::Object::sptr currentObj = this->getObject();
176  ReplaceMapType replaceMap;
177  for(const AppConfig::ParametersType::value_type& param : appConfig.parameters)
178  {
179  if(!param.isSeshat())
180  {
181  replaceMap[param.replace] = param.by;
182  }
183  else
184  {
185  std::string parameterToReplace = param.by;
186  if (parameterToReplace.substr(0, 1) == "!")
187  {
188  parameterToReplace.replace(0, 1, "@");
189  }
190 
191  ::fwData::Object::sptr obj = ::fwDataCamp::getObject(currentObj, parameterToReplace);
192  OSLM_ASSERT("Invalid seshat path : '"<<param.by<<"'", obj);
193 
194  ::fwData::String::sptr stringParameter = ::fwData::String::dynamicCast(obj);
195 
196  std::string parameterValue = obj->getID();
197 
198  if(stringParameter && param.by.substr(0, 1) == "!")
199  {
200  parameterValue = stringParameter->getValue();
201  }
202  replaceMap[param.replace] = parameterValue;
203  }
204  }
205  info.replaceMap = replaceMap;
206  return info;
207 }
208 
209 //------------------------------------------------------------------------------
210 
211 void DynamicView::updating()
212 {
213 }
214 
215 //------------------------------------------------------------------------------
216 
217 void DynamicView::swapping()
218 {
219 }
220 
221 //------------------------------------------------------------------------------
222 
223 void DynamicView::createTab( ::fwActivities::registry::ActivityMsg info )
224 {
225  DynamicViewInfo viewInfo;
226  viewInfo.title = info.getTitle();
227  viewInfo.tabID = info.getTabID();
228  viewInfo.closable = info.isClosable();
229  viewInfo.icon = info.getIconPath();
230  viewInfo.tooltip = info.getToolTip();
231  viewInfo.viewConfigID = info.getAppConfigID();
232  viewInfo.replaceMap = info.getReplaceMap();
233 
234  this->launchTab(viewInfo);
235 }
236 
237 //------------------------------------------------------------------------------
238 
239 void DynamicView::launchTab(DynamicViewInfo& info)
240 {
241  static int count = 0;
242  if(m_tabIDList.find(info.tabID) != m_tabIDList.end() )
243  {
245  "The tab " + info.title + " cannot be opened twice.",
246  ::fwGui::dialog::IMessageDialog::WARNING);
247  return;
248  }
249 
250  if ( m_titleToCount.find( info.title ) != m_titleToCount.end() )
251  {
252  m_titleToCount[ info.title ]++;
253  }
254  else
255  {
256  m_titleToCount[ info.title ] = 1;
257  }
258 
259  QString finalTitle = QString("%1 %2").arg( info.title.c_str(), "(%1)" ).arg( m_titleToCount[ info.title ] );
260  info.wid = QString("DynamicView-%1").arg(count++).toStdString();
261 
262  ::fwGuiQt::container::QtContainer::sptr subContainer = ::fwGuiQt::container::QtContainer::New();
263  QWidget* widget = new QWidget();
264  subContainer->setQtContainer(widget);
265  ::fwGui::GuiRegistry::registerWIDContainer(info.wid, subContainer);
266 
267  info.replaceMap[ "WID_PARENT" ] = info.wid;
268  std::string genericUidAdaptor = ::fwServices::registry::AppConfig::getUniqueIdentifier(info.viewConfigID);
269  info.replaceMap["GENERIC_UID"] = genericUidAdaptor;
270 
271  ::fwServices::IAppConfigManager::sptr helper = ::fwServices::IAppConfigManager::New();
272  helper->setConfig( info.viewConfigID, info.replaceMap );
273 
274  try
275  {
276  if (!m_dynamicConfigStartStop)
277  {
278  helper->launch();
279  }
280  else
281  {
282  helper->create();
283  }
284  }
285  catch( std::exception& e )
286  {
287  ::fwGui::dialog::MessageDialog::showMessageDialog("Activity launch failed",
288  e.what(),
289  ::fwGui::dialog::IMessageDialog::CRITICAL);
290  OSLM_ERROR(e.what());
291  }
292 
293  info.container = subContainer;
294  info.helper = helper;
295 
296  m_dynamicInfoMap[widget] = info;
297  m_tabIDList.insert(info.tabID);
298 
299  int index = m_tabWidget->addTab(widget, finalTitle );
300  if(!info.tooltip.empty())
301  {
302  m_tabWidget->setTabToolTip(index, QString::fromStdString(info.tooltip));
303  }
304  if(!info.icon.empty())
305  {
306  m_tabWidget->setTabIcon(index, QIcon(QString::fromStdString(info.icon)) );
307  }
308  m_tabWidget->setCurrentWidget(widget);
309 }
310 
311 //------------------------------------------------------------------------------
312 
313 void DynamicView::info( std::ostream& _sstream )
314 {
315 }
316 
317 //------------------------------------------------------------------------------
318 
319 void DynamicView::closeTabSignal( int index )
320 {
321  closeTab( index, false );
322 }
323 
324 //------------------------------------------------------------------------------
325 
326 void DynamicView::closeTab( int index, bool forceClose )
327 {
328  QWidget* widget = m_tabWidget->widget(index);
329 
330  SLM_ASSERT("Widget is not in dynamicInfoMap", m_dynamicInfoMap.find(widget) != m_dynamicInfoMap.end());
331  DynamicViewInfo info = m_dynamicInfoMap[widget];
332  if ( info.closable || forceClose )
333  {
334  m_tabIDList.erase(info.tabID);
335  if (!m_dynamicConfigStartStop)
336  {
337  info.helper->stopAndDestroy();
338  }
339  else
340  {
341  if (info.helper->isStarted())
342  {
343  info.helper->stop();
344  }
345  info.helper->destroy();
346  }
347  info.helper.reset();
348 
349  //Remove tab first, to avoid tab beeing removed by container->destroy
350  m_currentWidget = 0;
351  m_tabWidget->removeTab(index);
352 
354 
355  info.container->destroyContainer();
356  info.container.reset();
357  m_dynamicInfoMap.erase(widget);
358  }
359  else
360  {
362  "The tab " + info.title + " can not be closed.",
363  ::fwGui::dialog::IMessageDialog::INFO);
364  }
365 }
366 
367 //------------------------------------------------------------------------------
368 
369 void DynamicView::changedTab( int index )
370 {
371  QWidget* widget = m_tabWidget->widget(index);
372 
373  if (m_dynamicConfigStartStop && widget != m_currentWidget)
374  {
375  if (m_currentWidget)
376  {
377  DynamicViewInfo oldinfo = m_dynamicInfoMap[m_currentWidget];
378  oldinfo.helper->stop();
379  }
380 
381  if (widget)
382  {
383  DynamicViewInfo newinfo = m_dynamicInfoMap[widget];
384  if (!newinfo.helper->isStarted())
385  {
386  newinfo.helper->start();
387  newinfo.helper->update();
388  }
389  }
390 
391  }
392 
393  m_currentWidget = widget;
394 }
395 
396 //------------------------------------------------------------------------------
397 
398 }// namespace editor
399 }// namespace guiQt
const std::string & getTitle() const
Return activity title.
Definition: ActivityMsg.hpp:49
static FWGUI_API void unregisterWIDContainer(std::string wid)
Unregisters container associate with window ID.
Definition: GuiRegistry.cpp:96
const ReplaceMapType & getReplaceMap() const
Return the map of the string association to replace in config.
Definition: ActivityMsg.hpp:91
#define OSLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:310
#define SLM_TRACE_FUNC()
Trace contextual function signature.
Definition: spyLog.hpp:329
static FWGUI_API IMessageDialog::Buttons showMessageDialog(const std::string &title, const std::string &message,::fwGui::dialog::IMessageDialog::Icons icon=INFO)
STL namespace.
#define FW_DEPRECATED_MSG(message, version)
Use this macro when deprecating a function to warn the developer.
Definition: spyLog.hpp:360
This editor manages tabs containing sub-configurations.
const std::string & getTabID() const
Return tab identifier.
Definition: ActivityMsg.hpp:55
static FWGUI_API void registerWIDContainer(std::string wid,::fwGui::container::fwContainer::sptr container)
Registers container associate with window ID.
Definition: GuiRegistry.cpp:87
Activity information sent by signal to launch new activities in a tab.
Definition: ActivityMsg.hpp:25
const std::string & getAppConfigID() const
Return appConfig identifier.
Definition: ActivityMsg.hpp:61
#define OSLM_ERROR(message)
Definition: spyLog.hpp:274
const std::string & getIconPath() const
Return activity icon path.
Definition: ActivityMsg.hpp:73
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
FWGUI_API void create()
Creates view, sub-views and toolbar containers. Manages sub-views and toobar services.
bool isClosable() const
Return if the activity can be closed.
Definition: ActivityMsg.hpp:43
static FWSERVICES_API std::shared_ptr< IAppConfigManager > New()
static FWSERVICES_API std::string getUniqueIdentifier(const std::string &serviceUid="")
Create an unique identifier.
Definition: AppConfig.cpp:269
const std::string & getToolTip() const
Return tooltip.
Definition: ActivityMsg.hpp:79
The namespace guiQt contains the basic services to build the application IHM with Qt...
Definition: Code.hpp:21
Defines the service interface managing a view placed in main frame.
Definition: IView.hpp:23
FWGUI_API void initialize()
Initialize managers.