xref: /openbmc/linux/scripts/kconfig/qconf.cc (revision 15e3ae36)
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_changeable(sym) && list->optMode == normalOpt) {
156 			setPixmap(promptColIdx, QIcon());
157 			setText(noColIdx, QString());
158 			setText(modColIdx, QString());
159 			setText(yesColIdx, QString());
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());
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 	setObjectName(name);
313 	setSortingEnabled(false);
314 	setRootIsDecorated(true);
315 
316 	setVerticalScrollMode(ScrollPerPixel);
317 	setHorizontalScrollMode(ScrollPerPixel);
318 
319 	if (mode == symbolMode)
320 		setHeaderLabels(QStringList() << "Item" << "Name" << "N" << "M" << "Y" << "Value");
321 	else
322 		setHeaderLabels(QStringList() << "Option" << "Name" << "N" << "M" << "Y" << "Value");
323 
324 	connect(this, SIGNAL(itemSelectionChanged(void)),
325 		SLOT(updateSelection(void)));
326 
327 	if (name) {
328 		configSettings->beginGroup(name);
329 		showName = configSettings->value("/showName", false).toBool();
330 		showRange = configSettings->value("/showRange", false).toBool();
331 		showData = configSettings->value("/showData", false).toBool();
332 		optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
333 		configSettings->endGroup();
334 		connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
335 	}
336 
337 	addColumn(promptColIdx);
338 
339 	reinit();
340 }
341 
342 bool ConfigList::menuSkip(struct menu *menu)
343 {
344 	if (optMode == normalOpt && menu_is_visible(menu))
345 		return false;
346 	if (optMode == promptOpt && menu_has_prompt(menu))
347 		return false;
348 	if (optMode == allOpt)
349 		return false;
350 	return true;
351 }
352 
353 void ConfigList::reinit(void)
354 {
355 	removeColumn(dataColIdx);
356 	removeColumn(yesColIdx);
357 	removeColumn(modColIdx);
358 	removeColumn(noColIdx);
359 	removeColumn(nameColIdx);
360 
361 	if (showName)
362 		addColumn(nameColIdx);
363 	if (showRange) {
364 		addColumn(noColIdx);
365 		addColumn(modColIdx);
366 		addColumn(yesColIdx);
367 	}
368 	if (showData)
369 		addColumn(dataColIdx);
370 
371 	updateListAll();
372 }
373 
374 void ConfigList::saveSettings(void)
375 {
376 	if (!objectName().isEmpty()) {
377 		configSettings->beginGroup(objectName());
378 		configSettings->setValue("/showName", showName);
379 		configSettings->setValue("/showRange", showRange);
380 		configSettings->setValue("/showData", showData);
381 		configSettings->setValue("/optionMode", (int)optMode);
382 		configSettings->endGroup();
383 	}
384 }
385 
386 ConfigItem* ConfigList::findConfigItem(struct menu *menu)
387 {
388 	ConfigItem* item = (ConfigItem*)menu->data;
389 
390 	for (; item; item = item->nextItem) {
391 		if (this == item->listView())
392 			break;
393 	}
394 
395 	return item;
396 }
397 
398 void ConfigList::updateSelection(void)
399 {
400 	struct menu *menu;
401 	enum prop_type type;
402 
403 	if (mode == symbolMode)
404 		setHeaderLabels(QStringList() << "Item" << "Name" << "N" << "M" << "Y" << "Value");
405 	else
406 		setHeaderLabels(QStringList() << "Option" << "Name" << "N" << "M" << "Y" << "Value");
407 
408 	if (selectedItems().count() == 0)
409 		return;
410 
411 	ConfigItem* item = (ConfigItem*)selectedItems().first();
412 	if (!item)
413 		return;
414 
415 	menu = item->menu;
416 	emit menuChanged(menu);
417 	if (!menu)
418 		return;
419 	type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
420 	if (mode == menuMode && type == P_MENU)
421 		emit menuSelected(menu);
422 }
423 
424 void ConfigList::updateList(ConfigItem* item)
425 {
426 	ConfigItem* last = 0;
427 
428 	if (!rootEntry) {
429 		if (mode != listMode)
430 			goto update;
431 		QTreeWidgetItemIterator it(this);
432 		ConfigItem* item;
433 
434 		while (*it) {
435 			item = (ConfigItem*)(*it);
436 			if (!item->menu)
437 				continue;
438 			item->testUpdateMenu(menu_is_visible(item->menu));
439 
440 			++it;
441 		}
442 		return;
443 	}
444 
445 	if (rootEntry != &rootmenu && (mode == singleMode ||
446 	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
447 		item = (ConfigItem *)topLevelItem(0);
448 		if (!item)
449 			item = new ConfigItem(this, 0, true);
450 		last = item;
451 	}
452 	if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
453 	    rootEntry->sym && rootEntry->prompt) {
454 		item = last ? last->nextSibling() : firstChild();
455 		if (!item)
456 			item = new ConfigItem(this, last, rootEntry, true);
457 		else
458 			item->testUpdateMenu(true);
459 
460 		updateMenuList(item, rootEntry);
461 		update();
462 		resizeColumnToContents(0);
463 		return;
464 	}
465 update:
466 	updateMenuList(this, rootEntry);
467 	update();
468 	resizeColumnToContents(0);
469 }
470 
471 void ConfigList::setValue(ConfigItem* item, tristate val)
472 {
473 	struct symbol* sym;
474 	int type;
475 	tristate oldval;
476 
477 	sym = item->menu ? item->menu->sym : 0;
478 	if (!sym)
479 		return;
480 
481 	type = sym_get_type(sym);
482 	switch (type) {
483 	case S_BOOLEAN:
484 	case S_TRISTATE:
485 		oldval = sym_get_tristate_value(sym);
486 
487 		if (!sym_set_tristate_value(sym, val))
488 			return;
489 		if (oldval == no && item->menu->list)
490 			item->setExpanded(true);
491 		parent()->updateList(item);
492 		break;
493 	}
494 }
495 
496 void ConfigList::changeValue(ConfigItem* item)
497 {
498 	struct symbol* sym;
499 	struct menu* menu;
500 	int type, oldexpr, newexpr;
501 
502 	menu = item->menu;
503 	if (!menu)
504 		return;
505 	sym = menu->sym;
506 	if (!sym) {
507 		if (item->menu->list)
508 			item->setExpanded(!item->isExpanded());
509 		return;
510 	}
511 
512 	type = sym_get_type(sym);
513 	switch (type) {
514 	case S_BOOLEAN:
515 	case S_TRISTATE:
516 		oldexpr = sym_get_tristate_value(sym);
517 		newexpr = sym_toggle_tristate_value(sym);
518 		if (item->menu->list) {
519 			if (oldexpr == newexpr)
520 				item->setExpanded(!item->isExpanded());
521 			else if (oldexpr == no)
522 				item->setExpanded(true);
523 		}
524 		if (oldexpr != newexpr)
525 			parent()->updateList(item);
526 		break;
527 	case S_INT:
528 	case S_HEX:
529 	case S_STRING:
530 		parent()->lineEdit->show(item);
531 		break;
532 	}
533 }
534 
535 void ConfigList::setRootMenu(struct menu *menu)
536 {
537 	enum prop_type type;
538 
539 	if (rootEntry == menu)
540 		return;
541 	type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
542 	if (type != P_MENU)
543 		return;
544 	updateMenuList(this, 0);
545 	rootEntry = menu;
546 	updateListAll();
547 	if (currentItem()) {
548 		currentItem()->setSelected(hasFocus());
549 		scrollToItem(currentItem());
550 	}
551 }
552 
553 void ConfigList::setParentMenu(void)
554 {
555 	ConfigItem* item;
556 	struct menu *oldroot;
557 
558 	oldroot = rootEntry;
559 	if (rootEntry == &rootmenu)
560 		return;
561 	setRootMenu(menu_get_parent_menu(rootEntry->parent));
562 
563 	QTreeWidgetItemIterator it(this);
564 	while (*it) {
565 		item = (ConfigItem *)(*it);
566 		if (item->menu == oldroot) {
567 			setCurrentItem(item);
568 			scrollToItem(item);
569 			break;
570 		}
571 
572 		++it;
573 	}
574 }
575 
576 /*
577  * update all the children of a menu entry
578  *   removes/adds the entries from the parent widget as necessary
579  *
580  * parent: either the menu list widget or a menu entry widget
581  * menu: entry to be updated
582  */
583 void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
584 {
585 	struct menu* child;
586 	ConfigItem* item;
587 	ConfigItem* last;
588 	bool visible;
589 	enum prop_type type;
590 
591 	if (!menu) {
592 		while (parent->childCount() > 0)
593 		{
594 			delete parent->takeChild(0);
595 		}
596 
597 		return;
598 	}
599 
600 	last = parent->firstChild();
601 	if (last && !last->goParent)
602 		last = 0;
603 	for (child = menu->list; child; child = child->next) {
604 		item = last ? last->nextSibling() : parent->firstChild();
605 		type = child->prompt ? child->prompt->type : P_UNKNOWN;
606 
607 		switch (mode) {
608 		case menuMode:
609 			if (!(child->flags & MENU_ROOT))
610 				goto hide;
611 			break;
612 		case symbolMode:
613 			if (child->flags & MENU_ROOT)
614 				goto hide;
615 			break;
616 		default:
617 			break;
618 		}
619 
620 		visible = menu_is_visible(child);
621 		if (!menuSkip(child)) {
622 			if (!child->sym && !child->list && !child->prompt)
623 				continue;
624 			if (!item || item->menu != child)
625 				item = new ConfigItem(parent, last, child, visible);
626 			else
627 				item->testUpdateMenu(visible);
628 
629 			if (mode == fullMode || mode == menuMode || type != P_MENU)
630 				updateMenuList(item, child);
631 			else
632 				updateMenuList(item, 0);
633 			last = item;
634 			continue;
635 		}
636 hide:
637 		if (item && item->menu == child) {
638 			last = parent->firstChild();
639 			if (last == item)
640 				last = 0;
641 			else while (last->nextSibling() != item)
642 				last = last->nextSibling();
643 			delete item;
644 		}
645 	}
646 }
647 
648 void ConfigList::updateMenuList(ConfigList *parent, struct menu* menu)
649 {
650 	struct menu* child;
651 	ConfigItem* item;
652 	ConfigItem* last;
653 	bool visible;
654 	enum prop_type type;
655 
656 	if (!menu) {
657 		while (parent->topLevelItemCount() > 0)
658 		{
659 			delete parent->takeTopLevelItem(0);
660 		}
661 
662 		return;
663 	}
664 
665 	last = (ConfigItem*)parent->topLevelItem(0);
666 	if (last && !last->goParent)
667 		last = 0;
668 	for (child = menu->list; child; child = child->next) {
669 		item = last ? last->nextSibling() : (ConfigItem*)parent->topLevelItem(0);
670 		type = child->prompt ? child->prompt->type : P_UNKNOWN;
671 
672 		switch (mode) {
673 		case menuMode:
674 			if (!(child->flags & MENU_ROOT))
675 				goto hide;
676 			break;
677 		case symbolMode:
678 			if (child->flags & MENU_ROOT)
679 				goto hide;
680 			break;
681 		default:
682 			break;
683 		}
684 
685 		visible = menu_is_visible(child);
686 		if (!menuSkip(child)) {
687 			if (!child->sym && !child->list && !child->prompt)
688 				continue;
689 			if (!item || item->menu != child)
690 				item = new ConfigItem(parent, last, child, visible);
691 			else
692 				item->testUpdateMenu(visible);
693 
694 			if (mode == fullMode || mode == menuMode || type != P_MENU)
695 				updateMenuList(item, child);
696 			else
697 				updateMenuList(item, 0);
698 			last = item;
699 			continue;
700 		}
701 hide:
702 		if (item && item->menu == child) {
703 			last = (ConfigItem*)parent->topLevelItem(0);
704 			if (last == item)
705 				last = 0;
706 			else while (last->nextSibling() != item)
707 				last = last->nextSibling();
708 			delete item;
709 		}
710 	}
711 }
712 
713 void ConfigList::keyPressEvent(QKeyEvent* ev)
714 {
715 	QTreeWidgetItem* i = currentItem();
716 	ConfigItem* item;
717 	struct menu *menu;
718 	enum prop_type type;
719 
720 	if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
721 		emit parentSelected();
722 		ev->accept();
723 		return;
724 	}
725 
726 	if (!i) {
727 		Parent::keyPressEvent(ev);
728 		return;
729 	}
730 	item = (ConfigItem*)i;
731 
732 	switch (ev->key()) {
733 	case Qt::Key_Return:
734 	case Qt::Key_Enter:
735 		if (item->goParent) {
736 			emit parentSelected();
737 			break;
738 		}
739 		menu = item->menu;
740 		if (!menu)
741 			break;
742 		type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
743 		if (type == P_MENU && rootEntry != menu &&
744 		    mode != fullMode && mode != menuMode) {
745 			if (mode == menuMode)
746 				emit menuSelected(menu);
747 			else
748 				emit itemSelected(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();
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) {
856 		if (mode == singleMode)
857 			emit itemSelected(menu);
858 		else if (mode == symbolMode)
859 			emit menuSelected(menu);
860 	} else if (menu->sym)
861 		changeValue(item);
862 
863 skip:
864 	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
865 	Parent::mouseDoubleClickEvent(e);
866 }
867 
868 void ConfigList::focusInEvent(QFocusEvent *e)
869 {
870 	struct menu *menu = NULL;
871 
872 	Parent::focusInEvent(e);
873 
874 	ConfigItem* item = (ConfigItem *)currentItem();
875 	if (item) {
876 		item->setSelected(true);
877 		menu = item->menu;
878 	}
879 	emit gotFocus(menu);
880 }
881 
882 void ConfigList::contextMenuEvent(QContextMenuEvent *e)
883 {
884 	if (e->y() <= header()->geometry().bottom()) {
885 		if (!headerPopup) {
886 			QAction *action;
887 
888 			headerPopup = new QMenu(this);
889 			action = new QAction("Show Name", this);
890 			  action->setCheckable(true);
891 			  connect(action, SIGNAL(toggled(bool)),
892 				  parent(), SLOT(setShowName(bool)));
893 			  connect(parent(), SIGNAL(showNameChanged(bool)),
894 				  action, SLOT(setOn(bool)));
895 			  action->setChecked(showName);
896 			  headerPopup->addAction(action);
897 			action = new QAction("Show Range", this);
898 			  action->setCheckable(true);
899 			  connect(action, SIGNAL(toggled(bool)),
900 				  parent(), SLOT(setShowRange(bool)));
901 			  connect(parent(), SIGNAL(showRangeChanged(bool)),
902 				  action, SLOT(setOn(bool)));
903 			  action->setChecked(showRange);
904 			  headerPopup->addAction(action);
905 			action = new QAction("Show Data", this);
906 			  action->setCheckable(true);
907 			  connect(action, SIGNAL(toggled(bool)),
908 				  parent(), SLOT(setShowData(bool)));
909 			  connect(parent(), SIGNAL(showDataChanged(bool)),
910 				  action, SLOT(setOn(bool)));
911 			  action->setChecked(showData);
912 			  headerPopup->addAction(action);
913 		}
914 		headerPopup->exec(e->globalPos());
915 		e->accept();
916 	} else
917 		e->ignore();
918 }
919 
920 ConfigView*ConfigView::viewList;
921 QAction *ConfigView::showNormalAction;
922 QAction *ConfigView::showAllAction;
923 QAction *ConfigView::showPromptAction;
924 
925 ConfigView::ConfigView(QWidget* parent, const char *name)
926 	: Parent(parent)
927 {
928 	setObjectName(name);
929 	QVBoxLayout *verticalLayout = new QVBoxLayout(this);
930 	verticalLayout->setContentsMargins(0, 0, 0, 0);
931 
932 	list = new ConfigList(this);
933 	verticalLayout->addWidget(list);
934 	lineEdit = new ConfigLineEdit(this);
935 	lineEdit->hide();
936 	verticalLayout->addWidget(lineEdit);
937 
938 	this->nextView = viewList;
939 	viewList = this;
940 }
941 
942 ConfigView::~ConfigView(void)
943 {
944 	ConfigView** vp;
945 
946 	for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
947 		if (*vp == this) {
948 			*vp = nextView;
949 			break;
950 		}
951 	}
952 }
953 
954 void ConfigView::setOptionMode(QAction *act)
955 {
956 	if (act == showNormalAction)
957 		list->optMode = normalOpt;
958 	else if (act == showAllAction)
959 		list->optMode = allOpt;
960 	else
961 		list->optMode = promptOpt;
962 
963 	list->updateListAll();
964 }
965 
966 void ConfigView::setShowName(bool b)
967 {
968 	if (list->showName != b) {
969 		list->showName = b;
970 		list->reinit();
971 		emit showNameChanged(b);
972 	}
973 }
974 
975 void ConfigView::setShowRange(bool b)
976 {
977 	if (list->showRange != b) {
978 		list->showRange = b;
979 		list->reinit();
980 		emit showRangeChanged(b);
981 	}
982 }
983 
984 void ConfigView::setShowData(bool b)
985 {
986 	if (list->showData != b) {
987 		list->showData = b;
988 		list->reinit();
989 		emit showDataChanged(b);
990 	}
991 }
992 
993 void ConfigList::setAllOpen(bool open)
994 {
995 	QTreeWidgetItemIterator it(this);
996 
997 	while (*it) {
998 		(*it)->setExpanded(open);
999 
1000 		++it;
1001 	}
1002 }
1003 
1004 void ConfigView::updateList(ConfigItem* item)
1005 {
1006 	ConfigView* v;
1007 
1008 	for (v = viewList; v; v = v->nextView)
1009 		v->list->updateList(item);
1010 }
1011 
1012 void ConfigView::updateListAll(void)
1013 {
1014 	ConfigView* v;
1015 
1016 	for (v = viewList; v; v = v->nextView)
1017 		v->list->updateListAll();
1018 }
1019 
1020 ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
1021 	: Parent(parent), sym(0), _menu(0)
1022 {
1023 	setObjectName(name);
1024 
1025 
1026 	if (!objectName().isEmpty()) {
1027 		configSettings->beginGroup(objectName());
1028 		setShowDebug(configSettings->value("/showDebug", false).toBool());
1029 		configSettings->endGroup();
1030 		connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1031 	}
1032 }
1033 
1034 void ConfigInfoView::saveSettings(void)
1035 {
1036 	if (!objectName().isEmpty()) {
1037 		configSettings->beginGroup(objectName());
1038 		configSettings->setValue("/showDebug", showDebug());
1039 		configSettings->endGroup();
1040 	}
1041 }
1042 
1043 void ConfigInfoView::setShowDebug(bool b)
1044 {
1045 	if (_showDebug != b) {
1046 		_showDebug = b;
1047 		if (_menu)
1048 			menuInfo();
1049 		else if (sym)
1050 			symbolInfo();
1051 		emit showDebugChanged(b);
1052 	}
1053 }
1054 
1055 void ConfigInfoView::setInfo(struct menu *m)
1056 {
1057 	if (_menu == m)
1058 		return;
1059 	_menu = m;
1060 	sym = NULL;
1061 	if (!_menu)
1062 		clear();
1063 	else
1064 		menuInfo();
1065 }
1066 
1067 void ConfigInfoView::symbolInfo(void)
1068 {
1069 	QString str;
1070 
1071 	str += "<big>Symbol: <b>";
1072 	str += print_filter(sym->name);
1073 	str += "</b></big><br><br>value: ";
1074 	str += print_filter(sym_get_string_value(sym));
1075 	str += "<br>visibility: ";
1076 	str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
1077 	str += "<br>";
1078 	str += debug_info(sym);
1079 
1080 	setText(str);
1081 }
1082 
1083 void ConfigInfoView::menuInfo(void)
1084 {
1085 	struct symbol* sym;
1086 	QString head, debug, help;
1087 
1088 	sym = _menu->sym;
1089 	if (sym) {
1090 		if (_menu->prompt) {
1091 			head += "<big><b>";
1092 			head += print_filter(_menu->prompt->text);
1093 			head += "</b></big>";
1094 			if (sym->name) {
1095 				head += " (";
1096 				if (showDebug())
1097 					head += QString().sprintf("<a href=\"s%p\">", sym);
1098 				head += print_filter(sym->name);
1099 				if (showDebug())
1100 					head += "</a>";
1101 				head += ")";
1102 			}
1103 		} else if (sym->name) {
1104 			head += "<big><b>";
1105 			if (showDebug())
1106 				head += QString().sprintf("<a href=\"s%p\">", sym);
1107 			head += print_filter(sym->name);
1108 			if (showDebug())
1109 				head += "</a>";
1110 			head += "</b></big>";
1111 		}
1112 		head += "<br><br>";
1113 
1114 		if (showDebug())
1115 			debug = debug_info(sym);
1116 
1117 		struct gstr help_gstr = str_new();
1118 		menu_get_ext_help(_menu, &help_gstr);
1119 		help = print_filter(str_get(&help_gstr));
1120 		str_free(&help_gstr);
1121 	} else if (_menu->prompt) {
1122 		head += "<big><b>";
1123 		head += print_filter(_menu->prompt->text);
1124 		head += "</b></big><br><br>";
1125 		if (showDebug()) {
1126 			if (_menu->prompt->visible.expr) {
1127 				debug += "&nbsp;&nbsp;dep: ";
1128 				expr_print(_menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
1129 				debug += "<br><br>";
1130 			}
1131 		}
1132 	}
1133 	if (showDebug())
1134 		debug += QString().sprintf("defined at %s:%d<br><br>", _menu->file->name, _menu->lineno);
1135 
1136 	setText(head + debug + help);
1137 }
1138 
1139 QString ConfigInfoView::debug_info(struct symbol *sym)
1140 {
1141 	QString debug;
1142 
1143 	debug += "type: ";
1144 	debug += print_filter(sym_type_name(sym->type));
1145 	if (sym_is_choice(sym))
1146 		debug += " (choice)";
1147 	debug += "<br>";
1148 	if (sym->rev_dep.expr) {
1149 		debug += "reverse dep: ";
1150 		expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
1151 		debug += "<br>";
1152 	}
1153 	for (struct property *prop = sym->prop; prop; prop = prop->next) {
1154 		switch (prop->type) {
1155 		case P_PROMPT:
1156 		case P_MENU:
1157 			debug += QString().sprintf("prompt: <a href=\"m%p\">", prop->menu);
1158 			debug += print_filter(prop->text);
1159 			debug += "</a><br>";
1160 			break;
1161 		case P_DEFAULT:
1162 		case P_SELECT:
1163 		case P_RANGE:
1164 			debug += prop_get_type_name(prop->type);
1165 			debug += ": ";
1166 			expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1167 			debug += "<br>";
1168 			break;
1169 		case P_CHOICE:
1170 			if (sym_is_choice(sym)) {
1171 				debug += "choice: ";
1172 				expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1173 				debug += "<br>";
1174 			}
1175 			break;
1176 		default:
1177 			debug += "unknown property: ";
1178 			debug += prop_get_type_name(prop->type);
1179 			debug += "<br>";
1180 		}
1181 		if (prop->visible.expr) {
1182 			debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1183 			expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
1184 			debug += "<br>";
1185 		}
1186 	}
1187 	debug += "<br>";
1188 
1189 	return debug;
1190 }
1191 
1192 QString ConfigInfoView::print_filter(const QString &str)
1193 {
1194 	QRegExp re("[<>&\"\\n]");
1195 	QString res = str;
1196 	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1197 		switch (res[i].toLatin1()) {
1198 		case '<':
1199 			res.replace(i, 1, "&lt;");
1200 			i += 4;
1201 			break;
1202 		case '>':
1203 			res.replace(i, 1, "&gt;");
1204 			i += 4;
1205 			break;
1206 		case '&':
1207 			res.replace(i, 1, "&amp;");
1208 			i += 5;
1209 			break;
1210 		case '"':
1211 			res.replace(i, 1, "&quot;");
1212 			i += 6;
1213 			break;
1214 		case '\n':
1215 			res.replace(i, 1, "<br>");
1216 			i += 4;
1217 			break;
1218 		}
1219 	}
1220 	return res;
1221 }
1222 
1223 void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
1224 {
1225 	QString* text = reinterpret_cast<QString*>(data);
1226 	QString str2 = print_filter(str);
1227 
1228 	if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
1229 		*text += QString().sprintf("<a href=\"s%p\">", sym);
1230 		*text += str2;
1231 		*text += "</a>";
1232 	} else
1233 		*text += str2;
1234 }
1235 
1236 QMenu* ConfigInfoView::createStandardContextMenu(const QPoint & pos)
1237 {
1238 	QMenu* popup = Parent::createStandardContextMenu(pos);
1239 	QAction* action = new QAction("Show Debug Info", popup);
1240 
1241 	action->setCheckable(true);
1242 	connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
1243 	connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool)));
1244 	action->setChecked(showDebug());
1245 	popup->addSeparator();
1246 	popup->addAction(action);
1247 	return popup;
1248 }
1249 
1250 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e)
1251 {
1252 	Parent::contextMenuEvent(e);
1253 }
1254 
1255 ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name)
1256 	: Parent(parent), result(NULL)
1257 {
1258 	setObjectName(name);
1259 	setWindowTitle("Search Config");
1260 
1261 	QVBoxLayout* layout1 = new QVBoxLayout(this);
1262 	layout1->setContentsMargins(11, 11, 11, 11);
1263 	layout1->setSpacing(6);
1264 	QHBoxLayout* layout2 = new QHBoxLayout(0);
1265 	layout2->setContentsMargins(0, 0, 0, 0);
1266 	layout2->setSpacing(6);
1267 	layout2->addWidget(new QLabel("Find:", this));
1268 	editField = new QLineEdit(this);
1269 	connect(editField, SIGNAL(returnPressed()), SLOT(search()));
1270 	layout2->addWidget(editField);
1271 	searchButton = new QPushButton("Search", this);
1272 	searchButton->setAutoDefault(false);
1273 	connect(searchButton, SIGNAL(clicked()), SLOT(search()));
1274 	layout2->addWidget(searchButton);
1275 	layout1->addLayout(layout2);
1276 
1277 	split = new QSplitter(this);
1278 	split->setOrientation(Qt::Vertical);
1279 	list = new ConfigView(split, name);
1280 	list->list->mode = listMode;
1281 	info = new ConfigInfoView(split, name);
1282 	connect(list->list, SIGNAL(menuChanged(struct menu *)),
1283 		info, SLOT(setInfo(struct menu *)));
1284 	connect(list->list, SIGNAL(menuChanged(struct menu *)),
1285 		parent, SLOT(setMenuLink(struct menu *)));
1286 
1287 	layout1->addWidget(split);
1288 
1289 	if (name) {
1290 		QVariant x, y;
1291 		int width, height;
1292 		bool ok;
1293 
1294 		configSettings->beginGroup(name);
1295 		width = configSettings->value("/window width", parent->width() / 2).toInt();
1296 		height = configSettings->value("/window height", parent->height() / 2).toInt();
1297 		resize(width, height);
1298 		x = configSettings->value("/window x");
1299 		y = configSettings->value("/window y");
1300 		if ((x.isValid())&&(y.isValid()))
1301 			move(x.toInt(), y.toInt());
1302 		QList<int> sizes = configSettings->readSizes("/split", &ok);
1303 		if (ok)
1304 			split->setSizes(sizes);
1305 		configSettings->endGroup();
1306 		connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1307 	}
1308 }
1309 
1310 void ConfigSearchWindow::saveSettings(void)
1311 {
1312 	if (!objectName().isEmpty()) {
1313 		configSettings->beginGroup(objectName());
1314 		configSettings->setValue("/window x", pos().x());
1315 		configSettings->setValue("/window y", pos().y());
1316 		configSettings->setValue("/window width", size().width());
1317 		configSettings->setValue("/window height", size().height());
1318 		configSettings->writeSizes("/split", split->sizes());
1319 		configSettings->endGroup();
1320 	}
1321 }
1322 
1323 void ConfigSearchWindow::search(void)
1324 {
1325 	struct symbol **p;
1326 	struct property *prop;
1327 	ConfigItem *lastItem = NULL;
1328 
1329 	free(result);
1330 	list->list->clear();
1331 	info->clear();
1332 
1333 	result = sym_re_search(editField->text().toLatin1());
1334 	if (!result)
1335 		return;
1336 	for (p = result; *p; p++) {
1337 		for_all_prompts((*p), prop)
1338 			lastItem = new ConfigItem(list->list, lastItem, prop->menu,
1339 						  menu_is_visible(prop->menu));
1340 	}
1341 }
1342 
1343 /*
1344  * Construct the complete config widget
1345  */
1346 ConfigMainWindow::ConfigMainWindow(void)
1347 	: searchWindow(0)
1348 {
1349 	QMenuBar* menu;
1350 	bool ok = true;
1351 	QVariant x, y;
1352 	int width, height;
1353 	char title[256];
1354 
1355 	QDesktopWidget *d = configApp->desktop();
1356 	snprintf(title, sizeof(title), "%s%s",
1357 		rootmenu.prompt->text,
1358 		""
1359 		);
1360 	setWindowTitle(title);
1361 
1362 	width = configSettings->value("/window width", d->width() - 64).toInt();
1363 	height = configSettings->value("/window height", d->height() - 64).toInt();
1364 	resize(width, height);
1365 	x = configSettings->value("/window x");
1366 	y = configSettings->value("/window y");
1367 	if ((x.isValid())&&(y.isValid()))
1368 		move(x.toInt(), y.toInt());
1369 
1370 	QWidget *widget = new QWidget(this);
1371 	QVBoxLayout *layout = new QVBoxLayout(widget);
1372 	setCentralWidget(widget);
1373 
1374 	split1 = new QSplitter(widget);
1375 	split1->setOrientation(Qt::Horizontal);
1376 	split1->setChildrenCollapsible(false);
1377 
1378 	menuView = new ConfigView(widget, "menu");
1379 	menuList = menuView->list;
1380 
1381 	split2 = new QSplitter(widget);
1382 	split2->setChildrenCollapsible(false);
1383 	split2->setOrientation(Qt::Vertical);
1384 
1385 	// create config tree
1386 	configView = new ConfigView(widget, "config");
1387 	configList = configView->list;
1388 
1389 	helpText = new ConfigInfoView(widget, "help");
1390 
1391 	layout->addWidget(split2);
1392 	split2->addWidget(split1);
1393 	split1->addWidget(configView);
1394 	split1->addWidget(menuView);
1395 	split2->addWidget(helpText);
1396 
1397 	setTabOrder(configList, helpText);
1398 	configList->setFocus();
1399 
1400 	menu = menuBar();
1401 	toolBar = new QToolBar("Tools", this);
1402 	addToolBar(toolBar);
1403 
1404 	backAction = new QAction(QPixmap(xpm_back), "Back", this);
1405 	  connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack()));
1406 	  backAction->setEnabled(false);
1407 	QAction *quitAction = new QAction("&Quit", this);
1408 	quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1409 	  connect(quitAction, SIGNAL(triggered(bool)), SLOT(close()));
1410 	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1411 	loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
1412 	  connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig()));
1413 	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1414 	saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
1415 	  connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig()));
1416 	conf_set_changed_callback(conf_changed);
1417 	// Set saveAction's initial state
1418 	conf_changed();
1419 	configname = xstrdup(conf_get_configname());
1420 
1421 	QAction *saveAsAction = new QAction("Save &As...", this);
1422 	  connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs()));
1423 	QAction *searchAction = new QAction("&Find", this);
1424 	searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
1425 	  connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig()));
1426 	singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1427 	singleViewAction->setCheckable(true);
1428 	  connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView()));
1429 	splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1430 	splitViewAction->setCheckable(true);
1431 	  connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView()));
1432 	fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1433 	fullViewAction->setCheckable(true);
1434 	  connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView()));
1435 
1436 	QAction *showNameAction = new QAction("Show Name", this);
1437 	  showNameAction->setCheckable(true);
1438 	  connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
1439 	  showNameAction->setChecked(configView->showName());
1440 	QAction *showRangeAction = new QAction("Show Range", this);
1441 	  showRangeAction->setCheckable(true);
1442 	  connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
1443 	QAction *showDataAction = new QAction("Show Data", this);
1444 	  showDataAction->setCheckable(true);
1445 	  connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
1446 
1447 	QActionGroup *optGroup = new QActionGroup(this);
1448 	optGroup->setExclusive(true);
1449 	connect(optGroup, SIGNAL(triggered(QAction*)), configView,
1450 		SLOT(setOptionMode(QAction *)));
1451 	connect(optGroup, SIGNAL(triggered(QAction *)), menuView,
1452 		SLOT(setOptionMode(QAction *)));
1453 
1454 	configView->showNormalAction = new QAction("Show Normal Options", optGroup);
1455 	configView->showAllAction = new QAction("Show All Options", optGroup);
1456 	configView->showPromptAction = new QAction("Show Prompt Options", optGroup);
1457 	configView->showNormalAction->setCheckable(true);
1458 	configView->showAllAction->setCheckable(true);
1459 	configView->showPromptAction->setCheckable(true);
1460 
1461 	QAction *showDebugAction = new QAction("Show Debug Info", this);
1462 	  showDebugAction->setCheckable(true);
1463 	  connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
1464 	  showDebugAction->setChecked(helpText->showDebug());
1465 
1466 	QAction *showIntroAction = new QAction("Introduction", this);
1467 	  connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro()));
1468 	QAction *showAboutAction = new QAction("About", this);
1469 	  connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout()));
1470 
1471 	// init tool bar
1472 	toolBar->addAction(backAction);
1473 	toolBar->addSeparator();
1474 	toolBar->addAction(loadAction);
1475 	toolBar->addAction(saveAction);
1476 	toolBar->addSeparator();
1477 	toolBar->addAction(singleViewAction);
1478 	toolBar->addAction(splitViewAction);
1479 	toolBar->addAction(fullViewAction);
1480 
1481 	// create config menu
1482 	QMenu* config = menu->addMenu("&File");
1483 	config->addAction(loadAction);
1484 	config->addAction(saveAction);
1485 	config->addAction(saveAsAction);
1486 	config->addSeparator();
1487 	config->addAction(quitAction);
1488 
1489 	// create edit menu
1490 	QMenu* editMenu = menu->addMenu("&Edit");
1491 	editMenu->addAction(searchAction);
1492 
1493 	// create options menu
1494 	QMenu* optionMenu = menu->addMenu("&Option");
1495 	optionMenu->addAction(showNameAction);
1496 	optionMenu->addAction(showRangeAction);
1497 	optionMenu->addAction(showDataAction);
1498 	optionMenu->addSeparator();
1499 	optionMenu->addActions(optGroup->actions());
1500 	optionMenu->addSeparator();
1501 	optionMenu->addAction(showDebugAction);
1502 
1503 	// create help menu
1504 	menu->addSeparator();
1505 	QMenu* helpMenu = menu->addMenu("&Help");
1506 	helpMenu->addAction(showIntroAction);
1507 	helpMenu->addAction(showAboutAction);
1508 
1509 	connect(configList, SIGNAL(menuChanged(struct menu *)),
1510 		helpText, SLOT(setInfo(struct menu *)));
1511 	connect(configList, SIGNAL(menuSelected(struct menu *)),
1512 		SLOT(changeMenu(struct menu *)));
1513 	connect(configList, SIGNAL(itemSelected(struct menu *)),
1514 		SLOT(changeItens(struct menu *)));
1515 	connect(configList, SIGNAL(parentSelected()),
1516 		SLOT(goBack()));
1517 	connect(menuList, SIGNAL(menuChanged(struct menu *)),
1518 		helpText, SLOT(setInfo(struct menu *)));
1519 	connect(menuList, SIGNAL(menuSelected(struct menu *)),
1520 		SLOT(changeMenu(struct menu *)));
1521 
1522 	connect(configList, SIGNAL(gotFocus(struct menu *)),
1523 		helpText, SLOT(setInfo(struct menu *)));
1524 	connect(menuList, SIGNAL(gotFocus(struct menu *)),
1525 		helpText, SLOT(setInfo(struct menu *)));
1526 	connect(menuList, SIGNAL(gotFocus(struct menu *)),
1527 		SLOT(listFocusChanged(void)));
1528 	connect(helpText, SIGNAL(menuSelected(struct menu *)),
1529 		SLOT(setMenuLink(struct menu *)));
1530 
1531 	QString listMode = configSettings->value("/listMode", "symbol").toString();
1532 	if (listMode == "single")
1533 		showSingleView();
1534 	else if (listMode == "full")
1535 		showFullView();
1536 	else /*if (listMode == "split")*/
1537 		showSplitView();
1538 
1539 	// UI setup done, restore splitter positions
1540 	QList<int> sizes = configSettings->readSizes("/split1", &ok);
1541 	if (ok)
1542 		split1->setSizes(sizes);
1543 
1544 	sizes = configSettings->readSizes("/split2", &ok);
1545 	if (ok)
1546 		split2->setSizes(sizes);
1547 }
1548 
1549 void ConfigMainWindow::loadConfig(void)
1550 {
1551 	QString str;
1552 	QByteArray ba;
1553 	const char *name;
1554 
1555 	str = QFileDialog::getOpenFileName(this, "", configname);
1556 	if (str.isNull())
1557 		return;
1558 
1559 	ba = str.toLocal8Bit();
1560 	name = ba.data();
1561 
1562 	if (conf_read(name))
1563 		QMessageBox::information(this, "qconf", "Unable to load configuration!");
1564 
1565 	free(configname);
1566 	configname = xstrdup(name);
1567 
1568 	ConfigView::updateListAll();
1569 }
1570 
1571 bool ConfigMainWindow::saveConfig(void)
1572 {
1573 	if (conf_write(configname)) {
1574 		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1575 		return false;
1576 	}
1577 	conf_write_autoconf(0);
1578 
1579 	return true;
1580 }
1581 
1582 void ConfigMainWindow::saveConfigAs(void)
1583 {
1584 	QString str;
1585 	QByteArray ba;
1586 	const char *name;
1587 
1588 	str = QFileDialog::getSaveFileName(this, "", configname);
1589 	if (str.isNull())
1590 		return;
1591 
1592 	ba = str.toLocal8Bit();
1593 	name = ba.data();
1594 
1595 	if (conf_write(name)) {
1596 		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1597 	}
1598 	conf_write_autoconf(0);
1599 
1600 	free(configname);
1601 	configname = xstrdup(name);
1602 }
1603 
1604 void ConfigMainWindow::searchConfig(void)
1605 {
1606 	if (!searchWindow)
1607 		searchWindow = new ConfigSearchWindow(this, "search");
1608 	searchWindow->show();
1609 }
1610 
1611 void ConfigMainWindow::changeItens(struct menu *menu)
1612 {
1613 	configList->setRootMenu(menu);
1614 
1615 	if (configList->rootEntry->parent == &rootmenu)
1616 		backAction->setEnabled(false);
1617 	else
1618 		backAction->setEnabled(true);
1619 }
1620 
1621 void ConfigMainWindow::changeMenu(struct menu *menu)
1622 {
1623 	menuList->setRootMenu(menu);
1624 
1625 	if (menuList->rootEntry->parent == &rootmenu)
1626 		backAction->setEnabled(false);
1627 	else
1628 		backAction->setEnabled(true);
1629 }
1630 
1631 void ConfigMainWindow::setMenuLink(struct menu *menu)
1632 {
1633 	struct menu *parent;
1634 	ConfigList* list = NULL;
1635 	ConfigItem* item;
1636 
1637 	if (configList->menuSkip(menu))
1638 		return;
1639 
1640 	switch (configList->mode) {
1641 	case singleMode:
1642 		list = configList;
1643 		parent = menu_get_parent_menu(menu);
1644 		if (!parent)
1645 			return;
1646 		list->setRootMenu(parent);
1647 		break;
1648 	case symbolMode:
1649 		if (menu->flags & MENU_ROOT) {
1650 			configList->setRootMenu(menu);
1651 			configList->clearSelection();
1652 			list = menuList;
1653 		} else {
1654 			list = configList;
1655 			parent = menu_get_parent_menu(menu->parent);
1656 			if (!parent)
1657 				return;
1658 			item = menuList->findConfigItem(parent);
1659 			if (item) {
1660 				item->setSelected(true);
1661 				menuList->scrollToItem(item);
1662 			}
1663 			list->setRootMenu(parent);
1664 		}
1665 		break;
1666 	case fullMode:
1667 		list = configList;
1668 		break;
1669 	default:
1670 		break;
1671 	}
1672 
1673 	if (list) {
1674 		item = list->findConfigItem(menu);
1675 		if (item) {
1676 			item->setSelected(true);
1677 			list->scrollToItem(item);
1678 			list->setFocus();
1679 		}
1680 	}
1681 }
1682 
1683 void ConfigMainWindow::listFocusChanged(void)
1684 {
1685 	if (menuList->mode == menuMode)
1686 		configList->clearSelection();
1687 }
1688 
1689 void ConfigMainWindow::goBack(void)
1690 {
1691 	ConfigItem* item, *oldSelection;
1692 
1693 	configList->setParentMenu();
1694 	if (configList->rootEntry == &rootmenu)
1695 		backAction->setEnabled(false);
1696 
1697 	if (menuList->selectedItems().count() == 0)
1698 		return;
1699 
1700 	item = (ConfigItem*)menuList->selectedItems().first();
1701 	oldSelection = item;
1702 	while (item) {
1703 		if (item->menu == configList->rootEntry) {
1704 			oldSelection->setSelected(false);
1705 			item->setSelected(true);
1706 			break;
1707 		}
1708 		item = (ConfigItem*)item->parent();
1709 	}
1710 }
1711 
1712 void ConfigMainWindow::showSingleView(void)
1713 {
1714 	singleViewAction->setEnabled(false);
1715 	singleViewAction->setChecked(true);
1716 	splitViewAction->setEnabled(true);
1717 	splitViewAction->setChecked(false);
1718 	fullViewAction->setEnabled(true);
1719 	fullViewAction->setChecked(false);
1720 
1721 	menuView->hide();
1722 	menuList->setRootMenu(0);
1723 	configList->mode = singleMode;
1724 	if (configList->rootEntry == &rootmenu)
1725 		configList->updateListAll();
1726 	else
1727 		configList->setRootMenu(&rootmenu);
1728 	configList->setFocus();
1729 }
1730 
1731 void ConfigMainWindow::showSplitView(void)
1732 {
1733 	singleViewAction->setEnabled(true);
1734 	singleViewAction->setChecked(false);
1735 	splitViewAction->setEnabled(false);
1736 	splitViewAction->setChecked(true);
1737 	fullViewAction->setEnabled(true);
1738 	fullViewAction->setChecked(false);
1739 
1740 	configList->mode = menuMode;
1741 	if (configList->rootEntry == &rootmenu)
1742 		configList->updateListAll();
1743 	else
1744 		configList->setRootMenu(&rootmenu);
1745 	configList->setAllOpen(true);
1746 	configApp->processEvents();
1747 	menuList->mode = symbolMode;
1748 	menuList->setRootMenu(&rootmenu);
1749 	menuList->setAllOpen(true);
1750 	menuView->show();
1751 	menuList->setFocus();
1752 }
1753 
1754 void ConfigMainWindow::showFullView(void)
1755 {
1756 	singleViewAction->setEnabled(true);
1757 	singleViewAction->setChecked(false);
1758 	splitViewAction->setEnabled(true);
1759 	splitViewAction->setChecked(false);
1760 	fullViewAction->setEnabled(false);
1761 	fullViewAction->setChecked(true);
1762 
1763 	menuView->hide();
1764 	menuList->setRootMenu(0);
1765 	configList->mode = fullMode;
1766 	if (configList->rootEntry == &rootmenu)
1767 		configList->updateListAll();
1768 	else
1769 		configList->setRootMenu(&rootmenu);
1770 	configList->setFocus();
1771 }
1772 
1773 /*
1774  * ask for saving configuration before quitting
1775  */
1776 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1777 {
1778 	if (!conf_get_changed()) {
1779 		e->accept();
1780 		return;
1781 	}
1782 	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1783 			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1784 	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1785 	mb.setButtonText(QMessageBox::No, "&Discard Changes");
1786 	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1787 	switch (mb.exec()) {
1788 	case QMessageBox::Yes:
1789 		if (saveConfig())
1790 			e->accept();
1791 		else
1792 			e->ignore();
1793 		break;
1794 	case QMessageBox::No:
1795 		e->accept();
1796 		break;
1797 	case QMessageBox::Cancel:
1798 		e->ignore();
1799 		break;
1800 	}
1801 }
1802 
1803 void ConfigMainWindow::showIntro(void)
1804 {
1805 	static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
1806 		"For each option, a blank box indicates the feature is disabled, a check\n"
1807 		"indicates it is enabled, and a dot indicates that it is to be compiled\n"
1808 		"as a module.  Clicking on the box will cycle through the three states.\n\n"
1809 		"If you do not see an option (e.g., a device driver) that you believe\n"
1810 		"should be present, try turning on Show All Options under the Options menu.\n"
1811 		"Although there is no cross reference yet to help you figure out what other\n"
1812 		"options must be enabled to support the option you are interested in, you can\n"
1813 		"still view the help of a grayed-out option.\n\n"
1814 		"Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1815 		"which you can then match by examining other options.\n\n";
1816 
1817 	QMessageBox::information(this, "qconf", str);
1818 }
1819 
1820 void ConfigMainWindow::showAbout(void)
1821 {
1822 	static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1823 		"Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
1824 		"Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1825 
1826 	QMessageBox::information(this, "qconf", str);
1827 }
1828 
1829 void ConfigMainWindow::saveSettings(void)
1830 {
1831 	configSettings->setValue("/window x", pos().x());
1832 	configSettings->setValue("/window y", pos().y());
1833 	configSettings->setValue("/window width", size().width());
1834 	configSettings->setValue("/window height", size().height());
1835 
1836 	QString entry;
1837 	switch(configList->mode) {
1838 	case singleMode :
1839 		entry = "single";
1840 		break;
1841 
1842 	case symbolMode :
1843 		entry = "split";
1844 		break;
1845 
1846 	case fullMode :
1847 		entry = "full";
1848 		break;
1849 
1850 	default:
1851 		break;
1852 	}
1853 	configSettings->setValue("/listMode", entry);
1854 
1855 	configSettings->writeSizes("/split1", split1->sizes());
1856 	configSettings->writeSizes("/split2", split2->sizes());
1857 }
1858 
1859 void ConfigMainWindow::conf_changed(void)
1860 {
1861 	if (saveAction)
1862 		saveAction->setEnabled(conf_get_changed());
1863 }
1864 
1865 void fixup_rootmenu(struct menu *menu)
1866 {
1867 	struct menu *child;
1868 	static int menu_cnt = 0;
1869 
1870 	menu->flags |= MENU_ROOT;
1871 	for (child = menu->list; child; child = child->next) {
1872 		if (child->prompt && child->prompt->type == P_MENU) {
1873 			menu_cnt++;
1874 			fixup_rootmenu(child);
1875 			menu_cnt--;
1876 		} else if (!menu_cnt)
1877 			fixup_rootmenu(child);
1878 	}
1879 }
1880 
1881 static const char *progname;
1882 
1883 static void usage(void)
1884 {
1885 	printf("%s [-s] <config>\n", progname);
1886 	exit(0);
1887 }
1888 
1889 int main(int ac, char** av)
1890 {
1891 	ConfigMainWindow* v;
1892 	const char *name;
1893 
1894 	progname = av[0];
1895 	configApp = new QApplication(ac, av);
1896 	if (ac > 1 && av[1][0] == '-') {
1897 		switch (av[1][1]) {
1898 		case 's':
1899 			conf_set_message_callback(NULL);
1900 			break;
1901 		case 'h':
1902 		case '?':
1903 			usage();
1904 		}
1905 		name = av[2];
1906 	} else
1907 		name = av[1];
1908 	if (!name)
1909 		usage();
1910 
1911 	conf_parse(name);
1912 	fixup_rootmenu(&rootmenu);
1913 	conf_read(NULL);
1914 	//zconfdump(stdout);
1915 
1916 	configSettings = new ConfigSettings();
1917 	configSettings->beginGroup("/kconfig/qconf");
1918 	v = new ConfigMainWindow();
1919 
1920 	//zconfdump(stdout);
1921 	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1922 	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1923 	v->show();
1924 	configApp->exec();
1925 
1926 	configSettings->endGroup();
1927 	delete configSettings;
1928 	delete v;
1929 	delete configApp;
1930 
1931 	return 0;
1932 }
1933