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