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