xref: /linux/scripts/kconfig/qconf.cc (revision 975ef7ff81bb000af6e6c8e63e81f89f3468dcf7)
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
4  * Released under the terms of the GNU GPL v2.0.
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.c"
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 		case P_ENV:
1153 			debug += prop_get_type_name(prop->type);
1154 			debug += ": ";
1155 			expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1156 			debug += "<br>";
1157 			break;
1158 		case P_CHOICE:
1159 			if (sym_is_choice(sym)) {
1160 				debug += "choice: ";
1161 				expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1162 				debug += "<br>";
1163 			}
1164 			break;
1165 		default:
1166 			debug += "unknown property: ";
1167 			debug += prop_get_type_name(prop->type);
1168 			debug += "<br>";
1169 		}
1170 		if (prop->visible.expr) {
1171 			debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1172 			expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
1173 			debug += "<br>";
1174 		}
1175 	}
1176 	debug += "<br>";
1177 
1178 	return debug;
1179 }
1180 
1181 QString ConfigInfoView::print_filter(const QString &str)
1182 {
1183 	QRegExp re("[<>&\"\\n]");
1184 	QString res = str;
1185 	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1186 		switch (res[i].toLatin1()) {
1187 		case '<':
1188 			res.replace(i, 1, "&lt;");
1189 			i += 4;
1190 			break;
1191 		case '>':
1192 			res.replace(i, 1, "&gt;");
1193 			i += 4;
1194 			break;
1195 		case '&':
1196 			res.replace(i, 1, "&amp;");
1197 			i += 5;
1198 			break;
1199 		case '"':
1200 			res.replace(i, 1, "&quot;");
1201 			i += 6;
1202 			break;
1203 		case '\n':
1204 			res.replace(i, 1, "<br>");
1205 			i += 4;
1206 			break;
1207 		}
1208 	}
1209 	return res;
1210 }
1211 
1212 void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
1213 {
1214 	QString* text = reinterpret_cast<QString*>(data);
1215 	QString str2 = print_filter(str);
1216 
1217 	if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
1218 		*text += QString().sprintf("<a href=\"s%p\">", sym);
1219 		*text += str2;
1220 		*text += "</a>";
1221 	} else
1222 		*text += str2;
1223 }
1224 
1225 QMenu* ConfigInfoView::createStandardContextMenu(const QPoint & pos)
1226 {
1227 	QMenu* popup = Parent::createStandardContextMenu(pos);
1228 	QAction* action = new QAction("Show Debug Info", popup);
1229 	  action->setCheckable(true);
1230 	  connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
1231 	  connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool)));
1232 	  action->setChecked(showDebug());
1233 	popup->addSeparator();
1234 	popup->addAction(action);
1235 	return popup;
1236 }
1237 
1238 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e)
1239 {
1240 	Parent::contextMenuEvent(e);
1241 }
1242 
1243 ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name)
1244 	: Parent(parent), result(NULL)
1245 {
1246 	setObjectName(name);
1247 	setWindowTitle("Search Config");
1248 
1249 	QVBoxLayout* layout1 = new QVBoxLayout(this);
1250 	layout1->setContentsMargins(11, 11, 11, 11);
1251 	layout1->setSpacing(6);
1252 	QHBoxLayout* layout2 = new QHBoxLayout(0);
1253 	layout2->setContentsMargins(0, 0, 0, 0);
1254 	layout2->setSpacing(6);
1255 	layout2->addWidget(new QLabel("Find:", this));
1256 	editField = new QLineEdit(this);
1257 	connect(editField, SIGNAL(returnPressed()), SLOT(search()));
1258 	layout2->addWidget(editField);
1259 	searchButton = new QPushButton("Search", this);
1260 	searchButton->setAutoDefault(false);
1261 	connect(searchButton, SIGNAL(clicked()), SLOT(search()));
1262 	layout2->addWidget(searchButton);
1263 	layout1->addLayout(layout2);
1264 
1265 	split = new QSplitter(this);
1266 	split->setOrientation(Qt::Vertical);
1267 	list = new ConfigView(split, name);
1268 	list->list->mode = listMode;
1269 	info = new ConfigInfoView(split, name);
1270 	connect(list->list, SIGNAL(menuChanged(struct menu *)),
1271 		info, SLOT(setInfo(struct menu *)));
1272 	connect(list->list, SIGNAL(menuChanged(struct menu *)),
1273 		parent, SLOT(setMenuLink(struct menu *)));
1274 
1275 	layout1->addWidget(split);
1276 
1277 	if (name) {
1278 		QVariant x, y;
1279 		int width, height;
1280 		bool ok;
1281 
1282 		configSettings->beginGroup(name);
1283 		width = configSettings->value("/window width", parent->width() / 2).toInt();
1284 		height = configSettings->value("/window height", parent->height() / 2).toInt();
1285 		resize(width, height);
1286 		x = configSettings->value("/window x");
1287 		y = configSettings->value("/window y");
1288 		if ((x.isValid())&&(y.isValid()))
1289 			move(x.toInt(), y.toInt());
1290 		QList<int> sizes = configSettings->readSizes("/split", &ok);
1291 		if (ok)
1292 			split->setSizes(sizes);
1293 		configSettings->endGroup();
1294 		connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1295 	}
1296 }
1297 
1298 void ConfigSearchWindow::saveSettings(void)
1299 {
1300 	if (!objectName().isEmpty()) {
1301 		configSettings->beginGroup(objectName());
1302 		configSettings->setValue("/window x", pos().x());
1303 		configSettings->setValue("/window y", pos().y());
1304 		configSettings->setValue("/window width", size().width());
1305 		configSettings->setValue("/window height", size().height());
1306 		configSettings->writeSizes("/split", split->sizes());
1307 		configSettings->endGroup();
1308 	}
1309 }
1310 
1311 void ConfigSearchWindow::search(void)
1312 {
1313 	struct symbol **p;
1314 	struct property *prop;
1315 	ConfigItem *lastItem = NULL;
1316 
1317 	free(result);
1318 	list->list->clear();
1319 	info->clear();
1320 
1321 	result = sym_re_search(editField->text().toLatin1());
1322 	if (!result)
1323 		return;
1324 	for (p = result; *p; p++) {
1325 		for_all_prompts((*p), prop)
1326 			lastItem = new ConfigItem(list->list, lastItem, prop->menu,
1327 						  menu_is_visible(prop->menu));
1328 	}
1329 }
1330 
1331 /*
1332  * Construct the complete config widget
1333  */
1334 ConfigMainWindow::ConfigMainWindow(void)
1335 	: searchWindow(0)
1336 {
1337 	QMenuBar* menu;
1338 	bool ok = true;
1339 	QVariant x, y;
1340 	int width, height;
1341 	char title[256];
1342 
1343 	QDesktopWidget *d = configApp->desktop();
1344 	snprintf(title, sizeof(title), "%s%s",
1345 		rootmenu.prompt->text,
1346 		""
1347 		);
1348 	setWindowTitle(title);
1349 
1350 	width = configSettings->value("/window width", d->width() - 64).toInt();
1351 	height = configSettings->value("/window height", d->height() - 64).toInt();
1352 	resize(width, height);
1353 	x = configSettings->value("/window x");
1354 	y = configSettings->value("/window y");
1355 	if ((x.isValid())&&(y.isValid()))
1356 		move(x.toInt(), y.toInt());
1357 
1358 	split1 = new QSplitter(this);
1359 	split1->setOrientation(Qt::Horizontal);
1360 	setCentralWidget(split1);
1361 
1362 	menuView = new ConfigView(split1, "menu");
1363 	menuList = menuView->list;
1364 
1365 	split2 = new QSplitter(split1);
1366 	split2->setOrientation(Qt::Vertical);
1367 
1368 	// create config tree
1369 	configView = new ConfigView(split2, "config");
1370 	configList = configView->list;
1371 
1372 	helpText = new ConfigInfoView(split2, "help");
1373 
1374 	setTabOrder(configList, helpText);
1375 	configList->setFocus();
1376 
1377 	menu = menuBar();
1378 	toolBar = new QToolBar("Tools", this);
1379 	addToolBar(toolBar);
1380 
1381 	backAction = new QAction(QPixmap(xpm_back), "Back", this);
1382 	  connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack()));
1383 	  backAction->setEnabled(false);
1384 	QAction *quitAction = new QAction("&Quit", this);
1385 	quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1386 	  connect(quitAction, SIGNAL(triggered(bool)), SLOT(close()));
1387 	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1388 	loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
1389 	  connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig()));
1390 	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1391 	saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
1392 	  connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig()));
1393 	conf_set_changed_callback(conf_changed);
1394 	// Set saveAction's initial state
1395 	conf_changed();
1396 	QAction *saveAsAction = new QAction("Save &As...", this);
1397 	  connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs()));
1398 	QAction *searchAction = new QAction("&Find", this);
1399 	searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
1400 	  connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig()));
1401 	singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1402 	singleViewAction->setCheckable(true);
1403 	  connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView()));
1404 	splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1405 	splitViewAction->setCheckable(true);
1406 	  connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView()));
1407 	fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1408 	fullViewAction->setCheckable(true);
1409 	  connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView()));
1410 
1411 	QAction *showNameAction = new QAction("Show Name", this);
1412 	  showNameAction->setCheckable(true);
1413 	  connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
1414 	  showNameAction->setChecked(configView->showName());
1415 	QAction *showRangeAction = new QAction("Show Range", this);
1416 	  showRangeAction->setCheckable(true);
1417 	  connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
1418 	QAction *showDataAction = new QAction("Show Data", this);
1419 	  showDataAction->setCheckable(true);
1420 	  connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
1421 
1422 	QActionGroup *optGroup = new QActionGroup(this);
1423 	optGroup->setExclusive(true);
1424 	connect(optGroup, SIGNAL(triggered(QAction*)), configView,
1425 		SLOT(setOptionMode(QAction *)));
1426 	connect(optGroup, SIGNAL(triggered(QAction *)), menuView,
1427 		SLOT(setOptionMode(QAction *)));
1428 
1429 	configView->showNormalAction = new QAction("Show Normal Options", optGroup);
1430 	configView->showAllAction = new QAction("Show All Options", optGroup);
1431 	configView->showPromptAction = new QAction("Show Prompt Options", optGroup);
1432 	configView->showNormalAction->setCheckable(true);
1433 	configView->showAllAction->setCheckable(true);
1434 	configView->showPromptAction->setCheckable(true);
1435 
1436 	QAction *showDebugAction = new QAction("Show Debug Info", this);
1437 	  showDebugAction->setCheckable(true);
1438 	  connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
1439 	  showDebugAction->setChecked(helpText->showDebug());
1440 
1441 	QAction *showIntroAction = new QAction("Introduction", this);
1442 	  connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro()));
1443 	QAction *showAboutAction = new QAction("About", this);
1444 	  connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout()));
1445 
1446 	// init tool bar
1447 	toolBar->addAction(backAction);
1448 	toolBar->addSeparator();
1449 	toolBar->addAction(loadAction);
1450 	toolBar->addAction(saveAction);
1451 	toolBar->addSeparator();
1452 	toolBar->addAction(singleViewAction);
1453 	toolBar->addAction(splitViewAction);
1454 	toolBar->addAction(fullViewAction);
1455 
1456 	// create config menu
1457 	QMenu* config = menu->addMenu("&File");
1458 	config->addAction(loadAction);
1459 	config->addAction(saveAction);
1460 	config->addAction(saveAsAction);
1461 	config->addSeparator();
1462 	config->addAction(quitAction);
1463 
1464 	// create edit menu
1465 	QMenu* editMenu = menu->addMenu("&Edit");
1466 	editMenu->addAction(searchAction);
1467 
1468 	// create options menu
1469 	QMenu* optionMenu = menu->addMenu("&Option");
1470 	optionMenu->addAction(showNameAction);
1471 	optionMenu->addAction(showRangeAction);
1472 	optionMenu->addAction(showDataAction);
1473 	optionMenu->addSeparator();
1474 	optionMenu->addActions(optGroup->actions());
1475 	optionMenu->addSeparator();
1476 	optionMenu->addAction(showDebugAction);
1477 
1478 	// create help menu
1479 	menu->addSeparator();
1480 	QMenu* helpMenu = menu->addMenu("&Help");
1481 	helpMenu->addAction(showIntroAction);
1482 	helpMenu->addAction(showAboutAction);
1483 
1484 	connect(configList, SIGNAL(menuChanged(struct menu *)),
1485 		helpText, SLOT(setInfo(struct menu *)));
1486 	connect(configList, SIGNAL(menuSelected(struct menu *)),
1487 		SLOT(changeMenu(struct menu *)));
1488 	connect(configList, SIGNAL(parentSelected()),
1489 		SLOT(goBack()));
1490 	connect(menuList, SIGNAL(menuChanged(struct menu *)),
1491 		helpText, SLOT(setInfo(struct menu *)));
1492 	connect(menuList, SIGNAL(menuSelected(struct menu *)),
1493 		SLOT(changeMenu(struct menu *)));
1494 
1495 	connect(configList, SIGNAL(gotFocus(struct menu *)),
1496 		helpText, SLOT(setInfo(struct menu *)));
1497 	connect(menuList, SIGNAL(gotFocus(struct menu *)),
1498 		helpText, SLOT(setInfo(struct menu *)));
1499 	connect(menuList, SIGNAL(gotFocus(struct menu *)),
1500 		SLOT(listFocusChanged(void)));
1501 	connect(helpText, SIGNAL(menuSelected(struct menu *)),
1502 		SLOT(setMenuLink(struct menu *)));
1503 
1504 	QString listMode = configSettings->value("/listMode", "symbol").toString();
1505 	if (listMode == "single")
1506 		showSingleView();
1507 	else if (listMode == "full")
1508 		showFullView();
1509 	else /*if (listMode == "split")*/
1510 		showSplitView();
1511 
1512 	// UI setup done, restore splitter positions
1513 	QList<int> sizes = configSettings->readSizes("/split1", &ok);
1514 	if (ok)
1515 		split1->setSizes(sizes);
1516 
1517 	sizes = configSettings->readSizes("/split2", &ok);
1518 	if (ok)
1519 		split2->setSizes(sizes);
1520 }
1521 
1522 void ConfigMainWindow::loadConfig(void)
1523 {
1524 	QString s = QFileDialog::getOpenFileName(this, "", conf_get_configname());
1525 	if (s.isNull())
1526 		return;
1527 	if (conf_read(QFile::encodeName(s)))
1528 		QMessageBox::information(this, "qconf", "Unable to load configuration!");
1529 	ConfigView::updateListAll();
1530 }
1531 
1532 bool ConfigMainWindow::saveConfig(void)
1533 {
1534 	if (conf_write(NULL)) {
1535 		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1536 		return false;
1537 	}
1538 	return true;
1539 }
1540 
1541 void ConfigMainWindow::saveConfigAs(void)
1542 {
1543 	QString s = QFileDialog::getSaveFileName(this, "", conf_get_configname());
1544 	if (s.isNull())
1545 		return;
1546 	saveConfig();
1547 }
1548 
1549 void ConfigMainWindow::searchConfig(void)
1550 {
1551 	if (!searchWindow)
1552 		searchWindow = new ConfigSearchWindow(this, "search");
1553 	searchWindow->show();
1554 }
1555 
1556 void ConfigMainWindow::changeMenu(struct menu *menu)
1557 {
1558 	configList->setRootMenu(menu);
1559 	if (configList->rootEntry->parent == &rootmenu)
1560 		backAction->setEnabled(false);
1561 	else
1562 		backAction->setEnabled(true);
1563 }
1564 
1565 void ConfigMainWindow::setMenuLink(struct menu *menu)
1566 {
1567 	struct menu *parent;
1568 	ConfigList* list = NULL;
1569 	ConfigItem* item;
1570 
1571 	if (configList->menuSkip(menu))
1572 		return;
1573 
1574 	switch (configList->mode) {
1575 	case singleMode:
1576 		list = configList;
1577 		parent = menu_get_parent_menu(menu);
1578 		if (!parent)
1579 			return;
1580 		list->setRootMenu(parent);
1581 		break;
1582 	case symbolMode:
1583 		if (menu->flags & MENU_ROOT) {
1584 			configList->setRootMenu(menu);
1585 			configList->clearSelection();
1586 			list = menuList;
1587 		} else {
1588 			list = configList;
1589 			parent = menu_get_parent_menu(menu->parent);
1590 			if (!parent)
1591 				return;
1592 			item = menuList->findConfigItem(parent);
1593 			if (item) {
1594 				item->setSelected(true);
1595 				menuList->scrollToItem(item);
1596 			}
1597 			list->setRootMenu(parent);
1598 		}
1599 		break;
1600 	case fullMode:
1601 		list = configList;
1602 		break;
1603 	default:
1604 		break;
1605 	}
1606 
1607 	if (list) {
1608 		item = list->findConfigItem(menu);
1609 		if (item) {
1610 			item->setSelected(true);
1611 			list->scrollToItem(item);
1612 			list->setFocus();
1613 		}
1614 	}
1615 }
1616 
1617 void ConfigMainWindow::listFocusChanged(void)
1618 {
1619 	if (menuList->mode == menuMode)
1620 		configList->clearSelection();
1621 }
1622 
1623 void ConfigMainWindow::goBack(void)
1624 {
1625 	ConfigItem* item, *oldSelection;
1626 
1627 	configList->setParentMenu();
1628 	if (configList->rootEntry == &rootmenu)
1629 		backAction->setEnabled(false);
1630 
1631 	if (menuList->selectedItems().count() == 0)
1632 		return;
1633 
1634 	item = (ConfigItem*)menuList->selectedItems().first();
1635 	oldSelection = item;
1636 	while (item) {
1637 		if (item->menu == configList->rootEntry) {
1638 			oldSelection->setSelected(false);
1639 			item->setSelected(true);
1640 			break;
1641 		}
1642 		item = (ConfigItem*)item->parent();
1643 	}
1644 }
1645 
1646 void ConfigMainWindow::showSingleView(void)
1647 {
1648 	singleViewAction->setEnabled(false);
1649 	singleViewAction->setChecked(true);
1650 	splitViewAction->setEnabled(true);
1651 	splitViewAction->setChecked(false);
1652 	fullViewAction->setEnabled(true);
1653 	fullViewAction->setChecked(false);
1654 
1655 	menuView->hide();
1656 	menuList->setRootMenu(0);
1657 	configList->mode = singleMode;
1658 	if (configList->rootEntry == &rootmenu)
1659 		configList->updateListAll();
1660 	else
1661 		configList->setRootMenu(&rootmenu);
1662 	configList->setFocus();
1663 }
1664 
1665 void ConfigMainWindow::showSplitView(void)
1666 {
1667 	singleViewAction->setEnabled(true);
1668 	singleViewAction->setChecked(false);
1669 	splitViewAction->setEnabled(false);
1670 	splitViewAction->setChecked(true);
1671 	fullViewAction->setEnabled(true);
1672 	fullViewAction->setChecked(false);
1673 
1674 	configList->mode = symbolMode;
1675 	if (configList->rootEntry == &rootmenu)
1676 		configList->updateListAll();
1677 	else
1678 		configList->setRootMenu(&rootmenu);
1679 	configList->setAllOpen(true);
1680 	configApp->processEvents();
1681 	menuList->mode = menuMode;
1682 	menuList->setRootMenu(&rootmenu);
1683 	menuList->setAllOpen(true);
1684 	menuView->show();
1685 	menuList->setFocus();
1686 }
1687 
1688 void ConfigMainWindow::showFullView(void)
1689 {
1690 	singleViewAction->setEnabled(true);
1691 	singleViewAction->setChecked(false);
1692 	splitViewAction->setEnabled(true);
1693 	splitViewAction->setChecked(false);
1694 	fullViewAction->setEnabled(false);
1695 	fullViewAction->setChecked(true);
1696 
1697 	menuView->hide();
1698 	menuList->setRootMenu(0);
1699 	configList->mode = fullMode;
1700 	if (configList->rootEntry == &rootmenu)
1701 		configList->updateListAll();
1702 	else
1703 		configList->setRootMenu(&rootmenu);
1704 	configList->setFocus();
1705 }
1706 
1707 /*
1708  * ask for saving configuration before quitting
1709  * TODO ask only when something changed
1710  */
1711 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1712 {
1713 	if (!conf_get_changed()) {
1714 		e->accept();
1715 		return;
1716 	}
1717 	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1718 			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1719 	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1720 	mb.setButtonText(QMessageBox::No, "&Discard Changes");
1721 	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1722 	switch (mb.exec()) {
1723 	case QMessageBox::Yes:
1724 		if (saveConfig())
1725 			e->accept();
1726 		else
1727 			e->ignore();
1728 		break;
1729 	case QMessageBox::No:
1730 		e->accept();
1731 		break;
1732 	case QMessageBox::Cancel:
1733 		e->ignore();
1734 		break;
1735 	}
1736 }
1737 
1738 void ConfigMainWindow::showIntro(void)
1739 {
1740 	static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
1741 		"For each option, a blank box indicates the feature is disabled, a check\n"
1742 		"indicates it is enabled, and a dot indicates that it is to be compiled\n"
1743 		"as a module.  Clicking on the box will cycle through the three states.\n\n"
1744 		"If you do not see an option (e.g., a device driver) that you believe\n"
1745 		"should be present, try turning on Show All Options under the Options menu.\n"
1746 		"Although there is no cross reference yet to help you figure out what other\n"
1747 		"options must be enabled to support the option you are interested in, you can\n"
1748 		"still view the help of a grayed-out option.\n\n"
1749 		"Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1750 		"which you can then match by examining other options.\n\n";
1751 
1752 	QMessageBox::information(this, "qconf", str);
1753 }
1754 
1755 void ConfigMainWindow::showAbout(void)
1756 {
1757 	static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1758 		"Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
1759 		"Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1760 
1761 	QMessageBox::information(this, "qconf", str);
1762 }
1763 
1764 void ConfigMainWindow::saveSettings(void)
1765 {
1766 	configSettings->setValue("/window x", pos().x());
1767 	configSettings->setValue("/window y", pos().y());
1768 	configSettings->setValue("/window width", size().width());
1769 	configSettings->setValue("/window height", size().height());
1770 
1771 	QString entry;
1772 	switch(configList->mode) {
1773 	case singleMode :
1774 		entry = "single";
1775 		break;
1776 
1777 	case symbolMode :
1778 		entry = "split";
1779 		break;
1780 
1781 	case fullMode :
1782 		entry = "full";
1783 		break;
1784 
1785 	default:
1786 		break;
1787 	}
1788 	configSettings->setValue("/listMode", entry);
1789 
1790 	configSettings->writeSizes("/split1", split1->sizes());
1791 	configSettings->writeSizes("/split2", split2->sizes());
1792 }
1793 
1794 void ConfigMainWindow::conf_changed(void)
1795 {
1796 	if (saveAction)
1797 		saveAction->setEnabled(conf_get_changed());
1798 }
1799 
1800 void fixup_rootmenu(struct menu *menu)
1801 {
1802 	struct menu *child;
1803 	static int menu_cnt = 0;
1804 
1805 	menu->flags |= MENU_ROOT;
1806 	for (child = menu->list; child; child = child->next) {
1807 		if (child->prompt && child->prompt->type == P_MENU) {
1808 			menu_cnt++;
1809 			fixup_rootmenu(child);
1810 			menu_cnt--;
1811 		} else if (!menu_cnt)
1812 			fixup_rootmenu(child);
1813 	}
1814 }
1815 
1816 static const char *progname;
1817 
1818 static void usage(void)
1819 {
1820 	printf("%s [-s] <config>\n", progname);
1821 	exit(0);
1822 }
1823 
1824 int main(int ac, char** av)
1825 {
1826 	ConfigMainWindow* v;
1827 	const char *name;
1828 
1829 	progname = av[0];
1830 	configApp = new QApplication(ac, av);
1831 	if (ac > 1 && av[1][0] == '-') {
1832 		switch (av[1][1]) {
1833 		case 's':
1834 			conf_set_message_callback(NULL);
1835 			break;
1836 		case 'h':
1837 		case '?':
1838 			usage();
1839 		}
1840 		name = av[2];
1841 	} else
1842 		name = av[1];
1843 	if (!name)
1844 		usage();
1845 
1846 	conf_parse(name);
1847 	fixup_rootmenu(&rootmenu);
1848 	conf_read(NULL);
1849 	//zconfdump(stdout);
1850 
1851 	configSettings = new ConfigSettings();
1852 	configSettings->beginGroup("/kconfig/qconf");
1853 	v = new ConfigMainWindow();
1854 
1855 	//zconfdump(stdout);
1856 	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1857 	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1858 	v->show();
1859 	configApp->exec();
1860 
1861 	configSettings->endGroup();
1862 	delete configSettings;
1863 	delete v;
1864 	delete configApp;
1865 
1866 	return 0;
1867 }
1868