dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
uavgadgetinstancemanager.cpp
Go to the documentation of this file.
1 
13 /*
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  * for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, see <http://www.gnu.org/licenses/>
26  */
27 
29 #include "iuavgadget.h"
30 #include "uavgadgetdecorator.h"
31 #include "iuavgadgetfactory.h"
36 #include "icore.h"
37 
39 #include <QtCore/QStringList>
40 #include <QtCore/QSettings>
41 #include <QtCore/QDebug>
42 
43 using namespace Core;
44 
45 static const UAVConfigVersion m_versionUAVGadgetConfigurations = UAVConfigVersion("1.2.0");
46 
48  : QObject(parent)
49 {
50  m_pm = ExtensionSystem::PluginManager::instance();
51  QList<IUAVGadgetFactory *> factories = m_pm->getObjects<IUAVGadgetFactory>();
52  foreach (IUAVGadgetFactory *f, factories) {
53  if (!m_factories.contains(f)) {
54  m_factories.append(f);
55  QString classId = f->classId();
56  QString name = f->name();
57  QIcon icon = f->icon();
58  m_classIdNameMap.insert(classId, name);
59  m_classIdIconMap.insert(classId, icon);
60  }
61  }
62 }
63 
65 {
66  foreach (IOptionsPage *page, m_optionsPages) {
67  m_pm->removeObject(page);
68  delete page;
69  }
70 }
71 
73 {
74  while (!m_configurations.isEmpty()) {
75  emit configurationToBeDeleted(m_configurations.takeLast());
76  }
77  qs->beginGroup("UAVGadgetConfigurations");
78  UAVConfigInfo configInfo(qs);
79  configInfo.setNameOfConfigurable("UAVGadgetConfigurations");
80  if (configInfo.version() == UAVConfigVersion()) {
81  // If version is not set, assume its a old version before readable config (1.0.0).
82  // however compatibility to 1.0.0 is broken.
83  configInfo.setVersion("1.0.0");
84  }
85 
86  if (configInfo.version() == UAVConfigVersion("1.1.0")) {
87  configInfo.notify(tr("Migrating UAVGadgetConfigurations from version 1.1.0 to ")
88  + m_versionUAVGadgetConfigurations.toString());
89  readConfigs_1_1_0(qs); // this is fully compatible with 1.2.0
90  } else if (!configInfo.standardVersionHandlingOK(m_versionUAVGadgetConfigurations)) {
91  // We are in trouble now. User wants us to quit the import, but when he saves
92  // the GCS, his old config will be lost.
93  configInfo.notify(tr("You might want to save your old config NOW since it might be "
94  "replaced by broken one when you exit the GCS!"));
95  } else {
96  readConfigs_1_2_0(qs);
97  }
98 
99  qs->endGroup();
100  createOptionsPages();
101 }
102 
103 void UAVGadgetInstanceManager::readConfigs_1_2_0(QSettings *qs)
104 {
105  UAVConfigInfo configInfo;
106 
107  foreach (QString classId, m_classIdNameMap.keys()) {
108  IUAVGadgetFactory *f = factory(classId);
109  qs->beginGroup(classId);
110 
111  QStringList configs = QStringList();
112 
113  configs = qs->childGroups();
114  foreach (QString configName, configs) {
115  qs->beginGroup(configName);
116  configInfo.read(qs);
117  configInfo.setNameOfConfigurable(classId + "-" + configName);
118  qs->beginGroup("data");
119  IUAVGadgetConfiguration *config = f->createConfiguration(qs, &configInfo);
120  if (config) {
121  config->setName(configName);
122  config->setProvisionalName(configName);
123  config->setLocked(configInfo.locked());
124  int idx = indexForConfig(m_configurations, classId, configName);
125  if (idx >= 0) {
126  // We should replace the config, but it might be used, so just
127  // throw it out of the list. The GCS should be reinitialised soon.
128  m_configurations[idx] = config;
129  } else {
130  m_configurations.append(config);
131  }
132  }
133  qs->endGroup();
134  qs->endGroup();
135  }
136 
137  // In case no configuration settings were found, try to create a default configuration
138  if (configs.count() == 0) {
139  IUAVGadgetConfiguration *config = f->createConfiguration(nullptr, nullptr);
140  // It is not mandatory for uavgadgets to have a configuration (e.g. settings),
141  // therefore we have to check that "config" exists.
142  if (config) {
143  config->setName(tr("default"));
144  config->setProvisionalName(tr("default"));
145  m_configurations.append(config);
146  }
147  }
148  qs->endGroup();
149  }
150 }
151 
152 void UAVGadgetInstanceManager::readConfigs_1_1_0(QSettings *qs)
153 {
154  UAVConfigInfo configInfo;
155 
156  foreach (QString classId, m_classIdNameMap.keys()) {
157  IUAVGadgetFactory *f = factory(classId);
158  qs->beginGroup(classId);
159 
160  QStringList configs = QStringList();
161 
162  configs = qs->childGroups();
163  foreach (QString configName, configs) {
164  qs->beginGroup(configName);
165  bool locked = qs->value("config.locked").toBool();
166  configInfo.setNameOfConfigurable(classId + "-" + configName);
167  IUAVGadgetConfiguration *config = f->createConfiguration(qs, &configInfo);
168  if (config) {
169  config->setName(configName);
170  config->setProvisionalName(configName);
171  config->setLocked(locked);
172  int idx = indexForConfig(m_configurations, classId, configName);
173  if (idx >= 0) {
174  // We should replace the config, but it might be used, so just
175  // throw it out of the list. The GCS should be reinitialised soon.
176  m_configurations[idx] = config;
177  } else {
178  m_configurations.append(config);
179  }
180  }
181  qs->endGroup();
182  }
183 
184  if (configs.count() == 0) {
185  IUAVGadgetConfiguration *config = f->createConfiguration(nullptr, nullptr);
186  // it is not mandatory for uavgadgets to have any configurations (settings)
187  // and therefore we have to check for that
188  if (config) {
189  config->setName(tr("default"));
190  config->setProvisionalName(tr("default"));
191  m_configurations.append(config);
192  }
193  }
194  qs->endGroup();
195  }
196 }
197 
199 {
200  UAVConfigInfo *configInfo;
201  qs->beginGroup("UAVGadgetConfigurations");
202  qs->remove(""); // Remove existing configurations
203  configInfo = new UAVConfigInfo(m_versionUAVGadgetConfigurations, "UAVGadgetConfigurations");
204  configInfo->save(qs);
205  delete configInfo;
206  foreach (IUAVGadgetConfiguration *config, m_configurations) {
207  configInfo = new UAVConfigInfo(config);
208  qs->beginGroup(config->classId());
209  qs->beginGroup(config->name());
210  qs->beginGroup("data");
211  config->saveConfig(qs, configInfo);
212  qs->endGroup();
213  configInfo->save(qs);
214  qs->endGroup();
215  qs->endGroup();
216  delete configInfo;
217  }
218  qs->endGroup();
219 }
220 
221 void UAVGadgetInstanceManager::createOptionsPages()
222 {
223  // In case there are pages (import a configuration), remove them.
224  // Maybe they should be deleted as well (memory-leak),
225  // but this might lead to NULL-pointers?
226  while (!m_optionsPages.isEmpty()) {
227  m_pm->removeObject(m_optionsPages.takeLast());
228  }
229 
230  QMutableListIterator<IUAVGadgetConfiguration *> ite(m_configurations);
231  while (ite.hasNext()) {
232  IUAVGadgetConfiguration *config = ite.next();
233  IUAVGadgetFactory *f = factory(config->classId());
234  IOptionsPage *p = f->createOptionsPage(config);
235  if (p) {
236  emit splashMessages(QString(tr("Loading %1 plugin options page for configuration %2")
237  .arg(config->classId())
238  .arg(config->name())));
239  IOptionsPage *page =
241  page->setIcon(f->icon());
242  m_optionsPages.append(page);
243  m_pm->addObject(page);
244  } else {
245  qWarning() << "UAVGadgetInstanceManager::createOptionsPages - failed to create options "
246  "page for configuration "
247  + config->classId() + ":" + config->name() + ", configuration will be removed.";
248  // The m_optionsPages list and m_configurations list must be in synch otherwise nasty
249  // issues happen later
250  // so if we fail to create an options page we must remove the associated configuration
251  ite.remove();
252  }
253  }
254 }
255 
256 IUAVGadget *UAVGadgetInstanceManager::createGadget(QString classId, QWidget *parent,
257  bool forceLoadConfiguration)
258 {
259  IUAVGadgetFactory *f = factory(classId);
260  if (f) {
261  if (f->classId() != "EmptyGadget")
262  emit splashMessages(
263  QString(tr("Creating %1 with %2 configuration").arg(f->classId()).arg(f->name())));
264  else
265  emit splashMessages(tr("Loading EmptyGadget"));
266  QList<IUAVGadgetConfiguration *> *configs = configurations(classId);
267  IUAVGadget *g = f->createGadget(parent);
268  IUAVGadget *gadget = new UAVGadgetDecorator(g, configs, forceLoadConfiguration);
269  m_gadgetInstances.append(gadget);
270  connect(this, SIGNAL(configurationAdded(IUAVGadgetConfiguration *)), gadget,
272  connect(this, SIGNAL(configurationChanged(IUAVGadgetConfiguration *)), gadget,
274  connect(this, SIGNAL(configurationNameChanged(IUAVGadgetConfiguration *, QString, QString)),
275  gadget,
276  SLOT(configurationNameChanged(IUAVGadgetConfiguration *, QString, QString)));
277  connect(this, SIGNAL(configurationToBeDeleted(IUAVGadgetConfiguration *)), gadget,
279  return gadget;
280  }
281  return nullptr;
282 }
283 
285 {
286  if (m_gadgetInstances.contains(gadget)) {
287  m_gadgetInstances.removeOne(gadget);
288  delete gadget;
289  gadget = nullptr;
290  }
291 }
292 
301 {
302  foreach (IUAVGadget *gadget, m_gadgetInstances) {
303  m_gadgetInstances.removeOne(gadget);
304  delete gadget;
305  }
306 }
307 
309 {
310  // to be able to delete a configuration, no instance must be using it
311  foreach (IUAVGadget *gadget, m_gadgetInstances) {
312  if (gadget->activeConfiguration() == config) {
313  return false;
314  }
315  }
316  // and it cannot be the only configuration
317  foreach (IUAVGadgetConfiguration *c, m_configurations) {
318  if (c != config && c->classId() == config->classId())
319  return true;
320  }
321  return false;
322 }
323 
325 {
326  m_provisionalDeletes.append(config);
327  if (m_provisionalConfigs.contains(config)) {
328  int i = m_provisionalConfigs.indexOf(config);
329  m_provisionalConfigs.removeAt(i);
330  m_provisionalOptionsPages.removeAt(i);
331  int j = m_takenNames[config->classId()].indexOf(config->name());
332  m_takenNames[config->classId()].removeAt(j);
333  m_settingsDialog->deletePage();
334  } else if (m_configurations.contains(config)) {
335  m_settingsDialog->deletePage();
336  }
337  configurationNameEdited("", false);
338 }
339 
341 {
342  QString name = suggestName(configToClone->classId(), configToClone->name());
343 
344  IUAVGadgetConfiguration *config = configToClone->clone();
345  config->setName(name);
346  config->setProvisionalName(name);
347  IUAVGadgetFactory *f = factory(config->classId());
348  IOptionsPage *p = f->createOptionsPage(config);
349  IOptionsPage *page = new UAVGadgetOptionsPageDecorator(p, config);
350  page->setIcon(f->icon());
351  m_provisionalConfigs.append(config);
352  m_provisionalOptionsPages.append(page);
353  m_settingsDialog->insertPage(page);
354 }
355 
356 // "name" => "name 1", "Name 3" => "Name 4", "Name1" => "Name1 1"
357 QString UAVGadgetInstanceManager::suggestName(QString classId, QString name)
358 {
359  QString suggestedName;
360 
361  QString last = name.split(" ").last();
362  bool ok;
363  int num = last.toInt(&ok);
364  int i = 1;
365  if (ok) {
366  QStringList n = name.split(" ");
367  n.removeLast();
368  name = n.join(" ");
369  i = num + 1;
370  }
371  do {
372  suggestedName = name + " " + QString::number(i);
373  ++i;
374  } while (m_takenNames[classId].contains(suggestedName));
375 
376  m_takenNames[classId].append(suggestedName);
377  return suggestedName;
378 }
379 
381 {
382  if (m_provisionalDeletes.contains(config)) {
383  m_provisionalDeletes.removeAt(m_provisionalDeletes.indexOf(config));
384  int i = m_configurations.indexOf(config);
385  if (i >= 0) {
386  emit configurationToBeDeleted(config);
387  int j = m_takenNames[config->classId()].indexOf(config->name());
388  m_takenNames[config->classId()].removeAt(j);
389  m_pm->removeObject(m_optionsPages.at(i));
390  m_configurations.removeAt(i);
391  m_optionsPages.removeAt(i); // TODO delete
392  }
393  return;
394  }
395  if (config->provisionalName() != config->name()) {
396  emit configurationNameChanged(config, config->name(), config->provisionalName());
397  config->setName(config->provisionalName());
398  }
399  if (m_configurations.contains(config)) {
400  emit configurationChanged(config);
401  } else if (m_provisionalConfigs.contains(config)) {
402  emit configurationAdded(config);
403  int i = m_provisionalConfigs.indexOf(config);
404  IOptionsPage *page = m_provisionalOptionsPages.at(i);
405  m_configurations.append(config);
406  m_optionsPages.append(page);
407  m_provisionalConfigs.removeAt(i);
408  m_provisionalOptionsPages.removeAt(i);
409  m_pm->addObject(page);
410  }
411 }
412 
414 {
415  bool disable = false;
416  foreach (IUAVGadgetConfiguration *c, m_configurations) {
417  foreach (IUAVGadgetConfiguration *d, m_configurations) {
418  if (c != d && c->classId() == d->classId()
419  && c->provisionalName() == d->provisionalName()) {
420  qDebug() << "Two identically named configurations: " << c->classId() << "."
421  << c->provisionalName() << "and " << d->classId() << "."
422  << d->provisionalName();
423  disable = true;
424  }
425  }
426  foreach (IUAVGadgetConfiguration *d, m_provisionalConfigs) {
427  if (c != d && c->classId() == d->classId()
428  && c->provisionalName() == d->provisionalName()) {
429  qDebug() << "An identically named provisional and normal configuration: "
430  << c->classId() << "." << c->provisionalName() << "and " << d->classId()
431  << "." << d->provisionalName();
432  disable = true;
433  }
434  }
435  }
436  foreach (IUAVGadgetConfiguration *c, m_provisionalConfigs) {
437  foreach (IUAVGadgetConfiguration *d, m_provisionalConfigs) {
438  if (c != d && c->classId() == d->classId()
439  && c->provisionalName() == d->provisionalName()) {
440  qDebug() << "Two identically named provisional configurations: " << c->classId()
441  << "." << c->provisionalName() << "and " << d->classId() << "."
442  << d->provisionalName();
443  disable = true;
444  }
445  }
446  }
447  if (hasText && text == "")
448  disable = true;
449  m_settingsDialog->disableApplyOk(disable);
450  if (hasText)
451  m_settingsDialog->updateText(text);
452 }
453 
455 {
456  foreach (QString classId, classIds())
457  m_takenNames.insert(classId, configurationNames(classId));
458  m_settingsDialog = settingsDialog;
459 }
460 
462 {
463  m_takenNames.clear();
464  m_provisionalConfigs.clear();
465  m_provisionalDeletes.clear();
466  m_provisionalOptionsPages.clear(); // TODO delete
467  foreach (IUAVGadgetConfiguration *config, m_configurations)
468  config->setProvisionalName(config->name());
469  m_settingsDialog = nullptr;
470 }
471 
472 QStringList UAVGadgetInstanceManager::configurationNames(QString classId) const
473 {
474  QStringList names;
475  foreach (IUAVGadgetConfiguration *config, m_configurations) {
476  if (config->classId() == classId)
477  names.append(config->name());
478  }
479  return names;
480 }
481 
482 QString UAVGadgetInstanceManager::gadgetName(QString classId) const
483 {
484  return m_classIdNameMap.value(classId);
485 }
486 
487 QIcon UAVGadgetInstanceManager::gadgetIcon(QString classId) const
488 {
489  return m_classIdIconMap.value(classId);
490 }
491 
492 IUAVGadgetFactory *UAVGadgetInstanceManager::factory(QString classId) const
493 {
494  foreach (IUAVGadgetFactory *f, m_factories) {
495  if (f->classId() == classId)
496  return f;
497  }
498  return nullptr;
499 }
500 
501 QList<IUAVGadgetConfiguration *> *UAVGadgetInstanceManager::configurations(QString classId) const
502 {
504  foreach (IUAVGadgetConfiguration *config, m_configurations) {
505  if (config->classId() == classId)
506  configs->append(config);
507  }
508  return configs;
509 }
510 
511 int UAVGadgetInstanceManager::indexForConfig(QList<IUAVGadgetConfiguration *> configurations,
512  QString classId, QString configName)
513 {
514  for (int i = 0; i < configurations.length(); ++i) {
515  if (configurations.at(i)->classId() == classId
516  && configurations.at(i)->name() == configName)
517  return i;
518  }
519  return -1;
520 }
virtual IUAVGadget * createGadget(QWidget *parent)=0
virtual IUAVGadgetConfiguration * createConfiguration(QSettings *)
UAVGadgetInstanceManager(QObject *parent=nullptr)
void configurationToBeDeleted(IUAVGadgetConfiguration *config)
virtual IOptionsPage * createOptionsPage(IUAVGadgetConfiguration *)
Trim off last
void setIcon(QIcon icon)
Definition: ioptionspage.h:53
QIcon gadgetIcon(QString classId) const
IUAVGadget * createGadget(QString classId, QWidget *parent, bool forceLoadConfiguration=false)
for i
Definition: OPPlots.m:140
QString toString() const
void configurationAdded(IUAVGadgetConfiguration *config)
void setVersion(int major, int minor, int patch)
Definition: uavconfiginfo.h:77
UAVConfigVersion version()
Definition: uavconfiginfo.h:83
void save(QSettings *qs)
virtual IUAVGadgetConfiguration * clone()=0
void configurationNameEdited(QString text, bool hasText=true)
void applyChanges(IUAVGadgetConfiguration *config)
Eccentricity n
Definition: OPPlots.m:137
bool standardVersionHandlingOK(UAVConfigVersion programVersion)
Default version handling.
void settingsDialogShown(Core::Internal::SettingsDialog *settingsDialog)
void notify(QString message)
void deleteConfiguration(IUAVGadgetConfiguration *config)
void configurationNameChanged(IUAVGadgetConfiguration *config, QString oldName, QString newName)
bool canDeleteConfiguration(IUAVGadgetConfiguration *config)
The Config Info is a helper-class to handle version changes in GCS configuration files.
Definition: uavconfiginfo.h:53
void cloneConfiguration(IUAVGadgetConfiguration *config)
void read(QSettings *qs)
QStringList configurationNames(QString classId) const
void setNameOfConfigurable(const QString nameOfConfigurable)
Definition: uavconfiginfo.h:69
Definition: icore.h:39
void insertPage(IOptionsPage *page)
The IOptionsPage is an interface for providing options pages.
Definition: ioptionspage.h:42
QString gadgetName(QString classId) const
QString classId() const
virtual IUAVGadgetConfiguration * activeConfiguration()
Definition: iuavgadget.h:64
void configurationChanged(IUAVGadgetConfiguration *config)