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 
38 static QApplication *configApp;
39 static ConfigSettings *configSettings;
40 
41 QAction *ConfigMainWindow::saveAction;
42 
qgettext(const char * str)43 static inline QString qgettext(const char* str)
44 {
45 	return QString::fromLocal8Bit(str);
46 }
47 
ConfigSettings()48 ConfigSettings::ConfigSettings()
49 	: QSettings("kernel.org", "qconf")
50 {
51 }
52 
53 /**
54  * Reads a list of integer values from the application settings.
55  */
readSizes(const QString & key,bool * ok)56 QList<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  */
writeSizes(const QString & key,const QList<int> & value)79 bool 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  */
okRename(int col)96 void ConfigItem::okRename(int col)
97 {
98 }
99 
100 /*
101  * update the displayed of a menu entry
102  */
updateMenu(void)103 void 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)";
211 set_prompt:
212 	setText(promptColIdx, prompt);
213 }
214 
testUpdateMenu(bool v)215 void 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  */
init(void)237 void 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  */
~ConfigItem(void)254 ConfigItem::~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 
ConfigLineEdit(ConfigView * parent)267 ConfigLineEdit::ConfigLineEdit(ConfigView* parent)
268 	: Parent(parent)
269 {
270 	connect(this, SIGNAL(editingFinished()), SLOT(hide()));
271 }
272 
show(ConfigItem * i)273 void 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 
keyPressEvent(QKeyEvent * e)284 void 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 
ConfigList(ConfigView * p,const char * name)303 ConfigList::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 
menuSkip(struct menu * menu)341 bool 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 
reinit(void)352 void 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 
saveSettings(void)373 void 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 
findConfigItem(struct menu * menu)385 ConfigItem* 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 
updateSelection(void)397 void 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 
updateList(ConfigItem * item)418 void 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 	}
459 update:
460 	updateMenuList(this, rootEntry);
461 	update();
462 	resizeColumnToContents(0);
463 }
464 
setValue(ConfigItem * item,tristate val)465 void 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 
changeValue(ConfigItem * item)490 void 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 
setRootMenu(struct menu * menu)529 void 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 
setParentMenu(void)547 void 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  */
updateMenuList(ConfigItem * parent,struct menu * menu)577 void 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 
updateMenuList(ConfigList * parent,struct menu * menu)642 void 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 
keyPressEvent(QKeyEvent * ev)707 void 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 
mousePressEvent(QMouseEvent * e)761 void 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 
mouseReleaseEvent(QMouseEvent * e)768 void 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 
817 skip:
818 	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
819 	Parent::mouseReleaseEvent(e);
820 }
821 
mouseMoveEvent(QMouseEvent * e)822 void 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 
mouseDoubleClickEvent(QMouseEvent * e)829 void 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 
851 skip:
852 	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
853 	Parent::mouseDoubleClickEvent(e);
854 }
855 
focusInEvent(QFocusEvent * e)856 void 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 
contextMenuEvent(QContextMenuEvent * e)870 void 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 
908 ConfigView*ConfigView::viewList;
909 QAction *ConfigView::showNormalAction;
910 QAction *ConfigView::showAllAction;
911 QAction *ConfigView::showPromptAction;
912 
ConfigView(QWidget * parent,const char * name)913 ConfigView::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 
~ConfigView(void)930 ConfigView::~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 
setOptionMode(QAction * act)942 void 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 
setShowName(bool b)954 void ConfigView::setShowName(bool b)
955 {
956 	if (list->showName != b) {
957 		list->showName = b;
958 		list->reinit();
959 		emit showNameChanged(b);
960 	}
961 }
962 
setShowRange(bool b)963 void ConfigView::setShowRange(bool b)
964 {
965 	if (list->showRange != b) {
966 		list->showRange = b;
967 		list->reinit();
968 		emit showRangeChanged(b);
969 	}
970 }
971 
setShowData(bool b)972 void ConfigView::setShowData(bool b)
973 {
974 	if (list->showData != b) {
975 		list->showData = b;
976 		list->reinit();
977 		emit showDataChanged(b);
978 	}
979 }
980 
setAllOpen(bool open)981 void ConfigList::setAllOpen(bool open)
982 {
983 	QTreeWidgetItemIterator it(this);
984 
985 	while (*it) {
986 		(*it)->setExpanded(open);
987 
988 		++it;
989 	}
990 }
991 
updateList(ConfigItem * item)992 void ConfigView::updateList(ConfigItem* item)
993 {
994 	ConfigView* v;
995 
996 	for (v = viewList; v; v = v->nextView)
997 		v->list->updateList(item);
998 }
999 
updateListAll(void)1000 void ConfigView::updateListAll(void)
1001 {
1002 	ConfigView* v;
1003 
1004 	for (v = viewList; v; v = v->nextView)
1005 		v->list->updateListAll();
1006 }
1007 
ConfigInfoView(QWidget * parent,const char * name)1008 ConfigInfoView::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 
saveSettings(void)1022 void ConfigInfoView::saveSettings(void)
1023 {
1024 	if (!objectName().isEmpty()) {
1025 		configSettings->beginGroup(objectName());
1026 		configSettings->setValue("/showDebug", showDebug());
1027 		configSettings->endGroup();
1028 	}
1029 }
1030 
setShowDebug(bool b)1031 void 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 
setInfo(struct menu * m)1043 void 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 
symbolInfo(void)1055 void 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 
menuInfo(void)1071 void 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 
debug_info(struct symbol * sym)1127 QString 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 
print_filter(const QString & str)1180 QString 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 
expr_print_help(void * data,struct symbol * sym,const char * str)1211 void 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 
createStandardContextMenu(const QPoint & pos)1224 QMenu* 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 
contextMenuEvent(QContextMenuEvent * e)1237 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e)
1238 {
1239 	Parent::contextMenuEvent(e);
1240 }
1241 
ConfigSearchWindow(ConfigMainWindow * parent,const char * name)1242 ConfigSearchWindow::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 
saveSettings(void)1297 void 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 
search(void)1310 void 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  */
ConfigMainWindow(void)1333 ConfigMainWindow::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 
loadConfig(void)1521 void 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 
saveConfig(void)1531 bool 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 
saveConfigAs(void)1540 void ConfigMainWindow::saveConfigAs(void)
1541 {
1542 	QString s = QFileDialog::getSaveFileName(this, "", conf_get_configname());
1543 	if (s.isNull())
1544 		return;
1545 	saveConfig();
1546 }
1547 
searchConfig(void)1548 void ConfigMainWindow::searchConfig(void)
1549 {
1550 	if (!searchWindow)
1551 		searchWindow = new ConfigSearchWindow(this, "search");
1552 	searchWindow->show();
1553 }
1554 
changeMenu(struct menu * menu)1555 void 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 
setMenuLink(struct menu * menu)1564 void 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 
listFocusChanged(void)1616 void ConfigMainWindow::listFocusChanged(void)
1617 {
1618 	if (menuList->mode == menuMode)
1619 		configList->clearSelection();
1620 }
1621 
goBack(void)1622 void 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 
showSingleView(void)1645 void 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 
showSplitView(void)1664 void 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 
showFullView(void)1687 void 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  */
closeEvent(QCloseEvent * e)1710 void 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 
showIntro(void)1737 void 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 
showAbout(void)1754 void 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 
saveSettings(void)1763 void 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 
conf_changed(void)1793 void ConfigMainWindow::conf_changed(void)
1794 {
1795 	if (saveAction)
1796 		saveAction->setEnabled(conf_get_changed());
1797 }
1798 
fixup_rootmenu(struct menu * menu)1799 void 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 
1815 static const char *progname;
1816 
usage(void)1817 static void usage(void)
1818 {
1819 	printf("%s [-s] <config>\n", progname);
1820 	exit(0);
1821 }
1822 
main(int ac,char ** av)1823 int 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