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