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