PluginManagerDialog.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2008 National Institute For Space Research (INPE) - Brazil.
2 
3  This file is part of the TerraLib - a Framework for building GIS enabled
4  applications.
5 
6  TerraLib is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published by
8  the Free Software Foundation, either version 3 of the License,
9  or (at your option) any later version.
10 
11  TerraLib is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with TerraLib. See COPYING. If not, write to
18  TerraLib Team at <terralib-team@terralib.org>.
19  */
20 
21 /*!
22  \file terralib/plugins/manager/PluginManagerDialog.cpp
23 
24  \brief A Qt dialog for plugin management.
25 */
26 
27 // TerraLib
28 #include "PluginManagerDialog.h"
29 #include "../../../../Exception.h"
30 #include "../../../../core/plugin/Exception.h"
31 #include "../../../../core/plugin/PluginManager.h"
32 #include "../../../../core/plugin/Serializers.h"
33 #include "../../../../core/plugin/Utils.h"
34 #include "../../help/HelpManager.h"
35 #include "../../utils/CentralizedCheckBoxDelegate.h"
36 #include "../../utils/ResourceChooser.h"
37 #include "ui_PluginManagerDialogForm.h"
38 
39 // Boost
40 #include <boost/algorithm/string/join.hpp>
41 #include <boost/format.hpp>
42 
43 // Qt
44 #include <QMessageBox>
45 #include <QtCore/QDir>
46 #include <QtCore/QFileInfo>
47 
48 void GetDependents(const std::string& plugin_name,
49  std::vector<std::string>& dependents)
50 {
51  std::vector<std::string> curDeps =
53 
54  for(auto dep : curDeps)
55  {
56  GetDependents(dep, dependents);
57  }
58  auto it = std::find(dependents.begin(), dependents.end(), plugin_name);
59  if(it == dependents.end())
60  dependents.push_back(plugin_name);
61 }
62 
63 void ShowException(const boost::exception& e)
64 {
65  if(const std::string* d = boost::get_error_info<te::ErrorDescription>(e))
66  QMessageBox::warning(nullptr, "Plugin manager", d->c_str());
67  else
68  QMessageBox::warning(nullptr, "Plugin manager",
69  QObject::tr("Unknown error has occurred"));
70 }
71 
72 
74  Qt::WindowFlags f)
75  : QDialog(parent, f), m_ui(new Ui::PluginManagerDialogForm)
76 {
77  m_ui->setupUi(this);
78 
79  setWindowTitle(tr("Manage Application Plugins"));
80  // configure the table
81  QStringList labels;
82  labels << QObject::tr("Enabled") << QObject::tr("Plugin")
83  << QObject::tr("Version") << QObject::tr("License")
84  << QObject::tr("Site") << QObject::tr("Provider")
85  << QObject::tr("Provider site") << QObject::tr("Provider email")
86  << QObject::tr("Name");
87 
88  m_ui->m_tableWidget->setColumnCount(labels.size());
89  m_ui->m_tableWidget->setHorizontalHeaderLabels(labels);
90  m_ui->m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
91 
92  // fill the table with all the available plugins
94 
95  // set icons
96  m_ui->m_imgLabel->setPixmap(QIcon::fromTheme("plugin").pixmap(48, 48));
97  m_ui->m_addButton->setIcon(QIcon::fromTheme("list-add"));
98  m_ui->m_removeButton->setIcon(QIcon::fromTheme("list-remove"));
99 
100  // configure help button
101  m_ui->m_helpPushButton->setPageReference(
102  "widgets/pluginmanager/PluginManager.html");
103 
104  connect(m_ui->m_addButton, SIGNAL(clicked()), this,
105  SLOT(onAddPushButtonClicked()));
106  connect(m_ui->m_removeButton, SIGNAL(clicked()), this,
107  SLOT(onRemovePushButtonClicked()));
108  connect(m_ui->m_applyPushButton, SIGNAL(clicked()), this,
109  SLOT(onApplyPushButtonClicked()));
110  connect(m_ui->m_closePushButton, SIGNAL(clicked()), this,
111  SLOT(onClosePushButtonClicked()));
112  connect(m_ui->m_enableAll, SIGNAL(stateChanged(int)), this,
113  SLOT(onEnableAllChanged(int)));
114 }
115 
117 
119  const te::core::PluginInfo& pinfo, PluginStatus status)
120 {
121  int nRow = m_ui->m_tableWidget->rowCount();
122  int nCol = m_ui->m_tableWidget->columnCount();
123 
124  m_ui->m_tableWidget->insertRow(nRow);
125 
126  // creates the checkbox
127 
128  QWidget* widget = new QWidget();
129  QCheckBox* checkBox = new QCheckBox();
130  checkBox->setObjectName(pinfo.name.c_str());
131  QHBoxLayout* layout = new QHBoxLayout(widget);
132  checkBox->setChecked((status == PluginStatus::loaded) ? true : false);
133  if(status == PluginStatus::broken)
134  checkBox->setVisible(false);
135  layout->addWidget(checkBox);
136  layout->setAlignment(Qt::AlignCenter);
137  layout->setContentsMargins(0, 0, 0, 0);
138  widget->setLayout(layout);
139  m_ui->m_tableWidget->setCellWidget(nRow, PluginHeader::enabled, widget);
140  connect(checkBox, SIGNAL(stateChanged(int)), this,
141  SLOT(onEnabledChanged(int)));
142  // populate each cell
143  for(int col = 1; col < nCol; col++)
144  {
145  QTableWidgetItem* item = new QTableWidgetItem();
146  switch(col)
147  {
148  case PluginHeader::display_name:
149  item->setText(QString::fromUtf8(pinfo.display_name.c_str()));
150  break;
151  case PluginHeader::version:
152  item->setText(QString::fromUtf8(pinfo.version.c_str()));
153  break;
154  case PluginHeader::license_description:
155  item->setText(QString::fromUtf8(pinfo.license_description.c_str()));
156  break;
157  case PluginHeader::site:
158  item->setText(QString::fromUtf8(pinfo.site.c_str()));
159  break;
160  case PluginHeader::provider_name:
161  item->setText(QString::fromUtf8(pinfo.provider.name.c_str()));
162  break;
163  case PluginHeader::provider_site:
164  item->setText(QString::fromUtf8(pinfo.provider.site.c_str()));
165  break;
166  case PluginHeader::provider_email:
167  item->setText(QString::fromUtf8(pinfo.provider.email.c_str()));
168  break;
169  case PluginHeader::name:
170  item->setText(QString::fromUtf8(pinfo.name.c_str()));
171  break;
172  default:
173  break;
174  }
175  item->setFlags(Qt::ItemFlags(item->flags() ^ Qt::ItemIsEditable));
176  m_ui->m_tableWidget->setItem(nRow, col, item);
177  }
178  m_ui->m_tableWidget->resizeColumnsToContents();
179 }
180 
182  const std::string& plugin_name)
183 {
184  QLayout* layout =
185  m_ui->m_tableWidget
186  ->cellWidget(getPluginRow(plugin_name), PluginHeader::enabled)
187  ->layout();
188  QCheckBox* checkBox =
189  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
190 
191  checkBox->setCheckState(
192  (checkBox->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked);
193 }
194 
196 {
197  QWidget* widget = m_ui->m_tableWidget->cellWidget(row, 0);
198  widget->setEnabled(false);
199  int nCol = m_ui->m_tableWidget->columnCount();
200  for(int col = 0; col < nCol; ++col)
201  {
202  if(col == PluginHeader::enabled)
203  {
204  QWidget* widget = m_ui->m_tableWidget->cellWidget(row, col);
205  widget->setEnabled(false);
206  continue;
207  }
208  QTableWidgetItem* item = m_ui->m_tableWidget->item(row, col);
209  item->setFlags(Qt::ItemFlags(item->flags() ^ Qt::ItemIsEnabled));
210  }
211 }
212 
214 {
215  std::vector<te::core::PluginInfo> loaded =
217  std::vector<te::core::PluginInfo> unloaded =
219  std::vector<te::core::PluginInfo> broken =
221 
222  for(int i = 0; i < (int)loaded.size(); ++i)
223  addEntry(loaded[i], PluginStatus::loaded);
224 
225  for(int i = 0; i < (int)unloaded.size(); ++i)
226  addEntry(unloaded[i], PluginStatus::unloaded);
227 
228  for(int i = 0; i < (int)broken.size(); ++i)
229  addEntry(broken[i], PluginStatus::broken);
230 }
231 
233 {
234  QTableWidgetItem* item = m_ui->m_tableWidget->item(
235  row, te::qt::widgets::PluginManagerDialog::PluginHeader::name);
236  std::string plugin_name = item->text().toUtf8().data();
237  return plugin_name;
238 }
239 
241  const std::string& plugin_name)
242 {
243  int nRow = m_ui->m_tableWidget->rowCount();
244  for(int row = 0; row < nRow; ++row)
245  {
246  QTableWidgetItem* item = m_ui->m_tableWidget->item(
247  row, te::qt::widgets::PluginManagerDialog::PluginHeader::name);
248  if(item->text().toUtf8().data() == plugin_name)
249  return row;
250  }
251  return -1;
252 }
253 
256 {
257  QTableWidgetItem* item = m_ui->m_tableWidget->item(row, PluginHeader::name);
258  std::string plugin_name = item->text().toUtf8().data();
259 
260  QLayout* layout = m_ui->m_tableWidget->cellWidget(row, 0)->layout();
261  QCheckBox* checkBox =
262  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
263 
264  if(!checkBox->isEnabled())
266  if(!checkBox->isVisible())
267  return PluginStatus::broken;
268  if(checkBox->isChecked())
269  {
271  return PluginStatus::to_load;
272  return PluginStatus::loaded;
273  }
274  else
275  {
278  return PluginStatus::unloaded;
279  }
280 }
281 
283 {
284  std::vector<te::core::PluginInfo> v_broken =
286  for(auto broken : v_broken)
287  {
288  if(te::core::PluginManager::instance().isFixed(broken.name))
289  {
290  QLayout* layout =
291  m_ui->m_tableWidget->cellWidget(getPluginRow(broken.name), 0)
292  ->layout();
293  QCheckBox* checkBox =
294  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
295  checkBox->setVisible(true);
296  }
297  }
298 }
299 
301  std::vector<te::core::PluginInfo> v_pInfo)
302 {
303  v_pInfo = te::core::plugin::TopologicalSort(v_pInfo);
304  for(const te::core::PluginInfo& pinfo : v_pInfo)
305  {
306  try
307  {
309  setChanged(getPluginRow(pinfo.name), false);
310  }
311  catch(const boost::exception& e)
312  {
313  ShowException(e);
314  int row = getPluginRow(pinfo.name);
315  QLayout* layout = m_ui->m_tableWidget->cellWidget(row, 0)->layout();
316  QCheckBox* checkBox =
317  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
318  checkBox->setVisible(false);
319  checkBox->setChecked(false);
320  setChanged(getPluginRow(pinfo.name), false);
321  }
322  }
323  updateBroken();
324 }
325 
327 {
328  QString rsc = ResourceChooser::getResource(
329  qApp->applicationDirPath(),
330  tr("TerraLib Plug-in Files (*.teplg.json *.TEPLG.JSON)"), this);
331 
332  try
333  {
334  if(rsc.isEmpty())
335  return;
336 
337  QFileInfo info(rsc);
338 
339  te::core::PluginInfo pInfo;
340 
341  if(info.isFile())
342  {
344  info.absoluteFilePath().toUtf8().data());
345 
347 
349  }
350  else
351  {
352  if(!info.isDir())
353  throw tr("There's no resource selected, no plugins found.");
354 
355  QStringList filters;
356  filters << "*.teplg.json"
357  << "*.TEPLG.JSON";
358 
359  QDir dir(info.canonicalFilePath());
360 
361  QString qdir_s = dir.absolutePath();
362 
363  QStringList plgs = dir.entryList(filters, QDir::Files);
364 
365  if(plgs.isEmpty())
366  {
367  boost::format err_msg(
368  tr("There is no plugin in the following directory: %1%")
369  .toUtf8()
370  .data());
372  (err_msg % dir.absolutePath().toUtf8().data()).str());
373  }
374  QStringList::iterator it;
375 
376  for(it = plgs.begin(); it != plgs.end(); ++it)
377  {
379  dir.absoluteFilePath(*it).toUtf8().data());
380 
382 
384  }
385  }
386  }
387  catch(const boost::exception& e)
388  {
389  ShowException(e);
390  }
391  m_ui->m_applyPushButton->setFocus();
392  m_ui->m_applyPushButton->setEnabled(true);
393 }
394 
396 {
397  int nRow = m_ui->m_tableWidget->rowCount();
398  std::vector<te::core::PluginInfo> toUnload;
399  std::vector<te::core::PluginInfo> toLoad;
400  std::vector<te::core::PluginInfo> toRemove;
401  for(int row = 0; row < nRow; ++row)
402  {
403  std::string plugin_name = getPluginName(row);
404  try
405  {
406  switch(getPluginStatus(row))
407  {
409  toLoad.push_back(
410  te::core::PluginManager::instance().getPluginInfo(plugin_name));
411  break;
413  toUnload.push_back(
414  te::core::PluginManager::instance().getPluginInfo(plugin_name));
415  break;
417  toRemove.push_back(
418  te::core::PluginManager::instance().getPluginInfo(plugin_name));
419  break;
420  default:
421  break;
422  }
423  }
424  catch(const boost::exception& e)
425  {
426  ShowException(e);
427  }
428  }
429  if(!toUnload.empty())
430  unloadPlugins(toUnload);
431  if(!toRemove.empty())
432  removeEntries(toRemove);
433  if(!toLoad.empty())
434  loadPlugins(toLoad);
435 
436  m_ui->m_applyPushButton->setEnabled(false);
437 }
438 
440 {
441  reject();
442 }
443 
445 {
446  int nRow = m_ui->m_tableWidget->rowCount();
447  switch(state)
448  {
449  case Qt::Checked:
450  for(int row = 0; row < nRow; ++row)
451  {
452  QLayout* layout =
453  m_ui->m_tableWidget->cellWidget(row, PluginHeader::enabled)
454  ->layout();
455  QCheckBox* checkBox =
456  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
457  if(checkBox->isVisible())
458  checkBox->setChecked(true);
459  }
460  break;
461  case Qt::Unchecked:
462  for(int row = 0; row < nRow; ++row)
463  {
464  QLayout* layout =
465  m_ui->m_tableWidget->cellWidget(row, PluginHeader::enabled)
466  ->layout();
467  QCheckBox* checkBox =
468  findChild<QCheckBox*>(layout->itemAt(0)->widget()->objectName());
469  if(checkBox->isVisible())
470  checkBox->setChecked(false);
471  }
472  break;
473  default:
474  break;
475  }
476 }
477 
479 {
480  QCheckBox* checkBox = qobject_cast<QCheckBox*>(QObject::sender());
481  std::string plugin_name = checkBox->objectName().toUtf8().data();
482  int row = getPluginRow(plugin_name);
483  switch(getPluginStatus(row))
484  {
488  setChanged(row, true);
489  break;
490  default:
491  setChanged(row, false);
492  break;
493  }
494  m_ui->m_applyPushButton->setEnabled(true);
495 }
496 
498 {
500  "widgets/pluginmanager/PluginManager.html", "dpi.inpe.br.qtwidgets");
501 }
502 
504 {
505  QModelIndexList indexList =
506  m_ui->m_tableWidget->selectionModel()->selectedIndexes();
507  for(const auto index : indexList)
508  {
509  disableRow(index.row());
510  }
511  m_ui->m_applyPushButton->setFocus();
512  m_ui->m_applyPushButton->setEnabled(true);
513 }
514 
516  std::vector<te::core::PluginInfo> v_pInfo)
517 {
518  v_pInfo = te::core::plugin::TopologicalSort(v_pInfo);
519  unloadPlugins(v_pInfo);
520  for(const te::core::PluginInfo& pinfo : v_pInfo)
521  {
523  int row = getPluginRow(pinfo.name);
524  m_ui->m_tableWidget->removeRow(row);
525  }
526 }
527 
528 
530 {
531  int nCol = m_ui->m_tableWidget->columnCount();
532 
533  for(int col = 1; col < nCol; ++col)
534  {
535  QFont font;
536  font.setBold(bold);
537  m_ui->m_tableWidget->item(row, col)->setFont(font);
538  }
539  m_ui->m_tableWidget->resizeColumnsToContents();
540 }
541 
543  std::vector<te::core::PluginInfo> v_pInfo)
544 {
545  v_pInfo = te::core::plugin::TopologicalSort(v_pInfo);
546  for(auto plugin = v_pInfo.rbegin(); plugin != v_pInfo.rend(); ++plugin)
547  {
548  int row = getPluginRow(plugin->name);
549  try
550  {
551  te::core::PluginManager::instance().stop(plugin->name);
553  setChanged(row, false);
554  }
556  {
557  QMessageBox::StandardButton reply;
558  boost::format msg(tr("Unloading '%1%' will unload the following:\n"
559  "%2%\n"
560  "Do you want to continue?")
561  .toUtf8()
562  .data());
563 
564  std::vector<std::string> dependents;
565  GetDependents(plugin->name, dependents);
566 
567  dependents.erase(
568  std::remove(dependents.begin(), dependents.end(), plugin->name),
569  dependents.end());
570 
571  reply = QMessageBox::question(
572  this, "Warning!",
573  (msg % plugin->name % boost::algorithm::join(dependents, "\n"))
574  .str()
575  .c_str(),
576  QMessageBox::Yes | QMessageBox::No);
577 
579  if(reply == QMessageBox::Yes)
580  {
581  for(auto it = dependents.begin(); it != dependents.end(); ++it)
582  {
583  if(*it != plugin->name)
584  changePluginStatus(*it);
585  }
586  setChanged(row, false);
587  }
588  }
589  }
590 }
591 
std::string license_description
A brief description about the plugin license.
int getPluginRow(const std::string &plugin_name)
std::string email
The provider contact e-mail.
Definition: LibraryInfo.h:50
void recursiveUnload(const std::string &plugin_name)
Try to unload a given plugin and its dependents recursively.
void changePluginStatus(const std::string &plugin_name)
TECOREEXPORT std::vector< PluginInfo > TopologicalSort(const std::vector< PluginInfo > &v_pinfo)
void stop(const std::string &plugin_name)
Stop a loaded plugin.
An exception indicating an error when trying to shutdown a plugin with a dependent.
void insert(const PluginInfo &pinfo)
Adds plugin with its plugin information to the list of unloaded plugins.
std::vector< PluginInfo > getBrokenPlugins() const
Return the list of plugins that could not be loaded.
Basic information about a plugin.
void unloadPlugins(std::vector< core::PluginInfo > v_pInfo)
std::unique_ptr< Ui::PluginManagerDialogForm > m_ui
std::string site
The provider home page.
Definition: LibraryInfo.h:49
bool isUnloaded(const std::string &plugin_name) const
Returns true if the plugin is in the not-loaded list of plugins.
void load(const std::string &plugin_name, const bool start=true)
It tries to load the informed plugin.
PluginManagerDialog(QWidget *parent=0, Qt::WindowFlags f=0)
boost::error_info< struct tag_error_description, std::string > ErrorDescription
The base type for error report messages.
std::string display_name
The plugin name to be displayed in a graphical interface.
std::string name
The plugin name: an internal value used to identify the plugin in the system. Must be a unique value...
PluginStatus getPluginStatus(const int row)
static PluginManager & instance()
Access the singleton.
std::vector< PluginInfo > getUnloadedPlugins() const
Return the list of plugins that were not loaded.
void setChanged(const int row, bool bold)
static HelpManager & getInstance()
It returns a reference to the singleton instance.
std::vector< std::string > getDependents(const std::string plugin_name)
Return the list of plugins that depends of a given plugin.
std::string name
Provider name: may be a person or a company.
Definition: LibraryInfo.h:48
void removeEntries(std::vector< te::core::PluginInfo > v_pInfo)
void GetDependents(const std::string &plugin_name, std::vector< std::string > &dependents)
static te::dt::DateTime d(2010, 8, 9, 15, 58, 39)
bool isLoaded(const std::string &plugin_name) const
Returns true if the plugin is loaded otherwise returns false.
static QString getResource(const QString &initialPath, const QString &filter, QWidget *parent=0)
use the static when the ResourceChooser have to be presented as a Dialog.
void loadPlugins(std::vector< te::core::PluginInfo > v_pInfo)
void ShowException(const boost::exception &e)
TECOREEXPORT PluginInfo JSONPluginInfoSerializer(const std::string &file_name)
A plugin finder that search for plugins in some special directories defined by compile time macros...
Definition: Serializers.cpp:44
std::string site
An URL pointing to the plugin site.
void remove(const std::string &plugin_name)
Remove plugin from the manager.
std::string version
The plugin version.
A Qt dialog for plugin management.
An exception indicating that a given argument is not valid, for instance if a given item already exis...
Provider provider
Information about the plugin provider.
unsigned int col
std::vector< PluginInfo > getLoadedPlugins() const
Return the list of plugins that are loaded.
void addEntry(const te::core::PluginInfo &pinfo, PluginStatus status)
void unload(const std::string &plugin_name)
Try to unload a given plugin.