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