1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <qapplication.h>
7#include <qmainwindow.h>
8#include <qtoolbar.h>
9#include <qvbox.h>
10#include <qsplitter.h>
11#include <qlistview.h>
12#include <qtextview.h>
13#include <qlineedit.h>
14#include <qmenubar.h>
15#include <qmessagebox.h>
16#include <qaction.h>
17#include <qheader.h>
18#include <qfiledialog.h>
19#include <qregexp.h>
20
21#include <stdlib.h>
22
23#include "lkc.h"
24#include "qconf.h"
25
26#include "qconf.moc"
27#include "images.c"
28
29#ifdef _
30# undef _
31# define _ qgettext
32#endif
33
34static QApplication *configApp;
35
36static inline QString qgettext(const char* str)
37{
38  return QString::fromLocal8Bit(gettext(str));
39}
40
41static inline QString qgettext(const QString& str)
42{
43  return QString::fromLocal8Bit(gettext(str.latin1()));
44}
45
46ConfigSettings::ConfigSettings()
47	: showAll(false), showName(false), showRange(false), showData(false)
48{
49}
50
51#if QT_VERSION >= 300
52/**
53 * Reads the list column settings from the application settings.
54 */
55void ConfigSettings::readListSettings()
56{
57	showAll = readBoolEntry("/kconfig/qconf/showAll", false);
58	showName = readBoolEntry("/kconfig/qconf/showName", false);
59	showRange = readBoolEntry("/kconfig/qconf/showRange", false);
60	showData = readBoolEntry("/kconfig/qconf/showData", false);
61}
62
63/**
64 * Reads a list of integer values from the application settings.
65 */
66QValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
67{
68	QValueList<int> result;
69	QStringList entryList = readListEntry(key, ok);
70	if (ok) {
71		QStringList::Iterator it;
72		for (it = entryList.begin(); it != entryList.end(); ++it)
73			result.push_back((*it).toInt());
74	}
75
76	return result;
77}
78
79/**
80 * Writes a list of integer values to the application settings.
81 */
82bool ConfigSettings::writeSizes(const QString& key, const QValueList<int>& value)
83{
84	QStringList stringList;
85	QValueList<int>::ConstIterator it;
86
87	for (it = value.begin(); it != value.end(); ++it)
88		stringList.push_back(QString::number(*it));
89	return writeEntry(key, stringList);
90}
91#endif
92
93
94/*
95 * update all the children of a menu entry
96 *   removes/adds the entries from the parent widget as necessary
97 *
98 * parent: either the menu list widget or a menu entry widget
99 * menu: entry to be updated
100 */
101template <class P>
102void ConfigList::updateMenuList(P* parent, struct menu* menu)
103{
104	struct menu* child;
105	ConfigItem* item;
106	ConfigItem* last;
107	bool visible;
108	enum prop_type type;
109
110	if (!menu) {
111		while ((item = parent->firstChild()))
112			delete item;
113		return;
114	}
115
116	last = parent->firstChild();
117	if (last && !last->goParent)
118		last = 0;
119	for (child = menu->list; child; child = child->next) {
120		item = last ? last->nextSibling() : parent->firstChild();
121		type = child->prompt ? child->prompt->type : P_UNKNOWN;
122
123		switch (mode) {
124		case menuMode:
125			if (!(child->flags & MENU_ROOT))
126				goto hide;
127			break;
128		case symbolMode:
129			if (child->flags & MENU_ROOT)
130				goto hide;
131			break;
132		default:
133			break;
134		}
135
136		visible = menu_is_visible(child);
137		if (showAll || visible) {
138			if (!item || item->menu != child)
139				item = new ConfigItem(parent, last, child, visible);
140			else
141				item->testUpdateMenu(visible);
142
143			if (mode == fullMode || mode == menuMode || type != P_MENU)
144				updateMenuList(item, child);
145			else
146				updateMenuList(item, 0);
147			last = item;
148			continue;
149		}
150	hide:
151		if (item && item->menu == child) {
152			last = parent->firstChild();
153			if (last == item)
154				last = 0;
155			else while (last->nextSibling() != item)
156				last = last->nextSibling();
157			delete item;
158		}
159	}
160}
161
162#if QT_VERSION >= 300
163/*
164 * set the new data
165 * TODO check the value
166 */
167void ConfigItem::okRename(int col)
168{
169	Parent::okRename(col);
170	sym_set_string_value(menu->sym, text(dataColIdx).latin1());
171}
172#endif
173
174/*
175 * update the displayed of a menu entry
176 */
177void ConfigItem::updateMenu(void)
178{
179	ConfigList* list;
180	struct symbol* sym;
181	struct property *prop;
182	QString prompt;
183	int type;
184	tristate expr;
185
186	list = listView();
187	if (goParent) {
188		setPixmap(promptColIdx, list->menuBackPix);
189		prompt = "..";
190		goto set_prompt;
191	}
192
193	sym = menu->sym;
194	prop = menu->prompt;
195	prompt = QString::fromLocal8Bit(menu_get_prompt(menu));
196
197	if (prop) switch (prop->type) {
198	case P_MENU:
199		if (list->mode == singleMode || list->mode == symbolMode) {
200			/* a menuconfig entry is displayed differently
201			 * depending whether it's at the view root or a child.
202			 */
203			if (sym && list->rootEntry == menu)
204				break;
205			setPixmap(promptColIdx, list->menuPix);
206		} else {
207			if (sym)
208				break;
209			setPixmap(promptColIdx, 0);
210		}
211		goto set_prompt;
212	case P_COMMENT:
213		setPixmap(promptColIdx, 0);
214		goto set_prompt;
215	default:
216		;
217	}
218	if (!sym)
219		goto set_prompt;
220
221	setText(nameColIdx, QString::fromLocal8Bit(sym->name));
222
223	type = sym_get_type(sym);
224	switch (type) {
225	case S_BOOLEAN:
226	case S_TRISTATE:
227		char ch;
228
229		if (!sym_is_changable(sym) && !list->showAll) {
230			setPixmap(promptColIdx, 0);
231			setText(noColIdx, QString::null);
232			setText(modColIdx, QString::null);
233			setText(yesColIdx, QString::null);
234			break;
235		}
236		expr = sym_get_tristate_value(sym);
237		switch (expr) {
238		case yes:
239			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
240				setPixmap(promptColIdx, list->choiceYesPix);
241			else
242				setPixmap(promptColIdx, list->symbolYesPix);
243			setText(yesColIdx, "Y");
244			ch = 'Y';
245			break;
246		case mod:
247			setPixmap(promptColIdx, list->symbolModPix);
248			setText(modColIdx, "M");
249			ch = 'M';
250			break;
251		default:
252			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
253				setPixmap(promptColIdx, list->choiceNoPix);
254			else
255				setPixmap(promptColIdx, list->symbolNoPix);
256			setText(noColIdx, "N");
257			ch = 'N';
258			break;
259		}
260		if (expr != no)
261			setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0);
262		if (expr != mod)
263			setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0);
264		if (expr != yes)
265			setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0);
266
267		setText(dataColIdx, QChar(ch));
268		break;
269	case S_INT:
270	case S_HEX:
271	case S_STRING:
272		const char* data;
273
274		data = sym_get_string_value(sym);
275
276#if QT_VERSION >= 300
277		int i = list->mapIdx(dataColIdx);
278		if (i >= 0)
279			setRenameEnabled(i, TRUE);
280#endif
281		setText(dataColIdx, data);
282		if (type == S_STRING)
283			prompt = QString("%1: %2").arg(prompt).arg(data);
284		else
285			prompt = QString("(%2) %1").arg(prompt).arg(data);
286		break;
287	}
288	if (!sym_has_value(sym) && visible)
289		prompt += " (NEW)";
290set_prompt:
291	setText(promptColIdx, prompt);
292}
293
294void ConfigItem::testUpdateMenu(bool v)
295{
296	ConfigItem* i;
297
298	visible = v;
299	if (!menu)
300		return;
301
302	sym_calc_value(menu->sym);
303	if (menu->flags & MENU_CHANGED) {
304		/* the menu entry changed, so update all list items */
305		menu->flags &= ~MENU_CHANGED;
306		for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
307			i->updateMenu();
308	} else if (listView()->updateAll)
309		updateMenu();
310}
311
312void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
313{
314	ConfigList* list = listView();
315
316	if (visible) {
317		if (isSelected() && !list->hasFocus() && list->mode == menuMode)
318			Parent::paintCell(p, list->inactivedColorGroup, column, width, align);
319		else
320			Parent::paintCell(p, cg, column, width, align);
321	} else
322		Parent::paintCell(p, list->disabledColorGroup, column, width, align);
323}
324
325/*
326 * construct a menu entry
327 */
328void ConfigItem::init(void)
329{
330	if (menu) {
331		ConfigList* list = listView();
332		nextItem = (ConfigItem*)menu->data;
333		menu->data = this;
334
335		if (list->mode != fullMode)
336			setOpen(TRUE);
337		sym_calc_value(menu->sym);
338	}
339	updateMenu();
340}
341
342/*
343 * destruct a menu entry
344 */
345ConfigItem::~ConfigItem(void)
346{
347	if (menu) {
348		ConfigItem** ip = (ConfigItem**)&menu->data;
349		for (; *ip; ip = &(*ip)->nextItem) {
350			if (*ip == this) {
351				*ip = nextItem;
352				break;
353			}
354		}
355	}
356}
357
358void ConfigLineEdit::show(ConfigItem* i)
359{
360	item = i;
361	if (sym_get_string_value(item->menu->sym))
362		setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym)));
363	else
364		setText(QString::null);
365	Parent::show();
366	setFocus();
367}
368
369void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
370{
371	switch (e->key()) {
372	case Key_Escape:
373		break;
374	case Key_Return:
375	case Key_Enter:
376		sym_set_string_value(item->menu->sym, text().latin1());
377		parent()->updateList(item);
378		break;
379	default:
380		Parent::keyPressEvent(e);
381		return;
382	}
383	e->accept();
384	parent()->list->setFocus();
385	hide();
386}
387
388ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings)
389	: Parent(p), cview(cv),
390	  updateAll(false),
391	  symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no),
392	  choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no),
393	  menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void),
394	  showAll(false), showName(false), showRange(false), showData(false),
395	  rootEntry(0)
396{
397	int i;
398
399	setSorting(-1);
400	setRootIsDecorated(TRUE);
401	disabledColorGroup = palette().active();
402	disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text());
403	inactivedColorGroup = palette().active();
404	inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight());
405
406	connect(this, SIGNAL(selectionChanged(void)),
407		SLOT(updateSelection(void)));
408
409	if (configSettings) {
410		showAll = configSettings->showAll;
411		showName = configSettings->showName;
412		showRange = configSettings->showRange;
413		showData = configSettings->showData;
414	}
415
416	for (i = 0; i < colNr; i++)
417		colMap[i] = colRevMap[i] = -1;
418	addColumn(promptColIdx, "Option");
419
420	reinit();
421}
422
423void ConfigList::reinit(void)
424{
425	removeColumn(dataColIdx);
426	removeColumn(yesColIdx);
427	removeColumn(modColIdx);
428	removeColumn(noColIdx);
429	removeColumn(nameColIdx);
430
431	if (showName)
432		addColumn(nameColIdx, "Name");
433	if (showRange) {
434		addColumn(noColIdx, "N");
435		addColumn(modColIdx, "M");
436		addColumn(yesColIdx, "Y");
437	}
438	if (showData)
439		addColumn(dataColIdx, "Value");
440
441	updateListAll();
442}
443
444void ConfigList::updateSelection(void)
445{
446	struct menu *menu;
447	enum prop_type type;
448
449	ConfigItem* item = (ConfigItem*)selectedItem();
450	if (!item)
451		return;
452
453	cview->setHelp(item);
454
455	menu = item->menu;
456	if (!menu)
457		return;
458	type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
459	if (mode == menuMode && type == P_MENU)
460		emit menuSelected(menu);
461}
462
463void ConfigList::updateList(ConfigItem* item)
464{
465	ConfigItem* last = 0;
466
467	if (!rootEntry)
468		goto update;
469
470	if (rootEntry != &rootmenu && (mode == singleMode ||
471	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
472		item = firstChild();
473		if (!item)
474			item = new ConfigItem(this, 0, true);
475		last = item;
476	}
477	if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
478	    rootEntry->sym && rootEntry->prompt) {
479		item = last ? last->nextSibling() : firstChild();
480		if (!item)
481			item = new ConfigItem(this, last, rootEntry, true);
482		else
483			item->testUpdateMenu(true);
484
485		updateMenuList(item, rootEntry);
486		triggerUpdate();
487		return;
488	}
489update:
490	updateMenuList(this, rootEntry);
491	triggerUpdate();
492}
493
494void ConfigList::setAllOpen(bool open)
495{
496	QListViewItemIterator it(this);
497
498	for (; it.current(); it++)
499		it.current()->setOpen(open);
500}
501
502void ConfigList::setValue(ConfigItem* item, tristate val)
503{
504	struct symbol* sym;
505	int type;
506	tristate oldval;
507
508	sym = item->menu ? item->menu->sym : 0;
509	if (!sym)
510		return;
511
512	type = sym_get_type(sym);
513	switch (type) {
514	case S_BOOLEAN:
515	case S_TRISTATE:
516		oldval = sym_get_tristate_value(sym);
517
518		if (!sym_set_tristate_value(sym, val))
519			return;
520		if (oldval == no && item->menu->list)
521			item->setOpen(TRUE);
522		parent()->updateList(item);
523		break;
524	}
525}
526
527void ConfigList::changeValue(ConfigItem* item)
528{
529	struct symbol* sym;
530	struct menu* menu;
531	int type, oldexpr, newexpr;
532
533	menu = item->menu;
534	if (!menu)
535		return;
536	sym = menu->sym;
537	if (!sym) {
538		if (item->menu->list)
539			item->setOpen(!item->isOpen());
540		return;
541	}
542
543	type = sym_get_type(sym);
544	switch (type) {
545	case S_BOOLEAN:
546	case S_TRISTATE:
547		oldexpr = sym_get_tristate_value(sym);
548		newexpr = sym_toggle_tristate_value(sym);
549		if (item->menu->list) {
550			if (oldexpr == newexpr)
551				item->setOpen(!item->isOpen());
552			else if (oldexpr == no)
553				item->setOpen(TRUE);
554		}
555		if (oldexpr != newexpr)
556			parent()->updateList(item);
557		break;
558	case S_INT:
559	case S_HEX:
560	case S_STRING:
561#if QT_VERSION >= 300
562		if (colMap[dataColIdx] >= 0)
563			item->startRename(colMap[dataColIdx]);
564		else
565#endif
566			parent()->lineEdit->show(item);
567		break;
568	}
569}
570
571void ConfigList::setRootMenu(struct menu *menu)
572{
573	enum prop_type type;
574
575	if (rootEntry == menu)
576		return;
577	type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
578	if (type != P_MENU)
579		return;
580	updateMenuList(this, 0);
581	rootEntry = menu;
582	updateListAll();
583	setSelected(currentItem(), hasFocus());
584}
585
586void ConfigList::setParentMenu(void)
587{
588	ConfigItem* item;
589	struct menu *oldroot;
590
591	oldroot = rootEntry;
592	if (rootEntry == &rootmenu)
593		return;
594	setRootMenu(menu_get_parent_menu(rootEntry->parent));
595
596	QListViewItemIterator it(this);
597	for (; (item = (ConfigItem*)it.current()); it++) {
598		if (item->menu == oldroot) {
599			setCurrentItem(item);
600			ensureItemVisible(item);
601			break;
602		}
603	}
604}
605
606void ConfigList::keyPressEvent(QKeyEvent* ev)
607{
608	QListViewItem* i = currentItem();
609	ConfigItem* item;
610	struct menu *menu;
611	enum prop_type type;
612
613	if (ev->key() == Key_Escape && mode != fullMode) {
614		emit parentSelected();
615		ev->accept();
616		return;
617	}
618
619	if (!i) {
620		Parent::keyPressEvent(ev);
621		return;
622	}
623	item = (ConfigItem*)i;
624
625	switch (ev->key()) {
626	case Key_Return:
627	case Key_Enter:
628		if (item->goParent) {
629			emit parentSelected();
630			break;
631		}
632		menu = item->menu;
633		if (!menu)
634			break;
635		type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
636		if (type == P_MENU && rootEntry != menu &&
637		    mode != fullMode && mode != menuMode) {
638			emit menuSelected(menu);
639			break;
640		}
641	case Key_Space:
642		changeValue(item);
643		break;
644	case Key_N:
645		setValue(item, no);
646		break;
647	case Key_M:
648		setValue(item, mod);
649		break;
650	case Key_Y:
651		setValue(item, yes);
652		break;
653	default:
654		Parent::keyPressEvent(ev);
655		return;
656	}
657	ev->accept();
658}
659
660void ConfigList::contentsMousePressEvent(QMouseEvent* e)
661{
662	//QPoint p(contentsToViewport(e->pos()));
663	//printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
664	Parent::contentsMousePressEvent(e);
665}
666
667void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e)
668{
669	QPoint p(contentsToViewport(e->pos()));
670	ConfigItem* item = (ConfigItem*)itemAt(p);
671	struct menu *menu;
672	enum prop_type ptype;
673	const QPixmap* pm;
674	int idx, x;
675
676	if (!item)
677		goto skip;
678
679	menu = item->menu;
680	x = header()->offset() + p.x();
681	idx = colRevMap[header()->sectionAt(x)];
682	switch (idx) {
683	case promptColIdx:
684		pm = item->pixmap(promptColIdx);
685		if (pm) {
686			int off = header()->sectionPos(0) + itemMargin() +
687				treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0));
688			if (x >= off && x < off + pm->width()) {
689				if (item->goParent) {
690					emit parentSelected();
691					break;
692				} else if (!menu)
693					break;
694				ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
695				if (ptype == P_MENU && rootEntry != menu &&
696				    mode != fullMode && mode != menuMode)
697					emit menuSelected(menu);
698				else
699					changeValue(item);
700			}
701		}
702		break;
703	case noColIdx:
704		setValue(item, no);
705		break;
706	case modColIdx:
707		setValue(item, mod);
708		break;
709	case yesColIdx:
710		setValue(item, yes);
711		break;
712	case dataColIdx:
713		changeValue(item);
714		break;
715	}
716
717skip:
718	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
719	Parent::contentsMouseReleaseEvent(e);
720}
721
722void ConfigList::contentsMouseMoveEvent(QMouseEvent* e)
723{
724	//QPoint p(contentsToViewport(e->pos()));
725	//printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
726	Parent::contentsMouseMoveEvent(e);
727}
728
729void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e)
730{
731	QPoint p(contentsToViewport(e->pos()));
732	ConfigItem* item = (ConfigItem*)itemAt(p);
733	struct menu *menu;
734	enum prop_type ptype;
735
736	if (!item)
737		goto skip;
738	if (item->goParent) {
739		emit parentSelected();
740		goto skip;
741	}
742	menu = item->menu;
743	if (!menu)
744		goto skip;
745	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
746	if (ptype == P_MENU && (mode == singleMode || mode == symbolMode))
747		emit menuSelected(menu);
748	else if (menu->sym)
749		changeValue(item);
750
751skip:
752	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
753	Parent::contentsMouseDoubleClickEvent(e);
754}
755
756void ConfigList::focusInEvent(QFocusEvent *e)
757{
758	Parent::focusInEvent(e);
759
760	QListViewItem* item = currentItem();
761	if (!item)
762		return;
763
764	setSelected(item, TRUE);
765	emit gotFocus();
766}
767
768ConfigView* ConfigView::viewList;
769
770ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview,
771		       ConfigSettings *configSettings)
772	: Parent(parent)
773{
774	list = new ConfigList(this, cview, configSettings);
775	lineEdit = new ConfigLineEdit(this);
776	lineEdit->hide();
777
778	this->nextView = viewList;
779	viewList = this;
780}
781
782ConfigView::~ConfigView(void)
783{
784	ConfigView** vp;
785
786	for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
787		if (*vp == this) {
788			*vp = nextView;
789			break;
790		}
791	}
792}
793
794void ConfigView::updateList(ConfigItem* item)
795{
796	ConfigView* v;
797
798	for (v = viewList; v; v = v->nextView)
799		v->list->updateList(item);
800}
801
802void ConfigView::updateListAll(void)
803{
804	ConfigView* v;
805
806	for (v = viewList; v; v = v->nextView)
807		v->list->updateListAll();
808}
809
810/*
811 * Construct the complete config widget
812 */
813ConfigMainWindow::ConfigMainWindow(void)
814{
815	QMenuBar* menu;
816	bool ok;
817	int x, y, width, height;
818
819	QWidget *d = configApp->desktop();
820
821	ConfigSettings* configSettings = new ConfigSettings();
822#if QT_VERSION >= 300
823	width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64);
824	height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64);
825	resize(width, height);
826	x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok);
827	if (ok)
828		y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok);
829	if (ok)
830		move(x, y);
831	showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false);
832
833	// read list settings into configSettings, will be used later for ConfigList setup
834	configSettings->readListSettings();
835#else
836	width = d->width() - 64;
837	height = d->height() - 64;
838	resize(width, height);
839	showDebug = false;
840#endif
841
842	split1 = new QSplitter(this);
843	split1->setOrientation(QSplitter::Horizontal);
844	setCentralWidget(split1);
845
846	menuView = new ConfigView(split1, this, configSettings);
847	menuList = menuView->list;
848
849	split2 = new QSplitter(split1);
850	split2->setOrientation(QSplitter::Vertical);
851
852	// create config tree
853	configView = new ConfigView(split2, this, configSettings);
854	configList = configView->list;
855
856	helpText = new QTextView(split2);
857	helpText->setTextFormat(Qt::RichText);
858
859	setTabOrder(configList, helpText);
860	configList->setFocus();
861
862	menu = menuBar();
863	toolBar = new QToolBar("Tools", this);
864
865	backAction = new QAction("Back", QPixmap(xpm_back), "Back", 0, this);
866	  connect(backAction, SIGNAL(activated()), SLOT(goBack()));
867	  backAction->setEnabled(FALSE);
868	QAction *quitAction = new QAction("Quit", "&Quit", CTRL+Key_Q, this);
869	  connect(quitAction, SIGNAL(activated()), SLOT(close()));
870	QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this);
871	  connect(loadAction, SIGNAL(activated()), SLOT(loadConfig()));
872	QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
873	  connect(saveAction, SIGNAL(activated()), SLOT(saveConfig()));
874	QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this);
875	  connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs()));
876	QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this);
877	  connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView()));
878	QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this);
879	  connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView()));
880	QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), "Full View", 0, this);
881	  connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView()));
882
883	QAction *showNameAction = new QAction(NULL, "Show Name", 0, this);
884	  showNameAction->setToggleAction(TRUE);
885	  showNameAction->setOn(configList->showName);
886	  connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool)));
887	QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this);
888	  showRangeAction->setToggleAction(TRUE);
889	  showRangeAction->setOn(configList->showRange);
890	  connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool)));
891	QAction *showDataAction = new QAction(NULL, "Show Data", 0, this);
892	  showDataAction->setToggleAction(TRUE);
893	  showDataAction->setOn(configList->showData);
894	  connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool)));
895	QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this);
896	  showAllAction->setToggleAction(TRUE);
897	  showAllAction->setOn(configList->showAll);
898	  connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool)));
899	QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this);
900	  showDebugAction->setToggleAction(TRUE);
901	  showDebugAction->setOn(showDebug);
902	  connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
903
904	QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this);
905	  connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro()));
906	QAction *showAboutAction = new QAction(NULL, "About", 0, this);
907	  connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout()));
908
909	// init tool bar
910	backAction->addTo(toolBar);
911	toolBar->addSeparator();
912	loadAction->addTo(toolBar);
913	saveAction->addTo(toolBar);
914	toolBar->addSeparator();
915	singleViewAction->addTo(toolBar);
916	splitViewAction->addTo(toolBar);
917	fullViewAction->addTo(toolBar);
918
919	// create config menu
920	QPopupMenu* config = new QPopupMenu(this);
921	menu->insertItem("&File", config);
922	loadAction->addTo(config);
923	saveAction->addTo(config);
924	saveAsAction->addTo(config);
925	config->insertSeparator();
926	quitAction->addTo(config);
927
928	// create options menu
929	QPopupMenu* optionMenu = new QPopupMenu(this);
930	menu->insertItem("&Option", optionMenu);
931	showNameAction->addTo(optionMenu);
932	showRangeAction->addTo(optionMenu);
933	showDataAction->addTo(optionMenu);
934	optionMenu->insertSeparator();
935	showAllAction->addTo(optionMenu);
936	showDebugAction->addTo(optionMenu);
937
938	// create help menu
939	QPopupMenu* helpMenu = new QPopupMenu(this);
940	menu->insertSeparator();
941	menu->insertItem("&Help", helpMenu);
942	showIntroAction->addTo(helpMenu);
943	showAboutAction->addTo(helpMenu);
944
945	connect(configList, SIGNAL(menuSelected(struct menu *)),
946		SLOT(changeMenu(struct menu *)));
947	connect(configList, SIGNAL(parentSelected()),
948		SLOT(goBack()));
949	connect(menuList, SIGNAL(menuSelected(struct menu *)),
950		SLOT(changeMenu(struct menu *)));
951
952	connect(configList, SIGNAL(gotFocus(void)),
953		SLOT(listFocusChanged(void)));
954	connect(menuList, SIGNAL(gotFocus(void)),
955		SLOT(listFocusChanged(void)));
956
957#if QT_VERSION >= 300
958	QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol");
959	if (listMode == "single")
960		showSingleView();
961	else if (listMode == "full")
962		showFullView();
963	else /*if (listMode == "split")*/
964		showSplitView();
965
966	// UI setup done, restore splitter positions
967	QValueList<int> sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok);
968	if (ok)
969		split1->setSizes(sizes);
970
971	sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok);
972	if (ok)
973		split2->setSizes(sizes);
974#else
975	showSplitView();
976#endif
977	delete configSettings;
978}
979
980static QString print_filter(const QString &str)
981{
982	QRegExp re("[<>&\"\\n]");
983	QString res = str;
984	for (int i = 0; (i = res.find(re, i)) >= 0;) {
985		switch (res[i].latin1()) {
986		case '<':
987			res.replace(i, 1, "&lt;");
988			i += 4;
989			break;
990		case '>':
991			res.replace(i, 1, "&gt;");
992			i += 4;
993			break;
994		case '&':
995			res.replace(i, 1, "&amp;");
996			i += 5;
997			break;
998		case '"':
999			res.replace(i, 1, "&quot;");
1000			i += 6;
1001			break;
1002		case '\n':
1003			res.replace(i, 1, "<br>");
1004			i += 4;
1005			break;
1006		}
1007	}
1008	return res;
1009}
1010
1011static void expr_print_help(void *data, const char *str)
1012{
1013	reinterpret_cast<QString*>(data)->append(print_filter(str));
1014}
1015
1016/*
1017 * display a new help entry as soon as a new menu entry is selected
1018 */
1019void ConfigMainWindow::setHelp(QListViewItem* item)
1020{
1021	struct symbol* sym;
1022	struct menu* menu = 0;
1023
1024	configList->parent()->lineEdit->hide();
1025	if (item)
1026		menu = ((ConfigItem*)item)->menu;
1027	if (!menu) {
1028		helpText->setText(QString::null);
1029		return;
1030	}
1031
1032	QString head, debug, help;
1033	menu = ((ConfigItem*)item)->menu;
1034	sym = menu->sym;
1035	if (sym) {
1036		if (menu->prompt) {
1037			head += "<big><b>";
1038			head += print_filter(_(menu->prompt->text));
1039			head += "</b></big>";
1040			if (sym->name) {
1041				head += " (";
1042				head += print_filter(_(sym->name));
1043				head += ")";
1044			}
1045		} else if (sym->name) {
1046			head += "<big><b>";
1047			head += print_filter(_(sym->name));
1048			head += "</b></big>";
1049		}
1050		head += "<br><br>";
1051
1052		if (showDebug) {
1053			debug += "type: ";
1054			debug += print_filter(sym_type_name(sym->type));
1055			if (sym_is_choice(sym))
1056				debug += " (choice)";
1057			debug += "<br>";
1058			if (sym->rev_dep.expr) {
1059				debug += "reverse dep: ";
1060				expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
1061				debug += "<br>";
1062			}
1063			for (struct property *prop = sym->prop; prop; prop = prop->next) {
1064				switch (prop->type) {
1065				case P_PROMPT:
1066				case P_MENU:
1067					debug += "prompt: ";
1068					debug += print_filter(_(prop->text));
1069					debug += "<br>";
1070					break;
1071				case P_DEFAULT:
1072					debug += "default: ";
1073					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1074					debug += "<br>";
1075					break;
1076				case P_CHOICE:
1077					if (sym_is_choice(sym)) {
1078						debug += "choice: ";
1079						expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1080						debug += "<br>";
1081					}
1082					break;
1083				case P_SELECT:
1084					debug += "select: ";
1085					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1086					debug += "<br>";
1087					break;
1088				case P_RANGE:
1089					debug += "range: ";
1090					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1091					debug += "<br>";
1092					break;
1093				default:
1094					debug += "unknown property: ";
1095					debug += prop_get_type_name(prop->type);
1096					debug += "<br>";
1097				}
1098				if (prop->visible.expr) {
1099					debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1100					expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
1101					debug += "<br>";
1102				}
1103			}
1104			debug += "<br>";
1105		}
1106
1107		help = print_filter(_(sym->help));
1108	} else if (menu->prompt) {
1109		head += "<big><b>";
1110		head += print_filter(_(menu->prompt->text));
1111		head += "</b></big><br><br>";
1112		if (showDebug) {
1113			if (menu->prompt->visible.expr) {
1114				debug += "&nbsp;&nbsp;dep: ";
1115				expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
1116				debug += "<br><br>";
1117			}
1118		}
1119	}
1120	if (showDebug)
1121		debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
1122	helpText->setText(head + debug + help);
1123}
1124
1125void ConfigMainWindow::loadConfig(void)
1126{
1127	QString s = QFileDialog::getOpenFileName(".config", NULL, this);
1128	if (s.isNull())
1129		return;
1130	if (conf_read(QFile::encodeName(s)))
1131		QMessageBox::information(this, "qconf", "Unable to load configuration!");
1132	ConfigView::updateListAll();
1133}
1134
1135void ConfigMainWindow::saveConfig(void)
1136{
1137	if (conf_write(NULL))
1138		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1139}
1140
1141void ConfigMainWindow::saveConfigAs(void)
1142{
1143	QString s = QFileDialog::getSaveFileName(".config", NULL, this);
1144	if (s.isNull())
1145		return;
1146	if (conf_write(QFile::encodeName(s)))
1147		QMessageBox::information(this, "qconf", "Unable to save configuration!");
1148}
1149
1150void ConfigMainWindow::changeMenu(struct menu *menu)
1151{
1152	configList->setRootMenu(menu);
1153	backAction->setEnabled(TRUE);
1154}
1155
1156void ConfigMainWindow::listFocusChanged(void)
1157{
1158	if (menuList->hasFocus()) {
1159		if (menuList->mode == menuMode)
1160			configList->clearSelection();
1161		setHelp(menuList->selectedItem());
1162	} else if (configList->hasFocus()) {
1163		setHelp(configList->selectedItem());
1164	}
1165}
1166
1167void ConfigMainWindow::goBack(void)
1168{
1169	ConfigItem* item;
1170
1171	configList->setParentMenu();
1172	if (configList->rootEntry == &rootmenu)
1173		backAction->setEnabled(FALSE);
1174	item = (ConfigItem*)menuList->selectedItem();
1175	while (item) {
1176		if (item->menu == configList->rootEntry) {
1177			menuList->setSelected(item, TRUE);
1178			break;
1179		}
1180		item = (ConfigItem*)item->parent();
1181	}
1182}
1183
1184void ConfigMainWindow::showSingleView(void)
1185{
1186	menuView->hide();
1187	menuList->setRootMenu(0);
1188	configList->mode = singleMode;
1189	if (configList->rootEntry == &rootmenu)
1190		configList->updateListAll();
1191	else
1192		configList->setRootMenu(&rootmenu);
1193	configList->setAllOpen(TRUE);
1194	configList->setFocus();
1195}
1196
1197void ConfigMainWindow::showSplitView(void)
1198{
1199	configList->mode = symbolMode;
1200	if (configList->rootEntry == &rootmenu)
1201		configList->updateListAll();
1202	else
1203		configList->setRootMenu(&rootmenu);
1204	configList->setAllOpen(TRUE);
1205	configApp->processEvents();
1206	menuList->mode = menuMode;
1207	menuList->setRootMenu(&rootmenu);
1208	menuList->setAllOpen(TRUE);
1209	menuView->show();
1210	menuList->setFocus();
1211}
1212
1213void ConfigMainWindow::showFullView(void)
1214{
1215	menuView->hide();
1216	menuList->setRootMenu(0);
1217	configList->mode = fullMode;
1218	if (configList->rootEntry == &rootmenu)
1219		configList->updateListAll();
1220	else
1221		configList->setRootMenu(&rootmenu);
1222	configList->setAllOpen(FALSE);
1223	configList->setFocus();
1224}
1225
1226void ConfigMainWindow::setShowAll(bool b)
1227{
1228	if (configList->showAll == b)
1229		return;
1230	configList->showAll = b;
1231	configList->updateListAll();
1232	menuList->showAll = b;
1233	menuList->updateListAll();
1234}
1235
1236void ConfigMainWindow::setShowDebug(bool b)
1237{
1238	if (showDebug == b)
1239		return;
1240	showDebug = b;
1241}
1242
1243void ConfigMainWindow::setShowName(bool b)
1244{
1245	if (configList->showName == b)
1246		return;
1247	configList->showName = b;
1248	configList->reinit();
1249	menuList->showName = b;
1250	menuList->reinit();
1251}
1252
1253void ConfigMainWindow::setShowRange(bool b)
1254{
1255	if (configList->showRange == b)
1256		return;
1257	configList->showRange = b;
1258	configList->reinit();
1259	menuList->showRange = b;
1260	menuList->reinit();
1261}
1262
1263void ConfigMainWindow::setShowData(bool b)
1264{
1265	if (configList->showData == b)
1266		return;
1267	configList->showData = b;
1268	configList->reinit();
1269	menuList->showData = b;
1270	menuList->reinit();
1271}
1272
1273/*
1274 * ask for saving configuration before quitting
1275 * TODO ask only when something changed
1276 */
1277void ConfigMainWindow::closeEvent(QCloseEvent* e)
1278{
1279	if (!sym_change_count) {
1280		e->accept();
1281		return;
1282	}
1283	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1284			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1285	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1286	mb.setButtonText(QMessageBox::No, "&Discard Changes");
1287	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1288	switch (mb.exec()) {
1289	case QMessageBox::Yes:
1290		conf_write(NULL);
1291	case QMessageBox::No:
1292		e->accept();
1293		break;
1294	case QMessageBox::Cancel:
1295		e->ignore();
1296		break;
1297	}
1298}
1299
1300void ConfigMainWindow::showIntro(void)
1301{
1302	static char str[] = "Welcome to the qconf graphical busybox configuration tool for Linux.\n\n"
1303		"For each option, a blank box indicates the feature is disabled, a check\n"
1304		"indicates it is enabled, and a dot indicates that it is to be compiled\n"
1305		"as a module.  Clicking on the box will cycle through the three states.\n\n"
1306		"If you do not see an option (e.g., a device driver) that you believe\n"
1307		"should be present, try turning on Show All Options under the Options menu.\n"
1308		"Although there is no cross reference yet to help you figure out what other\n"
1309		"options must be enabled to support the option you are interested in, you can\n"
1310		"still view the help of a grayed-out option.\n\n"
1311		"Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1312		"which you can then match by examining other options.\n\n";
1313
1314	QMessageBox::information(this, "qconf", str);
1315}
1316
1317void ConfigMainWindow::showAbout(void)
1318{
1319	static char str[] = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n\n"
1320		"Bug reports and feature request can also be entered at http://bugs.busybox.net/\n";
1321
1322	QMessageBox::information(this, "qconf", str);
1323}
1324
1325void ConfigMainWindow::saveSettings(void)
1326{
1327#if QT_VERSION >= 300
1328	ConfigSettings *configSettings = new ConfigSettings;
1329	configSettings->writeEntry("/kconfig/qconf/window x", pos().x());
1330	configSettings->writeEntry("/kconfig/qconf/window y", pos().y());
1331	configSettings->writeEntry("/kconfig/qconf/window width", size().width());
1332	configSettings->writeEntry("/kconfig/qconf/window height", size().height());
1333	configSettings->writeEntry("/kconfig/qconf/showName", configList->showName);
1334	configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange);
1335	configSettings->writeEntry("/kconfig/qconf/showData", configList->showData);
1336	configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll);
1337	configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug);
1338
1339	QString entry;
1340	switch(configList->mode) {
1341	case singleMode :
1342		entry = "single";
1343		break;
1344
1345	case symbolMode :
1346		entry = "split";
1347		break;
1348
1349	case fullMode :
1350		entry = "full";
1351		break;
1352	}
1353	configSettings->writeEntry("/kconfig/qconf/listMode", entry);
1354
1355	configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes());
1356	configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes());
1357
1358	delete configSettings;
1359#endif
1360}
1361
1362void fixup_rootmenu(struct menu *menu)
1363{
1364	struct menu *child;
1365	static int menu_cnt = 0;
1366
1367	menu->flags |= MENU_ROOT;
1368	for (child = menu->list; child; child = child->next) {
1369		if (child->prompt && child->prompt->type == P_MENU) {
1370			menu_cnt++;
1371			fixup_rootmenu(child);
1372			menu_cnt--;
1373		} else if (!menu_cnt)
1374			fixup_rootmenu(child);
1375	}
1376}
1377
1378static const char *progname;
1379
1380static void usage(void)
1381{
1382	printf("%s <config>\n", progname);
1383	exit(0);
1384}
1385
1386int main(int ac, char** av)
1387{
1388	ConfigMainWindow* v;
1389	const char *name;
1390
1391	bindtextdomain(PACKAGE, LOCALEDIR);
1392	textdomain(PACKAGE);
1393
1394#ifndef LKC_DIRECT_LINK
1395	kconfig_load();
1396#endif
1397
1398	progname = av[0];
1399	configApp = new QApplication(ac, av);
1400	if (ac > 1 && av[1][0] == '-') {
1401		switch (av[1][1]) {
1402		case 'h':
1403		case '?':
1404			usage();
1405		}
1406		name = av[2];
1407	} else
1408		name = av[1];
1409	if (!name)
1410		usage();
1411
1412	conf_parse(name);
1413	fixup_rootmenu(&rootmenu);
1414	conf_read(NULL);
1415	//zconfdump(stdout);
1416
1417	v = new ConfigMainWindow();
1418
1419	//zconfdump(stdout);
1420	v->show();
1421	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1422	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1423	configApp->exec();
1424
1425	return 0;
1426}
1427