xref: /openbmc/linux/scripts/kconfig/qconf.cc (revision 79f382b9)
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 <QApplication>
9 #include <QCloseEvent>
10 #include <QDebug>
11 #include <QDesktopWidget>
12 #include <QFileDialog>
13 #include <QLabel>
14 #include <QLayout>
15 #include <QList>
16 #include <QMenu>
17 #include <QMenuBar>
18 #include <QMessageBox>
19 #include <QToolBar>
20 
21 #include <stdlib.h>
22 
23 #include "lkc.h"
24 #include "qconf.h"
25 
26 #include "images.h"
27 
28 
29 static QApplication *configApp;
30 static ConfigSettings *configSettings;
31 
32 QAction *ConfigMainWindow::saveAction;
33 
34 ConfigSettings::ConfigSettings()
35 	: QSettings("kernel.org", "qconf")
36 {
37 }
38 
39 /**
40  * Reads a list of integer values from the application settings.
41  */
42 QList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
43 {
44 	QList<int> result;
45 
46 	if (contains(key))
47 	{
48 		QStringList entryList = value(key).toStringList();
49 		QStringList::Iterator it;
50 
51 		for (it = entryList.begin(); it != entryList.end(); ++it)
52 			result.push_back((*it).toInt());
53 
54 		*ok = true;
55 	}
56 	else
57 		*ok = false;
58 
59 	return result;
60 }
61 
62 /**
63  * Writes a list of integer values to the application settings.
64  */
65 bool ConfigSettings::writeSizes(const QString& key, const QList<int>& value)
66 {
67 	QStringList stringList;
68 	QList<int>::ConstIterator it;
69 
70 	for (it = value.begin(); it != value.end(); ++it)
71 		stringList.push_back(QString::number(*it));
72 	setValue(key, stringList);
73 
74 	return true;
75 }
76 
77 QIcon ConfigItem::symbolYesIcon;
78 QIcon ConfigItem::symbolModIcon;
79 QIcon ConfigItem::symbolNoIcon;
80 QIcon ConfigItem::choiceYesIcon;
81 QIcon ConfigItem::choiceNoIcon;
82 QIcon ConfigItem::menuIcon;
83 QIcon ConfigItem::menubackIcon;
84 
85 /*
86  * update the displayed of a menu entry
87  */
88 void ConfigItem::updateMenu(void)
89 {
90 	ConfigList* list;
91 	struct symbol* sym;
92 	struct property *prop;
93 	QString prompt;
94 	int type;
95 	tristate expr;
96 
97 	list = listView();
98 	if (goParent) {
99 		setIcon(promptColIdx, menubackIcon);
100 		prompt = "..";
101 		goto set_prompt;
102 	}
103 
104 	sym = menu->sym;
105 	prop = menu->prompt;
106 	prompt = menu_get_prompt(menu);
107 
108 	if (prop) switch (prop->type) {
109 	case P_MENU:
110 		if (list->mode == singleMode || list->mode == symbolMode) {
111 			/* a menuconfig entry is displayed differently
112 			 * depending whether it's at the view root or a child.
113 			 */
114 			if (sym && list->rootEntry == menu)
115 				break;
116 			setIcon(promptColIdx, menuIcon);
117 		} else {
118 			if (sym)
119 				break;
120 			setIcon(promptColIdx, QIcon());
121 		}
122 		goto set_prompt;
123 	case P_COMMENT:
124 		setIcon(promptColIdx, QIcon());
125 		goto set_prompt;
126 	default:
127 		;
128 	}
129 	if (!sym)
130 		goto set_prompt;
131 
132 	setText(nameColIdx, sym->name);
133 
134 	type = sym_get_type(sym);
135 	switch (type) {
136 	case S_BOOLEAN:
137 	case S_TRISTATE:
138 		char ch;
139 
140 		if (!sym_is_changeable(sym) && list->optMode == normalOpt) {
141 			setIcon(promptColIdx, QIcon());
142 			break;
143 		}
144 		expr = sym_get_tristate_value(sym);
145 		switch (expr) {
146 		case yes:
147 			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
148 				setIcon(promptColIdx, choiceYesIcon);
149 			else
150 				setIcon(promptColIdx, symbolYesIcon);
151 			ch = 'Y';
152 			break;
153 		case mod:
154 			setIcon(promptColIdx, symbolModIcon);
155 			ch = 'M';
156 			break;
157 		default:
158 			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
159 				setIcon(promptColIdx, choiceNoIcon);
160 			else
161 				setIcon(promptColIdx, symbolNoIcon);
162 			ch = 'N';
163 			break;
164 		}
165 
166 		setText(dataColIdx, QChar(ch));
167 		break;
168 	case S_INT:
169 	case S_HEX:
170 	case S_STRING:
171 		setText(dataColIdx, sym_get_string_value(sym));
172 		break;
173 	}
174 	if (!sym_has_value(sym) && visible)
175 		prompt += " (NEW)";
176 set_prompt:
177 	setText(promptColIdx, prompt);
178 }
179 
180 void ConfigItem::testUpdateMenu(bool v)
181 {
182 	ConfigItem* i;
183 
184 	visible = v;
185 	if (!menu)
186 		return;
187 
188 	sym_calc_value(menu->sym);
189 	if (menu->flags & MENU_CHANGED) {
190 		/* the menu entry changed, so update all list items */
191 		menu->flags &= ~MENU_CHANGED;
192 		for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
193 			i->updateMenu();
194 	} else if (listView()->updateAll)
195 		updateMenu();
196 }
197 
198 
199 /*
200  * construct a menu entry
201  */
202 void ConfigItem::init(void)
203 {
204 	if (menu) {
205 		ConfigList* list = listView();
206 		nextItem = (ConfigItem*)menu->data;
207 		menu->data = this;
208 
209 		if (list->mode != fullMode)
210 			setExpanded(true);
211 		sym_calc_value(menu->sym);
212 
213 		if (menu->sym) {
214 			enum symbol_type type = menu->sym->type;
215 
216 			// Allow to edit "int", "hex", and "string" in-place in
217 			// the data column. Unfortunately, you cannot specify
218 			// the flags per column. Set ItemIsEditable for all
219 			// columns here, and check the column in createEditor().
220 			if (type == S_INT || type == S_HEX || type == S_STRING)
221 				setFlags(flags() | Qt::ItemIsEditable);
222 		}
223 	}
224 	updateMenu();
225 }
226 
227 /*
228  * destruct a menu entry
229  */
230 ConfigItem::~ConfigItem(void)
231 {
232 	if (menu) {
233 		ConfigItem** ip = (ConfigItem**)&menu->data;
234 		for (; *ip; ip = &(*ip)->nextItem) {
235 			if (*ip == this) {
236 				*ip = nextItem;
237 				break;
238 			}
239 		}
240 	}
241 }
242 
243 QWidget *ConfigItemDelegate::createEditor(QWidget *parent,
244 					  const QStyleOptionViewItem &option,
245 					  const QModelIndex &index) const
246 {
247 	ConfigItem *item;
248 
249 	// Only the data column is editable
250 	if (index.column() != dataColIdx)
251 		return nullptr;
252 
253 	// You cannot edit invisible menus
254 	item = static_cast<ConfigItem *>(index.internalPointer());
255 	if (!item || !item->menu || !menu_is_visible(item->menu))
256 		return nullptr;
257 
258 	return QStyledItemDelegate::createEditor(parent, option, index);
259 }
260 
261 void ConfigItemDelegate::setModelData(QWidget *editor,
262 				      QAbstractItemModel *model,
263 				      const QModelIndex &index) const
264 {
265 	QLineEdit *lineEdit;
266 	ConfigItem *item;
267 	struct symbol *sym;
268 	bool success;
269 
270 	lineEdit = qobject_cast<QLineEdit *>(editor);
271 	// If this is not a QLineEdit, use the parent's default.
272 	// (does this happen?)
273 	if (!lineEdit)
274 		goto parent;
275 
276 	item = static_cast<ConfigItem *>(index.internalPointer());
277 	if (!item || !item->menu)
278 		goto parent;
279 
280 	sym = item->menu->sym;
281 	if (!sym)
282 		goto parent;
283 
284 	success = sym_set_string_value(sym, lineEdit->text().toUtf8().data());
285 	if (success) {
286 		ConfigList::updateListForAll();
287 	} else {
288 		QMessageBox::information(editor, "qconf",
289 			"Cannot set the data (maybe due to out of range).\n"
290 			"Setting the old value.");
291 		lineEdit->setText(sym_get_string_value(sym));
292 	}
293 
294 parent:
295 	QStyledItemDelegate::setModelData(editor, model, index);
296 }
297 
298 ConfigList::ConfigList(QWidget *parent, const char *name)
299 	: QTreeWidget(parent),
300 	  updateAll(false),
301 	  showName(false), mode(singleMode), optMode(normalOpt),
302 	  rootEntry(0), headerPopup(0)
303 {
304 	setObjectName(name);
305 	setSortingEnabled(false);
306 	setRootIsDecorated(true);
307 
308 	setVerticalScrollMode(ScrollPerPixel);
309 	setHorizontalScrollMode(ScrollPerPixel);
310 
311 	setHeaderLabels(QStringList() << "Option" << "Name" << "Value");
312 
313 	connect(this, &ConfigList::itemSelectionChanged,
314 		this, &ConfigList::updateSelection);
315 
316 	if (name) {
317 		configSettings->beginGroup(name);
318 		showName = configSettings->value("/showName", false).toBool();
319 		optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
320 		configSettings->endGroup();
321 		connect(configApp, &QApplication::aboutToQuit,
322 			this, &ConfigList::saveSettings);
323 	}
324 
325 	showColumn(promptColIdx);
326 
327 	setItemDelegate(new ConfigItemDelegate(this));
328 
329 	allLists.append(this);
330 
331 	reinit();
332 }
333 
334 ConfigList::~ConfigList()
335 {
336 	allLists.removeOne(this);
337 }
338 
339 bool ConfigList::menuSkip(struct menu *menu)
340 {
341 	if (optMode == normalOpt && menu_is_visible(menu))
342 		return false;
343 	if (optMode == promptOpt && menu_has_prompt(menu))
344 		return false;
345 	if (optMode == allOpt)
346 		return false;
347 	return true;
348 }
349 
350 void ConfigList::reinit(void)
351 {
352 	hideColumn(nameColIdx);
353 
354 	if (showName)
355 		showColumn(nameColIdx);
356 
357 	updateListAll();
358 }
359 
360 void ConfigList::setOptionMode(QAction *action)
361 {
362 	if (action == showNormalAction)
363 		optMode = normalOpt;
364 	else if (action == showAllAction)
365 		optMode = allOpt;
366 	else
367 		optMode = promptOpt;
368 
369 	updateListAll();
370 }
371 
372 void ConfigList::saveSettings(void)
373 {
374 	if (!objectName().isEmpty()) {
375 		configSettings->beginGroup(objectName());
376 		configSettings->setValue("/showName", showName);
377 		configSettings->setValue("/optionMode", (int)optMode);
378 		configSettings->endGroup();
379 	}
380 }
381 
382 ConfigItem* ConfigList::findConfigItem(struct menu *menu)
383 {
384 	ConfigItem* item = (ConfigItem*)menu->data;
385 
386 	for (; item; item = item->nextItem) {
387 		if (this == item->listView())
388 			break;
389 	}
390 
391 	return item;
392 }
393 
394 void ConfigList::updateSelection(void)
395 {
396 	struct menu *menu;
397 	enum prop_type type;
398 
399 	if (selectedItems().count() == 0)
400 		return;
401 
402 	ConfigItem* item = (ConfigItem*)selectedItems().first();
403 	if (!item)
404 		return;
405 
406 	menu = item->menu;
407 	emit menuChanged(menu);
408 	if (!menu)
409 		return;
410 	type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
411 	if (mode == menuMode && type == P_MENU)
412 		emit menuSelected(menu);
413 }
414 
415 void ConfigList::updateList()
416 {
417 	ConfigItem* last = 0;
418 	ConfigItem *item;
419 
420 	if (!rootEntry) {
421 		if (mode != listMode)
422 			goto update;
423 		QTreeWidgetItemIterator it(this);
424 
425 		while (*it) {
426 			item = (ConfigItem*)(*it);
427 			if (!item->menu)
428 				continue;
429 			item->testUpdateMenu(menu_is_visible(item->menu));
430 
431 			++it;
432 		}
433 		return;
434 	}
435 
436 	if (rootEntry != &rootmenu && (mode == singleMode ||
437 	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
438 		item = (ConfigItem *)topLevelItem(0);
439 		if (!item)
440 			item = new ConfigItem(this, 0, true);
441 		last = item;
442 	}
443 	if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
444 	    rootEntry->sym && rootEntry->prompt) {
445 		item = last ? last->nextSibling() : nullptr;
446 		if (!item)
447 			item = new ConfigItem(this, last, rootEntry, true);
448 		else
449 			item->testUpdateMenu(true);
450 
451 		updateMenuList(item, rootEntry);
452 		update();
453 		resizeColumnToContents(0);
454 		return;
455 	}
456 update:
457 	updateMenuList(rootEntry);
458 	update();
459 	resizeColumnToContents(0);
460 }
461 
462 void ConfigList::updateListForAll()
463 {
464 	QListIterator<ConfigList *> it(allLists);
465 
466 	while (it.hasNext()) {
467 		ConfigList *list = it.next();
468 
469 		list->updateList();
470 	}
471 }
472 
473 void ConfigList::updateListAllForAll()
474 {
475 	QListIterator<ConfigList *> it(allLists);
476 
477 	while (it.hasNext()) {
478 		ConfigList *list = it.next();
479 
480 		list->updateList();
481 	}
482 }
483 
484 void ConfigList::setValue(ConfigItem* item, tristate val)
485 {
486 	struct symbol* sym;
487 	int type;
488 	tristate oldval;
489 
490 	sym = item->menu ? item->menu->sym : 0;
491 	if (!sym)
492 		return;
493 
494 	type = sym_get_type(sym);
495 	switch (type) {
496 	case S_BOOLEAN:
497 	case S_TRISTATE:
498 		oldval = sym_get_tristate_value(sym);
499 
500 		if (!sym_set_tristate_value(sym, val))
501 			return;
502 		if (oldval == no && item->menu->list)
503 			item->setExpanded(true);
504 		ConfigList::updateListForAll();
505 		break;
506 	}
507 }
508 
509 void ConfigList::changeValue(ConfigItem* item)
510 {
511 	struct symbol* sym;
512 	struct menu* menu;
513 	int type, oldexpr, newexpr;
514 
515 	menu = item->menu;
516 	if (!menu)
517 		return;
518 	sym = menu->sym;
519 	if (!sym) {
520 		if (item->menu->list)
521 			item->setExpanded(!item->isExpanded());
522 		return;
523 	}
524 
525 	type = sym_get_type(sym);
526 	switch (type) {
527 	case S_BOOLEAN:
528 	case S_TRISTATE:
529 		oldexpr = sym_get_tristate_value(sym);
530 		newexpr = sym_toggle_tristate_value(sym);
531 		if (item->menu->list) {
532 			if (oldexpr == newexpr)
533 				item->setExpanded(!item->isExpanded());
534 			else if (oldexpr == no)
535 				item->setExpanded(true);
536 		}
537 		if (oldexpr != newexpr)
538 			ConfigList::updateListForAll();
539 		break;
540 	default:
541 		break;
542 	}
543 }
544 
545 void ConfigList::setRootMenu(struct menu *menu)
546 {
547 	enum prop_type type;
548 
549 	if (rootEntry == menu)
550 		return;
551 	type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
552 	if (type != P_MENU)
553 		return;
554 	updateMenuList(0);
555 	rootEntry = menu;
556 	updateListAll();
557 	if (currentItem()) {
558 		setSelected(currentItem(), hasFocus());
559 		scrollToItem(currentItem());
560 	}
561 }
562 
563 void ConfigList::setParentMenu(void)
564 {
565 	ConfigItem* item;
566 	struct menu *oldroot;
567 
568 	oldroot = rootEntry;
569 	if (rootEntry == &rootmenu)
570 		return;
571 	setRootMenu(menu_get_parent_menu(rootEntry->parent));
572 
573 	QTreeWidgetItemIterator it(this);
574 	while (*it) {
575 		item = (ConfigItem *)(*it);
576 		if (item->menu == oldroot) {
577 			setCurrentItem(item);
578 			scrollToItem(item);
579 			break;
580 		}
581 
582 		++it;
583 	}
584 }
585 
586 /*
587  * update all the children of a menu entry
588  *   removes/adds the entries from the parent widget as necessary
589  *
590  * parent: either the menu list widget or a menu entry widget
591  * menu: entry to be updated
592  */
593 void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
594 {
595 	struct menu* child;
596 	ConfigItem* item;
597 	ConfigItem* last;
598 	bool visible;
599 	enum prop_type type;
600 
601 	if (!menu) {
602 		while (parent->childCount() > 0)
603 		{
604 			delete parent->takeChild(0);
605 		}
606 
607 		return;
608 	}
609 
610 	last = parent->firstChild();
611 	if (last && !last->goParent)
612 		last = 0;
613 	for (child = menu->list; child; child = child->next) {
614 		item = last ? last->nextSibling() : parent->firstChild();
615 		type = child->prompt ? child->prompt->type : P_UNKNOWN;
616 
617 		switch (mode) {
618 		case menuMode:
619 			if (!(child->flags & MENU_ROOT))
620 				goto hide;
621 			break;
622 		case symbolMode:
623 			if (child->flags & MENU_ROOT)
624 				goto hide;
625 			break;
626 		default:
627 			break;
628 		}
629 
630 		visible = menu_is_visible(child);
631 		if (!menuSkip(child)) {
632 			if (!child->sym && !child->list && !child->prompt)
633 				continue;
634 			if (!item || item->menu != child)
635 				item = new ConfigItem(parent, last, child, visible);
636 			else
637 				item->testUpdateMenu(visible);
638 
639 			if (mode == fullMode || mode == menuMode || type != P_MENU)
640 				updateMenuList(item, child);
641 			else
642 				updateMenuList(item, 0);
643 			last = item;
644 			continue;
645 		}
646 hide:
647 		if (item && item->menu == child) {
648 			last = parent->firstChild();
649 			if (last == item)
650 				last = 0;
651 			else while (last->nextSibling() != item)
652 				last = last->nextSibling();
653 			delete item;
654 		}
655 	}
656 }
657 
658 void ConfigList::updateMenuList(struct menu *menu)
659 {
660 	struct menu* child;
661 	ConfigItem* item;
662 	ConfigItem* last;
663 	bool visible;
664 	enum prop_type type;
665 
666 	if (!menu) {
667 		while (topLevelItemCount() > 0)
668 		{
669 			delete takeTopLevelItem(0);
670 		}
671 
672 		return;
673 	}
674 
675 	last = (ConfigItem *)topLevelItem(0);
676 	if (last && !last->goParent)
677 		last = 0;
678 	for (child = menu->list; child; child = child->next) {
679 		item = last ? last->nextSibling() : (ConfigItem *)topLevelItem(0);
680 		type = child->prompt ? child->prompt->type : P_UNKNOWN;
681 
682 		switch (mode) {
683 		case menuMode:
684 			if (!(child->flags & MENU_ROOT))
685 				goto hide;
686 			break;
687 		case symbolMode:
688 			if (child->flags & MENU_ROOT)
689 				goto hide;
690 			break;
691 		default:
692 			break;
693 		}
694 
695 		visible = menu_is_visible(child);
696 		if (!menuSkip(child)) {
697 			if (!child->sym && !child->list && !child->prompt)
698 				continue;
699 			if (!item || item->menu != child)
700 				item = new ConfigItem(this, last, child, visible);
701 			else
702 				item->testUpdateMenu(visible);
703 
704 			if (mode == fullMode || mode == menuMode || type != P_MENU)
705 				updateMenuList(item, child);
706 			else
707 				updateMenuList(item, 0);
708 			last = item;
709 			continue;
710 		}
711 hide:
712 		if (item && item->menu == child) {
713 			last = (ConfigItem *)topLevelItem(0);
714 			if (last == item)
715 				last = 0;
716 			else while (last->nextSibling() != item)
717 				last = last->nextSibling();
718 			delete item;
719 		}
720 	}
721 }
722 
723 void ConfigList::keyPressEvent(QKeyEvent* ev)
724 {
725 	QTreeWidgetItem* i = currentItem();
726 	ConfigItem* item;
727 	struct menu *menu;
728 	enum prop_type type;
729 
730 	if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
731 		emit parentSelected();
732 		ev->accept();
733 		return;
734 	}
735 
736 	if (!i) {
737 		Parent::keyPressEvent(ev);
738 		return;
739 	}
740 	item = (ConfigItem*)i;
741 
742 	switch (ev->key()) {
743 	case Qt::Key_Return:
744 	case Qt::Key_Enter:
745 		if (item->goParent) {
746 			emit parentSelected();
747 			break;
748 		}
749 		menu = item->menu;
750 		if (!menu)
751 			break;
752 		type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
753 		if (type == P_MENU && rootEntry != menu &&
754 		    mode != fullMode && mode != menuMode) {
755 			if (mode == menuMode)
756 				emit menuSelected(menu);
757 			else
758 				emit itemSelected(menu);
759 			break;
760 		}
761 	case Qt::Key_Space:
762 		changeValue(item);
763 		break;
764 	case Qt::Key_N:
765 		setValue(item, no);
766 		break;
767 	case Qt::Key_M:
768 		setValue(item, mod);
769 		break;
770 	case Qt::Key_Y:
771 		setValue(item, yes);
772 		break;
773 	default:
774 		Parent::keyPressEvent(ev);
775 		return;
776 	}
777 	ev->accept();
778 }
779 
780 void ConfigList::mousePressEvent(QMouseEvent* e)
781 {
782 	//QPoint p(contentsToViewport(e->pos()));
783 	//printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
784 	Parent::mousePressEvent(e);
785 }
786 
787 void ConfigList::mouseReleaseEvent(QMouseEvent* e)
788 {
789 	QPoint p = e->pos();
790 	ConfigItem* item = (ConfigItem*)itemAt(p);
791 	struct menu *menu;
792 	enum prop_type ptype;
793 	QIcon icon;
794 	int idx, x;
795 
796 	if (!item)
797 		goto skip;
798 
799 	menu = item->menu;
800 	x = header()->offset() + p.x();
801 	idx = header()->logicalIndexAt(x);
802 	switch (idx) {
803 	case promptColIdx:
804 		icon = item->icon(promptColIdx);
805 		if (!icon.isNull()) {
806 			int off = header()->sectionPosition(0) + visualRect(indexAt(p)).x() + 4; // 4 is Hardcoded image offset. There might be a way to do it properly.
807 			if (x >= off && x < off + icon.availableSizes().first().width()) {
808 				if (item->goParent) {
809 					emit parentSelected();
810 					break;
811 				} else if (!menu)
812 					break;
813 				ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
814 				if (ptype == P_MENU && rootEntry != menu &&
815 				    mode != fullMode && mode != menuMode &&
816                                     mode != listMode)
817 					emit menuSelected(menu);
818 				else
819 					changeValue(item);
820 			}
821 		}
822 		break;
823 	case dataColIdx:
824 		changeValue(item);
825 		break;
826 	}
827 
828 skip:
829 	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
830 	Parent::mouseReleaseEvent(e);
831 }
832 
833 void ConfigList::mouseMoveEvent(QMouseEvent* e)
834 {
835 	//QPoint p(contentsToViewport(e->pos()));
836 	//printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
837 	Parent::mouseMoveEvent(e);
838 }
839 
840 void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
841 {
842 	QPoint p = e->pos();
843 	ConfigItem* item = (ConfigItem*)itemAt(p);
844 	struct menu *menu;
845 	enum prop_type ptype;
846 
847 	if (!item)
848 		goto skip;
849 	if (item->goParent) {
850 		emit parentSelected();
851 		goto skip;
852 	}
853 	menu = item->menu;
854 	if (!menu)
855 		goto skip;
856 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
857 	if (ptype == P_MENU && mode != listMode) {
858 		if (mode == singleMode)
859 			emit itemSelected(menu);
860 		else if (mode == symbolMode)
861 			emit menuSelected(menu);
862 	} else if (menu->sym)
863 		changeValue(item);
864 
865 skip:
866 	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
867 	Parent::mouseDoubleClickEvent(e);
868 }
869 
870 void ConfigList::focusInEvent(QFocusEvent *e)
871 {
872 	struct menu *menu = NULL;
873 
874 	Parent::focusInEvent(e);
875 
876 	ConfigItem* item = (ConfigItem *)currentItem();
877 	if (item) {
878 		setSelected(item, true);
879 		menu = item->menu;
880 	}
881 	emit gotFocus(menu);
882 }
883 
884 void ConfigList::contextMenuEvent(QContextMenuEvent *e)
885 {
886 	if (!headerPopup) {
887 		QAction *action;
888 
889 		headerPopup = new QMenu(this);
890 		action = new QAction("Show Name", this);
891 		action->setCheckable(true);
892 		connect(action, &QAction::toggled,
893 			this, &ConfigList::setShowName);
894 		connect(this, &ConfigList::showNameChanged,
895 			action, &QAction::setChecked);
896 		action->setChecked(showName);
897 		headerPopup->addAction(action);
898 	}
899 
900 	headerPopup->exec(e->globalPos());
901 	e->accept();
902 }
903 
904 void ConfigList::setShowName(bool on)
905 {
906 	if (showName == on)
907 		return;
908 
909 	showName = on;
910 	reinit();
911 	emit showNameChanged(on);
912 }
913 
914 QList<ConfigList *> ConfigList::allLists;
915 QAction *ConfigList::showNormalAction;
916 QAction *ConfigList::showAllAction;
917 QAction *ConfigList::showPromptAction;
918 
919 void ConfigList::setAllOpen(bool open)
920 {
921 	QTreeWidgetItemIterator it(this);
922 
923 	while (*it) {
924 		(*it)->setExpanded(open);
925 
926 		++it;
927 	}
928 }
929 
930 ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
931 	: Parent(parent), sym(0), _menu(0)
932 {
933 	setObjectName(name);
934 	setOpenLinks(false);
935 
936 	if (!objectName().isEmpty()) {
937 		configSettings->beginGroup(objectName());
938 		setShowDebug(configSettings->value("/showDebug", false).toBool());
939 		configSettings->endGroup();
940 		connect(configApp, &QApplication::aboutToQuit,
941 			this, &ConfigInfoView::saveSettings);
942 	}
943 
944 	contextMenu = createStandardContextMenu();
945 	QAction *action = new QAction("Show Debug Info", contextMenu);
946 
947 	action->setCheckable(true);
948 	connect(action, &QAction::toggled,
949 		this, &ConfigInfoView::setShowDebug);
950 	connect(this, &ConfigInfoView::showDebugChanged,
951 		action, &QAction::setChecked);
952 	action->setChecked(showDebug());
953 	contextMenu->addSeparator();
954 	contextMenu->addAction(action);
955 }
956 
957 void ConfigInfoView::saveSettings(void)
958 {
959 	if (!objectName().isEmpty()) {
960 		configSettings->beginGroup(objectName());
961 		configSettings->setValue("/showDebug", showDebug());
962 		configSettings->endGroup();
963 	}
964 }
965 
966 void ConfigInfoView::setShowDebug(bool b)
967 {
968 	if (_showDebug != b) {
969 		_showDebug = b;
970 		if (_menu)
971 			menuInfo();
972 		else if (sym)
973 			symbolInfo();
974 		emit showDebugChanged(b);
975 	}
976 }
977 
978 void ConfigInfoView::setInfo(struct menu *m)
979 {
980 	if (_menu == m)
981 		return;
982 	_menu = m;
983 	sym = NULL;
984 	if (!_menu)
985 		clear();
986 	else
987 		menuInfo();
988 }
989 
990 void ConfigInfoView::symbolInfo(void)
991 {
992 	QString str;
993 
994 	str += "<big>Symbol: <b>";
995 	str += print_filter(sym->name);
996 	str += "</b></big><br><br>value: ";
997 	str += print_filter(sym_get_string_value(sym));
998 	str += "<br>visibility: ";
999 	str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
1000 	str += "<br>";
1001 	str += debug_info(sym);
1002 
1003 	setText(str);
1004 }
1005 
1006 void ConfigInfoView::menuInfo(void)
1007 {
1008 	struct symbol* sym;
1009 	QString info;
1010 	QTextStream stream(&info);
1011 
1012 	sym = _menu->sym;
1013 	if (sym) {
1014 		if (_menu->prompt) {
1015 			stream << "<big><b>";
1016 			stream << print_filter(_menu->prompt->text);
1017 			stream << "</b></big>";
1018 			if (sym->name) {
1019 				stream << " (";
1020 				if (showDebug())
1021 					stream << "<a href=\"s" << sym->name << "\">";
1022 				stream << print_filter(sym->name);
1023 				if (showDebug())
1024 					stream << "</a>";
1025 				stream << ")";
1026 			}
1027 		} else if (sym->name) {
1028 			stream << "<big><b>";
1029 			if (showDebug())
1030 				stream << "<a href=\"s" << sym->name << "\">";
1031 			stream << print_filter(sym->name);
1032 			if (showDebug())
1033 				stream << "</a>";
1034 			stream << "</b></big>";
1035 		}
1036 		stream << "<br><br>";
1037 
1038 		if (showDebug())
1039 			stream << debug_info(sym);
1040 
1041 		struct gstr help_gstr = str_new();
1042 
1043 		menu_get_ext_help(_menu, &help_gstr);
1044 		stream << print_filter(str_get(&help_gstr));
1045 		str_free(&help_gstr);
1046 	} else if (_menu->prompt) {
1047 		stream << "<big><b>";
1048 		stream << print_filter(_menu->prompt->text);
1049 		stream << "</b></big><br><br>";
1050 		if (showDebug()) {
1051 			if (_menu->prompt->visible.expr) {
1052 				stream << "&nbsp;&nbsp;dep: ";
1053 				expr_print(_menu->prompt->visible.expr,
1054 					   expr_print_help, &stream, E_NONE);
1055 				stream << "<br><br>";
1056 			}
1057 
1058 			stream << "defined at " << _menu->file->name << ":"
1059 			       << _menu->lineno << "<br><br>";
1060 		}
1061 	}
1062 
1063 	setText(info);
1064 }
1065 
1066 QString ConfigInfoView::debug_info(struct symbol *sym)
1067 {
1068 	QString debug;
1069 	QTextStream stream(&debug);
1070 
1071 	stream << "type: ";
1072 	stream << print_filter(sym_type_name(sym->type));
1073 	if (sym_is_choice(sym))
1074 		stream << " (choice)";
1075 	debug += "<br>";
1076 	if (sym->rev_dep.expr) {
1077 		stream << "reverse dep: ";
1078 		expr_print(sym->rev_dep.expr, expr_print_help, &stream, E_NONE);
1079 		stream << "<br>";
1080 	}
1081 	for (struct property *prop = sym->prop; prop; prop = prop->next) {
1082 		switch (prop->type) {
1083 		case P_PROMPT:
1084 		case P_MENU:
1085 			stream << "prompt: <a href=\"m" << sym->name << "\">";
1086 			stream << print_filter(prop->text);
1087 			stream << "</a><br>";
1088 			break;
1089 		case P_DEFAULT:
1090 		case P_SELECT:
1091 		case P_RANGE:
1092 		case P_COMMENT:
1093 		case P_IMPLY:
1094 		case P_SYMBOL:
1095 			stream << prop_get_type_name(prop->type);
1096 			stream << ": ";
1097 			expr_print(prop->expr, expr_print_help,
1098 				   &stream, E_NONE);
1099 			stream << "<br>";
1100 			break;
1101 		case P_CHOICE:
1102 			if (sym_is_choice(sym)) {
1103 				stream << "choice: ";
1104 				expr_print(prop->expr, expr_print_help,
1105 					   &stream, E_NONE);
1106 				stream << "<br>";
1107 			}
1108 			break;
1109 		default:
1110 			stream << "unknown property: ";
1111 			stream << prop_get_type_name(prop->type);
1112 			stream << "<br>";
1113 		}
1114 		if (prop->visible.expr) {
1115 			stream << "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1116 			expr_print(prop->visible.expr, expr_print_help,
1117 				   &stream, E_NONE);
1118 			stream << "<br>";
1119 		}
1120 	}
1121 	stream << "<br>";
1122 
1123 	return debug;
1124 }
1125 
1126 QString ConfigInfoView::print_filter(const QString &str)
1127 {
1128 	QRegExp re("[<>&\"\\n]");
1129 	QString res = str;
1130 	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1131 		switch (res[i].toLatin1()) {
1132 		case '<':
1133 			res.replace(i, 1, "&lt;");
1134 			i += 4;
1135 			break;
1136 		case '>':
1137 			res.replace(i, 1, "&gt;");
1138 			i += 4;
1139 			break;
1140 		case '&':
1141 			res.replace(i, 1, "&amp;");
1142 			i += 5;
1143 			break;
1144 		case '"':
1145 			res.replace(i, 1, "&quot;");
1146 			i += 6;
1147 			break;
1148 		case '\n':
1149 			res.replace(i, 1, "<br>");
1150 			i += 4;
1151 			break;
1152 		}
1153 	}
1154 	return res;
1155 }
1156 
1157 void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
1158 {
1159 	QTextStream *stream = reinterpret_cast<QTextStream *>(data);
1160 
1161 	if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
1162 		*stream << "<a href=\"s" << sym->name << "\">";
1163 		*stream << print_filter(str);
1164 		*stream << "</a>";
1165 	} else {
1166 		*stream << print_filter(str);
1167 	}
1168 }
1169 
1170 void ConfigInfoView::clicked(const QUrl &url)
1171 {
1172 	QByteArray str = url.toEncoded();
1173 	const std::size_t count = str.size();
1174 	char *data = new char[count + 1];
1175 	struct symbol **result;
1176 	struct menu *m = NULL;
1177 
1178 	if (count < 1) {
1179 		delete[] data;
1180 		return;
1181 	}
1182 
1183 	memcpy(data, str.constData(), count);
1184 	data[count] = '\0';
1185 
1186 	/* Seek for exact match */
1187 	data[0] = '^';
1188 	strcat(data, "$");
1189 	result = sym_re_search(data);
1190 	if (!result) {
1191 		delete[] data;
1192 		return;
1193 	}
1194 
1195 	sym = *result;
1196 
1197 	/* Seek for the menu which holds the symbol */
1198 	for (struct property *prop = sym->prop; prop; prop = prop->next) {
1199 		    if (prop->type != P_PROMPT && prop->type != P_MENU)
1200 			    continue;
1201 		    m = prop->menu;
1202 		    break;
1203 	}
1204 
1205 	if (!m) {
1206 		/* Symbol is not visible as a menu */
1207 		symbolInfo();
1208 		emit showDebugChanged(true);
1209 	} else {
1210 		emit menuSelected(m);
1211 	}
1212 
1213 	free(result);
1214 	delete[] data;
1215 }
1216 
1217 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event)
1218 {
1219 	contextMenu->popup(event->globalPos());
1220 	event->accept();
1221 }
1222 
1223 ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
1224 	: Parent(parent), result(NULL)
1225 {
1226 	setObjectName("search");
1227 	setWindowTitle("Search Config");
1228 
1229 	QVBoxLayout* layout1 = new QVBoxLayout(this);
1230 	layout1->setContentsMargins(11, 11, 11, 11);
1231 	layout1->setSpacing(6);
1232 
1233 	QHBoxLayout* layout2 = new QHBoxLayout();
1234 	layout2->setContentsMargins(0, 0, 0, 0);
1235 	layout2->setSpacing(6);
1236 	layout2->addWidget(new QLabel("Find:", this));
1237 	editField = new QLineEdit(this);
1238 	connect(editField, &QLineEdit::returnPressed,
1239 		this, &ConfigSearchWindow::search);
1240 	layout2->addWidget(editField);
1241 	searchButton = new QPushButton("Search", this);
1242 	searchButton->setAutoDefault(false);
1243 	connect(searchButton, &QPushButton::clicked,
1244 		this, &ConfigSearchWindow::search);
1245 	layout2->addWidget(searchButton);
1246 	layout1->addLayout(layout2);
1247 
1248 	split = new QSplitter(this);
1249 	split->setOrientation(Qt::Vertical);
1250 	list = new ConfigList(split, "search");
1251 	list->mode = listMode;
1252 	info = new ConfigInfoView(split, "search");
1253 	connect(list, &ConfigList::menuChanged,
1254 		info, &ConfigInfoView::setInfo);
1255 	connect(list, &ConfigList::menuChanged,
1256 		parent, &ConfigMainWindow::setMenuLink);
1257 
1258 	layout1->addWidget(split);
1259 
1260 	QVariant x, y;
1261 	int width, height;
1262 	bool ok;
1263 
1264 	configSettings->beginGroup("search");
1265 	width = configSettings->value("/window width", parent->width() / 2).toInt();
1266 	height = configSettings->value("/window height", parent->height() / 2).toInt();
1267 	resize(width, height);
1268 	x = configSettings->value("/window x");
1269 	y = configSettings->value("/window y");
1270 	if (x.isValid() && y.isValid())
1271 		move(x.toInt(), y.toInt());
1272 	QList<int> sizes = configSettings->readSizes("/split", &ok);
1273 	if (ok)
1274 		split->setSizes(sizes);
1275 	configSettings->endGroup();
1276 	connect(configApp, &QApplication::aboutToQuit,
1277 		this, &ConfigSearchWindow::saveSettings);
1278 }
1279 
1280 void ConfigSearchWindow::saveSettings(void)
1281 {
1282 	if (!objectName().isEmpty()) {
1283 		configSettings->beginGroup(objectName());
1284 		configSettings->setValue("/window x", pos().x());
1285 		configSettings->setValue("/window y", pos().y());
1286 		configSettings->setValue("/window width", size().width());
1287 		configSettings->setValue("/window height", size().height());
1288 		configSettings->writeSizes("/split", split->sizes());
1289 		configSettings->endGroup();
1290 	}
1291 }
1292 
1293 void ConfigSearchWindow::search(void)
1294 {
1295 	struct symbol **p;
1296 	struct property *prop;
1297 	ConfigItem *lastItem = NULL;
1298 
1299 	free(result);
1300 	list->clear();
1301 	info->clear();
1302 
1303 	result = sym_re_search(editField->text().toLatin1());
1304 	if (!result)
1305 		return;
1306 	for (p = result; *p; p++) {
1307 		for_all_prompts((*p), prop)
1308 			lastItem = new ConfigItem(list, lastItem, prop->menu,
1309 						  menu_is_visible(prop->menu));
1310 	}
1311 }
1312 
1313 /*
1314  * Construct the complete config widget
1315  */
1316 ConfigMainWindow::ConfigMainWindow(void)
1317 	: searchWindow(0)
1318 {
1319 	bool ok = true;
1320 	QVariant x, y;
1321 	int width, height;
1322 	char title[256];
1323 
1324 	QDesktopWidget *d = configApp->desktop();
1325 	snprintf(title, sizeof(title), "%s%s",
1326 		rootmenu.prompt->text,
1327 		""
1328 		);
1329 	setWindowTitle(title);
1330 
1331 	width = configSettings->value("/window width", d->width() - 64).toInt();
1332 	height = configSettings->value("/window height", d->height() - 64).toInt();
1333 	resize(width, height);
1334 	x = configSettings->value("/window x");
1335 	y = configSettings->value("/window y");
1336 	if ((x.isValid())&&(y.isValid()))
1337 		move(x.toInt(), y.toInt());
1338 
1339 	// set up icons
1340 	ConfigItem::symbolYesIcon = QIcon(QPixmap(xpm_symbol_yes));
1341 	ConfigItem::symbolModIcon = QIcon(QPixmap(xpm_symbol_mod));
1342 	ConfigItem::symbolNoIcon = QIcon(QPixmap(xpm_symbol_no));
1343 	ConfigItem::choiceYesIcon = QIcon(QPixmap(xpm_choice_yes));
1344 	ConfigItem::choiceNoIcon = QIcon(QPixmap(xpm_choice_no));
1345 	ConfigItem::menuIcon = QIcon(QPixmap(xpm_menu));
1346 	ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback));
1347 
1348 	QWidget *widget = new QWidget(this);
1349 	QVBoxLayout *layout = new QVBoxLayout(widget);
1350 	setCentralWidget(widget);
1351 
1352 	split1 = new QSplitter(widget);
1353 	split1->setOrientation(Qt::Horizontal);
1354 	split1->setChildrenCollapsible(false);
1355 
1356 	menuList = new ConfigList(widget, "menu");
1357 
1358 	split2 = new QSplitter(widget);
1359 	split2->setChildrenCollapsible(false);
1360 	split2->setOrientation(Qt::Vertical);
1361 
1362 	// create config tree
1363 	configList = new ConfigList(widget, "config");
1364 
1365 	helpText = new ConfigInfoView(widget, "help");
1366 
1367 	layout->addWidget(split2);
1368 	split2->addWidget(split1);
1369 	split1->addWidget(configList);
1370 	split1->addWidget(menuList);
1371 	split2->addWidget(helpText);
1372 
1373 	setTabOrder(configList, helpText);
1374 	configList->setFocus();
1375 
1376 	backAction = new QAction(QPixmap(xpm_back), "Back", this);
1377 	connect(backAction, &QAction::triggered,
1378 		this, &ConfigMainWindow::goBack);
1379 
1380 	QAction *quitAction = new QAction("&Quit", this);
1381 	quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1382 	connect(quitAction, &QAction::triggered,
1383 		this, &ConfigMainWindow::close);
1384 
1385 	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1386 	loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
1387 	connect(loadAction, &QAction::triggered,
1388 		this, &ConfigMainWindow::loadConfig);
1389 
1390 	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1391 	saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
1392 	connect(saveAction, &QAction::triggered,
1393 		this, &ConfigMainWindow::saveConfig);
1394 
1395 	conf_set_changed_callback(conf_changed);
1396 
1397 	// Set saveAction's initial state
1398 	conf_changed();
1399 	configname = xstrdup(conf_get_configname());
1400 
1401 	QAction *saveAsAction = new QAction("Save &As...", this);
1402 	connect(saveAsAction, &QAction::triggered,
1403 		this, &ConfigMainWindow::saveConfigAs);
1404 	QAction *searchAction = new QAction("&Find", this);
1405 	searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
1406 	connect(searchAction, &QAction::triggered,
1407 		this, &ConfigMainWindow::searchConfig);
1408 	singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1409 	singleViewAction->setCheckable(true);
1410 	connect(singleViewAction, &QAction::triggered,
1411 		this, &ConfigMainWindow::showSingleView);
1412 	splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1413 	splitViewAction->setCheckable(true);
1414 	connect(splitViewAction, &QAction::triggered,
1415 		this, &ConfigMainWindow::showSplitView);
1416 	fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1417 	fullViewAction->setCheckable(true);
1418 	connect(fullViewAction, &QAction::triggered,
1419 		this, &ConfigMainWindow::showFullView);
1420 
1421 	QAction *showNameAction = new QAction("Show Name", this);
1422 	  showNameAction->setCheckable(true);
1423 	connect(showNameAction, &QAction::toggled,
1424 		configList, &ConfigList::setShowName);
1425 	showNameAction->setChecked(configList->showName);
1426 
1427 	QActionGroup *optGroup = new QActionGroup(this);
1428 	optGroup->setExclusive(true);
1429 	connect(optGroup, &QActionGroup::triggered,
1430 		configList, &ConfigList::setOptionMode);
1431 	connect(optGroup, &QActionGroup::triggered,
1432 		menuList, &ConfigList::setOptionMode);
1433 
1434 	ConfigList::showNormalAction = new QAction("Show Normal Options", optGroup);
1435 	ConfigList::showNormalAction->setCheckable(true);
1436 	ConfigList::showAllAction = new QAction("Show All Options", optGroup);
1437 	ConfigList::showAllAction->setCheckable(true);
1438 	ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
1439 	ConfigList::showPromptAction->setCheckable(true);
1440 
1441 	QAction *showDebugAction = new QAction("Show Debug Info", this);
1442 	  showDebugAction->setCheckable(true);
1443 	connect(showDebugAction, &QAction::toggled,
1444 		helpText, &ConfigInfoView::setShowDebug);
1445 	  showDebugAction->setChecked(helpText->showDebug());
1446 
1447 	QAction *showIntroAction = new QAction("Introduction", this);
1448 	connect(showIntroAction, &QAction::triggered,
1449 		this, &ConfigMainWindow::showIntro);
1450 	QAction *showAboutAction = new QAction("About", this);
1451 	connect(showAboutAction, &QAction::triggered,
1452 		this, &ConfigMainWindow::showAbout);
1453 
1454 	// init tool bar
1455 	QToolBar *toolBar = addToolBar("Tools");
1456 	toolBar->addAction(backAction);
1457 	toolBar->addSeparator();
1458 	toolBar->addAction(loadAction);
1459 	toolBar->addAction(saveAction);
1460 	toolBar->addSeparator();
1461 	toolBar->addAction(singleViewAction);
1462 	toolBar->addAction(splitViewAction);
1463 	toolBar->addAction(fullViewAction);
1464 
1465 	// create file menu
1466 	QMenu *menu = menuBar()->addMenu("&File");
1467 	menu->addAction(loadAction);
1468 	menu->addAction(saveAction);
1469 	menu->addAction(saveAsAction);
1470 	menu->addSeparator();
1471 	menu->addAction(quitAction);
1472 
1473 	// create edit menu
1474 	menu = menuBar()->addMenu("&Edit");
1475 	menu->addAction(searchAction);
1476 
1477 	// create options menu
1478 	menu = menuBar()->addMenu("&Option");
1479 	menu->addAction(showNameAction);
1480 	menu->addSeparator();
1481 	menu->addActions(optGroup->actions());
1482 	menu->addSeparator();
1483 	menu->addAction(showDebugAction);
1484 
1485 	// create help menu
1486 	menu = menuBar()->addMenu("&Help");
1487 	menu->addAction(showIntroAction);
1488 	menu->addAction(showAboutAction);
1489 
1490 	connect(helpText, &ConfigInfoView::anchorClicked,
1491 		helpText, &ConfigInfoView::clicked);
1492 
1493 	connect(configList, &ConfigList::menuChanged,
1494 		helpText, &ConfigInfoView::setInfo);
1495 	connect(configList, &ConfigList::menuSelected,
1496 		this, &ConfigMainWindow::changeMenu);
1497 	connect(configList, &ConfigList::itemSelected,
1498 		this, &ConfigMainWindow::changeItens);
1499 	connect(configList, &ConfigList::parentSelected,
1500 		this, &ConfigMainWindow::goBack);
1501 	connect(menuList, &ConfigList::menuChanged,
1502 		helpText, &ConfigInfoView::setInfo);
1503 	connect(menuList, &ConfigList::menuSelected,
1504 		this, &ConfigMainWindow::changeMenu);
1505 
1506 	connect(configList, &ConfigList::gotFocus,
1507 		helpText, &ConfigInfoView::setInfo);
1508 	connect(menuList, &ConfigList::gotFocus,
1509 		helpText, &ConfigInfoView::setInfo);
1510 	connect(menuList, &ConfigList::gotFocus,
1511 		this, &ConfigMainWindow::listFocusChanged);
1512 	connect(helpText, &ConfigInfoView::menuSelected,
1513 		this, &ConfigMainWindow::setMenuLink);
1514 
1515 	QString listMode = configSettings->value("/listMode", "symbol").toString();
1516 	if (listMode == "single")
1517 		showSingleView();
1518 	else if (listMode == "full")
1519 		showFullView();
1520 	else /*if (listMode == "split")*/
1521 		showSplitView();
1522 
1523 	// UI setup done, restore splitter positions
1524 	QList<int> sizes = configSettings->readSizes("/split1", &ok);
1525 	if (ok)
1526 		split1->setSizes(sizes);
1527 
1528 	sizes = configSettings->readSizes("/split2", &ok);
1529 	if (ok)
1530 		split2->setSizes(sizes);
1531 }
1532 
1533 void ConfigMainWindow::loadConfig(void)
1534 {
1535 	QString str;
1536 	QByteArray ba;
1537 	const char *name;
1538 
1539 	str = QFileDialog::getOpenFileName(this, "", configname);
1540 	if (str.isNull())
1541 		return;
1542 
1543 	ba = str.toLocal8Bit();
1544 	name = ba.data();
1545 
1546 	if (conf_read(name))
1547 		QMessageBox::information(this, "qconf", "Unable to load configuration!");
1548 
1549 	free(configname);
1550 	configname = xstrdup(name);
1551 
1552 	ConfigList::updateListAllForAll();
1553 }
1554 
1555 bool ConfigMainWindow::saveConfig(void)
1556 {
1557 	if (conf_write(configname)) {
1558 		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1559 		return false;
1560 	}
1561 	conf_write_autoconf(0);
1562 
1563 	return true;
1564 }
1565 
1566 void ConfigMainWindow::saveConfigAs(void)
1567 {
1568 	QString str;
1569 	QByteArray ba;
1570 	const char *name;
1571 
1572 	str = QFileDialog::getSaveFileName(this, "", configname);
1573 	if (str.isNull())
1574 		return;
1575 
1576 	ba = str.toLocal8Bit();
1577 	name = ba.data();
1578 
1579 	if (conf_write(name)) {
1580 		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1581 	}
1582 	conf_write_autoconf(0);
1583 
1584 	free(configname);
1585 	configname = xstrdup(name);
1586 }
1587 
1588 void ConfigMainWindow::searchConfig(void)
1589 {
1590 	if (!searchWindow)
1591 		searchWindow = new ConfigSearchWindow(this);
1592 	searchWindow->show();
1593 }
1594 
1595 void ConfigMainWindow::changeItens(struct menu *menu)
1596 {
1597 	configList->setRootMenu(menu);
1598 }
1599 
1600 void ConfigMainWindow::changeMenu(struct menu *menu)
1601 {
1602 	menuList->setRootMenu(menu);
1603 }
1604 
1605 void ConfigMainWindow::setMenuLink(struct menu *menu)
1606 {
1607 	struct menu *parent;
1608 	ConfigList* list = NULL;
1609 	ConfigItem* item;
1610 
1611 	if (configList->menuSkip(menu))
1612 		return;
1613 
1614 	switch (configList->mode) {
1615 	case singleMode:
1616 		list = configList;
1617 		parent = menu_get_parent_menu(menu);
1618 		if (!parent)
1619 			return;
1620 		list->setRootMenu(parent);
1621 		break;
1622 	case menuMode:
1623 		if (menu->flags & MENU_ROOT) {
1624 			menuList->setRootMenu(menu);
1625 			configList->clearSelection();
1626 			list = configList;
1627 		} else {
1628 			parent = menu_get_parent_menu(menu->parent);
1629 			if (!parent)
1630 				return;
1631 
1632 			/* Select the config view */
1633 			item = configList->findConfigItem(parent);
1634 			if (item) {
1635 				configList->setSelected(item, true);
1636 				configList->scrollToItem(item);
1637 			}
1638 
1639 			menuList->setRootMenu(parent);
1640 			menuList->clearSelection();
1641 			list = menuList;
1642 		}
1643 		break;
1644 	case fullMode:
1645 		list = configList;
1646 		break;
1647 	default:
1648 		break;
1649 	}
1650 
1651 	if (list) {
1652 		item = list->findConfigItem(menu);
1653 		if (item) {
1654 			list->setSelected(item, true);
1655 			list->scrollToItem(item);
1656 			list->setFocus();
1657 			helpText->setInfo(menu);
1658 		}
1659 	}
1660 }
1661 
1662 void ConfigMainWindow::listFocusChanged(void)
1663 {
1664 	if (menuList->mode == menuMode)
1665 		configList->clearSelection();
1666 }
1667 
1668 void ConfigMainWindow::goBack(void)
1669 {
1670 	if (configList->rootEntry == &rootmenu)
1671 		return;
1672 
1673 	configList->setParentMenu();
1674 }
1675 
1676 void ConfigMainWindow::showSingleView(void)
1677 {
1678 	singleViewAction->setEnabled(false);
1679 	singleViewAction->setChecked(true);
1680 	splitViewAction->setEnabled(true);
1681 	splitViewAction->setChecked(false);
1682 	fullViewAction->setEnabled(true);
1683 	fullViewAction->setChecked(false);
1684 
1685 	backAction->setEnabled(true);
1686 
1687 	menuList->hide();
1688 	menuList->setRootMenu(0);
1689 	configList->mode = singleMode;
1690 	if (configList->rootEntry == &rootmenu)
1691 		configList->updateListAll();
1692 	else
1693 		configList->setRootMenu(&rootmenu);
1694 	configList->setFocus();
1695 }
1696 
1697 void ConfigMainWindow::showSplitView(void)
1698 {
1699 	singleViewAction->setEnabled(true);
1700 	singleViewAction->setChecked(false);
1701 	splitViewAction->setEnabled(false);
1702 	splitViewAction->setChecked(true);
1703 	fullViewAction->setEnabled(true);
1704 	fullViewAction->setChecked(false);
1705 
1706 	backAction->setEnabled(false);
1707 
1708 	configList->mode = menuMode;
1709 	if (configList->rootEntry == &rootmenu)
1710 		configList->updateListAll();
1711 	else
1712 		configList->setRootMenu(&rootmenu);
1713 	configList->setAllOpen(true);
1714 	configApp->processEvents();
1715 	menuList->mode = symbolMode;
1716 	menuList->setRootMenu(&rootmenu);
1717 	menuList->setAllOpen(true);
1718 	menuList->show();
1719 	menuList->setFocus();
1720 }
1721 
1722 void ConfigMainWindow::showFullView(void)
1723 {
1724 	singleViewAction->setEnabled(true);
1725 	singleViewAction->setChecked(false);
1726 	splitViewAction->setEnabled(true);
1727 	splitViewAction->setChecked(false);
1728 	fullViewAction->setEnabled(false);
1729 	fullViewAction->setChecked(true);
1730 
1731 	backAction->setEnabled(false);
1732 
1733 	menuList->hide();
1734 	menuList->setRootMenu(0);
1735 	configList->mode = fullMode;
1736 	if (configList->rootEntry == &rootmenu)
1737 		configList->updateListAll();
1738 	else
1739 		configList->setRootMenu(&rootmenu);
1740 	configList->setFocus();
1741 }
1742 
1743 /*
1744  * ask for saving configuration before quitting
1745  */
1746 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1747 {
1748 	if (!conf_get_changed()) {
1749 		e->accept();
1750 		return;
1751 	}
1752 	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1753 			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1754 	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1755 	mb.setButtonText(QMessageBox::No, "&Discard Changes");
1756 	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1757 	switch (mb.exec()) {
1758 	case QMessageBox::Yes:
1759 		if (saveConfig())
1760 			e->accept();
1761 		else
1762 			e->ignore();
1763 		break;
1764 	case QMessageBox::No:
1765 		e->accept();
1766 		break;
1767 	case QMessageBox::Cancel:
1768 		e->ignore();
1769 		break;
1770 	}
1771 }
1772 
1773 void ConfigMainWindow::showIntro(void)
1774 {
1775 	static const QString str =
1776 		"Welcome to the qconf graphical configuration tool.\n"
1777 		"\n"
1778 		"For bool and tristate options, a blank box indicates the "
1779 		"feature is disabled, a check indicates it is enabled, and a "
1780 		"dot indicates that it is to be compiled as a module. Clicking "
1781 		"on the box will cycle through the three states. For int, hex, "
1782 		"and string options, double-clicking or pressing F2 on the "
1783 		"Value cell will allow you to edit the value.\n"
1784 		"\n"
1785 		"If you do not see an option (e.g., a device driver) that you "
1786 		"believe should be present, try turning on Show All Options "
1787 		"under the Options menu. Enabling Show Debug Info will help you"
1788 		"figure out what other options must be enabled to support the "
1789 		"option you are interested in, and hyperlinks will navigate to "
1790 		"them.\n"
1791 		"\n"
1792 		"Toggling Show Debug Info under the Options menu will show the "
1793 		"dependencies, which you can then match by examining other "
1794 		"options.\n";
1795 
1796 	QMessageBox::information(this, "qconf", str);
1797 }
1798 
1799 void ConfigMainWindow::showAbout(void)
1800 {
1801 	static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1802 		"Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n"
1803 		"\n"
1804 		"Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"
1805 		"\n"
1806 		"Qt Version: ";
1807 
1808 	QMessageBox::information(this, "qconf", str + qVersion());
1809 }
1810 
1811 void ConfigMainWindow::saveSettings(void)
1812 {
1813 	configSettings->setValue("/window x", pos().x());
1814 	configSettings->setValue("/window y", pos().y());
1815 	configSettings->setValue("/window width", size().width());
1816 	configSettings->setValue("/window height", size().height());
1817 
1818 	QString entry;
1819 	switch(configList->mode) {
1820 	case singleMode :
1821 		entry = "single";
1822 		break;
1823 
1824 	case symbolMode :
1825 		entry = "split";
1826 		break;
1827 
1828 	case fullMode :
1829 		entry = "full";
1830 		break;
1831 
1832 	default:
1833 		break;
1834 	}
1835 	configSettings->setValue("/listMode", entry);
1836 
1837 	configSettings->writeSizes("/split1", split1->sizes());
1838 	configSettings->writeSizes("/split2", split2->sizes());
1839 }
1840 
1841 void ConfigMainWindow::conf_changed(void)
1842 {
1843 	if (saveAction)
1844 		saveAction->setEnabled(conf_get_changed());
1845 }
1846 
1847 void fixup_rootmenu(struct menu *menu)
1848 {
1849 	struct menu *child;
1850 	static int menu_cnt = 0;
1851 
1852 	menu->flags |= MENU_ROOT;
1853 	for (child = menu->list; child; child = child->next) {
1854 		if (child->prompt && child->prompt->type == P_MENU) {
1855 			menu_cnt++;
1856 			fixup_rootmenu(child);
1857 			menu_cnt--;
1858 		} else if (!menu_cnt)
1859 			fixup_rootmenu(child);
1860 	}
1861 }
1862 
1863 static const char *progname;
1864 
1865 static void usage(void)
1866 {
1867 	printf("%s [-s] <config>\n", progname);
1868 	exit(0);
1869 }
1870 
1871 int main(int ac, char** av)
1872 {
1873 	ConfigMainWindow* v;
1874 	const char *name;
1875 
1876 	progname = av[0];
1877 	if (ac > 1 && av[1][0] == '-') {
1878 		switch (av[1][1]) {
1879 		case 's':
1880 			conf_set_message_callback(NULL);
1881 			break;
1882 		case 'h':
1883 		case '?':
1884 			usage();
1885 		}
1886 		name = av[2];
1887 	} else
1888 		name = av[1];
1889 	if (!name)
1890 		usage();
1891 
1892 	conf_parse(name);
1893 	fixup_rootmenu(&rootmenu);
1894 	conf_read(NULL);
1895 	//zconfdump(stdout);
1896 
1897 	configApp = new QApplication(ac, av);
1898 
1899 	configSettings = new ConfigSettings();
1900 	configSettings->beginGroup("/kconfig/qconf");
1901 	v = new ConfigMainWindow();
1902 
1903 	//zconfdump(stdout);
1904 	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1905 	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1906 	v->show();
1907 	configApp->exec();
1908 
1909 	configSettings->endGroup();
1910 	delete configSettings;
1911 	delete v;
1912 	delete configApp;
1913 
1914 	return 0;
1915 }
1916