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