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