cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qconf.cc (44449B)


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