Merge tag 'perf-urgent-2020-04-05' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / scripts / kconfig / qconf.cc
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4  * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
5  */
6
7 #include <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.h"
36
37
38 static QApplication *configApp;
39 static ConfigSettings *configSettings;
40
41 QAction *ConfigMainWindow::saveAction;
42
43 static inline QString qgettext(const char* str)
44 {
45         return QString::fromLocal8Bit(str);
46 }
47
48 ConfigSettings::ConfigSettings()
49         : QSettings("kernel.org", "qconf")
50 {
51 }
52
53 /**
54  * Reads a list of integer values from the application settings.
55  */
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  */
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  */
96 void ConfigItem::okRename(int col)
97 {
98 }
99
100 /*
101  * update the displayed of a menu entry
102  */
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_changeable(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
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  */
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  */
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
267 ConfigLineEdit::ConfigLineEdit(ConfigView* parent)
268         : Parent(parent)
269 {
270         connect(this, SIGNAL(editingFinished()), SLOT(hide()));
271 }
272
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
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
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         setObjectName(name);
313         setSortingEnabled(false);
314         setRootIsDecorated(true);
315
316         setVerticalScrollMode(ScrollPerPixel);
317         setHorizontalScrollMode(ScrollPerPixel);
318
319         setHeaderLabels(QStringList() << "Option" << "Name" << "N" << "M" << "Y" << "Value");
320
321         connect(this, SIGNAL(itemSelectionChanged(void)),
322                 SLOT(updateSelection(void)));
323
324         if (name) {
325                 configSettings->beginGroup(name);
326                 showName = configSettings->value("/showName", false).toBool();
327                 showRange = configSettings->value("/showRange", false).toBool();
328                 showData = configSettings->value("/showData", false).toBool();
329                 optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
330                 configSettings->endGroup();
331                 connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
332         }
333
334         addColumn(promptColIdx);
335
336         reinit();
337 }
338
339 bool ConfigList::menuSkip(struct menu *menu)
340 {
341         if (optMode == normalOpt && menu_is_visible(menu))
342                 return false;
343         if (optMode == promptOpt && menu_has_prompt(menu))
344                 return false;
345         if (optMode == allOpt)
346                 return false;
347         return true;
348 }
349
350 void ConfigList::reinit(void)
351 {
352         removeColumn(dataColIdx);
353         removeColumn(yesColIdx);
354         removeColumn(modColIdx);
355         removeColumn(noColIdx);
356         removeColumn(nameColIdx);
357
358         if (showName)
359                 addColumn(nameColIdx);
360         if (showRange) {
361                 addColumn(noColIdx);
362                 addColumn(modColIdx);
363                 addColumn(yesColIdx);
364         }
365         if (showData)
366                 addColumn(dataColIdx);
367
368         updateListAll();
369 }
370
371 void ConfigList::saveSettings(void)
372 {
373         if (!objectName().isEmpty()) {
374                 configSettings->beginGroup(objectName());
375                 configSettings->setValue("/showName", showName);
376                 configSettings->setValue("/showRange", showRange);
377                 configSettings->setValue("/showData", showData);
378                 configSettings->setValue("/optionMode", (int)optMode);
379                 configSettings->endGroup();
380         }
381 }
382
383 ConfigItem* ConfigList::findConfigItem(struct menu *menu)
384 {
385         ConfigItem* item = (ConfigItem*)menu->data;
386
387         for (; item; item = item->nextItem) {
388                 if (this == item->listView())
389                         break;
390         }
391
392         return item;
393 }
394
395 void ConfigList::updateSelection(void)
396 {
397         struct menu *menu;
398         enum prop_type type;
399
400         if (selectedItems().count() == 0)
401                 return;
402
403         ConfigItem* item = (ConfigItem*)selectedItems().first();
404         if (!item)
405                 return;
406
407         menu = item->menu;
408         emit menuChanged(menu);
409         if (!menu)
410                 return;
411         type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
412         if (mode == menuMode && type == P_MENU)
413                 emit menuSelected(menu);
414 }
415
416 void ConfigList::updateList(ConfigItem* item)
417 {
418         ConfigItem* last = 0;
419
420         if (!rootEntry) {
421                 if (mode != listMode)
422                         goto update;
423                 QTreeWidgetItemIterator it(this);
424                 ConfigItem* item;
425
426                 while (*it) {
427                         item = (ConfigItem*)(*it);
428                         if (!item->menu)
429                                 continue;
430                         item->testUpdateMenu(menu_is_visible(item->menu));
431
432                         ++it;
433                 }
434                 return;
435         }
436
437         if (rootEntry != &rootmenu && (mode == singleMode ||
438             (mode == symbolMode && rootEntry->parent != &rootmenu))) {
439                 item = (ConfigItem *)topLevelItem(0);
440                 if (!item)
441                         item = new ConfigItem(this, 0, true);
442                 last = item;
443         }
444         if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
445             rootEntry->sym && rootEntry->prompt) {
446                 item = last ? last->nextSibling() : firstChild();
447                 if (!item)
448                         item = new ConfigItem(this, last, rootEntry, true);
449                 else
450                         item->testUpdateMenu(true);
451
452                 updateMenuList(item, rootEntry);
453                 update();
454                 resizeColumnToContents(0);
455                 return;
456         }
457 update:
458         updateMenuList(this, rootEntry);
459         update();
460         resizeColumnToContents(0);
461 }
462
463 void ConfigList::setValue(ConfigItem* item, tristate val)
464 {
465         struct symbol* sym;
466         int type;
467         tristate oldval;
468
469         sym = item->menu ? item->menu->sym : 0;
470         if (!sym)
471                 return;
472
473         type = sym_get_type(sym);
474         switch (type) {
475         case S_BOOLEAN:
476         case S_TRISTATE:
477                 oldval = sym_get_tristate_value(sym);
478
479                 if (!sym_set_tristate_value(sym, val))
480                         return;
481                 if (oldval == no && item->menu->list)
482                         item->setExpanded(true);
483                 parent()->updateList(item);
484                 break;
485         }
486 }
487
488 void ConfigList::changeValue(ConfigItem* item)
489 {
490         struct symbol* sym;
491         struct menu* menu;
492         int type, oldexpr, newexpr;
493
494         menu = item->menu;
495         if (!menu)
496                 return;
497         sym = menu->sym;
498         if (!sym) {
499                 if (item->menu->list)
500                         item->setExpanded(!item->isExpanded());
501                 return;
502         }
503
504         type = sym_get_type(sym);
505         switch (type) {
506         case S_BOOLEAN:
507         case S_TRISTATE:
508                 oldexpr = sym_get_tristate_value(sym);
509                 newexpr = sym_toggle_tristate_value(sym);
510                 if (item->menu->list) {
511                         if (oldexpr == newexpr)
512                                 item->setExpanded(!item->isExpanded());
513                         else if (oldexpr == no)
514                                 item->setExpanded(true);
515                 }
516                 if (oldexpr != newexpr)
517                         parent()->updateList(item);
518                 break;
519         case S_INT:
520         case S_HEX:
521         case S_STRING:
522                 parent()->lineEdit->show(item);
523                 break;
524         }
525 }
526
527 void ConfigList::setRootMenu(struct menu *menu)
528 {
529         enum prop_type type;
530
531         if (rootEntry == menu)
532                 return;
533         type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
534         if (type != P_MENU)
535                 return;
536         updateMenuList(this, 0);
537         rootEntry = menu;
538         updateListAll();
539         if (currentItem()) {
540                 currentItem()->setSelected(hasFocus());
541                 scrollToItem(currentItem());
542         }
543 }
544
545 void ConfigList::setParentMenu(void)
546 {
547         ConfigItem* item;
548         struct menu *oldroot;
549
550         oldroot = rootEntry;
551         if (rootEntry == &rootmenu)
552                 return;
553         setRootMenu(menu_get_parent_menu(rootEntry->parent));
554
555         QTreeWidgetItemIterator it(this);
556         while (*it) {
557                 item = (ConfigItem *)(*it);
558                 if (item->menu == oldroot) {
559                         setCurrentItem(item);
560                         scrollToItem(item);
561                         break;
562                 }
563
564                 ++it;
565         }
566 }
567
568 /*
569  * update all the children of a menu entry
570  *   removes/adds the entries from the parent widget as necessary
571  *
572  * parent: either the menu list widget or a menu entry widget
573  * menu: entry to be updated
574  */
575 void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
576 {
577         struct menu* child;
578         ConfigItem* item;
579         ConfigItem* last;
580         bool visible;
581         enum prop_type type;
582
583         if (!menu) {
584                 while (parent->childCount() > 0)
585                 {
586                         delete parent->takeChild(0);
587                 }
588
589                 return;
590         }
591
592         last = parent->firstChild();
593         if (last && !last->goParent)
594                 last = 0;
595         for (child = menu->list; child; child = child->next) {
596                 item = last ? last->nextSibling() : parent->firstChild();
597                 type = child->prompt ? child->prompt->type : P_UNKNOWN;
598
599                 switch (mode) {
600                 case menuMode:
601                         if (!(child->flags & MENU_ROOT))
602                                 goto hide;
603                         break;
604                 case symbolMode:
605                         if (child->flags & MENU_ROOT)
606                                 goto hide;
607                         break;
608                 default:
609                         break;
610                 }
611
612                 visible = menu_is_visible(child);
613                 if (!menuSkip(child)) {
614                         if (!child->sym && !child->list && !child->prompt)
615                                 continue;
616                         if (!item || item->menu != child)
617                                 item = new ConfigItem(parent, last, child, visible);
618                         else
619                                 item->testUpdateMenu(visible);
620
621                         if (mode == fullMode || mode == menuMode || type != P_MENU)
622                                 updateMenuList(item, child);
623                         else
624                                 updateMenuList(item, 0);
625                         last = item;
626                         continue;
627                 }
628         hide:
629                 if (item && item->menu == child) {
630                         last = parent->firstChild();
631                         if (last == item)
632                                 last = 0;
633                         else while (last->nextSibling() != item)
634                                 last = last->nextSibling();
635                         delete item;
636                 }
637         }
638 }
639
640 void ConfigList::updateMenuList(ConfigList *parent, struct menu* menu)
641 {
642         struct menu* child;
643         ConfigItem* item;
644         ConfigItem* last;
645         bool visible;
646         enum prop_type type;
647
648         if (!menu) {
649                 while (parent->topLevelItemCount() > 0)
650                 {
651                         delete parent->takeTopLevelItem(0);
652                 }
653
654                 return;
655         }
656
657         last = (ConfigItem*)parent->topLevelItem(0);
658         if (last && !last->goParent)
659                 last = 0;
660         for (child = menu->list; child; child = child->next) {
661                 item = last ? last->nextSibling() : (ConfigItem*)parent->topLevelItem(0);
662                 type = child->prompt ? child->prompt->type : P_UNKNOWN;
663
664                 switch (mode) {
665                 case menuMode:
666                         if (!(child->flags & MENU_ROOT))
667                                 goto hide;
668                         break;
669                 case symbolMode:
670                         if (child->flags & MENU_ROOT)
671                                 goto hide;
672                         break;
673                 default:
674                         break;
675                 }
676
677                 visible = menu_is_visible(child);
678                 if (!menuSkip(child)) {
679                         if (!child->sym && !child->list && !child->prompt)
680                                 continue;
681                         if (!item || item->menu != child)
682                                 item = new ConfigItem(parent, last, child, visible);
683                         else
684                                 item->testUpdateMenu(visible);
685
686                         if (mode == fullMode || mode == menuMode || type != P_MENU)
687                                 updateMenuList(item, child);
688                         else
689                                 updateMenuList(item, 0);
690                         last = item;
691                         continue;
692                 }
693         hide:
694                 if (item && item->menu == child) {
695                         last = (ConfigItem*)parent->topLevelItem(0);
696                         if (last == item)
697                                 last = 0;
698                         else while (last->nextSibling() != item)
699                                 last = last->nextSibling();
700                         delete item;
701                 }
702         }
703 }
704
705 void ConfigList::keyPressEvent(QKeyEvent* ev)
706 {
707         QTreeWidgetItem* i = currentItem();
708         ConfigItem* item;
709         struct menu *menu;
710         enum prop_type type;
711
712         if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
713                 emit parentSelected();
714                 ev->accept();
715                 return;
716         }
717
718         if (!i) {
719                 Parent::keyPressEvent(ev);
720                 return;
721         }
722         item = (ConfigItem*)i;
723
724         switch (ev->key()) {
725         case Qt::Key_Return:
726         case Qt::Key_Enter:
727                 if (item->goParent) {
728                         emit parentSelected();
729                         break;
730                 }
731                 menu = item->menu;
732                 if (!menu)
733                         break;
734                 type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
735                 if (type == P_MENU && rootEntry != menu &&
736                     mode != fullMode && mode != menuMode) {
737                         emit menuSelected(menu);
738                         break;
739                 }
740         case Qt::Key_Space:
741                 changeValue(item);
742                 break;
743         case Qt::Key_N:
744                 setValue(item, no);
745                 break;
746         case Qt::Key_M:
747                 setValue(item, mod);
748                 break;
749         case Qt::Key_Y:
750                 setValue(item, yes);
751                 break;
752         default:
753                 Parent::keyPressEvent(ev);
754                 return;
755         }
756         ev->accept();
757 }
758
759 void ConfigList::mousePressEvent(QMouseEvent* e)
760 {
761         //QPoint p(contentsToViewport(e->pos()));
762         //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
763         Parent::mousePressEvent(e);
764 }
765
766 void ConfigList::mouseReleaseEvent(QMouseEvent* e)
767 {
768         QPoint p = e->pos();
769         ConfigItem* item = (ConfigItem*)itemAt(p);
770         struct menu *menu;
771         enum prop_type ptype;
772         QIcon icon;
773         int idx, x;
774
775         if (!item)
776                 goto skip;
777
778         menu = item->menu;
779         x = header()->offset() + p.x();
780         idx = header()->logicalIndexAt(x);
781         switch (idx) {
782         case promptColIdx:
783                 icon = item->pixmap(promptColIdx);
784                 if (!icon.isNull()) {
785                         int off = header()->sectionPosition(0) + visualRect(indexAt(p)).x() + 4; // 4 is Hardcoded image offset. There might be a way to do it properly.
786                         if (x >= off && x < off + icon.availableSizes().first().width()) {
787                                 if (item->goParent) {
788                                         emit parentSelected();
789                                         break;
790                                 } else if (!menu)
791                                         break;
792                                 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
793                                 if (ptype == P_MENU && rootEntry != menu &&
794                                     mode != fullMode && mode != menuMode)
795                                         emit menuSelected(menu);
796                                 else
797                                         changeValue(item);
798                         }
799                 }
800                 break;
801         case noColIdx:
802                 setValue(item, no);
803                 break;
804         case modColIdx:
805                 setValue(item, mod);
806                 break;
807         case yesColIdx:
808                 setValue(item, yes);
809                 break;
810         case dataColIdx:
811                 changeValue(item);
812                 break;
813         }
814
815 skip:
816         //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
817         Parent::mouseReleaseEvent(e);
818 }
819
820 void ConfigList::mouseMoveEvent(QMouseEvent* e)
821 {
822         //QPoint p(contentsToViewport(e->pos()));
823         //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
824         Parent::mouseMoveEvent(e);
825 }
826
827 void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
828 {
829         QPoint p = e->pos(); // TODO: Check if this works(was contentsToViewport).
830         ConfigItem* item = (ConfigItem*)itemAt(p);
831         struct menu *menu;
832         enum prop_type ptype;
833
834         if (!item)
835                 goto skip;
836         if (item->goParent) {
837                 emit parentSelected();
838                 goto skip;
839         }
840         menu = item->menu;
841         if (!menu)
842                 goto skip;
843         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
844         if (ptype == P_MENU && (mode == singleMode || mode == symbolMode))
845                 emit menuSelected(menu);
846         else if (menu->sym)
847                 changeValue(item);
848
849 skip:
850         //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
851         Parent::mouseDoubleClickEvent(e);
852 }
853
854 void ConfigList::focusInEvent(QFocusEvent *e)
855 {
856         struct menu *menu = NULL;
857
858         Parent::focusInEvent(e);
859
860         ConfigItem* item = (ConfigItem *)currentItem();
861         if (item) {
862                 item->setSelected(true);
863                 menu = item->menu;
864         }
865         emit gotFocus(menu);
866 }
867
868 void ConfigList::contextMenuEvent(QContextMenuEvent *e)
869 {
870         if (e->y() <= header()->geometry().bottom()) {
871                 if (!headerPopup) {
872                         QAction *action;
873
874                         headerPopup = new QMenu(this);
875                         action = new QAction("Show Name", this);
876                           action->setCheckable(true);
877                           connect(action, SIGNAL(toggled(bool)),
878                                   parent(), SLOT(setShowName(bool)));
879                           connect(parent(), SIGNAL(showNameChanged(bool)),
880                                   action, SLOT(setOn(bool)));
881                           action->setChecked(showName);
882                           headerPopup->addAction(action);
883                         action = new QAction("Show Range", this);
884                           action->setCheckable(true);
885                           connect(action, SIGNAL(toggled(bool)),
886                                   parent(), SLOT(setShowRange(bool)));
887                           connect(parent(), SIGNAL(showRangeChanged(bool)),
888                                   action, SLOT(setOn(bool)));
889                           action->setChecked(showRange);
890                           headerPopup->addAction(action);
891                         action = new QAction("Show Data", this);
892                           action->setCheckable(true);
893                           connect(action, SIGNAL(toggled(bool)),
894                                   parent(), SLOT(setShowData(bool)));
895                           connect(parent(), SIGNAL(showDataChanged(bool)),
896                                   action, SLOT(setOn(bool)));
897                           action->setChecked(showData);
898                           headerPopup->addAction(action);
899                 }
900                 headerPopup->exec(e->globalPos());
901                 e->accept();
902         } else
903                 e->ignore();
904 }
905
906 ConfigView*ConfigView::viewList;
907 QAction *ConfigView::showNormalAction;
908 QAction *ConfigView::showAllAction;
909 QAction *ConfigView::showPromptAction;
910
911 ConfigView::ConfigView(QWidget* parent, const char *name)
912         : Parent(parent)
913 {
914         setObjectName(name);
915         QVBoxLayout *verticalLayout = new QVBoxLayout(this);
916         verticalLayout->setContentsMargins(0, 0, 0, 0);
917
918         list = new ConfigList(this);
919         verticalLayout->addWidget(list);
920         lineEdit = new ConfigLineEdit(this);
921         lineEdit->hide();
922         verticalLayout->addWidget(lineEdit);
923
924         this->nextView = viewList;
925         viewList = this;
926 }
927
928 ConfigView::~ConfigView(void)
929 {
930         ConfigView** vp;
931
932         for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
933                 if (*vp == this) {
934                         *vp = nextView;
935                         break;
936                 }
937         }
938 }
939
940 void ConfigView::setOptionMode(QAction *act)
941 {
942         if (act == showNormalAction)
943                 list->optMode = normalOpt;
944         else if (act == showAllAction)
945                 list->optMode = allOpt;
946         else
947                 list->optMode = promptOpt;
948
949         list->updateListAll();
950 }
951
952 void ConfigView::setShowName(bool b)
953 {
954         if (list->showName != b) {
955                 list->showName = b;
956                 list->reinit();
957                 emit showNameChanged(b);
958         }
959 }
960
961 void ConfigView::setShowRange(bool b)
962 {
963         if (list->showRange != b) {
964                 list->showRange = b;
965                 list->reinit();
966                 emit showRangeChanged(b);
967         }
968 }
969
970 void ConfigView::setShowData(bool b)
971 {
972         if (list->showData != b) {
973                 list->showData = b;
974                 list->reinit();
975                 emit showDataChanged(b);
976         }
977 }
978
979 void ConfigList::setAllOpen(bool open)
980 {
981         QTreeWidgetItemIterator it(this);
982
983         while (*it) {
984                 (*it)->setExpanded(open);
985
986                 ++it;
987         }
988 }
989
990 void ConfigView::updateList(ConfigItem* item)
991 {
992         ConfigView* v;
993
994         for (v = viewList; v; v = v->nextView)
995                 v->list->updateList(item);
996 }
997
998 void ConfigView::updateListAll(void)
999 {
1000         ConfigView* v;
1001
1002         for (v = viewList; v; v = v->nextView)
1003                 v->list->updateListAll();
1004 }
1005
1006 ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
1007         : Parent(parent), sym(0), _menu(0)
1008 {
1009         setObjectName(name);
1010
1011
1012         if (!objectName().isEmpty()) {
1013                 configSettings->beginGroup(objectName());
1014                 setShowDebug(configSettings->value("/showDebug", false).toBool());
1015                 configSettings->endGroup();
1016                 connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1017         }
1018 }
1019
1020 void ConfigInfoView::saveSettings(void)
1021 {
1022         if (!objectName().isEmpty()) {
1023                 configSettings->beginGroup(objectName());
1024                 configSettings->setValue("/showDebug", showDebug());
1025                 configSettings->endGroup();
1026         }
1027 }
1028
1029 void ConfigInfoView::setShowDebug(bool b)
1030 {
1031         if (_showDebug != b) {
1032                 _showDebug = b;
1033                 if (_menu)
1034                         menuInfo();
1035                 else if (sym)
1036                         symbolInfo();
1037                 emit showDebugChanged(b);
1038         }
1039 }
1040
1041 void ConfigInfoView::setInfo(struct menu *m)
1042 {
1043         if (_menu == m)
1044                 return;
1045         _menu = m;
1046         sym = NULL;
1047         if (!_menu)
1048                 clear();
1049         else
1050                 menuInfo();
1051 }
1052
1053 void ConfigInfoView::symbolInfo(void)
1054 {
1055         QString str;
1056
1057         str += "<big>Symbol: <b>";
1058         str += print_filter(sym->name);
1059         str += "</b></big><br><br>value: ";
1060         str += print_filter(sym_get_string_value(sym));
1061         str += "<br>visibility: ";
1062         str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
1063         str += "<br>";
1064         str += debug_info(sym);
1065
1066         setText(str);
1067 }
1068
1069 void ConfigInfoView::menuInfo(void)
1070 {
1071         struct symbol* sym;
1072         QString head, debug, help;
1073
1074         sym = _menu->sym;
1075         if (sym) {
1076                 if (_menu->prompt) {
1077                         head += "<big><b>";
1078                         head += print_filter(_menu->prompt->text);
1079                         head += "</b></big>";
1080                         if (sym->name) {
1081                                 head += " (";
1082                                 if (showDebug())
1083                                         head += QString().sprintf("<a href=\"s%p\">", sym);
1084                                 head += print_filter(sym->name);
1085                                 if (showDebug())
1086                                         head += "</a>";
1087                                 head += ")";
1088                         }
1089                 } else if (sym->name) {
1090                         head += "<big><b>";
1091                         if (showDebug())
1092                                 head += QString().sprintf("<a href=\"s%p\">", sym);
1093                         head += print_filter(sym->name);
1094                         if (showDebug())
1095                                 head += "</a>";
1096                         head += "</b></big>";
1097                 }
1098                 head += "<br><br>";
1099
1100                 if (showDebug())
1101                         debug = debug_info(sym);
1102
1103                 struct gstr help_gstr = str_new();
1104                 menu_get_ext_help(_menu, &help_gstr);
1105                 help = print_filter(str_get(&help_gstr));
1106                 str_free(&help_gstr);
1107         } else if (_menu->prompt) {
1108                 head += "<big><b>";
1109                 head += print_filter(_menu->prompt->text);
1110                 head += "</b></big><br><br>";
1111                 if (showDebug()) {
1112                         if (_menu->prompt->visible.expr) {
1113                                 debug += "&nbsp;&nbsp;dep: ";
1114                                 expr_print(_menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
1115                                 debug += "<br><br>";
1116                         }
1117                 }
1118         }
1119         if (showDebug())
1120                 debug += QString().sprintf("defined at %s:%d<br><br>", _menu->file->name, _menu->lineno);
1121
1122         setText(head + debug + help);
1123 }
1124
1125 QString ConfigInfoView::debug_info(struct symbol *sym)
1126 {
1127         QString debug;
1128
1129         debug += "type: ";
1130         debug += print_filter(sym_type_name(sym->type));
1131         if (sym_is_choice(sym))
1132                 debug += " (choice)";
1133         debug += "<br>";
1134         if (sym->rev_dep.expr) {
1135                 debug += "reverse dep: ";
1136                 expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
1137                 debug += "<br>";
1138         }
1139         for (struct property *prop = sym->prop; prop; prop = prop->next) {
1140                 switch (prop->type) {
1141                 case P_PROMPT:
1142                 case P_MENU:
1143                         debug += QString().sprintf("prompt: <a href=\"m%p\">", prop->menu);
1144                         debug += print_filter(prop->text);
1145                         debug += "</a><br>";
1146                         break;
1147                 case P_DEFAULT:
1148                 case P_SELECT:
1149                 case P_RANGE:
1150                         debug += prop_get_type_name(prop->type);
1151                         debug += ": ";
1152                         expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1153                         debug += "<br>";
1154                         break;
1155                 case P_CHOICE:
1156                         if (sym_is_choice(sym)) {
1157                                 debug += "choice: ";
1158                                 expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1159                                 debug += "<br>";
1160                         }
1161                         break;
1162                 default:
1163                         debug += "unknown property: ";
1164                         debug += prop_get_type_name(prop->type);
1165                         debug += "<br>";
1166                 }
1167                 if (prop->visible.expr) {
1168                         debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1169                         expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
1170                         debug += "<br>";
1171                 }
1172         }
1173         debug += "<br>";
1174
1175         return debug;
1176 }
1177
1178 QString ConfigInfoView::print_filter(const QString &str)
1179 {
1180         QRegExp re("[<>&\"\\n]");
1181         QString res = str;
1182         for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
1183                 switch (res[i].toLatin1()) {
1184                 case '<':
1185                         res.replace(i, 1, "&lt;");
1186                         i += 4;
1187                         break;
1188                 case '>':
1189                         res.replace(i, 1, "&gt;");
1190                         i += 4;
1191                         break;
1192                 case '&':
1193                         res.replace(i, 1, "&amp;");
1194                         i += 5;
1195                         break;
1196                 case '"':
1197                         res.replace(i, 1, "&quot;");
1198                         i += 6;
1199                         break;
1200                 case '\n':
1201                         res.replace(i, 1, "<br>");
1202                         i += 4;
1203                         break;
1204                 }
1205         }
1206         return res;
1207 }
1208
1209 void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
1210 {
1211         QString* text = reinterpret_cast<QString*>(data);
1212         QString str2 = print_filter(str);
1213
1214         if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
1215                 *text += QString().sprintf("<a href=\"s%p\">", sym);
1216                 *text += str2;
1217                 *text += "</a>";
1218         } else
1219                 *text += str2;
1220 }
1221
1222 QMenu* ConfigInfoView::createStandardContextMenu(const QPoint & pos)
1223 {
1224         QMenu* popup = Parent::createStandardContextMenu(pos);
1225         QAction* action = new QAction("Show Debug Info", popup);
1226           action->setCheckable(true);
1227           connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
1228           connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool)));
1229           action->setChecked(showDebug());
1230         popup->addSeparator();
1231         popup->addAction(action);
1232         return popup;
1233 }
1234
1235 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e)
1236 {
1237         Parent::contextMenuEvent(e);
1238 }
1239
1240 ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name)
1241         : Parent(parent), result(NULL)
1242 {
1243         setObjectName(name);
1244         setWindowTitle("Search Config");
1245
1246         QVBoxLayout* layout1 = new QVBoxLayout(this);
1247         layout1->setContentsMargins(11, 11, 11, 11);
1248         layout1->setSpacing(6);
1249         QHBoxLayout* layout2 = new QHBoxLayout(0);
1250         layout2->setContentsMargins(0, 0, 0, 0);
1251         layout2->setSpacing(6);
1252         layout2->addWidget(new QLabel("Find:", this));
1253         editField = new QLineEdit(this);
1254         connect(editField, SIGNAL(returnPressed()), SLOT(search()));
1255         layout2->addWidget(editField);
1256         searchButton = new QPushButton("Search", this);
1257         searchButton->setAutoDefault(false);
1258         connect(searchButton, SIGNAL(clicked()), SLOT(search()));
1259         layout2->addWidget(searchButton);
1260         layout1->addLayout(layout2);
1261
1262         split = new QSplitter(this);
1263         split->setOrientation(Qt::Vertical);
1264         list = new ConfigView(split, name);
1265         list->list->mode = listMode;
1266         info = new ConfigInfoView(split, name);
1267         connect(list->list, SIGNAL(menuChanged(struct menu *)),
1268                 info, SLOT(setInfo(struct menu *)));
1269         connect(list->list, SIGNAL(menuChanged(struct menu *)),
1270                 parent, SLOT(setMenuLink(struct menu *)));
1271
1272         layout1->addWidget(split);
1273
1274         if (name) {
1275                 QVariant x, y;
1276                 int width, height;
1277                 bool ok;
1278
1279                 configSettings->beginGroup(name);
1280                 width = configSettings->value("/window width", parent->width() / 2).toInt();
1281                 height = configSettings->value("/window height", parent->height() / 2).toInt();
1282                 resize(width, height);
1283                 x = configSettings->value("/window x");
1284                 y = configSettings->value("/window y");
1285                 if ((x.isValid())&&(y.isValid()))
1286                         move(x.toInt(), y.toInt());
1287                 QList<int> sizes = configSettings->readSizes("/split", &ok);
1288                 if (ok)
1289                         split->setSizes(sizes);
1290                 configSettings->endGroup();
1291                 connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1292         }
1293 }
1294
1295 void ConfigSearchWindow::saveSettings(void)
1296 {
1297         if (!objectName().isEmpty()) {
1298                 configSettings->beginGroup(objectName());
1299                 configSettings->setValue("/window x", pos().x());
1300                 configSettings->setValue("/window y", pos().y());
1301                 configSettings->setValue("/window width", size().width());
1302                 configSettings->setValue("/window height", size().height());
1303                 configSettings->writeSizes("/split", split->sizes());
1304                 configSettings->endGroup();
1305         }
1306 }
1307
1308 void ConfigSearchWindow::search(void)
1309 {
1310         struct symbol **p;
1311         struct property *prop;
1312         ConfigItem *lastItem = NULL;
1313
1314         free(result);
1315         list->list->clear();
1316         info->clear();
1317
1318         result = sym_re_search(editField->text().toLatin1());
1319         if (!result)
1320                 return;
1321         for (p = result; *p; p++) {
1322                 for_all_prompts((*p), prop)
1323                         lastItem = new ConfigItem(list->list, lastItem, prop->menu,
1324                                                   menu_is_visible(prop->menu));
1325         }
1326 }
1327
1328 /*
1329  * Construct the complete config widget
1330  */
1331 ConfigMainWindow::ConfigMainWindow(void)
1332         : searchWindow(0)
1333 {
1334         QMenuBar* menu;
1335         bool ok = true;
1336         QVariant x, y;
1337         int width, height;
1338         char title[256];
1339
1340         QDesktopWidget *d = configApp->desktop();
1341         snprintf(title, sizeof(title), "%s%s",
1342                 rootmenu.prompt->text,
1343                 ""
1344                 );
1345         setWindowTitle(title);
1346
1347         width = configSettings->value("/window width", d->width() - 64).toInt();
1348         height = configSettings->value("/window height", d->height() - 64).toInt();
1349         resize(width, height);
1350         x = configSettings->value("/window x");
1351         y = configSettings->value("/window y");
1352         if ((x.isValid())&&(y.isValid()))
1353                 move(x.toInt(), y.toInt());
1354
1355         split1 = new QSplitter(this);
1356         split1->setOrientation(Qt::Horizontal);
1357         setCentralWidget(split1);
1358
1359         menuView = new ConfigView(split1, "menu");
1360         menuList = menuView->list;
1361
1362         split2 = new QSplitter(split1);
1363         split2->setOrientation(Qt::Vertical);
1364
1365         // create config tree
1366         configView = new ConfigView(split2, "config");
1367         configList = configView->list;
1368
1369         helpText = new ConfigInfoView(split2, "help");
1370
1371         setTabOrder(configList, helpText);
1372         configList->setFocus();
1373
1374         menu = menuBar();
1375         toolBar = new QToolBar("Tools", this);
1376         addToolBar(toolBar);
1377
1378         backAction = new QAction(QPixmap(xpm_back), "Back", this);
1379           connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack()));
1380           backAction->setEnabled(false);
1381         QAction *quitAction = new QAction("&Quit", this);
1382         quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1383           connect(quitAction, SIGNAL(triggered(bool)), SLOT(close()));
1384         QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1385         loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
1386           connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig()));
1387         saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1388         saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
1389           connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig()));
1390         conf_set_changed_callback(conf_changed);
1391         // Set saveAction's initial state
1392         conf_changed();
1393         configname = xstrdup(conf_get_configname());
1394
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
1521 void ConfigMainWindow::loadConfig(void)
1522 {
1523         QString str;
1524         QByteArray ba;
1525         const char *name;
1526
1527         str = QFileDialog::getOpenFileName(this, "", configname);
1528         if (str.isNull())
1529                 return;
1530
1531         ba = str.toLocal8Bit();
1532         name = ba.data();
1533
1534         if (conf_read(name))
1535                 QMessageBox::information(this, "qconf", "Unable to load configuration!");
1536
1537         free(configname);
1538         configname = xstrdup(name);
1539
1540         ConfigView::updateListAll();
1541 }
1542
1543 bool ConfigMainWindow::saveConfig(void)
1544 {
1545         if (conf_write(configname)) {
1546                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1547                 return false;
1548         }
1549         conf_write_autoconf(0);
1550
1551         return true;
1552 }
1553
1554 void ConfigMainWindow::saveConfigAs(void)
1555 {
1556         QString str;
1557         QByteArray ba;
1558         const char *name;
1559
1560         str = QFileDialog::getSaveFileName(this, "", configname);
1561         if (str.isNull())
1562                 return;
1563
1564         ba = str.toLocal8Bit();
1565         name = ba.data();
1566
1567         if (conf_write(name)) {
1568                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1569         }
1570         conf_write_autoconf(0);
1571
1572         free(configname);
1573         configname = xstrdup(name);
1574 }
1575
1576 void ConfigMainWindow::searchConfig(void)
1577 {
1578         if (!searchWindow)
1579                 searchWindow = new ConfigSearchWindow(this, "search");
1580         searchWindow->show();
1581 }
1582
1583 void ConfigMainWindow::changeMenu(struct menu *menu)
1584 {
1585         configList->setRootMenu(menu);
1586         if (configList->rootEntry->parent == &rootmenu)
1587                 backAction->setEnabled(false);
1588         else
1589                 backAction->setEnabled(true);
1590 }
1591
1592 void ConfigMainWindow::setMenuLink(struct menu *menu)
1593 {
1594         struct menu *parent;
1595         ConfigList* list = NULL;
1596         ConfigItem* item;
1597
1598         if (configList->menuSkip(menu))
1599                 return;
1600
1601         switch (configList->mode) {
1602         case singleMode:
1603                 list = configList;
1604                 parent = menu_get_parent_menu(menu);
1605                 if (!parent)
1606                         return;
1607                 list->setRootMenu(parent);
1608                 break;
1609         case symbolMode:
1610                 if (menu->flags & MENU_ROOT) {
1611                         configList->setRootMenu(menu);
1612                         configList->clearSelection();
1613                         list = menuList;
1614                 } else {
1615                         list = configList;
1616                         parent = menu_get_parent_menu(menu->parent);
1617                         if (!parent)
1618                                 return;
1619                         item = menuList->findConfigItem(parent);
1620                         if (item) {
1621                                 item->setSelected(true);
1622                                 menuList->scrollToItem(item);
1623                         }
1624                         list->setRootMenu(parent);
1625                 }
1626                 break;
1627         case fullMode:
1628                 list = configList;
1629                 break;
1630         default:
1631                 break;
1632         }
1633
1634         if (list) {
1635                 item = list->findConfigItem(menu);
1636                 if (item) {
1637                         item->setSelected(true);
1638                         list->scrollToItem(item);
1639                         list->setFocus();
1640                 }
1641         }
1642 }
1643
1644 void ConfigMainWindow::listFocusChanged(void)
1645 {
1646         if (menuList->mode == menuMode)
1647                 configList->clearSelection();
1648 }
1649
1650 void ConfigMainWindow::goBack(void)
1651 {
1652         ConfigItem* item, *oldSelection;
1653
1654         configList->setParentMenu();
1655         if (configList->rootEntry == &rootmenu)
1656                 backAction->setEnabled(false);
1657
1658         if (menuList->selectedItems().count() == 0)
1659                 return;
1660
1661         item = (ConfigItem*)menuList->selectedItems().first();
1662         oldSelection = item;
1663         while (item) {
1664                 if (item->menu == configList->rootEntry) {
1665                         oldSelection->setSelected(false);
1666                         item->setSelected(true);
1667                         break;
1668                 }
1669                 item = (ConfigItem*)item->parent();
1670         }
1671 }
1672
1673 void ConfigMainWindow::showSingleView(void)
1674 {
1675         singleViewAction->setEnabled(false);
1676         singleViewAction->setChecked(true);
1677         splitViewAction->setEnabled(true);
1678         splitViewAction->setChecked(false);
1679         fullViewAction->setEnabled(true);
1680         fullViewAction->setChecked(false);
1681
1682         menuView->hide();
1683         menuList->setRootMenu(0);
1684         configList->mode = singleMode;
1685         if (configList->rootEntry == &rootmenu)
1686                 configList->updateListAll();
1687         else
1688                 configList->setRootMenu(&rootmenu);
1689         configList->setFocus();
1690 }
1691
1692 void ConfigMainWindow::showSplitView(void)
1693 {
1694         singleViewAction->setEnabled(true);
1695         singleViewAction->setChecked(false);
1696         splitViewAction->setEnabled(false);
1697         splitViewAction->setChecked(true);
1698         fullViewAction->setEnabled(true);
1699         fullViewAction->setChecked(false);
1700
1701         configList->mode = symbolMode;
1702         if (configList->rootEntry == &rootmenu)
1703                 configList->updateListAll();
1704         else
1705                 configList->setRootMenu(&rootmenu);
1706         configList->setAllOpen(true);
1707         configApp->processEvents();
1708         menuList->mode = menuMode;
1709         menuList->setRootMenu(&rootmenu);
1710         menuList->setAllOpen(true);
1711         menuView->show();
1712         menuList->setFocus();
1713 }
1714
1715 void ConfigMainWindow::showFullView(void)
1716 {
1717         singleViewAction->setEnabled(true);
1718         singleViewAction->setChecked(false);
1719         splitViewAction->setEnabled(true);
1720         splitViewAction->setChecked(false);
1721         fullViewAction->setEnabled(false);
1722         fullViewAction->setChecked(true);
1723
1724         menuView->hide();
1725         menuList->setRootMenu(0);
1726         configList->mode = fullMode;
1727         if (configList->rootEntry == &rootmenu)
1728                 configList->updateListAll();
1729         else
1730                 configList->setRootMenu(&rootmenu);
1731         configList->setFocus();
1732 }
1733
1734 /*
1735  * ask for saving configuration before quitting
1736  * TODO ask only when something changed
1737  */
1738 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1739 {
1740         if (!conf_get_changed()) {
1741                 e->accept();
1742                 return;
1743         }
1744         QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1745                         QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1746         mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1747         mb.setButtonText(QMessageBox::No, "&Discard Changes");
1748         mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1749         switch (mb.exec()) {
1750         case QMessageBox::Yes:
1751                 if (saveConfig())
1752                         e->accept();
1753                 else
1754                         e->ignore();
1755                 break;
1756         case QMessageBox::No:
1757                 e->accept();
1758                 break;
1759         case QMessageBox::Cancel:
1760                 e->ignore();
1761                 break;
1762         }
1763 }
1764
1765 void ConfigMainWindow::showIntro(void)
1766 {
1767         static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
1768                 "For each option, a blank box indicates the feature is disabled, a check\n"
1769                 "indicates it is enabled, and a dot indicates that it is to be compiled\n"
1770                 "as a module.  Clicking on the box will cycle through the three states.\n\n"
1771                 "If you do not see an option (e.g., a device driver) that you believe\n"
1772                 "should be present, try turning on Show All Options under the Options menu.\n"
1773                 "Although there is no cross reference yet to help you figure out what other\n"
1774                 "options must be enabled to support the option you are interested in, you can\n"
1775                 "still view the help of a grayed-out option.\n\n"
1776                 "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1777                 "which you can then match by examining other options.\n\n";
1778
1779         QMessageBox::information(this, "qconf", str);
1780 }
1781
1782 void ConfigMainWindow::showAbout(void)
1783 {
1784         static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1785                 "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
1786                 "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1787
1788         QMessageBox::information(this, "qconf", str);
1789 }
1790
1791 void ConfigMainWindow::saveSettings(void)
1792 {
1793         configSettings->setValue("/window x", pos().x());
1794         configSettings->setValue("/window y", pos().y());
1795         configSettings->setValue("/window width", size().width());
1796         configSettings->setValue("/window height", size().height());
1797
1798         QString entry;
1799         switch(configList->mode) {
1800         case singleMode :
1801                 entry = "single";
1802                 break;
1803
1804         case symbolMode :
1805                 entry = "split";
1806                 break;
1807
1808         case fullMode :
1809                 entry = "full";
1810                 break;
1811
1812         default:
1813                 break;
1814         }
1815         configSettings->setValue("/listMode", entry);
1816
1817         configSettings->writeSizes("/split1", split1->sizes());
1818         configSettings->writeSizes("/split2", split2->sizes());
1819 }
1820
1821 void ConfigMainWindow::conf_changed(void)
1822 {
1823         if (saveAction)
1824                 saveAction->setEnabled(conf_get_changed());
1825 }
1826
1827 void fixup_rootmenu(struct menu *menu)
1828 {
1829         struct menu *child;
1830         static int menu_cnt = 0;
1831
1832         menu->flags |= MENU_ROOT;
1833         for (child = menu->list; child; child = child->next) {
1834                 if (child->prompt && child->prompt->type == P_MENU) {
1835                         menu_cnt++;
1836                         fixup_rootmenu(child);
1837                         menu_cnt--;
1838                 } else if (!menu_cnt)
1839                         fixup_rootmenu(child);
1840         }
1841 }
1842
1843 static const char *progname;
1844
1845 static void usage(void)
1846 {
1847         printf("%s [-s] <config>\n", progname);
1848         exit(0);
1849 }
1850
1851 int main(int ac, char** av)
1852 {
1853         ConfigMainWindow* v;
1854         const char *name;
1855
1856         progname = av[0];
1857         configApp = new QApplication(ac, av);
1858         if (ac > 1 && av[1][0] == '-') {
1859                 switch (av[1][1]) {
1860                 case 's':
1861                         conf_set_message_callback(NULL);
1862                         break;
1863                 case 'h':
1864                 case '?':
1865                         usage();
1866                 }
1867                 name = av[2];
1868         } else
1869                 name = av[1];
1870         if (!name)
1871                 usage();
1872
1873         conf_parse(name);
1874         fixup_rootmenu(&rootmenu);
1875         conf_read(NULL);
1876         //zconfdump(stdout);
1877
1878         configSettings = new ConfigSettings();
1879         configSettings->beginGroup("/kconfig/qconf");
1880         v = new ConfigMainWindow();
1881
1882         //zconfdump(stdout);
1883         configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1884         configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1885         v->show();
1886         configApp->exec();
1887
1888         configSettings->endGroup();
1889         delete configSettings;
1890         delete v;
1891         delete configApp;
1892
1893         return 0;
1894 }