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