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