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