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