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