blob: 620a3527c767af47e6cac0f36ee7533171626f2f [file] [log] [blame]
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001// SPDX-License-Identifier: GPL-2.0
Patrick Georgi0588d192009-08-12 15:00:51 +00002/*
3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
zbao11a262c2016-01-22 18:54:22 +08004 * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
Patrick Georgi0588d192009-08-12 15:00:51 +00005 */
6
zbao11a262c2016-01-22 18:54:22 +08007#include <QAction>
Patrick Georgi0eab62b2023-11-20 19:49:29 +01008#include <QActionGroup>
Patrick Georgi53ea1d42019-11-22 16:55:58 +01009#include <QApplication>
10#include <QCloseEvent>
11#include <QDebug>
zbao11a262c2016-01-22 18:54:22 +080012#include <QFileDialog>
Patrick Georgi53ea1d42019-11-22 16:55:58 +010013#include <QLabel>
14#include <QLayout>
15#include <QList>
zbao11a262c2016-01-22 18:54:22 +080016#include <QMenu>
Patrick Georgi53ea1d42019-11-22 16:55:58 +010017#include <QMenuBar>
18#include <QMessageBox>
Patrick Georgi0eab62b2023-11-20 19:49:29 +010019#include <QRegularExpression>
20#include <QScreen>
Patrick Georgi53ea1d42019-11-22 16:55:58 +010021#include <QToolBar>
Patrick Georgi0588d192009-08-12 15:00:51 +000022
23#include <stdlib.h>
24
25#include "lkc.h"
26#include "qconf.h"
27
Patrick Georgi53ea1d42019-11-22 16:55:58 +010028#include "images.h"
Patrick Georgi0588d192009-08-12 15:00:51 +000029
Patrick Georgi0588d192009-08-12 15:00:51 +000030
31static QApplication *configApp;
32static ConfigSettings *configSettings;
33
zbao11a262c2016-01-22 18:54:22 +080034QAction *ConfigMainWindow::saveAction;
Patrick Georgi0588d192009-08-12 15:00:51 +000035
Patrick Georgid5208402014-04-11 20:24:06 +020036ConfigSettings::ConfigSettings()
37 : QSettings("kernel.org", "qconf")
38{
39}
40
Patrick Georgi0588d192009-08-12 15:00:51 +000041/**
42 * Reads a list of integer values from the application settings.
43 */
zbao11a262c2016-01-22 18:54:22 +080044QList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
Patrick Georgi0588d192009-08-12 15:00:51 +000045{
zbao11a262c2016-01-22 18:54:22 +080046 QList<int> result;
Patrick Georgid5208402014-04-11 20:24:06 +020047
Patrick Georgi53ea1d42019-11-22 16:55:58 +010048 if (contains(key))
49 {
50 QStringList entryList = value(key).toStringList();
51 QStringList::Iterator it;
52
53 for (it = entryList.begin(); it != entryList.end(); ++it)
54 result.push_back((*it).toInt());
55
56 *ok = true;
57 }
58 else
59 *ok = false;
Patrick Georgi0588d192009-08-12 15:00:51 +000060
61 return result;
62}
63
64/**
65 * Writes a list of integer values to the application settings.
66 */
zbao11a262c2016-01-22 18:54:22 +080067bool ConfigSettings::writeSizes(const QString& key, const QList<int>& value)
Patrick Georgi0588d192009-08-12 15:00:51 +000068{
69 QStringList stringList;
zbao11a262c2016-01-22 18:54:22 +080070 QList<int>::ConstIterator it;
Patrick Georgi0588d192009-08-12 15:00:51 +000071
72 for (it = value.begin(); it != value.end(); ++it)
73 stringList.push_back(QString::number(*it));
zbao11a262c2016-01-22 18:54:22 +080074 setValue(key, stringList);
75
76 return true;
Patrick Georgi0588d192009-08-12 15:00:51 +000077}
78
Patrick Georgi53ea1d42019-11-22 16:55:58 +010079QIcon ConfigItem::symbolYesIcon;
80QIcon ConfigItem::symbolModIcon;
81QIcon ConfigItem::symbolNoIcon;
82QIcon ConfigItem::choiceYesIcon;
83QIcon ConfigItem::choiceNoIcon;
84QIcon ConfigItem::menuIcon;
85QIcon ConfigItem::menubackIcon;
Patrick Georgi0588d192009-08-12 15:00:51 +000086
87/*
88 * update the displayed of a menu entry
89 */
90void ConfigItem::updateMenu(void)
91{
92 ConfigList* list;
93 struct symbol* sym;
94 struct property *prop;
95 QString prompt;
96 int type;
97 tristate expr;
98
99 list = listView();
100 if (goParent) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100101 setIcon(promptColIdx, menubackIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000102 prompt = "..";
103 goto set_prompt;
104 }
105
106 sym = menu->sym;
107 prop = menu->prompt;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100108 prompt = menu_get_prompt(menu);
Patrick Georgi0588d192009-08-12 15:00:51 +0000109
110 if (prop) switch (prop->type) {
111 case P_MENU:
112 if (list->mode == singleMode || list->mode == symbolMode) {
113 /* a menuconfig entry is displayed differently
114 * depending whether it's at the view root or a child.
115 */
116 if (sym && list->rootEntry == menu)
117 break;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100118 setIcon(promptColIdx, menuIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000119 } else {
120 if (sym)
121 break;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100122 setIcon(promptColIdx, QIcon());
Patrick Georgi0588d192009-08-12 15:00:51 +0000123 }
124 goto set_prompt;
125 case P_COMMENT:
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100126 setIcon(promptColIdx, QIcon());
127 prompt = "*** " + prompt + " ***";
Patrick Georgi0588d192009-08-12 15:00:51 +0000128 goto set_prompt;
129 default:
130 ;
131 }
132 if (!sym)
133 goto set_prompt;
134
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100135 setText(nameColIdx, sym->name);
Patrick Georgi0588d192009-08-12 15:00:51 +0000136
137 type = sym_get_type(sym);
138 switch (type) {
139 case S_BOOLEAN:
140 case S_TRISTATE:
141 char ch;
142
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100143 if (!sym_is_changeable(sym) && list->optMode == normalOpt) {
144 setIcon(promptColIdx, QIcon());
Patrick Georgi0588d192009-08-12 15:00:51 +0000145 break;
146 }
147 expr = sym_get_tristate_value(sym);
148 switch (expr) {
149 case yes:
150 if (sym_is_choice_value(sym) && type == S_BOOLEAN)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100151 setIcon(promptColIdx, choiceYesIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000152 else
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100153 setIcon(promptColIdx, symbolYesIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000154 ch = 'Y';
155 break;
156 case mod:
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100157 setIcon(promptColIdx, symbolModIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000158 ch = 'M';
159 break;
160 default:
161 if (sym_is_choice_value(sym) && type == S_BOOLEAN)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100162 setIcon(promptColIdx, choiceNoIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000163 else
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100164 setIcon(promptColIdx, symbolNoIcon);
Patrick Georgi0588d192009-08-12 15:00:51 +0000165 ch = 'N';
166 break;
167 }
Patrick Georgi0588d192009-08-12 15:00:51 +0000168
169 setText(dataColIdx, QChar(ch));
170 break;
171 case S_INT:
172 case S_HEX:
173 case S_STRING:
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100174 setText(dataColIdx, sym_get_string_value(sym));
Patrick Georgi0588d192009-08-12 15:00:51 +0000175 break;
176 }
177 if (!sym_has_value(sym) && visible)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100178 prompt += " (NEW)";
Patrick Georgi0588d192009-08-12 15:00:51 +0000179set_prompt:
180 setText(promptColIdx, prompt);
181}
182
183void ConfigItem::testUpdateMenu(bool v)
184{
185 ConfigItem* i;
186
187 visible = v;
188 if (!menu)
189 return;
190
191 sym_calc_value(menu->sym);
192 if (menu->flags & MENU_CHANGED) {
193 /* the menu entry changed, so update all list items */
194 menu->flags &= ~MENU_CHANGED;
195 for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
196 i->updateMenu();
197 } else if (listView()->updateAll)
198 updateMenu();
199}
200
Patrick Georgi0588d192009-08-12 15:00:51 +0000201
202/*
203 * construct a menu entry
204 */
205void ConfigItem::init(void)
206{
207 if (menu) {
208 ConfigList* list = listView();
209 nextItem = (ConfigItem*)menu->data;
210 menu->data = this;
211
212 if (list->mode != fullMode)
zbao11a262c2016-01-22 18:54:22 +0800213 setExpanded(true);
Patrick Georgi0588d192009-08-12 15:00:51 +0000214 sym_calc_value(menu->sym);
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100215
216 if (menu->sym) {
217 enum symbol_type type = menu->sym->type;
218
219 // Allow to edit "int", "hex", and "string" in-place in
220 // the data column. Unfortunately, you cannot specify
221 // the flags per column. Set ItemIsEditable for all
222 // columns here, and check the column in createEditor().
223 if (type == S_INT || type == S_HEX || type == S_STRING)
224 setFlags(flags() | Qt::ItemIsEditable);
225 }
Patrick Georgi0588d192009-08-12 15:00:51 +0000226 }
227 updateMenu();
228}
229
230/*
231 * destruct a menu entry
232 */
233ConfigItem::~ConfigItem(void)
234{
235 if (menu) {
236 ConfigItem** ip = (ConfigItem**)&menu->data;
237 for (; *ip; ip = &(*ip)->nextItem) {
238 if (*ip == this) {
239 *ip = nextItem;
240 break;
241 }
242 }
243 }
244}
245
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100246QWidget *ConfigItemDelegate::createEditor(QWidget *parent,
247 const QStyleOptionViewItem &option,
248 const QModelIndex &index) const
Patrick Georgi0588d192009-08-12 15:00:51 +0000249{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100250 ConfigItem *item;
251
252 // Only the data column is editable
253 if (index.column() != dataColIdx)
254 return nullptr;
255
256 // You cannot edit invisible menus
257 item = static_cast<ConfigItem *>(index.internalPointer());
258 if (!item || !item->menu || !menu_is_visible(item->menu))
259 return nullptr;
260
261 return QStyledItemDelegate::createEditor(parent, option, index);
Patrick Georgi0588d192009-08-12 15:00:51 +0000262}
263
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100264void ConfigItemDelegate::setModelData(QWidget *editor,
265 QAbstractItemModel *model,
266 const QModelIndex &index) const
Patrick Georgi0588d192009-08-12 15:00:51 +0000267{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100268 QLineEdit *lineEdit;
269 ConfigItem *item;
270 struct symbol *sym;
271 bool success;
Patrick Georgi0588d192009-08-12 15:00:51 +0000272
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100273 lineEdit = qobject_cast<QLineEdit *>(editor);
274 // If this is not a QLineEdit, use the parent's default.
275 // (does this happen?)
276 if (!lineEdit)
277 goto parent;
278
279 item = static_cast<ConfigItem *>(index.internalPointer());
280 if (!item || !item->menu)
281 goto parent;
282
283 sym = item->menu->sym;
284 if (!sym)
285 goto parent;
286
287 success = sym_set_string_value(sym, lineEdit->text().toUtf8().data());
288 if (success) {
289 ConfigList::updateListForAll();
290 } else {
291 QMessageBox::information(editor, "qconf",
292 "Cannot set the data (maybe due to out of range).\n"
293 "Setting the old value.");
294 lineEdit->setText(sym_get_string_value(sym));
Patrick Georgi0588d192009-08-12 15:00:51 +0000295 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100296
297parent:
298 QStyledItemDelegate::setModelData(editor, model, index);
Patrick Georgi0588d192009-08-12 15:00:51 +0000299}
300
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100301ConfigList::ConfigList(QWidget *parent, const char *name)
302 : QTreeWidget(parent),
Patrick Georgi0588d192009-08-12 15:00:51 +0000303 updateAll(false),
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100304 showName(false), mode(singleMode), optMode(normalOpt),
Patrick Georgi0588d192009-08-12 15:00:51 +0000305 rootEntry(0), headerPopup(0)
306{
zbao11a262c2016-01-22 18:54:22 +0800307 setObjectName(name);
308 setSortingEnabled(false);
309 setRootIsDecorated(true);
Patrick Georgi0588d192009-08-12 15:00:51 +0000310
zbao11a262c2016-01-22 18:54:22 +0800311 setVerticalScrollMode(ScrollPerPixel);
312 setHorizontalScrollMode(ScrollPerPixel);
313
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100314 setHeaderLabels(QStringList() << "Option" << "Name" << "Value");
zbao11a262c2016-01-22 18:54:22 +0800315
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100316 connect(this, &ConfigList::itemSelectionChanged,
317 this, &ConfigList::updateSelection);
Patrick Georgi0588d192009-08-12 15:00:51 +0000318
319 if (name) {
320 configSettings->beginGroup(name);
zbao11a262c2016-01-22 18:54:22 +0800321 showName = configSettings->value("/showName", false).toBool();
zbao11a262c2016-01-22 18:54:22 +0800322 optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
Patrick Georgi0588d192009-08-12 15:00:51 +0000323 configSettings->endGroup();
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100324 connect(configApp, &QApplication::aboutToQuit,
325 this, &ConfigList::saveSettings);
Patrick Georgi0588d192009-08-12 15:00:51 +0000326 }
327
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100328 showColumn(promptColIdx);
329
330 setItemDelegate(new ConfigItemDelegate(this));
331
332 allLists.append(this);
Patrick Georgi0588d192009-08-12 15:00:51 +0000333
334 reinit();
335}
336
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100337ConfigList::~ConfigList()
338{
339 allLists.removeOne(this);
340}
341
Patrick Georgid5208402014-04-11 20:24:06 +0200342bool ConfigList::menuSkip(struct menu *menu)
343{
344 if (optMode == normalOpt && menu_is_visible(menu))
345 return false;
346 if (optMode == promptOpt && menu_has_prompt(menu))
347 return false;
348 if (optMode == allOpt)
349 return false;
350 return true;
351}
352
Patrick Georgi0588d192009-08-12 15:00:51 +0000353void ConfigList::reinit(void)
354{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100355 hideColumn(nameColIdx);
Patrick Georgi0588d192009-08-12 15:00:51 +0000356
357 if (showName)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100358 showColumn(nameColIdx);
359
360 updateListAll();
361}
362
363void ConfigList::setOptionMode(QAction *action)
364{
365 if (action == showNormalAction)
366 optMode = normalOpt;
367 else if (action == showAllAction)
368 optMode = allOpt;
369 else
370 optMode = promptOpt;
Patrick Georgi0588d192009-08-12 15:00:51 +0000371
372 updateListAll();
373}
374
375void ConfigList::saveSettings(void)
376{
zbao11a262c2016-01-22 18:54:22 +0800377 if (!objectName().isEmpty()) {
378 configSettings->beginGroup(objectName());
379 configSettings->setValue("/showName", showName);
zbao11a262c2016-01-22 18:54:22 +0800380 configSettings->setValue("/optionMode", (int)optMode);
Patrick Georgi0588d192009-08-12 15:00:51 +0000381 configSettings->endGroup();
382 }
383}
384
385ConfigItem* ConfigList::findConfigItem(struct menu *menu)
386{
387 ConfigItem* item = (ConfigItem*)menu->data;
388
389 for (; item; item = item->nextItem) {
390 if (this == item->listView())
391 break;
392 }
393
394 return item;
395}
396
397void ConfigList::updateSelection(void)
398{
399 struct menu *menu;
400 enum prop_type type;
401
zbao11a262c2016-01-22 18:54:22 +0800402 if (selectedItems().count() == 0)
403 return;
404
405 ConfigItem* item = (ConfigItem*)selectedItems().first();
Patrick Georgi0588d192009-08-12 15:00:51 +0000406 if (!item)
407 return;
408
409 menu = item->menu;
410 emit menuChanged(menu);
411 if (!menu)
412 return;
413 type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
414 if (mode == menuMode && type == P_MENU)
415 emit menuSelected(menu);
416}
417
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100418void ConfigList::updateList()
Patrick Georgi0588d192009-08-12 15:00:51 +0000419{
420 ConfigItem* last = 0;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100421 ConfigItem *item;
Patrick Georgi0588d192009-08-12 15:00:51 +0000422
423 if (!rootEntry) {
424 if (mode != listMode)
425 goto update;
zbao11a262c2016-01-22 18:54:22 +0800426 QTreeWidgetItemIterator it(this);
Patrick Georgi0588d192009-08-12 15:00:51 +0000427
zbao11a262c2016-01-22 18:54:22 +0800428 while (*it) {
429 item = (ConfigItem*)(*it);
Patrick Georgi0588d192009-08-12 15:00:51 +0000430 if (!item->menu)
431 continue;
432 item->testUpdateMenu(menu_is_visible(item->menu));
zbao11a262c2016-01-22 18:54:22 +0800433
434 ++it;
Patrick Georgi0588d192009-08-12 15:00:51 +0000435 }
436 return;
437 }
438
439 if (rootEntry != &rootmenu && (mode == singleMode ||
440 (mode == symbolMode && rootEntry->parent != &rootmenu))) {
zbao11a262c2016-01-22 18:54:22 +0800441 item = (ConfigItem *)topLevelItem(0);
Patrick Georgi0588d192009-08-12 15:00:51 +0000442 if (!item)
443 item = new ConfigItem(this, 0, true);
444 last = item;
445 }
446 if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
447 rootEntry->sym && rootEntry->prompt) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100448 item = last ? last->nextSibling() : nullptr;
Patrick Georgi0588d192009-08-12 15:00:51 +0000449 if (!item)
450 item = new ConfigItem(this, last, rootEntry, true);
451 else
452 item->testUpdateMenu(true);
453
454 updateMenuList(item, rootEntry);
zbao11a262c2016-01-22 18:54:22 +0800455 update();
456 resizeColumnToContents(0);
Patrick Georgi0588d192009-08-12 15:00:51 +0000457 return;
458 }
459update:
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100460 updateMenuList(rootEntry);
zbao11a262c2016-01-22 18:54:22 +0800461 update();
462 resizeColumnToContents(0);
Patrick Georgi0588d192009-08-12 15:00:51 +0000463}
464
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100465void ConfigList::updateListForAll()
466{
467 QListIterator<ConfigList *> it(allLists);
468
469 while (it.hasNext()) {
470 ConfigList *list = it.next();
471
472 list->updateList();
473 }
474}
475
476void ConfigList::updateListAllForAll()
477{
478 QListIterator<ConfigList *> it(allLists);
479
480 while (it.hasNext()) {
481 ConfigList *list = it.next();
482
483 list->updateList();
484 }
485}
486
Patrick Georgi0588d192009-08-12 15:00:51 +0000487void ConfigList::setValue(ConfigItem* item, tristate val)
488{
489 struct symbol* sym;
490 int type;
491 tristate oldval;
492
493 sym = item->menu ? item->menu->sym : 0;
494 if (!sym)
495 return;
496
497 type = sym_get_type(sym);
498 switch (type) {
499 case S_BOOLEAN:
500 case S_TRISTATE:
501 oldval = sym_get_tristate_value(sym);
502
503 if (!sym_set_tristate_value(sym, val))
504 return;
505 if (oldval == no && item->menu->list)
zbao11a262c2016-01-22 18:54:22 +0800506 item->setExpanded(true);
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100507 ConfigList::updateListForAll();
Patrick Georgi0588d192009-08-12 15:00:51 +0000508 break;
509 }
510}
511
512void ConfigList::changeValue(ConfigItem* item)
513{
514 struct symbol* sym;
515 struct menu* menu;
516 int type, oldexpr, newexpr;
517
518 menu = item->menu;
519 if (!menu)
520 return;
521 sym = menu->sym;
522 if (!sym) {
523 if (item->menu->list)
zbao11a262c2016-01-22 18:54:22 +0800524 item->setExpanded(!item->isExpanded());
Patrick Georgi0588d192009-08-12 15:00:51 +0000525 return;
526 }
527
528 type = sym_get_type(sym);
529 switch (type) {
530 case S_BOOLEAN:
531 case S_TRISTATE:
532 oldexpr = sym_get_tristate_value(sym);
533 newexpr = sym_toggle_tristate_value(sym);
534 if (item->menu->list) {
535 if (oldexpr == newexpr)
zbao11a262c2016-01-22 18:54:22 +0800536 item->setExpanded(!item->isExpanded());
Patrick Georgi0588d192009-08-12 15:00:51 +0000537 else if (oldexpr == no)
zbao11a262c2016-01-22 18:54:22 +0800538 item->setExpanded(true);
Patrick Georgi0588d192009-08-12 15:00:51 +0000539 }
540 if (oldexpr != newexpr)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100541 ConfigList::updateListForAll();
Patrick Georgi0588d192009-08-12 15:00:51 +0000542 break;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100543 default:
Patrick Georgi0588d192009-08-12 15:00:51 +0000544 break;
545 }
546}
547
548void ConfigList::setRootMenu(struct menu *menu)
549{
550 enum prop_type type;
551
552 if (rootEntry == menu)
553 return;
554 type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
555 if (type != P_MENU)
556 return;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100557 updateMenuList(0);
Patrick Georgi0588d192009-08-12 15:00:51 +0000558 rootEntry = menu;
559 updateListAll();
zbao11a262c2016-01-22 18:54:22 +0800560 if (currentItem()) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100561 setSelected(currentItem(), hasFocus());
zbao11a262c2016-01-22 18:54:22 +0800562 scrollToItem(currentItem());
563 }
Patrick Georgi0588d192009-08-12 15:00:51 +0000564}
565
566void ConfigList::setParentMenu(void)
567{
568 ConfigItem* item;
569 struct menu *oldroot;
570
571 oldroot = rootEntry;
572 if (rootEntry == &rootmenu)
573 return;
574 setRootMenu(menu_get_parent_menu(rootEntry->parent));
575
zbao11a262c2016-01-22 18:54:22 +0800576 QTreeWidgetItemIterator it(this);
577 while (*it) {
578 item = (ConfigItem *)(*it);
Patrick Georgi0588d192009-08-12 15:00:51 +0000579 if (item->menu == oldroot) {
580 setCurrentItem(item);
zbao11a262c2016-01-22 18:54:22 +0800581 scrollToItem(item);
Patrick Georgi0588d192009-08-12 15:00:51 +0000582 break;
583 }
zbao11a262c2016-01-22 18:54:22 +0800584
585 ++it;
Patrick Georgi0588d192009-08-12 15:00:51 +0000586 }
587}
588
589/*
590 * update all the children of a menu entry
591 * removes/adds the entries from the parent widget as necessary
592 *
593 * parent: either the menu list widget or a menu entry widget
594 * menu: entry to be updated
595 */
zbao11a262c2016-01-22 18:54:22 +0800596void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
Patrick Georgi0588d192009-08-12 15:00:51 +0000597{
598 struct menu* child;
599 ConfigItem* item;
600 ConfigItem* last;
601 bool visible;
602 enum prop_type type;
603
604 if (!menu) {
zbao11a262c2016-01-22 18:54:22 +0800605 while (parent->childCount() > 0)
606 {
607 delete parent->takeChild(0);
608 }
609
Patrick Georgi0588d192009-08-12 15:00:51 +0000610 return;
611 }
612
613 last = parent->firstChild();
614 if (last && !last->goParent)
615 last = 0;
616 for (child = menu->list; child; child = child->next) {
617 item = last ? last->nextSibling() : parent->firstChild();
618 type = child->prompt ? child->prompt->type : P_UNKNOWN;
619
620 switch (mode) {
621 case menuMode:
622 if (!(child->flags & MENU_ROOT))
623 goto hide;
624 break;
625 case symbolMode:
626 if (child->flags & MENU_ROOT)
627 goto hide;
628 break;
629 default:
630 break;
631 }
632
633 visible = menu_is_visible(child);
Patrick Georgid5208402014-04-11 20:24:06 +0200634 if (!menuSkip(child)) {
Patrick Georgi0588d192009-08-12 15:00:51 +0000635 if (!child->sym && !child->list && !child->prompt)
636 continue;
637 if (!item || item->menu != child)
638 item = new ConfigItem(parent, last, child, visible);
639 else
640 item->testUpdateMenu(visible);
641
642 if (mode == fullMode || mode == menuMode || type != P_MENU)
643 updateMenuList(item, child);
644 else
645 updateMenuList(item, 0);
646 last = item;
647 continue;
648 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100649hide:
Patrick Georgi0588d192009-08-12 15:00:51 +0000650 if (item && item->menu == child) {
651 last = parent->firstChild();
652 if (last == item)
653 last = 0;
654 else while (last->nextSibling() != item)
655 last = last->nextSibling();
656 delete item;
657 }
658 }
659}
660
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100661void ConfigList::updateMenuList(struct menu *menu)
zbao11a262c2016-01-22 18:54:22 +0800662{
663 struct menu* child;
664 ConfigItem* item;
665 ConfigItem* last;
666 bool visible;
667 enum prop_type type;
668
669 if (!menu) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100670 while (topLevelItemCount() > 0)
zbao11a262c2016-01-22 18:54:22 +0800671 {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100672 delete takeTopLevelItem(0);
zbao11a262c2016-01-22 18:54:22 +0800673 }
674
675 return;
676 }
677
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100678 last = (ConfigItem *)topLevelItem(0);
zbao11a262c2016-01-22 18:54:22 +0800679 if (last && !last->goParent)
680 last = 0;
681 for (child = menu->list; child; child = child->next) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100682 item = last ? last->nextSibling() : (ConfigItem *)topLevelItem(0);
zbao11a262c2016-01-22 18:54:22 +0800683 type = child->prompt ? child->prompt->type : P_UNKNOWN;
684
685 switch (mode) {
686 case menuMode:
687 if (!(child->flags & MENU_ROOT))
688 goto hide;
689 break;
690 case symbolMode:
691 if (child->flags & MENU_ROOT)
692 goto hide;
693 break;
694 default:
695 break;
696 }
697
698 visible = menu_is_visible(child);
699 if (!menuSkip(child)) {
700 if (!child->sym && !child->list && !child->prompt)
701 continue;
702 if (!item || item->menu != child)
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100703 item = new ConfigItem(this, last, child, visible);
zbao11a262c2016-01-22 18:54:22 +0800704 else
705 item->testUpdateMenu(visible);
706
707 if (mode == fullMode || mode == menuMode || type != P_MENU)
708 updateMenuList(item, child);
709 else
710 updateMenuList(item, 0);
711 last = item;
712 continue;
713 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100714hide:
zbao11a262c2016-01-22 18:54:22 +0800715 if (item && item->menu == child) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100716 last = (ConfigItem *)topLevelItem(0);
zbao11a262c2016-01-22 18:54:22 +0800717 if (last == item)
718 last = 0;
719 else while (last->nextSibling() != item)
720 last = last->nextSibling();
721 delete item;
722 }
723 }
724}
725
Patrick Georgi0588d192009-08-12 15:00:51 +0000726void ConfigList::keyPressEvent(QKeyEvent* ev)
727{
zbao11a262c2016-01-22 18:54:22 +0800728 QTreeWidgetItem* i = currentItem();
Patrick Georgi0588d192009-08-12 15:00:51 +0000729 ConfigItem* item;
730 struct menu *menu;
731 enum prop_type type;
732
Patrick Georgid5208402014-04-11 20:24:06 +0200733 if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
Patrick Georgi0588d192009-08-12 15:00:51 +0000734 emit parentSelected();
735 ev->accept();
736 return;
737 }
738
739 if (!i) {
740 Parent::keyPressEvent(ev);
741 return;
742 }
743 item = (ConfigItem*)i;
744
745 switch (ev->key()) {
Patrick Georgid5208402014-04-11 20:24:06 +0200746 case Qt::Key_Return:
747 case Qt::Key_Enter:
Patrick Georgi0588d192009-08-12 15:00:51 +0000748 if (item->goParent) {
749 emit parentSelected();
750 break;
751 }
752 menu = item->menu;
753 if (!menu)
754 break;
755 type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
756 if (type == P_MENU && rootEntry != menu &&
757 mode != fullMode && mode != menuMode) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100758 if (mode == menuMode)
759 emit menuSelected(menu);
760 else
761 emit itemSelected(menu);
Patrick Georgi0588d192009-08-12 15:00:51 +0000762 break;
763 }
Patrick Georgid5208402014-04-11 20:24:06 +0200764 case Qt::Key_Space:
Patrick Georgi0588d192009-08-12 15:00:51 +0000765 changeValue(item);
766 break;
Patrick Georgid5208402014-04-11 20:24:06 +0200767 case Qt::Key_N:
Patrick Georgi0588d192009-08-12 15:00:51 +0000768 setValue(item, no);
769 break;
Patrick Georgid5208402014-04-11 20:24:06 +0200770 case Qt::Key_M:
Patrick Georgi0588d192009-08-12 15:00:51 +0000771 setValue(item, mod);
772 break;
Patrick Georgid5208402014-04-11 20:24:06 +0200773 case Qt::Key_Y:
Patrick Georgi0588d192009-08-12 15:00:51 +0000774 setValue(item, yes);
775 break;
776 default:
777 Parent::keyPressEvent(ev);
778 return;
779 }
780 ev->accept();
781}
782
zbao11a262c2016-01-22 18:54:22 +0800783void ConfigList::mousePressEvent(QMouseEvent* e)
Patrick Georgi0588d192009-08-12 15:00:51 +0000784{
785 //QPoint p(contentsToViewport(e->pos()));
786 //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
zbao11a262c2016-01-22 18:54:22 +0800787 Parent::mousePressEvent(e);
Patrick Georgi0588d192009-08-12 15:00:51 +0000788}
789
zbao11a262c2016-01-22 18:54:22 +0800790void ConfigList::mouseReleaseEvent(QMouseEvent* e)
Patrick Georgi0588d192009-08-12 15:00:51 +0000791{
zbao11a262c2016-01-22 18:54:22 +0800792 QPoint p = e->pos();
Patrick Georgi0588d192009-08-12 15:00:51 +0000793 ConfigItem* item = (ConfigItem*)itemAt(p);
794 struct menu *menu;
795 enum prop_type ptype;
zbao11a262c2016-01-22 18:54:22 +0800796 QIcon icon;
Patrick Georgi0588d192009-08-12 15:00:51 +0000797 int idx, x;
798
799 if (!item)
800 goto skip;
801
802 menu = item->menu;
803 x = header()->offset() + p.x();
zbao11a262c2016-01-22 18:54:22 +0800804 idx = header()->logicalIndexAt(x);
Patrick Georgi0588d192009-08-12 15:00:51 +0000805 switch (idx) {
806 case promptColIdx:
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100807 icon = item->icon(promptColIdx);
zbao11a262c2016-01-22 18:54:22 +0800808 if (!icon.isNull()) {
809 int off = header()->sectionPosition(0) + visualRect(indexAt(p)).x() + 4; // 4 is Hardcoded image offset. There might be a way to do it properly.
810 if (x >= off && x < off + icon.availableSizes().first().width()) {
Patrick Georgi0588d192009-08-12 15:00:51 +0000811 if (item->goParent) {
812 emit parentSelected();
813 break;
814 } else if (!menu)
815 break;
816 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
817 if (ptype == P_MENU && rootEntry != menu &&
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100818 mode != fullMode && mode != menuMode &&
819 mode != listMode)
Patrick Georgi0588d192009-08-12 15:00:51 +0000820 emit menuSelected(menu);
821 else
822 changeValue(item);
823 }
824 }
825 break;
Patrick Georgi0588d192009-08-12 15:00:51 +0000826 case dataColIdx:
827 changeValue(item);
828 break;
829 }
830
831skip:
832 //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
zbao11a262c2016-01-22 18:54:22 +0800833 Parent::mouseReleaseEvent(e);
Patrick Georgi0588d192009-08-12 15:00:51 +0000834}
835
zbao11a262c2016-01-22 18:54:22 +0800836void ConfigList::mouseMoveEvent(QMouseEvent* e)
Patrick Georgi0588d192009-08-12 15:00:51 +0000837{
838 //QPoint p(contentsToViewport(e->pos()));
839 //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
zbao11a262c2016-01-22 18:54:22 +0800840 Parent::mouseMoveEvent(e);
Patrick Georgi0588d192009-08-12 15:00:51 +0000841}
842
zbao11a262c2016-01-22 18:54:22 +0800843void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
Patrick Georgi0588d192009-08-12 15:00:51 +0000844{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100845 QPoint p = e->pos();
Patrick Georgi0588d192009-08-12 15:00:51 +0000846 ConfigItem* item = (ConfigItem*)itemAt(p);
847 struct menu *menu;
848 enum prop_type ptype;
849
850 if (!item)
851 goto skip;
852 if (item->goParent) {
853 emit parentSelected();
854 goto skip;
855 }
856 menu = item->menu;
857 if (!menu)
858 goto skip;
859 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100860 if (ptype == P_MENU && mode != listMode) {
861 if (mode == singleMode)
862 emit itemSelected(menu);
863 else if (mode == symbolMode)
864 emit menuSelected(menu);
865 } else if (menu->sym)
Patrick Georgi0588d192009-08-12 15:00:51 +0000866 changeValue(item);
867
868skip:
869 //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
zbao11a262c2016-01-22 18:54:22 +0800870 Parent::mouseDoubleClickEvent(e);
Patrick Georgi0588d192009-08-12 15:00:51 +0000871}
872
873void ConfigList::focusInEvent(QFocusEvent *e)
874{
875 struct menu *menu = NULL;
876
877 Parent::focusInEvent(e);
878
879 ConfigItem* item = (ConfigItem *)currentItem();
880 if (item) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100881 setSelected(item, true);
Patrick Georgi0588d192009-08-12 15:00:51 +0000882 menu = item->menu;
883 }
884 emit gotFocus(menu);
885}
886
887void ConfigList::contextMenuEvent(QContextMenuEvent *e)
888{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100889 if (!headerPopup) {
890 QAction *action;
Patrick Georgi0588d192009-08-12 15:00:51 +0000891
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100892 headerPopup = new QMenu(this);
893 action = new QAction("Show Name", this);
894 action->setCheckable(true);
895 connect(action, &QAction::toggled,
896 this, &ConfigList::setShowName);
897 connect(this, &ConfigList::showNameChanged,
898 action, &QAction::setChecked);
899 action->setChecked(showName);
900 headerPopup->addAction(action);
Patrick Georgi0588d192009-08-12 15:00:51 +0000901 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100902
903 headerPopup->exec(e->globalPos());
904 e->accept();
Patrick Georgi0588d192009-08-12 15:00:51 +0000905}
906
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100907void ConfigList::setShowName(bool on)
Patrick Georgi0588d192009-08-12 15:00:51 +0000908{
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100909 if (showName == on)
910 return;
Patrick Georgid5208402014-04-11 20:24:06 +0200911
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100912 showName = on;
913 reinit();
914 emit showNameChanged(on);
Patrick Georgi0588d192009-08-12 15:00:51 +0000915}
916
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100917QList<ConfigList *> ConfigList::allLists;
918QAction *ConfigList::showNormalAction;
919QAction *ConfigList::showAllAction;
920QAction *ConfigList::showPromptAction;
Patrick Georgi0588d192009-08-12 15:00:51 +0000921
922void ConfigList::setAllOpen(bool open)
923{
zbao11a262c2016-01-22 18:54:22 +0800924 QTreeWidgetItemIterator it(this);
Patrick Georgi0588d192009-08-12 15:00:51 +0000925
zbao11a262c2016-01-22 18:54:22 +0800926 while (*it) {
927 (*it)->setExpanded(open);
928
929 ++it;
930 }
Patrick Georgi0588d192009-08-12 15:00:51 +0000931}
932
Patrick Georgi0588d192009-08-12 15:00:51 +0000933ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
zbao11a262c2016-01-22 18:54:22 +0800934 : Parent(parent), sym(0), _menu(0)
Patrick Georgi0588d192009-08-12 15:00:51 +0000935{
zbao11a262c2016-01-22 18:54:22 +0800936 setObjectName(name);
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100937 setOpenLinks(false);
zbao11a262c2016-01-22 18:54:22 +0800938
939 if (!objectName().isEmpty()) {
940 configSettings->beginGroup(objectName());
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100941 setShowDebug(configSettings->value("/showDebug", false).toBool());
Patrick Georgi0588d192009-08-12 15:00:51 +0000942 configSettings->endGroup();
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100943 connect(configApp, &QApplication::aboutToQuit,
944 this, &ConfigInfoView::saveSettings);
Patrick Georgi0588d192009-08-12 15:00:51 +0000945 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100946
947 contextMenu = createStandardContextMenu();
948 QAction *action = new QAction("Show Debug Info", contextMenu);
949
950 action->setCheckable(true);
951 connect(action, &QAction::toggled,
952 this, &ConfigInfoView::setShowDebug);
953 connect(this, &ConfigInfoView::showDebugChanged,
954 action, &QAction::setChecked);
955 action->setChecked(showDebug());
956 contextMenu->addSeparator();
957 contextMenu->addAction(action);
Patrick Georgi0588d192009-08-12 15:00:51 +0000958}
959
960void ConfigInfoView::saveSettings(void)
961{
zbao11a262c2016-01-22 18:54:22 +0800962 if (!objectName().isEmpty()) {
963 configSettings->beginGroup(objectName());
964 configSettings->setValue("/showDebug", showDebug());
Patrick Georgi0588d192009-08-12 15:00:51 +0000965 configSettings->endGroup();
966 }
967}
968
969void ConfigInfoView::setShowDebug(bool b)
970{
971 if (_showDebug != b) {
972 _showDebug = b;
Patrick Georgid5208402014-04-11 20:24:06 +0200973 if (_menu)
Patrick Georgi0588d192009-08-12 15:00:51 +0000974 menuInfo();
975 else if (sym)
976 symbolInfo();
977 emit showDebugChanged(b);
978 }
979}
980
981void ConfigInfoView::setInfo(struct menu *m)
982{
Patrick Georgid5208402014-04-11 20:24:06 +0200983 if (_menu == m)
Patrick Georgi0588d192009-08-12 15:00:51 +0000984 return;
Patrick Georgid5208402014-04-11 20:24:06 +0200985 _menu = m;
Patrick Georgi0588d192009-08-12 15:00:51 +0000986 sym = NULL;
Patrick Georgid5208402014-04-11 20:24:06 +0200987 if (!_menu)
Patrick Georgi0588d192009-08-12 15:00:51 +0000988 clear();
989 else
990 menuInfo();
991}
992
Patrick Georgi0588d192009-08-12 15:00:51 +0000993void ConfigInfoView::symbolInfo(void)
994{
995 QString str;
996
997 str += "<big>Symbol: <b>";
998 str += print_filter(sym->name);
999 str += "</b></big><br><br>value: ";
1000 str += print_filter(sym_get_string_value(sym));
1001 str += "<br>visibility: ";
1002 str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
1003 str += "<br>";
1004 str += debug_info(sym);
1005
1006 setText(str);
1007}
1008
1009void ConfigInfoView::menuInfo(void)
1010{
1011 struct symbol* sym;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001012 QString info;
1013 QTextStream stream(&info);
Patrick Georgi0588d192009-08-12 15:00:51 +00001014
Patrick Georgid5208402014-04-11 20:24:06 +02001015 sym = _menu->sym;
Patrick Georgi0588d192009-08-12 15:00:51 +00001016 if (sym) {
Patrick Georgid5208402014-04-11 20:24:06 +02001017 if (_menu->prompt) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001018 stream << "<big><b>";
1019 stream << print_filter(_menu->prompt->text);
1020 stream << "</b></big>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001021 if (sym->name) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001022 stream << " (";
Patrick Georgi0588d192009-08-12 15:00:51 +00001023 if (showDebug())
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001024 stream << "<a href=\"s" << sym->name << "\">";
1025 stream << print_filter(sym->name);
Patrick Georgi0588d192009-08-12 15:00:51 +00001026 if (showDebug())
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001027 stream << "</a>";
1028 stream << ")";
Patrick Georgi0588d192009-08-12 15:00:51 +00001029 }
1030 } else if (sym->name) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001031 stream << "<big><b>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001032 if (showDebug())
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001033 stream << "<a href=\"s" << sym->name << "\">";
1034 stream << print_filter(sym->name);
Patrick Georgi0588d192009-08-12 15:00:51 +00001035 if (showDebug())
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001036 stream << "</a>";
1037 stream << "</b></big>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001038 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001039 stream << "<br><br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001040
1041 if (showDebug())
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001042 stream << debug_info(sym);
Patrick Georgi0588d192009-08-12 15:00:51 +00001043
Patrick Georgid5208402014-04-11 20:24:06 +02001044 struct gstr help_gstr = str_new();
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001045
Patrick Georgid5208402014-04-11 20:24:06 +02001046 menu_get_ext_help(_menu, &help_gstr);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001047 stream << print_filter(str_get(&help_gstr));
Patrick Georgid5208402014-04-11 20:24:06 +02001048 str_free(&help_gstr);
1049 } else if (_menu->prompt) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001050 stream << "<big><b>";
1051 stream << print_filter(_menu->prompt->text);
1052 stream << "</b></big><br><br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001053 if (showDebug()) {
Patrick Georgid5208402014-04-11 20:24:06 +02001054 if (_menu->prompt->visible.expr) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001055 stream << "&nbsp;&nbsp;dep: ";
1056 expr_print(_menu->prompt->visible.expr,
1057 expr_print_help, &stream, E_NONE);
1058 stream << "<br><br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001059 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001060
1061 stream << "defined at " << _menu->file->name << ":"
1062 << _menu->lineno << "<br><br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001063 }
1064 }
Patrick Georgi0588d192009-08-12 15:00:51 +00001065
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001066 setText(info);
Patrick Georgi0588d192009-08-12 15:00:51 +00001067}
1068
1069QString ConfigInfoView::debug_info(struct symbol *sym)
1070{
1071 QString debug;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001072 QTextStream stream(&debug);
Patrick Georgi0588d192009-08-12 15:00:51 +00001073
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001074 stream << "type: ";
1075 stream << print_filter(sym_type_name(sym->type));
Patrick Georgi0588d192009-08-12 15:00:51 +00001076 if (sym_is_choice(sym))
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001077 stream << " (choice)";
Patrick Georgi0588d192009-08-12 15:00:51 +00001078 debug += "<br>";
1079 if (sym->rev_dep.expr) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001080 stream << "reverse dep: ";
1081 expr_print(sym->rev_dep.expr, expr_print_help, &stream, E_NONE);
1082 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001083 }
1084 for (struct property *prop = sym->prop; prop; prop = prop->next) {
1085 switch (prop->type) {
1086 case P_PROMPT:
1087 case P_MENU:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001088 stream << "prompt: <a href=\"m" << sym->name << "\">";
1089 stream << print_filter(prop->text);
1090 stream << "</a><br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001091 break;
1092 case P_DEFAULT:
1093 case P_SELECT:
1094 case P_RANGE:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001095 case P_COMMENT:
1096 case P_IMPLY:
1097 case P_SYMBOL:
1098 stream << prop_get_type_name(prop->type);
1099 stream << ": ";
1100 expr_print(prop->expr, expr_print_help,
1101 &stream, E_NONE);
1102 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001103 break;
1104 case P_CHOICE:
1105 if (sym_is_choice(sym)) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001106 stream << "choice: ";
1107 expr_print(prop->expr, expr_print_help,
1108 &stream, E_NONE);
1109 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001110 }
1111 break;
1112 default:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001113 stream << "unknown property: ";
1114 stream << prop_get_type_name(prop->type);
1115 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001116 }
1117 if (prop->visible.expr) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001118 stream << "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1119 expr_print(prop->visible.expr, expr_print_help,
1120 &stream, E_NONE);
1121 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001122 }
1123 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001124 stream << "<br>";
Patrick Georgi0588d192009-08-12 15:00:51 +00001125
1126 return debug;
1127}
1128
1129QString ConfigInfoView::print_filter(const QString &str)
1130{
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001131 QRegularExpression re("[<>&\"\\n]");
Patrick Georgi0588d192009-08-12 15:00:51 +00001132 QString res = str;
zbao11a262c2016-01-22 18:54:22 +08001133 for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1134 switch (res[i].toLatin1()) {
Patrick Georgi0588d192009-08-12 15:00:51 +00001135 case '<':
1136 res.replace(i, 1, "&lt;");
1137 i += 4;
1138 break;
1139 case '>':
1140 res.replace(i, 1, "&gt;");
1141 i += 4;
1142 break;
1143 case '&':
1144 res.replace(i, 1, "&amp;");
1145 i += 5;
1146 break;
1147 case '"':
1148 res.replace(i, 1, "&quot;");
1149 i += 6;
1150 break;
1151 case '\n':
1152 res.replace(i, 1, "<br>");
1153 i += 4;
1154 break;
1155 }
1156 }
1157 return res;
1158}
1159
1160void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
1161{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001162 QTextStream *stream = reinterpret_cast<QTextStream *>(data);
Patrick Georgi0588d192009-08-12 15:00:51 +00001163
1164 if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001165 *stream << "<a href=\"s" << sym->name << "\">";
1166 *stream << print_filter(str);
1167 *stream << "</a>";
1168 } else {
1169 *stream << print_filter(str);
1170 }
Patrick Georgi0588d192009-08-12 15:00:51 +00001171}
1172
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001173void ConfigInfoView::clicked(const QUrl &url)
Patrick Georgi0588d192009-08-12 15:00:51 +00001174{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001175 QByteArray str = url.toEncoded();
1176 const std::size_t count = str.size();
1177 char *data = new char[count + 1];
1178 struct symbol **result;
1179 struct menu *m = NULL;
1180
1181 if (count < 1) {
1182 delete[] data;
1183 return;
1184 }
1185
1186 memcpy(data, str.constData(), count);
1187 data[count] = '\0';
1188
1189 /* Seek for exact match */
1190 data[0] = '^';
1191 strcat(data, "$");
1192 result = sym_re_search(data);
1193 if (!result) {
1194 delete[] data;
1195 return;
1196 }
1197
1198 sym = *result;
1199
1200 /* Seek for the menu which holds the symbol */
1201 for (struct property *prop = sym->prop; prop; prop = prop->next) {
1202 if (prop->type != P_PROMPT && prop->type != P_MENU)
1203 continue;
1204 m = prop->menu;
1205 break;
1206 }
1207
1208 if (!m) {
1209 /* Symbol is not visible as a menu */
1210 symbolInfo();
1211 emit showDebugChanged(true);
1212 } else {
1213 emit menuSelected(m);
1214 }
1215
1216 free(result);
1217 delete[] data;
Patrick Georgi0588d192009-08-12 15:00:51 +00001218}
1219
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001220void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event)
Patrick Georgi0588d192009-08-12 15:00:51 +00001221{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001222 contextMenu->popup(event->globalPos());
1223 event->accept();
Patrick Georgi0588d192009-08-12 15:00:51 +00001224}
1225
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001226ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
zbao11a262c2016-01-22 18:54:22 +08001227 : Parent(parent), result(NULL)
Patrick Georgi0588d192009-08-12 15:00:51 +00001228{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001229 setObjectName("search");
zbao11a262c2016-01-22 18:54:22 +08001230 setWindowTitle("Search Config");
Patrick Georgi0588d192009-08-12 15:00:51 +00001231
zbao11a262c2016-01-22 18:54:22 +08001232 QVBoxLayout* layout1 = new QVBoxLayout(this);
1233 layout1->setContentsMargins(11, 11, 11, 11);
1234 layout1->setSpacing(6);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001235
1236 QHBoxLayout* layout2 = new QHBoxLayout();
zbao11a262c2016-01-22 18:54:22 +08001237 layout2->setContentsMargins(0, 0, 0, 0);
1238 layout2->setSpacing(6);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001239 layout2->addWidget(new QLabel("Find:", this));
Patrick Georgi0588d192009-08-12 15:00:51 +00001240 editField = new QLineEdit(this);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001241 connect(editField, &QLineEdit::returnPressed,
1242 this, &ConfigSearchWindow::search);
Patrick Georgi0588d192009-08-12 15:00:51 +00001243 layout2->addWidget(editField);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001244 searchButton = new QPushButton("Search", this);
zbao11a262c2016-01-22 18:54:22 +08001245 searchButton->setAutoDefault(false);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001246 connect(searchButton, &QPushButton::clicked,
1247 this, &ConfigSearchWindow::search);
Patrick Georgi0588d192009-08-12 15:00:51 +00001248 layout2->addWidget(searchButton);
1249 layout1->addLayout(layout2);
1250
1251 split = new QSplitter(this);
Patrick Georgid5208402014-04-11 20:24:06 +02001252 split->setOrientation(Qt::Vertical);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001253 list = new ConfigList(split, "search");
1254 list->mode = listMode;
1255 info = new ConfigInfoView(split, "search");
1256 connect(list, &ConfigList::menuChanged,
1257 info, &ConfigInfoView::setInfo);
1258 connect(list, &ConfigList::menuChanged,
1259 parent, &ConfigMainWindow::setMenuLink);
Patrick Georgi0588d192009-08-12 15:00:51 +00001260
1261 layout1->addWidget(split);
1262
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001263 QVariant x, y;
1264 int width, height;
1265 bool ok;
Patrick Georgi0588d192009-08-12 15:00:51 +00001266
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001267 configSettings->beginGroup("search");
1268 width = configSettings->value("/window width", parent->width() / 2).toInt();
1269 height = configSettings->value("/window height", parent->height() / 2).toInt();
1270 resize(width, height);
1271 x = configSettings->value("/window x");
1272 y = configSettings->value("/window y");
1273 if (x.isValid() && y.isValid())
1274 move(x.toInt(), y.toInt());
1275 QList<int> sizes = configSettings->readSizes("/split", &ok);
1276 if (ok)
1277 split->setSizes(sizes);
1278 configSettings->endGroup();
1279 connect(configApp, &QApplication::aboutToQuit,
1280 this, &ConfigSearchWindow::saveSettings);
Patrick Georgi0588d192009-08-12 15:00:51 +00001281}
1282
1283void ConfigSearchWindow::saveSettings(void)
1284{
zbao11a262c2016-01-22 18:54:22 +08001285 if (!objectName().isEmpty()) {
1286 configSettings->beginGroup(objectName());
1287 configSettings->setValue("/window x", pos().x());
1288 configSettings->setValue("/window y", pos().y());
1289 configSettings->setValue("/window width", size().width());
1290 configSettings->setValue("/window height", size().height());
Patrick Georgi0588d192009-08-12 15:00:51 +00001291 configSettings->writeSizes("/split", split->sizes());
1292 configSettings->endGroup();
1293 }
1294}
1295
1296void ConfigSearchWindow::search(void)
1297{
1298 struct symbol **p;
1299 struct property *prop;
1300 ConfigItem *lastItem = NULL;
1301
1302 free(result);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001303 list->clear();
Patrick Georgi0588d192009-08-12 15:00:51 +00001304 info->clear();
1305
zbao11a262c2016-01-22 18:54:22 +08001306 result = sym_re_search(editField->text().toLatin1());
Patrick Georgi0588d192009-08-12 15:00:51 +00001307 if (!result)
1308 return;
1309 for (p = result; *p; p++) {
1310 for_all_prompts((*p), prop)
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001311 lastItem = new ConfigItem(list, lastItem, prop->menu,
Patrick Georgi0588d192009-08-12 15:00:51 +00001312 menu_is_visible(prop->menu));
1313 }
1314}
1315
1316/*
1317 * Construct the complete config widget
1318 */
1319ConfigMainWindow::ConfigMainWindow(void)
1320 : searchWindow(0)
1321{
zbao11a262c2016-01-22 18:54:22 +08001322 bool ok = true;
1323 QVariant x, y;
1324 int width, height;
Patrick Georgi0588d192009-08-12 15:00:51 +00001325 char title[256];
1326
Patrick Georgid5208402014-04-11 20:24:06 +02001327 snprintf(title, sizeof(title), "%s%s",
1328 rootmenu.prompt->text,
Patrick Georgid5208402014-04-11 20:24:06 +02001329 ""
Patrick Georgid5208402014-04-11 20:24:06 +02001330 );
zbao11a262c2016-01-22 18:54:22 +08001331 setWindowTitle(title);
Patrick Georgi0588d192009-08-12 15:00:51 +00001332
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001333 QRect g = configApp->primaryScreen()->geometry();
1334 width = configSettings->value("/window width", g.width() - 64).toInt();
1335 height = configSettings->value("/window height", g.height() - 64).toInt();
Patrick Georgi0588d192009-08-12 15:00:51 +00001336 resize(width, height);
zbao11a262c2016-01-22 18:54:22 +08001337 x = configSettings->value("/window x");
1338 y = configSettings->value("/window y");
1339 if ((x.isValid())&&(y.isValid()))
1340 move(x.toInt(), y.toInt());
Patrick Georgi0588d192009-08-12 15:00:51 +00001341
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001342 // set up icons
1343 ConfigItem::symbolYesIcon = QIcon(QPixmap(xpm_symbol_yes));
1344 ConfigItem::symbolModIcon = QIcon(QPixmap(xpm_symbol_mod));
1345 ConfigItem::symbolNoIcon = QIcon(QPixmap(xpm_symbol_no));
1346 ConfigItem::choiceYesIcon = QIcon(QPixmap(xpm_choice_yes));
1347 ConfigItem::choiceNoIcon = QIcon(QPixmap(xpm_choice_no));
1348 ConfigItem::menuIcon = QIcon(QPixmap(xpm_menu));
1349 ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback));
1350
1351 QWidget *widget = new QWidget(this);
1352 QVBoxLayout *layout = new QVBoxLayout(widget);
1353 setCentralWidget(widget);
1354
1355 split1 = new QSplitter(widget);
Patrick Georgid5208402014-04-11 20:24:06 +02001356 split1->setOrientation(Qt::Horizontal);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001357 split1->setChildrenCollapsible(false);
Patrick Georgi0588d192009-08-12 15:00:51 +00001358
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001359 menuList = new ConfigList(widget, "menu");
Patrick Georgi0588d192009-08-12 15:00:51 +00001360
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001361 split2 = new QSplitter(widget);
1362 split2->setChildrenCollapsible(false);
Patrick Georgid5208402014-04-11 20:24:06 +02001363 split2->setOrientation(Qt::Vertical);
Patrick Georgi0588d192009-08-12 15:00:51 +00001364
1365 // create config tree
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001366 configList = new ConfigList(widget, "config");
Patrick Georgi0588d192009-08-12 15:00:51 +00001367
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001368 helpText = new ConfigInfoView(widget, "help");
1369
1370 layout->addWidget(split2);
1371 split2->addWidget(split1);
1372 split1->addWidget(configList);
1373 split1->addWidget(menuList);
1374 split2->addWidget(helpText);
Patrick Georgi0588d192009-08-12 15:00:51 +00001375
1376 setTabOrder(configList, helpText);
1377 configList->setFocus();
1378
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001379 backAction = new QAction(QPixmap(xpm_back), "Back", this);
1380 connect(backAction, &QAction::triggered,
1381 this, &ConfigMainWindow::goBack);
Patrick Georgi0588d192009-08-12 15:00:51 +00001382
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001383 QAction *quitAction = new QAction("&Quit", this);
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001384 quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001385 connect(quitAction, &QAction::triggered,
1386 this, &ConfigMainWindow::close);
1387
1388 QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001389 loadAction->setShortcut(Qt::CTRL | Qt::Key_L);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001390 connect(loadAction, &QAction::triggered,
1391 this, &ConfigMainWindow::loadConfig);
1392
1393 saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001394 saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001395 connect(saveAction, &QAction::triggered,
1396 this, &ConfigMainWindow::saveConfig);
1397
Patrick Georgi0588d192009-08-12 15:00:51 +00001398 conf_set_changed_callback(conf_changed);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001399
Patrick Georgi0588d192009-08-12 15:00:51 +00001400 // Set saveAction's initial state
1401 conf_changed();
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001402 configname = xstrdup(conf_get_configname());
Patrick Georgi0588d192009-08-12 15:00:51 +00001403
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001404 QAction *saveAsAction = new QAction("Save &As...", this);
1405 connect(saveAsAction, &QAction::triggered,
1406 this, &ConfigMainWindow::saveConfigAs);
1407 QAction *searchAction = new QAction("&Find", this);
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001408 searchAction->setShortcut(Qt::CTRL | Qt::Key_F);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001409 connect(searchAction, &QAction::triggered,
1410 this, &ConfigMainWindow::searchConfig);
1411 singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1412 singleViewAction->setCheckable(true);
1413 connect(singleViewAction, &QAction::triggered,
1414 this, &ConfigMainWindow::showSingleView);
1415 splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1416 splitViewAction->setCheckable(true);
1417 connect(splitViewAction, &QAction::triggered,
1418 this, &ConfigMainWindow::showSplitView);
1419 fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1420 fullViewAction->setCheckable(true);
1421 connect(fullViewAction, &QAction::triggered,
1422 this, &ConfigMainWindow::showFullView);
1423
1424 QAction *showNameAction = new QAction("Show Name", this);
zbao11a262c2016-01-22 18:54:22 +08001425 showNameAction->setCheckable(true);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001426 connect(showNameAction, &QAction::toggled,
1427 configList, &ConfigList::setShowName);
1428 showNameAction->setChecked(configList->showName);
Patrick Georgid5208402014-04-11 20:24:06 +02001429
1430 QActionGroup *optGroup = new QActionGroup(this);
zbao11a262c2016-01-22 18:54:22 +08001431 optGroup->setExclusive(true);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001432 connect(optGroup, &QActionGroup::triggered,
1433 configList, &ConfigList::setOptionMode);
1434 connect(optGroup, &QActionGroup::triggered,
1435 menuList, &ConfigList::setOptionMode);
Patrick Georgid5208402014-04-11 20:24:06 +02001436
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001437 ConfigList::showNormalAction = new QAction("Show Normal Options", optGroup);
1438 ConfigList::showNormalAction->setCheckable(true);
1439 ConfigList::showAllAction = new QAction("Show All Options", optGroup);
1440 ConfigList::showAllAction->setCheckable(true);
1441 ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
1442 ConfigList::showPromptAction->setCheckable(true);
Patrick Georgid5208402014-04-11 20:24:06 +02001443
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001444 QAction *showDebugAction = new QAction("Show Debug Info", this);
zbao11a262c2016-01-22 18:54:22 +08001445 showDebugAction->setCheckable(true);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001446 connect(showDebugAction, &QAction::toggled,
1447 helpText, &ConfigInfoView::setShowDebug);
zbao11a262c2016-01-22 18:54:22 +08001448 showDebugAction->setChecked(helpText->showDebug());
Patrick Georgi0588d192009-08-12 15:00:51 +00001449
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001450 QAction *showIntroAction = new QAction("Introduction", this);
1451 connect(showIntroAction, &QAction::triggered,
1452 this, &ConfigMainWindow::showIntro);
1453 QAction *showAboutAction = new QAction("About", this);
1454 connect(showAboutAction, &QAction::triggered,
1455 this, &ConfigMainWindow::showAbout);
Patrick Georgi0588d192009-08-12 15:00:51 +00001456
1457 // init tool bar
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001458 QToolBar *toolBar = addToolBar("Tools");
zbao11a262c2016-01-22 18:54:22 +08001459 toolBar->addAction(backAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001460 toolBar->addSeparator();
zbao11a262c2016-01-22 18:54:22 +08001461 toolBar->addAction(loadAction);
1462 toolBar->addAction(saveAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001463 toolBar->addSeparator();
zbao11a262c2016-01-22 18:54:22 +08001464 toolBar->addAction(singleViewAction);
1465 toolBar->addAction(splitViewAction);
1466 toolBar->addAction(fullViewAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001467
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001468 // create file menu
1469 QMenu *menu = menuBar()->addMenu("&File");
1470 menu->addAction(loadAction);
1471 menu->addAction(saveAction);
1472 menu->addAction(saveAsAction);
1473 menu->addSeparator();
1474 menu->addAction(quitAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001475
1476 // create edit menu
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001477 menu = menuBar()->addMenu("&Edit");
1478 menu->addAction(searchAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001479
1480 // create options menu
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001481 menu = menuBar()->addMenu("&Option");
1482 menu->addAction(showNameAction);
1483 menu->addSeparator();
1484 menu->addActions(optGroup->actions());
1485 menu->addSeparator();
1486 menu->addAction(showDebugAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001487
1488 // create help menu
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001489 menu = menuBar()->addMenu("&Help");
1490 menu->addAction(showIntroAction);
1491 menu->addAction(showAboutAction);
Patrick Georgi0588d192009-08-12 15:00:51 +00001492
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001493 connect(helpText, &ConfigInfoView::anchorClicked,
1494 helpText, &ConfigInfoView::clicked);
Patrick Georgi0588d192009-08-12 15:00:51 +00001495
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001496 connect(configList, &ConfigList::menuChanged,
1497 helpText, &ConfigInfoView::setInfo);
1498 connect(configList, &ConfigList::menuSelected,
1499 this, &ConfigMainWindow::changeMenu);
1500 connect(configList, &ConfigList::itemSelected,
1501 this, &ConfigMainWindow::changeItens);
1502 connect(configList, &ConfigList::parentSelected,
1503 this, &ConfigMainWindow::goBack);
1504 connect(menuList, &ConfigList::menuChanged,
1505 helpText, &ConfigInfoView::setInfo);
1506 connect(menuList, &ConfigList::menuSelected,
1507 this, &ConfigMainWindow::changeMenu);
1508
1509 connect(configList, &ConfigList::gotFocus,
1510 helpText, &ConfigInfoView::setInfo);
1511 connect(menuList, &ConfigList::gotFocus,
1512 helpText, &ConfigInfoView::setInfo);
1513 connect(menuList, &ConfigList::gotFocus,
1514 this, &ConfigMainWindow::listFocusChanged);
1515 connect(helpText, &ConfigInfoView::menuSelected,
1516 this, &ConfigMainWindow::setMenuLink);
Patrick Georgi0588d192009-08-12 15:00:51 +00001517
zbao11a262c2016-01-22 18:54:22 +08001518 QString listMode = configSettings->value("/listMode", "symbol").toString();
Patrick Georgi0588d192009-08-12 15:00:51 +00001519 if (listMode == "single")
1520 showSingleView();
1521 else if (listMode == "full")
1522 showFullView();
1523 else /*if (listMode == "split")*/
1524 showSplitView();
1525
1526 // UI setup done, restore splitter positions
zbao11a262c2016-01-22 18:54:22 +08001527 QList<int> sizes = configSettings->readSizes("/split1", &ok);
Patrick Georgi0588d192009-08-12 15:00:51 +00001528 if (ok)
1529 split1->setSizes(sizes);
1530
1531 sizes = configSettings->readSizes("/split2", &ok);
1532 if (ok)
1533 split2->setSizes(sizes);
1534}
1535
1536void ConfigMainWindow::loadConfig(void)
1537{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001538 QString str;
1539 QByteArray ba;
1540 const char *name;
1541
1542 str = QFileDialog::getOpenFileName(this, "", configname);
1543 if (str.isNull())
Patrick Georgi0588d192009-08-12 15:00:51 +00001544 return;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001545
1546 ba = str.toLocal8Bit();
1547 name = ba.data();
1548
1549 if (conf_read(name))
1550 QMessageBox::information(this, "qconf", "Unable to load configuration!");
1551
1552 free(configname);
1553 configname = xstrdup(name);
1554
1555 ConfigList::updateListAllForAll();
Patrick Georgi0588d192009-08-12 15:00:51 +00001556}
1557
Patrick Georgid5208402014-04-11 20:24:06 +02001558bool ConfigMainWindow::saveConfig(void)
Patrick Georgi0588d192009-08-12 15:00:51 +00001559{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001560 if (conf_write(configname)) {
1561 QMessageBox::information(this, "qconf", "Unable to save configuration!");
Patrick Georgid5208402014-04-11 20:24:06 +02001562 return false;
1563 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001564 conf_write_autoconf(0);
1565
Patrick Georgid5208402014-04-11 20:24:06 +02001566 return true;
Patrick Georgi0588d192009-08-12 15:00:51 +00001567}
1568
1569void ConfigMainWindow::saveConfigAs(void)
1570{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001571 QString str;
1572 QByteArray ba;
1573 const char *name;
1574
1575 str = QFileDialog::getSaveFileName(this, "", configname);
1576 if (str.isNull())
Patrick Georgi0588d192009-08-12 15:00:51 +00001577 return;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001578
1579 ba = str.toLocal8Bit();
1580 name = ba.data();
1581
1582 if (conf_write(name)) {
1583 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1584 }
1585 conf_write_autoconf(0);
1586
1587 free(configname);
1588 configname = xstrdup(name);
Patrick Georgi0588d192009-08-12 15:00:51 +00001589}
1590
1591void ConfigMainWindow::searchConfig(void)
1592{
1593 if (!searchWindow)
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001594 searchWindow = new ConfigSearchWindow(this);
Patrick Georgi0588d192009-08-12 15:00:51 +00001595 searchWindow->show();
1596}
1597
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001598void ConfigMainWindow::changeItens(struct menu *menu)
1599{
1600 configList->setRootMenu(menu);
1601}
1602
Patrick Georgi0588d192009-08-12 15:00:51 +00001603void ConfigMainWindow::changeMenu(struct menu *menu)
1604{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001605 menuList->setRootMenu(menu);
Patrick Georgi0588d192009-08-12 15:00:51 +00001606}
1607
1608void ConfigMainWindow::setMenuLink(struct menu *menu)
1609{
1610 struct menu *parent;
1611 ConfigList* list = NULL;
1612 ConfigItem* item;
1613
Patrick Georgid5208402014-04-11 20:24:06 +02001614 if (configList->menuSkip(menu))
Patrick Georgi0588d192009-08-12 15:00:51 +00001615 return;
1616
1617 switch (configList->mode) {
1618 case singleMode:
1619 list = configList;
1620 parent = menu_get_parent_menu(menu);
1621 if (!parent)
1622 return;
1623 list->setRootMenu(parent);
1624 break;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001625 case menuMode:
Patrick Georgi0588d192009-08-12 15:00:51 +00001626 if (menu->flags & MENU_ROOT) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001627 menuList->setRootMenu(menu);
Patrick Georgi0588d192009-08-12 15:00:51 +00001628 configList->clearSelection();
Patrick Georgi0588d192009-08-12 15:00:51 +00001629 list = configList;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001630 } else {
Patrick Georgi0588d192009-08-12 15:00:51 +00001631 parent = menu_get_parent_menu(menu->parent);
1632 if (!parent)
1633 return;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001634
1635 /* Select the config view */
1636 item = configList->findConfigItem(parent);
Patrick Georgi0588d192009-08-12 15:00:51 +00001637 if (item) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001638 configList->setSelected(item, true);
1639 configList->scrollToItem(item);
Patrick Georgi0588d192009-08-12 15:00:51 +00001640 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001641
1642 menuList->setRootMenu(parent);
1643 menuList->clearSelection();
1644 list = menuList;
Patrick Georgi0588d192009-08-12 15:00:51 +00001645 }
1646 break;
1647 case fullMode:
1648 list = configList;
1649 break;
Patrick Georgid5208402014-04-11 20:24:06 +02001650 default:
1651 break;
Patrick Georgi0588d192009-08-12 15:00:51 +00001652 }
1653
1654 if (list) {
1655 item = list->findConfigItem(menu);
1656 if (item) {
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001657 list->setSelected(item, true);
zbao11a262c2016-01-22 18:54:22 +08001658 list->scrollToItem(item);
Patrick Georgi0588d192009-08-12 15:00:51 +00001659 list->setFocus();
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001660 helpText->setInfo(menu);
Patrick Georgi0588d192009-08-12 15:00:51 +00001661 }
1662 }
1663}
1664
1665void ConfigMainWindow::listFocusChanged(void)
1666{
1667 if (menuList->mode == menuMode)
1668 configList->clearSelection();
1669}
1670
1671void ConfigMainWindow::goBack(void)
1672{
Patrick Georgi0588d192009-08-12 15:00:51 +00001673 if (configList->rootEntry == &rootmenu)
zbao11a262c2016-01-22 18:54:22 +08001674 return;
1675
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001676 configList->setParentMenu();
Patrick Georgi0588d192009-08-12 15:00:51 +00001677}
1678
1679void ConfigMainWindow::showSingleView(void)
1680{
zbao11a262c2016-01-22 18:54:22 +08001681 singleViewAction->setEnabled(false);
1682 singleViewAction->setChecked(true);
1683 splitViewAction->setEnabled(true);
1684 splitViewAction->setChecked(false);
1685 fullViewAction->setEnabled(true);
1686 fullViewAction->setChecked(false);
1687
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001688 backAction->setEnabled(true);
1689
1690 menuList->hide();
Patrick Georgi0588d192009-08-12 15:00:51 +00001691 menuList->setRootMenu(0);
1692 configList->mode = singleMode;
1693 if (configList->rootEntry == &rootmenu)
1694 configList->updateListAll();
1695 else
1696 configList->setRootMenu(&rootmenu);
Patrick Georgi0588d192009-08-12 15:00:51 +00001697 configList->setFocus();
1698}
1699
1700void ConfigMainWindow::showSplitView(void)
1701{
zbao11a262c2016-01-22 18:54:22 +08001702 singleViewAction->setEnabled(true);
1703 singleViewAction->setChecked(false);
1704 splitViewAction->setEnabled(false);
1705 splitViewAction->setChecked(true);
1706 fullViewAction->setEnabled(true);
1707 fullViewAction->setChecked(false);
1708
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001709 backAction->setEnabled(false);
1710
1711 configList->mode = menuMode;
Patrick Georgi0588d192009-08-12 15:00:51 +00001712 if (configList->rootEntry == &rootmenu)
1713 configList->updateListAll();
1714 else
1715 configList->setRootMenu(&rootmenu);
zbao11a262c2016-01-22 18:54:22 +08001716 configList->setAllOpen(true);
Patrick Georgi0588d192009-08-12 15:00:51 +00001717 configApp->processEvents();
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001718 menuList->mode = symbolMode;
Patrick Georgi0588d192009-08-12 15:00:51 +00001719 menuList->setRootMenu(&rootmenu);
zbao11a262c2016-01-22 18:54:22 +08001720 menuList->setAllOpen(true);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001721 menuList->show();
Patrick Georgi0588d192009-08-12 15:00:51 +00001722 menuList->setFocus();
1723}
1724
1725void ConfigMainWindow::showFullView(void)
1726{
zbao11a262c2016-01-22 18:54:22 +08001727 singleViewAction->setEnabled(true);
1728 singleViewAction->setChecked(false);
1729 splitViewAction->setEnabled(true);
1730 splitViewAction->setChecked(false);
1731 fullViewAction->setEnabled(false);
1732 fullViewAction->setChecked(true);
1733
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001734 backAction->setEnabled(false);
1735
1736 menuList->hide();
Patrick Georgi0588d192009-08-12 15:00:51 +00001737 menuList->setRootMenu(0);
1738 configList->mode = fullMode;
1739 if (configList->rootEntry == &rootmenu)
1740 configList->updateListAll();
1741 else
1742 configList->setRootMenu(&rootmenu);
Patrick Georgi0588d192009-08-12 15:00:51 +00001743 configList->setFocus();
1744}
1745
1746/*
1747 * ask for saving configuration before quitting
Patrick Georgi0588d192009-08-12 15:00:51 +00001748 */
1749void ConfigMainWindow::closeEvent(QCloseEvent* e)
1750{
1751 if (!conf_get_changed()) {
1752 e->accept();
1753 return;
1754 }
Patrick Georgi0eab62b2023-11-20 19:49:29 +01001755
1756 QMessageBox mb(QMessageBox::Icon::Warning, "qconf",
1757 "Save configuration?");
1758
1759 QPushButton *yb = mb.addButton(QMessageBox::Yes);
1760 QPushButton *db = mb.addButton(QMessageBox::No);
1761 QPushButton *cb = mb.addButton(QMessageBox::Cancel);
1762
1763 yb->setText("&Save Changes");
1764 db->setText("&Discard Changes");
1765 cb->setText("Cancel Exit");
1766
1767 mb.setDefaultButton(yb);
1768 mb.setEscapeButton(cb);
1769
Patrick Georgi0588d192009-08-12 15:00:51 +00001770 switch (mb.exec()) {
1771 case QMessageBox::Yes:
Patrick Georgid5208402014-04-11 20:24:06 +02001772 if (saveConfig())
1773 e->accept();
1774 else
1775 e->ignore();
1776 break;
Patrick Georgi0588d192009-08-12 15:00:51 +00001777 case QMessageBox::No:
1778 e->accept();
1779 break;
1780 case QMessageBox::Cancel:
1781 e->ignore();
1782 break;
1783 }
1784}
1785
1786void ConfigMainWindow::showIntro(void)
1787{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001788 static const QString str =
1789 "Welcome to the qconf graphical configuration tool.\n"
1790 "\n"
1791 "For bool and tristate options, a blank box indicates the "
1792 "feature is disabled, a check indicates it is enabled, and a "
1793 "dot indicates that it is to be compiled as a module. Clicking "
1794 "on the box will cycle through the three states. For int, hex, "
1795 "and string options, double-clicking or pressing F2 on the "
1796 "Value cell will allow you to edit the value.\n"
1797 "\n"
1798 "If you do not see an option (e.g., a device driver) that you "
1799 "believe should be present, try turning on Show All Options "
1800 "under the Options menu. Enabling Show Debug Info will help you"
1801 "figure out what other options must be enabled to support the "
1802 "option you are interested in, and hyperlinks will navigate to "
1803 "them.\n"
1804 "\n"
1805 "Toggling Show Debug Info under the Options menu will show the "
1806 "dependencies, which you can then match by examining other "
1807 "options.\n";
Patrick Georgi0588d192009-08-12 15:00:51 +00001808
1809 QMessageBox::information(this, "qconf", str);
1810}
1811
1812void ConfigMainWindow::showAbout(void)
1813{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001814 static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1815 "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n"
1816 "\n"
1817 "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"
1818 "\n"
1819 "Qt Version: ";
Patrick Georgi0588d192009-08-12 15:00:51 +00001820
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001821 QMessageBox::information(this, "qconf", str + qVersion());
Patrick Georgi0588d192009-08-12 15:00:51 +00001822}
1823
1824void ConfigMainWindow::saveSettings(void)
1825{
zbao11a262c2016-01-22 18:54:22 +08001826 configSettings->setValue("/window x", pos().x());
1827 configSettings->setValue("/window y", pos().y());
1828 configSettings->setValue("/window width", size().width());
1829 configSettings->setValue("/window height", size().height());
Patrick Georgi0588d192009-08-12 15:00:51 +00001830
1831 QString entry;
1832 switch(configList->mode) {
1833 case singleMode :
1834 entry = "single";
1835 break;
1836
1837 case symbolMode :
1838 entry = "split";
1839 break;
1840
1841 case fullMode :
1842 entry = "full";
1843 break;
Patrick Georgid5208402014-04-11 20:24:06 +02001844
1845 default:
1846 break;
Patrick Georgi0588d192009-08-12 15:00:51 +00001847 }
zbao11a262c2016-01-22 18:54:22 +08001848 configSettings->setValue("/listMode", entry);
Patrick Georgi0588d192009-08-12 15:00:51 +00001849
1850 configSettings->writeSizes("/split1", split1->sizes());
1851 configSettings->writeSizes("/split2", split2->sizes());
1852}
1853
1854void ConfigMainWindow::conf_changed(void)
1855{
1856 if (saveAction)
1857 saveAction->setEnabled(conf_get_changed());
1858}
1859
1860void fixup_rootmenu(struct menu *menu)
1861{
1862 struct menu *child;
1863 static int menu_cnt = 0;
1864
1865 menu->flags |= MENU_ROOT;
1866 for (child = menu->list; child; child = child->next) {
1867 if (child->prompt && child->prompt->type == P_MENU) {
1868 menu_cnt++;
1869 fixup_rootmenu(child);
1870 menu_cnt--;
1871 } else if (!menu_cnt)
1872 fixup_rootmenu(child);
1873 }
1874}
1875
1876static const char *progname;
1877
1878static void usage(void)
1879{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001880 printf("%s [-s] <config>\n", progname);
Patrick Georgi0588d192009-08-12 15:00:51 +00001881 exit(0);
1882}
1883
1884int main(int ac, char** av)
1885{
1886 ConfigMainWindow* v;
1887 const char *name;
1888
Patrick Georgi0588d192009-08-12 15:00:51 +00001889 progname = av[0];
Patrick Georgi0588d192009-08-12 15:00:51 +00001890 if (ac > 1 && av[1][0] == '-') {
1891 switch (av[1][1]) {
zbao11a262c2016-01-22 18:54:22 +08001892 case 's':
1893 conf_set_message_callback(NULL);
1894 break;
Patrick Georgi0588d192009-08-12 15:00:51 +00001895 case 'h':
1896 case '?':
1897 usage();
1898 }
1899 name = av[2];
1900 } else
1901 name = av[1];
1902 if (!name)
1903 usage();
1904
1905 conf_parse(name);
1906 fixup_rootmenu(&rootmenu);
1907 conf_read(NULL);
1908 //zconfdump(stdout);
1909
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001910 configApp = new QApplication(ac, av);
1911
Patrick Georgi0588d192009-08-12 15:00:51 +00001912 configSettings = new ConfigSettings();
1913 configSettings->beginGroup("/kconfig/qconf");
1914 v = new ConfigMainWindow();
1915
1916 //zconfdump(stdout);
Patrick Georgi0588d192009-08-12 15:00:51 +00001917 configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1918 configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1919 v->show();
1920 configApp->exec();
1921
1922 configSettings->endGroup();
1923 delete configSettings;
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001924 delete v;
1925 delete configApp;
Patrick Georgi0588d192009-08-12 15:00:51 +00001926
1927 return 0;
1928}