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