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