Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / coreplugin / dialogs / settingsdialog.cpp
blobb09cc54d9a57ff8ffd78465819236bf8ac927e91
1 /**
2 ******************************************************************************
4 * @file settingsdialog.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup CorePlugin Core Plugin
10 * @{
11 * @brief The Core GCS plugin
12 *****************************************************************************/
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.
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.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "settingsdialog.h"
31 #include <extensionsystem/pluginmanager.h>
32 #include "icore.h"
33 #include "coreplugin/uavgadgetinstancemanager.h"
34 #include "coreplugin/uavgadgetoptionspagedecorator.h"
36 #include <QDebug>
37 #include <QSettings>
38 #include <QHeaderView>
39 #include <QLabel>
40 #include <QPushButton>
42 using namespace Core;
43 using namespace Core::Internal;
45 namespace {
46 struct PageData {
47 int index;
48 QString category;
49 QString id;
52 // helper to sort by translated category and name
53 bool compareOptionsPageByCategoryAndNameTr(const IOptionsPage *p1, const IOptionsPage *p2)
55 const UAVGadgetOptionsPageDecorator *gp1 = qobject_cast<const UAVGadgetOptionsPageDecorator *>(p1);
56 const UAVGadgetOptionsPageDecorator *gp2 = qobject_cast<const UAVGadgetOptionsPageDecorator *>(p2);
58 if (gp1 && !gp2) {
59 return false;
61 if (gp2 && !gp1) {
62 return true;
64 if (const int cc = QString::localeAwareCompare(p1->trCategory(), p2->trCategory())) {
65 return cc < 0;
67 return QString::localeAwareCompare(p1->trName(), p2->trName()) < 0;
70 // helper to sort by category and id
71 bool compareOptionsPageByCategoryAndId(const IOptionsPage *p1, const IOptionsPage *p2)
73 const UAVGadgetOptionsPageDecorator *gp1 = qobject_cast<const UAVGadgetOptionsPageDecorator *>(p1);
74 const UAVGadgetOptionsPageDecorator *gp2 = qobject_cast<const UAVGadgetOptionsPageDecorator *>(p2);
76 if (gp1 && !gp2) {
77 return false;
79 if (gp2 && !gp1) {
80 return true;
82 if (const int cc = QString::localeAwareCompare(p1->category(), p2->category())) {
83 return cc < 0;
85 return QString::localeAwareCompare(p1->id(), p2->id()) < 0;
89 Q_DECLARE_METATYPE(::PageData) SettingsDialog::SettingsDialog(QWidget *parent, const QString &categoryId, const QString &pageId) :
90 QDialog(parent), m_applied(false)
92 setupUi(this);
93 #ifdef Q_OS_MAC
94 setWindowTitle(tr("Preferences"));
95 #else
96 setWindowTitle(tr("Options"));
97 #endif
99 QSettings settings;
101 settings.beginGroup("General");
102 settings.beginGroup("Settings");
104 // restore last displayed category and page
105 // this is done only if no category or page was provided through the constructor
106 QString initialCategory = categoryId;
107 QString initialPage = pageId;
108 qDebug() << "SettingsDialog constructor initial category:" << initialCategory << ", initial page:" << initialPage;
109 if (initialCategory.isEmpty() && initialPage.isEmpty()) {
110 initialCategory = settings.value("LastPreferenceCategory", QVariant(QString())).toString();
111 initialPage = settings.value("LastPreferencePage", QVariant(QString())).toString();
112 qDebug() << "SettingsDialog settings initial category:" << initialCategory << ", initial page: " << initialPage;
115 // restore window size
116 int windowWidth = settings.value("WindowWidth", 0).toInt();
117 int windowHeight = settings.value("WindowHeight", 0).toInt();
118 qDebug() << "SettingsDialog window width :" << windowWidth << ", height:" << windowHeight;
119 if (windowWidth > 0 && windowHeight > 0) {
120 resize(windowWidth, windowHeight);
123 // restore splitter size
124 int splitterPosition = settings.value("SplitterPosition", 350).toInt();
125 qDebug() << "SettingsDialog splitter position:" << splitterPosition;
126 QList<int> sizes;
127 sizes << splitterPosition << 400;
128 splitter->setSizes(sizes);
130 settings.endGroup();
131 settings.endGroup();
133 // all extra space must go to the option page and none to the tree
134 splitter->setStretchFactor(0, 0);
135 splitter->setStretchFactor(1, 1);
137 buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
139 connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply()));
141 m_instanceManager = Core::ICore::instance()->uavGadgetInstanceManager();
143 connect(this, SIGNAL(settingsDialogShown(Core::Internal::SettingsDialog *)), m_instanceManager,
144 SLOT(settingsDialogShown(Core::Internal::SettingsDialog *)));
145 connect(this, SIGNAL(settingsDialogRemoved()), m_instanceManager, SLOT(settingsDialogRemoved()));
147 // needs to be queued to be able to change the selection from the selection change signal call
148 connect(this, SIGNAL(categoryItemSelected()), this, SLOT(onCategorySelected()), Qt::QueuedConnection);
150 splitter->setCollapsible(0, false);
151 splitter->setCollapsible(1, false);
152 pageTree->header()->setVisible(false);
154 connect(pageTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(onItemSelected()));
156 QList<Core::IOptionsPage *> pluginPages;
157 QList<Core::IOptionsPage *> gadgetPages;
159 // get all pages and split them between plugin and gadget list
160 QList<Core::IOptionsPage *> pages = ExtensionSystem::PluginManager::instance()->getObjects<IOptionsPage>();
161 foreach(IOptionsPage * page, pages) {
162 if (qobject_cast<UAVGadgetOptionsPageDecorator *>(page)) {
163 gadgetPages.append(page);
164 } else {
165 pluginPages.append(page);
169 // the plugin options page list is sorted by untranslated category and names
170 // this is done to facilitate access to the language settings when GCS is not running in a language understood by the user.
171 qStableSort(pluginPages.begin(), pluginPages.end(), compareOptionsPageByCategoryAndId);
173 // the plugin options page list is sorted by translated names
174 qStableSort(gadgetPages.begin(), gadgetPages.end(), compareOptionsPageByCategoryAndNameTr);
176 // will hold the initially selected item if any
177 QTreeWidgetItem *initialItem = 0;
179 // add plugin pages
180 foreach(IOptionsPage * page, pluginPages) {
181 QTreeWidgetItem *item = addPage(page);
183 // automatically expand all plugin categories
184 item->parent()->setExpanded(true);
185 if (page->id() == initialPage && page->category() == initialCategory) {
186 initialItem = item;
190 // insert separator between plugin and gadget pages
191 QTreeWidgetItem *separator = new QTreeWidgetItem(pageTree);
192 separator->setFlags(separator->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled);
193 separator->setText(0, QString(30, 0xB7));
195 // add gadget pages
196 foreach(IOptionsPage * page, gadgetPages) {
197 QTreeWidgetItem *item = addPage(page);
199 if (page->id() == initialPage && page->category() == initialCategory) {
200 initialItem = item;
204 // handle initially selected item
205 if (initialItem) {
206 if (initialItem->isHidden()) {
207 // item is hidden, meaning it is single child
208 // so select parent category item instead
209 initialItem = initialItem->parent();
211 pageTree->setCurrentItem(initialItem);
215 SettingsDialog::~SettingsDialog()
217 foreach(QString category, m_categoryItemsMap.keys()) {
218 QList<QTreeWidgetItem *> *categoryItemList = m_categoryItemsMap.value(category, NULL);
219 delete categoryItemList;
221 // delete place holders
222 for (int i = 0; i < stackedPages->count(); i++) {
223 QLabel *widget = dynamic_cast<QLabel *>(stackedPages->widget(i));
224 if (widget) {
225 delete widget;
230 QTreeWidgetItem *SettingsDialog::addPage(IOptionsPage *page)
232 PageData pageData;
234 pageData.index = m_pages.count();
235 pageData.category = page->category();
236 pageData.id = page->id();
238 QString category = page->category();
240 QList<QTreeWidgetItem *> *categoryItemList = m_categoryItemsMap.value(category, NULL);
241 if (!categoryItemList) {
242 categoryItemList = new QList<QTreeWidgetItem *>();
243 m_categoryItemsMap.insert(category, categoryItemList);
246 QTreeWidgetItem *categoryItem = NULL;
247 for (int i = 0; i < pageTree->topLevelItemCount(); ++i) {
248 QTreeWidgetItem *tw = pageTree->topLevelItem(i);
249 PageData data = tw->data(0, Qt::UserRole).value<PageData>();
250 if (data.category == page->category()) {
251 categoryItem = tw;
252 break;
255 if (!categoryItem) {
256 categoryItem = new QTreeWidgetItem(pageTree);
257 categoryItem->setIcon(0, page->icon());
258 categoryItem->setText(0, page->trCategory());
259 categoryItem->setData(0, Qt::UserRole, qVariantFromValue(pageData));
262 QTreeWidgetItem *item = new QTreeWidgetItem(categoryItem);
263 item->setText(0, page->trName());
264 item->setData(0, Qt::UserRole, qVariantFromValue(pageData));
266 switch (categoryItemList->size()) {
267 case 0:
268 item->setHidden(true);
269 break;
270 case 1:
271 categoryItemList->at(0)->setHidden(false);
272 break;
273 default:
274 break;
277 categoryItemList->append(item);
279 m_pages.append(page);
281 // creating all option pages upfront is slow, so we create place holder widgets instead
282 // the real option page widget will be created later when the user selects it
283 // the place holder is a QLabel and we assume that no option page will be a QLabel...
284 QLabel *placeholderWidget = new QLabel(stackedPages);
285 stackedPages->addWidget(placeholderWidget);
287 return item;
290 void SettingsDialog::onItemSelected()
292 QTreeWidgetItem *item = pageTree->currentItem();
294 if (!item) {
295 return;
298 if (pageTree->indexOfTopLevelItem(item) >= 0) {
299 if (item->childCount() == 1) {
300 // single child : category will not be expanded
301 item = item->child(0);
302 } else if (item->childCount() > 1) {
303 // multiple children : expand category and select 1st child
304 emit categoryItemSelected();
305 return;
309 // get user data
310 PageData data = item->data(0, Qt::UserRole).value<PageData>();
311 int index = data.index;
312 m_currentCategory = data.category;
313 m_currentPage = data.id;
315 // check if we are looking at a place holder or not
316 QWidget *widget = dynamic_cast<QLabel *>(stackedPages->widget(index));
317 if (widget) {
318 // place holder found, get rid of it...
319 stackedPages->removeWidget(widget);
320 delete widget;
321 // and replace place holder with actual option page
322 IOptionsPage *page = m_pages.at(index);
323 stackedPages->insertWidget(index, page->createPage(stackedPages));
326 IOptionsPage *page = m_pages.at(index);
327 page->updateState();
329 stackedPages->setCurrentIndex(index);
332 void SettingsDialog::onCategorySelected()
334 QTreeWidgetItem *item = pageTree->currentItem();
336 if (item->childCount() > 1) {
337 item->setExpanded(true);
338 pageTree->setCurrentItem(item->child(0), 0, QItemSelectionModel::SelectCurrent);
342 void SettingsDialog::deletePage()
344 QTreeWidgetItem *item = pageTree->currentItem();
346 PageData data = item->data(0, Qt::UserRole).value<PageData>();
347 QString category = data.category;
349 QList<QTreeWidgetItem *> *categoryItemList = m_categoryItemsMap.value(category, NULL);
350 if (categoryItemList) {
351 categoryItemList->removeOne(item);
352 QTreeWidgetItem *parentItem = item->parent();
353 parentItem->removeChild(item);
354 if (parentItem->childCount() == 1) {
355 parentItem->child(0)->setHidden(true);
356 pageTree->setCurrentItem(parentItem, 0, QItemSelectionModel::SelectCurrent);
361 // TODO duplicates a lot of the addPage code...
362 void SettingsDialog::insertPage(IOptionsPage *page)
364 PageData pageData;
366 pageData.index = m_pages.count();
367 pageData.category = page->category();
368 pageData.id = page->id();
370 QTreeWidgetItem *categoryItem = 0;
371 for (int i = 0; i < pageTree->topLevelItemCount(); ++i) {
372 QTreeWidgetItem *tw = pageTree->topLevelItem(i);
373 PageData data = tw->data(0, Qt::UserRole).value<PageData>();
374 if (data.category == page->category()) {
375 categoryItem = tw;
376 break;
379 if (!categoryItem) {
380 return;
383 // If this category has no child right now
384 // we need to add the "default child"
385 QList<QTreeWidgetItem *> *categoryItemList = m_categoryItemsMap.value(page->category(), NULL);
386 if (categoryItem->childCount() == 1) {
387 QTreeWidgetItem *defaultItem = categoryItemList->at(0);
388 defaultItem->setHidden(false);
391 QTreeWidgetItem *item = new QTreeWidgetItem;
392 item->setText(0, page->trName());
393 item->setData(0, Qt::UserRole, qVariantFromValue(pageData));
395 categoryItem->addChild(item);
396 categoryItemList->append(item);
398 m_pages.append(page);
399 stackedPages->addWidget(page->createPage(stackedPages));
401 stackedPages->setCurrentIndex(stackedPages->count());
402 pageTree->setCurrentItem(item);
405 void SettingsDialog::updateText(QString text)
407 QTreeWidgetItem *item = pageTree->currentItem();
409 item->setText(0, text);
412 void SettingsDialog::disableApplyOk(bool disable)
414 buttonBox->button(QDialogButtonBox::Apply)->setDisabled(disable);
415 buttonBox->button(QDialogButtonBox::Ok)->setDisabled(disable);
418 void SettingsDialog::accept()
420 m_applied = true;
421 for (int i = 0; i < m_pages.size(); i++) {
422 QWidget *widget = dynamic_cast<QLabel *>(stackedPages->widget(i));
423 if (!widget) {
424 IOptionsPage *page = m_pages.at(i);
425 page->apply();
426 page->finish();
429 done(QDialog::Accepted);
432 void SettingsDialog::reject()
434 for (int i = 0; i < m_pages.size(); i++) {
435 QWidget *widget = dynamic_cast<QLabel *>(stackedPages->widget(i));
436 if (!widget) {
437 IOptionsPage *page = m_pages.at(i);
438 page->finish();
441 done(QDialog::Rejected);
444 void SettingsDialog::apply()
446 for (int i = 0; i < m_pages.size(); i++) {
447 QWidget *widget = dynamic_cast<QLabel *>(stackedPages->widget(i));
448 if (!widget) {
449 IOptionsPage *page = m_pages.at(i);
450 page->apply();
453 m_applied = true;
456 bool SettingsDialog::execDialog()
458 m_applied = false;
459 emit settingsDialogShown(this);
460 exec();
461 emit settingsDialogRemoved();
462 return m_applied;
465 void SettingsDialog::done(int val)
467 QSettings settings;
469 settings.beginGroup("General");
470 settings.beginGroup("Settings");
472 settings.setValue("LastPreferenceCategory", m_currentCategory);
473 settings.setValue("LastPreferencePage", m_currentPage);
475 settings.setValue("WindowWidth", this->width());
476 settings.setValue("WindowHeight", this->height());
478 QList<int> sizes = splitter->sizes();
479 settings.setValue("SplitterPosition", sizes[0]);
481 settings.endGroup();
482 settings.endGroup();
484 QDialog::done(val);