Linux Audio

Check our new training course

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