1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4 * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
5 */
6
7#include <QAction>
8#include <QActionGroup>
9#include <QApplication>
10#include <QCloseEvent>
11#include <QDebug>
12#include <QFileDialog>
13#include <QLabel>
14#include <QLayout>
15#include <QList>
16#include <QMenu>
17#include <QMenuBar>
18#include <QMessageBox>
19#include <QRegularExpression>
20#include <QScreen>
21#include <QToolBar>
22
23#include <stdlib.h>
24
25#include "lkc.h"
26#include "qconf.h"
27
28#include "images.h"
29
30
31static QApplication *configApp;
32static ConfigSettings *configSettings;
33
34QAction *ConfigMainWindow::saveAction;
35
36ConfigSettings::ConfigSettings()
37	: QSettings("kernel.org", "qconf")
38{
39}
40
41/**
42 * Reads a list of integer values from the application settings.
43 */
44QList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
45{
46	QList<int> result;
47
48	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;
60
61	return result;
62}
63
64/**
65 * Writes a list of integer values to the application settings.
66 */
67bool ConfigSettings::writeSizes(const QString& key, const QList<int>& value)
68{
69	QStringList stringList;
70	QList<int>::ConstIterator it;
71
72	for (it = value.begin(); it != value.end(); ++it)
73		stringList.push_back(QString::number(*it));
74	setValue(key, stringList);
75
76	return true;
77}
78
79QIcon ConfigItem::symbolYesIcon;
80QIcon ConfigItem::symbolModIcon;
81QIcon ConfigItem::symbolNoIcon;
82QIcon ConfigItem::choiceYesIcon;
83QIcon ConfigItem::choiceNoIcon;
84QIcon ConfigItem::menuIcon;
85QIcon ConfigItem::menubackIcon;
86
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) {
101		setIcon(promptColIdx, menubackIcon);
102		prompt = "..";
103		goto set_prompt;
104	}
105
106	sym = menu->sym;
107	prop = menu->prompt;
108	prompt = menu_get_prompt(menu);
109
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;
118			setIcon(promptColIdx, menuIcon);
119		} else {
120			if (sym)
121				break;
122			setIcon(promptColIdx, QIcon());
123		}
124		goto set_prompt;
125	case P_COMMENT:
126		setIcon(promptColIdx, QIcon());
127		prompt = "*** " + prompt + " ***";
128		goto set_prompt;
129	default:
130		;
131	}
132	if (!sym)
133		goto set_prompt;
134
135	setText(nameColIdx, sym->name);
136
137	type = sym_get_type(sym);
138	switch (type) {
139	case S_BOOLEAN:
140	case S_TRISTATE:
141		char ch;
142
143		if (!sym_is_changeable(sym) && list->optMode == normalOpt) {
144			setIcon(promptColIdx, QIcon());
145			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)
151				setIcon(promptColIdx, choiceYesIcon);
152			else
153				setIcon(promptColIdx, symbolYesIcon);
154			ch = 'Y';
155			break;
156		case mod:
157			setIcon(promptColIdx, symbolModIcon);
158			ch = 'M';
159			break;
160		default:
161			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
162				setIcon(promptColIdx, choiceNoIcon);
163			else
164				setIcon(promptColIdx, symbolNoIcon);
165			ch = 'N';
166			break;
167		}
168
169		setText(dataColIdx, QChar(ch));
170		break;
171	case S_INT:
172	case S_HEX:
173	case S_STRING:
174		setText(dataColIdx, sym_get_string_value(sym));
175		break;
176	}
177	if (!sym_has_value(sym) && visible)
178		prompt += " (NEW)";
179set_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
201
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)
213			setExpanded(true);
214		sym_calc_value(menu->sym);
215
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		}
226	}
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
246QWidget *ConfigItemDelegate::createEditor(QWidget *parent,
247					  const QStyleOptionViewItem &option,
248					  const QModelIndex &index) const
249{
250	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);
262}
263
264void ConfigItemDelegate::setModelData(QWidget *editor,
265				      QAbstractItemModel *model,
266				      const QModelIndex &index) const
267{
268	QLineEdit *lineEdit;
269	ConfigItem *item;
270	struct symbol *sym;
271	bool success;
272
273	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));
295	}
296
297parent:
298	QStyledItemDelegate::setModelData(editor, model, index);
299}
300
301ConfigList::ConfigList(QWidget *parent, const char *name)
302	: QTreeWidget(parent),
303	  updateAll(false),
304	  showName(false), mode(singleMode), optMode(normalOpt),
305	  rootEntry(0), headerPopup(0)
306{
307	setObjectName(name);
308	setSortingEnabled(false);
309	setRootIsDecorated(true);
310
311	setVerticalScrollMode(ScrollPerPixel);
312	setHorizontalScrollMode(ScrollPerPixel);
313
314	setHeaderLabels(QStringList() << "Option" << "Name" << "Value");
315
316	connect(this, &ConfigList::itemSelectionChanged,
317		this, &ConfigList::updateSelection);
318
319	if (name) {
320		configSettings->beginGroup(name);
321		showName = configSettings->value("/showName", false).toBool();
322		optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
323		configSettings->endGroup();
324		connect(configApp, &QApplication::aboutToQuit,
325			this, &ConfigList::saveSettings);
326	}
327
328	showColumn(promptColIdx);
329
330	setItemDelegate(new ConfigItemDelegate(this));
331
332	allLists.append(this);
333
334	reinit();
335}
336
337ConfigList::~ConfigList()
338{
339	allLists.removeOne(this);
340}
341
342bool 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
353void ConfigList::reinit(void)
354{
355	hideColumn(nameColIdx);
356
357	if (showName)
358		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;
371
372	updateListAll();
373}
374
375void ConfigList::saveSettings(void)
376{
377	if (!objectName().isEmpty()) {
378		configSettings->beginGroup(objectName());
379		configSettings->setValue("/showName", showName);
380		configSettings->setValue("/optionMode", (int)optMode);
381		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
402	if (selectedItems().count() == 0)
403		return;
404
405	ConfigItem* item = (ConfigItem*)selectedItems().first();
406	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
418void ConfigList::updateList()
419{
420	ConfigItem* last = 0;
421	ConfigItem *item;
422
423	if (!rootEntry) {
424		if (mode != listMode)
425			goto update;
426		QTreeWidgetItemIterator it(this);
427
428		while (*it) {
429			item = (ConfigItem*)(*it);
430			if (!item->menu)
431				continue;
432			item->testUpdateMenu(menu_is_visible(item->menu));
433
434			++it;
435		}
436		return;
437	}
438
439	if (rootEntry != &rootmenu && (mode == singleMode ||
440	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
441		item = (ConfigItem *)topLevelItem(0);
442		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) {
448		item = last ? last->nextSibling() : nullptr;
449		if (!item)
450			item = new ConfigItem(this, last, rootEntry, true);
451		else
452			item->testUpdateMenu(true);
453
454		updateMenuList(item, rootEntry);
455		update();
456		resizeColumnToContents(0);
457		return;
458	}
459update:
460	updateMenuList(rootEntry);
461	update();
462	resizeColumnToContents(0);
463}
464
465void 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
487void 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)
506			item->setExpanded(true);
507		ConfigList::updateListForAll();
508		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)
524			item->setExpanded(!item->isExpanded());
525		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)
536				item->setExpanded(!item->isExpanded());
537			else if (oldexpr == no)
538				item->setExpanded(true);
539		}
540		if (oldexpr != newexpr)
541			ConfigList::updateListForAll();
542		break;
543	default:
544		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;
557	updateMenuList(0);
558	rootEntry = menu;
559	updateListAll();
560	if (currentItem()) {
561		setSelected(currentItem(), hasFocus());
562		scrollToItem(currentItem());
563	}
564}
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
576	QTreeWidgetItemIterator it(this);
577	while (*it) {
578		item = (ConfigItem *)(*it);
579		if (item->menu == oldroot) {
580			setCurrentItem(item);
581			scrollToItem(item);
582			break;
583		}
584
585		++it;
586	}
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 */
596void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
597{
598	struct menu* child;
599	ConfigItem* item;
600	ConfigItem* last;
601	bool visible;
602	enum prop_type type;
603
604	if (!menu) {
605		while (parent->childCount() > 0)
606		{
607			delete parent->takeChild(0);
608		}
609
610		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);
634		if (!menuSkip(child)) {
635			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		}
649hide:
650		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
661void ConfigList::updateMenuList(struct menu *menu)
662{
663	struct menu* child;
664	ConfigItem* item;
665	ConfigItem* last;
666	bool visible;
667	enum prop_type type;
668
669	if (!menu) {
670		while (topLevelItemCount() > 0)
671		{
672			delete takeTopLevelItem(0);
673		}
674
675		return;
676	}
677
678	last = (ConfigItem *)topLevelItem(0);
679	if (last && !last->goParent)
680		last = 0;
681	for (child = menu->list; child; child = child->next) {
682		item = last ? last->nextSibling() : (ConfigItem *)topLevelItem(0);
683		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)
703				item = new ConfigItem(this, last, child, visible);
704			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		}
714hide:
715		if (item && item->menu == child) {
716			last = (ConfigItem *)topLevelItem(0);
717			if (last == item)
718				last = 0;
719			else while (last->nextSibling() != item)
720				last = last->nextSibling();
721			delete item;
722		}
723	}
724}
725
726void ConfigList::keyPressEvent(QKeyEvent* ev)
727{
728	QTreeWidgetItem* i = currentItem();
729	ConfigItem* item;
730	struct menu *menu;
731	enum prop_type type;
732
733	if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
734		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()) {
746	case Qt::Key_Return:
747	case Qt::Key_Enter:
748		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) {
758			if (mode == menuMode)
759				emit menuSelected(menu);
760			else
761				emit itemSelected(menu);
762			break;
763		}
764	case Qt::Key_Space:
765		changeValue(item);
766		break;
767	case Qt::Key_N:
768		setValue(item, no);
769		break;
770	case Qt::Key_M:
771		setValue(item, mod);
772		break;
773	case Qt::Key_Y:
774		setValue(item, yes);
775		break;
776	default:
777		Parent::keyPressEvent(ev);
778		return;
779	}
780	ev->accept();
781}
782
783void ConfigList::mousePressEvent(QMouseEvent* e)
784{
785	//QPoint p(contentsToViewport(e->pos()));
786	//printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
787	Parent::mousePressEvent(e);
788}
789
790void ConfigList::mouseReleaseEvent(QMouseEvent* e)
791{
792	QPoint p = e->pos();
793	ConfigItem* item = (ConfigItem*)itemAt(p);
794	struct menu *menu;
795	enum prop_type ptype;
796	QIcon icon;
797	int idx, x;
798
799	if (!item)
800		goto skip;
801
802	menu = item->menu;
803	x = header()->offset() + p.x();
804	idx = header()->logicalIndexAt(x);
805	switch (idx) {
806	case promptColIdx:
807		icon = item->icon(promptColIdx);
808		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()) {
811				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 &&
818				    mode != fullMode && mode != menuMode &&
819                                    mode != listMode)
820					emit menuSelected(menu);
821				else
822					changeValue(item);
823			}
824		}
825		break;
826	case dataColIdx:
827		changeValue(item);
828		break;
829	}
830
831skip:
832	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
833	Parent::mouseReleaseEvent(e);
834}
835
836void ConfigList::mouseMoveEvent(QMouseEvent* e)
837{
838	//QPoint p(contentsToViewport(e->pos()));
839	//printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
840	Parent::mouseMoveEvent(e);
841}
842
843void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
844{
845	QPoint p = e->pos();
846	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;
860	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)
866		changeValue(item);
867
868skip:
869	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
870	Parent::mouseDoubleClickEvent(e);
871}
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) {
881		setSelected(item, true);
882		menu = item->menu;
883	}
884	emit gotFocus(menu);
885}
886
887void ConfigList::contextMenuEvent(QContextMenuEvent *e)
888{
889	if (!headerPopup) {
890		QAction *action;
891
892		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);
901	}
902
903	headerPopup->exec(e->globalPos());
904	e->accept();
905}
906
907void ConfigList::setShowName(bool on)
908{
909	if (showName == on)
910		return;
911
912	showName = on;
913	reinit();
914	emit showNameChanged(on);
915}
916
917QList<ConfigList *> ConfigList::allLists;
918QAction *ConfigList::showNormalAction;
919QAction *ConfigList::showAllAction;
920QAction *ConfigList::showPromptAction;
921
922void ConfigList::setAllOpen(bool open)
923{
924	QTreeWidgetItemIterator it(this);
925
926	while (*it) {
927		(*it)->setExpanded(open);
928
929		++it;
930	}
931}
932
933ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
934	: Parent(parent), sym(0), _menu(0)
935{
936	setObjectName(name);
937	setOpenLinks(false);
938
939	if (!objectName().isEmpty()) {
940		configSettings->beginGroup(objectName());
941		setShowDebug(configSettings->value("/showDebug", false).toBool());
942		configSettings->endGroup();
943		connect(configApp, &QApplication::aboutToQuit,
944			this, &ConfigInfoView::saveSettings);
945	}
946
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);
958}
959
960void ConfigInfoView::saveSettings(void)
961{
962	if (!objectName().isEmpty()) {
963		configSettings->beginGroup(objectName());
964		configSettings->setValue("/showDebug", showDebug());
965		configSettings->endGroup();
966	}
967}
968
969void ConfigInfoView::setShowDebug(bool b)
970{
971	if (_showDebug != b) {
972		_showDebug = b;
973		if (_menu)
974			menuInfo();
975		else if (sym)
976			symbolInfo();
977		emit showDebugChanged(b);
978	}
979}
980
981void ConfigInfoView::setInfo(struct menu *m)
982{
983	if (_menu == m)
984		return;
985	_menu = m;
986	sym = NULL;
987	if (!_menu)
988		clear();
989	else
990		menuInfo();
991}
992
993void 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;
1012	QString info;
1013	QTextStream stream(&info);
1014
1015	sym = _menu->sym;
1016	if (sym) {
1017		if (_menu->prompt) {
1018			stream << "<big><b>";
1019			stream << print_filter(_menu->prompt->text);
1020			stream << "</b></big>";
1021			if (sym->name) {
1022				stream << " (";
1023				if (showDebug())
1024					stream << "<a href=\"s" << sym->name << "\">";
1025				stream << print_filter(sym->name);
1026				if (showDebug())
1027					stream << "</a>";
1028				stream << ")";
1029			}
1030		} else if (sym->name) {
1031			stream << "<big><b>";
1032			if (showDebug())
1033				stream << "<a href=\"s" << sym->name << "\">";
1034			stream << print_filter(sym->name);
1035			if (showDebug())
1036				stream << "</a>";
1037			stream << "</b></big>";
1038		}
1039		stream << "<br><br>";
1040
1041		if (showDebug())
1042			stream << debug_info(sym);
1043
1044		struct gstr help_gstr = str_new();
1045
1046		menu_get_ext_help(_menu, &help_gstr);
1047		stream << print_filter(str_get(&help_gstr));
1048		str_free(&help_gstr);
1049	} else if (_menu->prompt) {
1050		stream << "<big><b>";
1051		stream << print_filter(_menu->prompt->text);
1052		stream << "</b></big><br><br>";
1053		if (showDebug()) {
1054			if (_menu->prompt->visible.expr) {
1055				stream << "&nbsp;&nbsp;dep: ";
1056				expr_print(_menu->prompt->visible.expr,
1057					   expr_print_help, &stream, E_NONE);
1058				stream << "<br><br>";
1059			}
1060
1061			stream << "defined at " << _menu->filename << ":"
1062			       << _menu->lineno << "<br><br>";
1063		}
1064	}
1065
1066	setText(info);
1067}
1068
1069QString ConfigInfoView::debug_info(struct symbol *sym)
1070{
1071	QString debug;
1072	QTextStream stream(&debug);
1073
1074	stream << "type: ";
1075	stream << print_filter(sym_type_name(sym->type));
1076	if (sym_is_choice(sym))
1077		stream << " (choice)";
1078	debug += "<br>";
1079	if (sym->rev_dep.expr) {
1080		stream << "reverse dep: ";
1081		expr_print(sym->rev_dep.expr, expr_print_help, &stream, E_NONE);
1082		stream << "<br>";
1083	}
1084	for (struct property *prop = sym->prop; prop; prop = prop->next) {
1085		switch (prop->type) {
1086		case P_PROMPT:
1087		case P_MENU:
1088			stream << "prompt: <a href=\"m" << sym->name << "\">";
1089			stream << print_filter(prop->text);
1090			stream << "</a><br>";
1091			break;
1092		case P_DEFAULT:
1093		case P_SELECT:
1094		case P_RANGE:
1095		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>";
1103			break;
1104		case P_CHOICE:
1105			if (sym_is_choice(sym)) {
1106				stream << "choice: ";
1107				expr_print(prop->expr, expr_print_help,
1108					   &stream, E_NONE);
1109				stream << "<br>";
1110			}
1111			break;
1112		default:
1113			stream << "unknown property: ";
1114			stream << prop_get_type_name(prop->type);
1115			stream << "<br>";
1116		}
1117		if (prop->visible.expr) {
1118			stream << "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1119			expr_print(prop->visible.expr, expr_print_help,
1120				   &stream, E_NONE);
1121			stream << "<br>";
1122		}
1123	}
1124	stream << "<br>";
1125
1126	return debug;
1127}
1128
1129QString ConfigInfoView::print_filter(const QString &str)
1130{
1131	QRegularExpression re("[<>&\"\\n]");
1132	QString res = str;
1133	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1134		switch (res[i].toLatin1()) {
1135		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{
1162	QTextStream *stream = reinterpret_cast<QTextStream *>(data);
1163
1164	if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
1165		*stream << "<a href=\"s" << sym->name << "\">";
1166		*stream << print_filter(str);
1167		*stream << "</a>";
1168	} else {
1169		*stream << print_filter(str);
1170	}
1171}
1172
1173void ConfigInfoView::clicked(const QUrl &url)
1174{
1175	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;
1218}
1219
1220void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event)
1221{
1222	contextMenu->popup(event->globalPos());
1223	event->accept();
1224}
1225
1226ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
1227	: Parent(parent), result(NULL)
1228{
1229	setObjectName("search");
1230	setWindowTitle("Search Config");
1231
1232	QVBoxLayout* layout1 = new QVBoxLayout(this);
1233	layout1->setContentsMargins(11, 11, 11, 11);
1234	layout1->setSpacing(6);
1235
1236	QHBoxLayout* layout2 = new QHBoxLayout();
1237	layout2->setContentsMargins(0, 0, 0, 0);
1238	layout2->setSpacing(6);
1239	layout2->addWidget(new QLabel("Find:", this));
1240	editField = new QLineEdit(this);
1241	connect(editField, &QLineEdit::returnPressed,
1242		this, &ConfigSearchWindow::search);
1243	layout2->addWidget(editField);
1244	searchButton = new QPushButton("Search", this);
1245	searchButton->setAutoDefault(false);
1246	connect(searchButton, &QPushButton::clicked,
1247		this, &ConfigSearchWindow::search);
1248	layout2->addWidget(searchButton);
1249	layout1->addLayout(layout2);
1250
1251	split = new QSplitter(this);
1252	split->setOrientation(Qt::Vertical);
1253	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);
1260
1261	layout1->addWidget(split);
1262
1263	QVariant x, y;
1264	int width, height;
1265	bool ok;
1266
1267	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);
1281}
1282
1283void ConfigSearchWindow::saveSettings(void)
1284{
1285	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());
1291		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);
1303	list->clear();
1304	info->clear();
1305
1306	result = sym_re_search(editField->text().toLatin1());
1307	if (!result)
1308		return;
1309	for (p = result; *p; p++) {
1310		for_all_prompts((*p), prop)
1311			lastItem = new ConfigItem(list, lastItem, prop->menu,
1312						  menu_is_visible(prop->menu));
1313	}
1314}
1315
1316/*
1317 * Construct the complete config widget
1318 */
1319ConfigMainWindow::ConfigMainWindow(void)
1320	: searchWindow(0)
1321{
1322	bool ok = true;
1323	QVariant x, y;
1324	int width, height;
1325	char title[256];
1326
1327	snprintf(title, sizeof(title), "%s%s",
1328		rootmenu.prompt->text,
1329		""
1330		);
1331	setWindowTitle(title);
1332
1333	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();
1336	resize(width, height);
1337	x = configSettings->value("/window x");
1338	y = configSettings->value("/window y");
1339	if ((x.isValid())&&(y.isValid()))
1340		move(x.toInt(), y.toInt());
1341
1342	// 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);
1356	split1->setOrientation(Qt::Horizontal);
1357	split1->setChildrenCollapsible(false);
1358
1359	menuList = new ConfigList(widget, "menu");
1360
1361	split2 = new QSplitter(widget);
1362	split2->setChildrenCollapsible(false);
1363	split2->setOrientation(Qt::Vertical);
1364
1365	// create config tree
1366	configList = new ConfigList(widget, "config");
1367
1368	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);
1375
1376	setTabOrder(configList, helpText);
1377	configList->setFocus();
1378
1379	backAction = new QAction(QPixmap(xpm_back), "Back", this);
1380	connect(backAction, &QAction::triggered,
1381		this, &ConfigMainWindow::goBack);
1382
1383	QAction *quitAction = new QAction("&Quit", this);
1384	quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
1385	connect(quitAction, &QAction::triggered,
1386		this, &ConfigMainWindow::close);
1387
1388	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1389	loadAction->setShortcut(Qt::CTRL | Qt::Key_L);
1390	connect(loadAction, &QAction::triggered,
1391		this, &ConfigMainWindow::loadConfig);
1392
1393	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1394	saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
1395	connect(saveAction, &QAction::triggered,
1396		this, &ConfigMainWindow::saveConfig);
1397
1398	conf_set_changed_callback(conf_changed);
1399
1400	// Set saveAction's initial state
1401	conf_changed();
1402	configname = xstrdup(conf_get_configname());
1403
1404	QAction *saveAsAction = new QAction("Save &As...", this);
1405	connect(saveAsAction, &QAction::triggered,
1406		this, &ConfigMainWindow::saveConfigAs);
1407	QAction *searchAction = new QAction("&Find", this);
1408	searchAction->setShortcut(Qt::CTRL | Qt::Key_F);
1409	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);
1425	  showNameAction->setCheckable(true);
1426	connect(showNameAction, &QAction::toggled,
1427		configList, &ConfigList::setShowName);
1428	showNameAction->setChecked(configList->showName);
1429
1430	QActionGroup *optGroup = new QActionGroup(this);
1431	optGroup->setExclusive(true);
1432	connect(optGroup, &QActionGroup::triggered,
1433		configList, &ConfigList::setOptionMode);
1434	connect(optGroup, &QActionGroup::triggered,
1435		menuList, &ConfigList::setOptionMode);
1436
1437	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);
1443
1444	QAction *showDebugAction = new QAction("Show Debug Info", this);
1445	  showDebugAction->setCheckable(true);
1446	connect(showDebugAction, &QAction::toggled,
1447		helpText, &ConfigInfoView::setShowDebug);
1448	  showDebugAction->setChecked(helpText->showDebug());
1449
1450	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);
1456
1457	// init tool bar
1458	QToolBar *toolBar = addToolBar("Tools");
1459	toolBar->addAction(backAction);
1460	toolBar->addSeparator();
1461	toolBar->addAction(loadAction);
1462	toolBar->addAction(saveAction);
1463	toolBar->addSeparator();
1464	toolBar->addAction(singleViewAction);
1465	toolBar->addAction(splitViewAction);
1466	toolBar->addAction(fullViewAction);
1467
1468	// 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);
1475
1476	// create edit menu
1477	menu = menuBar()->addMenu("&Edit");
1478	menu->addAction(searchAction);
1479
1480	// create options menu
1481	menu = menuBar()->addMenu("&Option");
1482	menu->addAction(showNameAction);
1483	menu->addSeparator();
1484	menu->addActions(optGroup->actions());
1485	menu->addSeparator();
1486	menu->addAction(showDebugAction);
1487
1488	// create help menu
1489	menu = menuBar()->addMenu("&Help");
1490	menu->addAction(showIntroAction);
1491	menu->addAction(showAboutAction);
1492
1493	connect(helpText, &ConfigInfoView::anchorClicked,
1494		helpText, &ConfigInfoView::clicked);
1495
1496	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);
1517
1518	QString listMode = configSettings->value("/listMode", "symbol").toString();
1519	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
1527	QList<int> sizes = configSettings->readSizes("/split1", &ok);
1528	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{
1538	QString str;
1539	QByteArray ba;
1540	const char *name;
1541
1542	str = QFileDialog::getOpenFileName(this, "", configname);
1543	if (str.isNull())
1544		return;
1545
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();
1556}
1557
1558bool ConfigMainWindow::saveConfig(void)
1559{
1560	if (conf_write(configname)) {
1561		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1562		return false;
1563	}
1564	conf_write_autoconf(0);
1565
1566	return true;
1567}
1568
1569void ConfigMainWindow::saveConfigAs(void)
1570{
1571	QString str;
1572	QByteArray ba;
1573	const char *name;
1574
1575	str = QFileDialog::getSaveFileName(this, "", configname);
1576	if (str.isNull())
1577		return;
1578
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);
1589}
1590
1591void ConfigMainWindow::searchConfig(void)
1592{
1593	if (!searchWindow)
1594		searchWindow = new ConfigSearchWindow(this);
1595	searchWindow->show();
1596}
1597
1598void ConfigMainWindow::changeItens(struct menu *menu)
1599{
1600	configList->setRootMenu(menu);
1601}
1602
1603void ConfigMainWindow::changeMenu(struct menu *menu)
1604{
1605	menuList->setRootMenu(menu);
1606}
1607
1608void ConfigMainWindow::setMenuLink(struct menu *menu)
1609{
1610	struct menu *parent;
1611	ConfigList* list = NULL;
1612	ConfigItem* item;
1613
1614	if (configList->menuSkip(menu))
1615		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;
1625	case menuMode:
1626		if (menu->flags & MENU_ROOT) {
1627			menuList->setRootMenu(menu);
1628			configList->clearSelection();
1629			list = configList;
1630		} else {
1631			parent = menu_get_parent_menu(menu->parent);
1632			if (!parent)
1633				return;
1634
1635			/* Select the config view */
1636			item = configList->findConfigItem(parent);
1637			if (item) {
1638				configList->setSelected(item, true);
1639				configList->scrollToItem(item);
1640			}
1641
1642			menuList->setRootMenu(parent);
1643			menuList->clearSelection();
1644			list = menuList;
1645		}
1646		break;
1647	case fullMode:
1648		list = configList;
1649		break;
1650	default:
1651		break;
1652	}
1653
1654	if (list) {
1655		item = list->findConfigItem(menu);
1656		if (item) {
1657			list->setSelected(item, true);
1658			list->scrollToItem(item);
1659			list->setFocus();
1660			helpText->setInfo(menu);
1661		}
1662	}
1663}
1664
1665void ConfigMainWindow::listFocusChanged(void)
1666{
1667	if (menuList->mode == menuMode)
1668		configList->clearSelection();
1669}
1670
1671void ConfigMainWindow::goBack(void)
1672{
1673	if (configList->rootEntry == &rootmenu)
1674		return;
1675
1676	configList->setParentMenu();
1677}
1678
1679void ConfigMainWindow::showSingleView(void)
1680{
1681	singleViewAction->setEnabled(false);
1682	singleViewAction->setChecked(true);
1683	splitViewAction->setEnabled(true);
1684	splitViewAction->setChecked(false);
1685	fullViewAction->setEnabled(true);
1686	fullViewAction->setChecked(false);
1687
1688	backAction->setEnabled(true);
1689
1690	menuList->hide();
1691	menuList->setRootMenu(0);
1692	configList->mode = singleMode;
1693	if (configList->rootEntry == &rootmenu)
1694		configList->updateListAll();
1695	else
1696		configList->setRootMenu(&rootmenu);
1697	configList->setFocus();
1698}
1699
1700void ConfigMainWindow::showSplitView(void)
1701{
1702	singleViewAction->setEnabled(true);
1703	singleViewAction->setChecked(false);
1704	splitViewAction->setEnabled(false);
1705	splitViewAction->setChecked(true);
1706	fullViewAction->setEnabled(true);
1707	fullViewAction->setChecked(false);
1708
1709	backAction->setEnabled(false);
1710
1711	configList->mode = menuMode;
1712	if (configList->rootEntry == &rootmenu)
1713		configList->updateListAll();
1714	else
1715		configList->setRootMenu(&rootmenu);
1716	configList->setAllOpen(true);
1717	configApp->processEvents();
1718	menuList->mode = symbolMode;
1719	menuList->setRootMenu(&rootmenu);
1720	menuList->setAllOpen(true);
1721	menuList->show();
1722	menuList->setFocus();
1723}
1724
1725void ConfigMainWindow::showFullView(void)
1726{
1727	singleViewAction->setEnabled(true);
1728	singleViewAction->setChecked(false);
1729	splitViewAction->setEnabled(true);
1730	splitViewAction->setChecked(false);
1731	fullViewAction->setEnabled(false);
1732	fullViewAction->setChecked(true);
1733
1734	backAction->setEnabled(false);
1735
1736	menuList->hide();
1737	menuList->setRootMenu(0);
1738	configList->mode = fullMode;
1739	if (configList->rootEntry == &rootmenu)
1740		configList->updateListAll();
1741	else
1742		configList->setRootMenu(&rootmenu);
1743	configList->setFocus();
1744}
1745
1746/*
1747 * ask for saving configuration before quitting
1748 */
1749void ConfigMainWindow::closeEvent(QCloseEvent* e)
1750{
1751	if (!conf_get_changed()) {
1752		e->accept();
1753		return;
1754	}
1755
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
1770	switch (mb.exec()) {
1771	case QMessageBox::Yes:
1772		if (saveConfig())
1773			e->accept();
1774		else
1775			e->ignore();
1776		break;
1777	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{
1788	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";
1808
1809	QMessageBox::information(this, "qconf", str);
1810}
1811
1812void ConfigMainWindow::showAbout(void)
1813{
1814	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: ";
1820
1821	QMessageBox::information(this, "qconf", str + qVersion());
1822}
1823
1824void ConfigMainWindow::saveSettings(void)
1825{
1826	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());
1830
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;
1844
1845	default:
1846		break;
1847	}
1848	configSettings->setValue("/listMode", entry);
1849
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{
1880	printf("%s [-s] <config>\n", progname);
1881	exit(0);
1882}
1883
1884int main(int ac, char** av)
1885{
1886	ConfigMainWindow* v;
1887	const char *name;
1888
1889	progname = av[0];
1890	if (ac > 1 && av[1][0] == '-') {
1891		switch (av[1][1]) {
1892		case 's':
1893			conf_set_message_callback(NULL);
1894			break;
1895		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
1910	configApp = new QApplication(ac, av);
1911
1912	configSettings = new ConfigSettings();
1913	configSettings->beginGroup("/kconfig/qconf");
1914	v = new ConfigMainWindow();
1915
1916	//zconfdump(stdout);
1917	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;
1924	delete v;
1925	delete configApp;
1926
1927	return 0;
1928}
1929