Linux Audio

Check our new training course

Loading...
v5.14.15
   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}
v6.2
   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}