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