build: scripts/config - update to kconfig-v5.6
[oweals/openwrt.git] / scripts / config / 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 (e->y() <= header()->geometry().bottom()) {
873                 if (!headerPopup) {
874                         QAction *action;
875
876                         headerPopup = new QMenu(this);
877                         action = new QAction("Show Name", this);
878                           action->setCheckable(true);
879                           connect(action, SIGNAL(toggled(bool)),
880                                   parent(), SLOT(setShowName(bool)));
881                           connect(parent(), SIGNAL(showNameChanged(bool)),
882                                   action, SLOT(setOn(bool)));
883                           action->setChecked(showName);
884                           headerPopup->addAction(action);
885                         action = new QAction("Show Range", this);
886                           action->setCheckable(true);
887                           connect(action, SIGNAL(toggled(bool)),
888                                   parent(), SLOT(setShowRange(bool)));
889                           connect(parent(), SIGNAL(showRangeChanged(bool)),
890                                   action, SLOT(setOn(bool)));
891                           action->setChecked(showRange);
892                           headerPopup->addAction(action);
893                         action = new QAction("Show Data", this);
894                           action->setCheckable(true);
895                           connect(action, SIGNAL(toggled(bool)),
896                                   parent(), SLOT(setShowData(bool)));
897                           connect(parent(), SIGNAL(showDataChanged(bool)),
898                                   action, SLOT(setOn(bool)));
899                           action->setChecked(showData);
900                           headerPopup->addAction(action);
901                 }
902                 headerPopup->exec(e->globalPos());
903                 e->accept();
904         } else
905                 e->ignore();
906 }
907
908 ConfigView*ConfigView::viewList;
909 QAction *ConfigView::showNormalAction;
910 QAction *ConfigView::showAllAction;
911 QAction *ConfigView::showPromptAction;
912
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           action->setCheckable(true);
1229           connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
1230           connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool)));
1231           action->setChecked(showDebug());
1232         popup->addSeparator();
1233         popup->addAction(action);
1234         return popup;
1235 }
1236
1237 void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e)
1238 {
1239         Parent::contextMenuEvent(e);
1240 }
1241
1242 ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name)
1243         : Parent(parent), result(NULL)
1244 {
1245         setObjectName(name);
1246         setWindowTitle("Search Config");
1247
1248         QVBoxLayout* layout1 = new QVBoxLayout(this);
1249         layout1->setContentsMargins(11, 11, 11, 11);
1250         layout1->setSpacing(6);
1251         QHBoxLayout* layout2 = new QHBoxLayout(0);
1252         layout2->setContentsMargins(0, 0, 0, 0);
1253         layout2->setSpacing(6);
1254         layout2->addWidget(new QLabel("Find:", this));
1255         editField = new QLineEdit(this);
1256         connect(editField, SIGNAL(returnPressed()), SLOT(search()));
1257         layout2->addWidget(editField);
1258         searchButton = new QPushButton("Search", this);
1259         searchButton->setAutoDefault(false);
1260         connect(searchButton, SIGNAL(clicked()), SLOT(search()));
1261         layout2->addWidget(searchButton);
1262         layout1->addLayout(layout2);
1263
1264         split = new QSplitter(this);
1265         split->setOrientation(Qt::Vertical);
1266         list = new ConfigView(split, name);
1267         list->list->mode = listMode;
1268         info = new ConfigInfoView(split, name);
1269         connect(list->list, SIGNAL(menuChanged(struct menu *)),
1270                 info, SLOT(setInfo(struct menu *)));
1271         connect(list->list, SIGNAL(menuChanged(struct menu *)),
1272                 parent, SLOT(setMenuLink(struct menu *)));
1273
1274         layout1->addWidget(split);
1275
1276         if (name) {
1277                 QVariant x, y;
1278                 int width, height;
1279                 bool ok;
1280
1281                 configSettings->beginGroup(name);
1282                 width = configSettings->value("/window width", parent->width() / 2).toInt();
1283                 height = configSettings->value("/window height", parent->height() / 2).toInt();
1284                 resize(width, height);
1285                 x = configSettings->value("/window x");
1286                 y = configSettings->value("/window y");
1287                 if ((x.isValid())&&(y.isValid()))
1288                         move(x.toInt(), y.toInt());
1289                 QList<int> sizes = configSettings->readSizes("/split", &ok);
1290                 if (ok)
1291                         split->setSizes(sizes);
1292                 configSettings->endGroup();
1293                 connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
1294         }
1295 }
1296
1297 void ConfigSearchWindow::saveSettings(void)
1298 {
1299         if (!objectName().isEmpty()) {
1300                 configSettings->beginGroup(objectName());
1301                 configSettings->setValue("/window x", pos().x());
1302                 configSettings->setValue("/window y", pos().y());
1303                 configSettings->setValue("/window width", size().width());
1304                 configSettings->setValue("/window height", size().height());
1305                 configSettings->writeSizes("/split", split->sizes());
1306                 configSettings->endGroup();
1307         }
1308 }
1309
1310 void ConfigSearchWindow::search(void)
1311 {
1312         struct symbol **p;
1313         struct property *prop;
1314         ConfigItem *lastItem = NULL;
1315
1316         free(result);
1317         list->list->clear();
1318         info->clear();
1319
1320         result = sym_re_search(editField->text().toLatin1());
1321         if (!result)
1322                 return;
1323         for (p = result; *p; p++) {
1324                 for_all_prompts((*p), prop)
1325                         lastItem = new ConfigItem(list->list, lastItem, prop->menu,
1326                                                   menu_is_visible(prop->menu));
1327         }
1328 }
1329
1330 /*
1331  * Construct the complete config widget
1332  */
1333 ConfigMainWindow::ConfigMainWindow(void)
1334         : searchWindow(0)
1335 {
1336         QMenuBar* menu;
1337         bool ok = true;
1338         QVariant x, y;
1339         int width, height;
1340         char title[256];
1341
1342         QDesktopWidget *d = configApp->desktop();
1343         snprintf(title, sizeof(title), "%s%s",
1344                 rootmenu.prompt->text,
1345                 ""
1346                 );
1347         setWindowTitle(title);
1348
1349         width = configSettings->value("/window width", d->width() - 64).toInt();
1350         height = configSettings->value("/window height", d->height() - 64).toInt();
1351         resize(width, height);
1352         x = configSettings->value("/window x");
1353         y = configSettings->value("/window y");
1354         if ((x.isValid())&&(y.isValid()))
1355                 move(x.toInt(), y.toInt());
1356
1357         split1 = new QSplitter(this);
1358         split1->setOrientation(Qt::Horizontal);
1359         setCentralWidget(split1);
1360
1361         menuView = new ConfigView(split1, "menu");
1362         menuList = menuView->list;
1363
1364         split2 = new QSplitter(split1);
1365         split2->setOrientation(Qt::Vertical);
1366
1367         // create config tree
1368         configView = new ConfigView(split2, "config");
1369         configList = configView->list;
1370
1371         helpText = new ConfigInfoView(split2, "help");
1372
1373         setTabOrder(configList, helpText);
1374         configList->setFocus();
1375
1376         menu = menuBar();
1377         toolBar = new QToolBar("Tools", this);
1378         addToolBar(toolBar);
1379
1380         backAction = new QAction(QPixmap(xpm_back), "Back", this);
1381           connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack()));
1382           backAction->setEnabled(false);
1383         QAction *quitAction = new QAction("&Quit", this);
1384         quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1385           connect(quitAction, SIGNAL(triggered(bool)), SLOT(close()));
1386         QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
1387         loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
1388           connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig()));
1389         saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
1390         saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
1391           connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig()));
1392         conf_set_changed_callback(conf_changed);
1393         // Set saveAction's initial state
1394         conf_changed();
1395         configname = xstrdup(conf_get_configname());
1396
1397         QAction *saveAsAction = new QAction("Save &As...", this);
1398           connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs()));
1399         QAction *searchAction = new QAction("&Find", this);
1400         searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
1401           connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig()));
1402         singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1403         singleViewAction->setCheckable(true);
1404           connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView()));
1405         splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1406         splitViewAction->setCheckable(true);
1407           connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView()));
1408         fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1409         fullViewAction->setCheckable(true);
1410           connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView()));
1411
1412         QAction *showNameAction = new QAction("Show Name", this);
1413           showNameAction->setCheckable(true);
1414           connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
1415           showNameAction->setChecked(configView->showName());
1416         QAction *showRangeAction = new QAction("Show Range", this);
1417           showRangeAction->setCheckable(true);
1418           connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
1419         QAction *showDataAction = new QAction("Show Data", this);
1420           showDataAction->setCheckable(true);
1421           connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
1422
1423         QActionGroup *optGroup = new QActionGroup(this);
1424         optGroup->setExclusive(true);
1425         connect(optGroup, SIGNAL(triggered(QAction*)), configView,
1426                 SLOT(setOptionMode(QAction *)));
1427         connect(optGroup, SIGNAL(triggered(QAction *)), menuView,
1428                 SLOT(setOptionMode(QAction *)));
1429
1430         configView->showNormalAction = new QAction("Show Normal Options", optGroup);
1431         configView->showAllAction = new QAction("Show All Options", optGroup);
1432         configView->showPromptAction = new QAction("Show Prompt Options", optGroup);
1433         configView->showNormalAction->setCheckable(true);
1434         configView->showAllAction->setCheckable(true);
1435         configView->showPromptAction->setCheckable(true);
1436
1437         QAction *showDebugAction = new QAction("Show Debug Info", this);
1438           showDebugAction->setCheckable(true);
1439           connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
1440           showDebugAction->setChecked(helpText->showDebug());
1441
1442         QAction *showIntroAction = new QAction("Introduction", this);
1443           connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro()));
1444         QAction *showAboutAction = new QAction("About", this);
1445           connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout()));
1446
1447         // init tool bar
1448         toolBar->addAction(backAction);
1449         toolBar->addSeparator();
1450         toolBar->addAction(loadAction);
1451         toolBar->addAction(saveAction);
1452         toolBar->addSeparator();
1453         toolBar->addAction(singleViewAction);
1454         toolBar->addAction(splitViewAction);
1455         toolBar->addAction(fullViewAction);
1456
1457         // create config menu
1458         QMenu* config = menu->addMenu("&File");
1459         config->addAction(loadAction);
1460         config->addAction(saveAction);
1461         config->addAction(saveAsAction);
1462         config->addSeparator();
1463         config->addAction(quitAction);
1464
1465         // create edit menu
1466         QMenu* editMenu = menu->addMenu("&Edit");
1467         editMenu->addAction(searchAction);
1468
1469         // create options menu
1470         QMenu* optionMenu = menu->addMenu("&Option");
1471         optionMenu->addAction(showNameAction);
1472         optionMenu->addAction(showRangeAction);
1473         optionMenu->addAction(showDataAction);
1474         optionMenu->addSeparator();
1475         optionMenu->addActions(optGroup->actions());
1476         optionMenu->addSeparator();
1477         optionMenu->addAction(showDebugAction);
1478
1479         // create help menu
1480         menu->addSeparator();
1481         QMenu* helpMenu = menu->addMenu("&Help");
1482         helpMenu->addAction(showIntroAction);
1483         helpMenu->addAction(showAboutAction);
1484
1485         connect(configList, SIGNAL(menuChanged(struct menu *)),
1486                 helpText, SLOT(setInfo(struct menu *)));
1487         connect(configList, SIGNAL(menuSelected(struct menu *)),
1488                 SLOT(changeMenu(struct menu *)));
1489         connect(configList, SIGNAL(parentSelected()),
1490                 SLOT(goBack()));
1491         connect(menuList, SIGNAL(menuChanged(struct menu *)),
1492                 helpText, SLOT(setInfo(struct menu *)));
1493         connect(menuList, SIGNAL(menuSelected(struct menu *)),
1494                 SLOT(changeMenu(struct menu *)));
1495
1496         connect(configList, SIGNAL(gotFocus(struct menu *)),
1497                 helpText, SLOT(setInfo(struct menu *)));
1498         connect(menuList, SIGNAL(gotFocus(struct menu *)),
1499                 helpText, SLOT(setInfo(struct menu *)));
1500         connect(menuList, SIGNAL(gotFocus(struct menu *)),
1501                 SLOT(listFocusChanged(void)));
1502         connect(helpText, SIGNAL(menuSelected(struct menu *)),
1503                 SLOT(setMenuLink(struct menu *)));
1504
1505         QString listMode = configSettings->value("/listMode", "symbol").toString();
1506         if (listMode == "single")
1507                 showSingleView();
1508         else if (listMode == "full")
1509                 showFullView();
1510         else /*if (listMode == "split")*/
1511                 showSplitView();
1512
1513         // UI setup done, restore splitter positions
1514         QList<int> sizes = configSettings->readSizes("/split1", &ok);
1515         if (ok)
1516                 split1->setSizes(sizes);
1517
1518         sizes = configSettings->readSizes("/split2", &ok);
1519         if (ok)
1520                 split2->setSizes(sizes);
1521 }
1522
1523 void ConfigMainWindow::loadConfig(void)
1524 {
1525         QString str;
1526         QByteArray ba;
1527         const char *name;
1528
1529         str = QFileDialog::getOpenFileName(this, "", configname);
1530         if (str.isNull())
1531                 return;
1532
1533         ba = str.toLocal8Bit();
1534         name = ba.data();
1535
1536         if (conf_read(name))
1537                 QMessageBox::information(this, "qconf", "Unable to load configuration!");
1538
1539         free(configname);
1540         configname = xstrdup(name);
1541
1542         ConfigView::updateListAll();
1543 }
1544
1545 bool ConfigMainWindow::saveConfig(void)
1546 {
1547         if (conf_write(configname)) {
1548                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1549                 return false;
1550         }
1551         conf_write_autoconf(0);
1552
1553         return true;
1554 }
1555
1556 void ConfigMainWindow::saveConfigAs(void)
1557 {
1558         QString str;
1559         QByteArray ba;
1560         const char *name;
1561
1562         str = QFileDialog::getSaveFileName(this, "", configname);
1563         if (str.isNull())
1564                 return;
1565
1566         ba = str.toLocal8Bit();
1567         name = ba.data();
1568
1569         if (conf_write(name)) {
1570                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1571         }
1572         conf_write_autoconf(0);
1573
1574         free(configname);
1575         configname = xstrdup(name);
1576 }
1577
1578 void ConfigMainWindow::searchConfig(void)
1579 {
1580         if (!searchWindow)
1581                 searchWindow = new ConfigSearchWindow(this, "search");
1582         searchWindow->show();
1583 }
1584
1585 void ConfigMainWindow::changeMenu(struct menu *menu)
1586 {
1587         configList->setRootMenu(menu);
1588         if (configList->rootEntry->parent == &rootmenu)
1589                 backAction->setEnabled(false);
1590         else
1591                 backAction->setEnabled(true);
1592 }
1593
1594 void ConfigMainWindow::setMenuLink(struct menu *menu)
1595 {
1596         struct menu *parent;
1597         ConfigList* list = NULL;
1598         ConfigItem* item;
1599
1600         if (configList->menuSkip(menu))
1601                 return;
1602
1603         switch (configList->mode) {
1604         case singleMode:
1605                 list = configList;
1606                 parent = menu_get_parent_menu(menu);
1607                 if (!parent)
1608                         return;
1609                 list->setRootMenu(parent);
1610                 break;
1611         case symbolMode:
1612                 if (menu->flags & MENU_ROOT) {
1613                         configList->setRootMenu(menu);
1614                         configList->clearSelection();
1615                         list = menuList;
1616                 } else {
1617                         list = configList;
1618                         parent = menu_get_parent_menu(menu->parent);
1619                         if (!parent)
1620                                 return;
1621                         item = menuList->findConfigItem(parent);
1622                         if (item) {
1623                                 item->setSelected(true);
1624                                 menuList->scrollToItem(item);
1625                         }
1626                         list->setRootMenu(parent);
1627                 }
1628                 break;
1629         case fullMode:
1630                 list = configList;
1631                 break;
1632         default:
1633                 break;
1634         }
1635
1636         if (list) {
1637                 item = list->findConfigItem(menu);
1638                 if (item) {
1639                         item->setSelected(true);
1640                         list->scrollToItem(item);
1641                         list->setFocus();
1642                 }
1643         }
1644 }
1645
1646 void ConfigMainWindow::listFocusChanged(void)
1647 {
1648         if (menuList->mode == menuMode)
1649                 configList->clearSelection();
1650 }
1651
1652 void ConfigMainWindow::goBack(void)
1653 {
1654         ConfigItem* item, *oldSelection;
1655
1656         configList->setParentMenu();
1657         if (configList->rootEntry == &rootmenu)
1658                 backAction->setEnabled(false);
1659
1660         if (menuList->selectedItems().count() == 0)
1661                 return;
1662
1663         item = (ConfigItem*)menuList->selectedItems().first();
1664         oldSelection = item;
1665         while (item) {
1666                 if (item->menu == configList->rootEntry) {
1667                         oldSelection->setSelected(false);
1668                         item->setSelected(true);
1669                         break;
1670                 }
1671                 item = (ConfigItem*)item->parent();
1672         }
1673 }
1674
1675 void ConfigMainWindow::showSingleView(void)
1676 {
1677         singleViewAction->setEnabled(false);
1678         singleViewAction->setChecked(true);
1679         splitViewAction->setEnabled(true);
1680         splitViewAction->setChecked(false);
1681         fullViewAction->setEnabled(true);
1682         fullViewAction->setChecked(false);
1683
1684         menuView->hide();
1685         menuList->setRootMenu(0);
1686         configList->mode = singleMode;
1687         if (configList->rootEntry == &rootmenu)
1688                 configList->updateListAll();
1689         else
1690                 configList->setRootMenu(&rootmenu);
1691         configList->setFocus();
1692 }
1693
1694 void ConfigMainWindow::showSplitView(void)
1695 {
1696         singleViewAction->setEnabled(true);
1697         singleViewAction->setChecked(false);
1698         splitViewAction->setEnabled(false);
1699         splitViewAction->setChecked(true);
1700         fullViewAction->setEnabled(true);
1701         fullViewAction->setChecked(false);
1702
1703         configList->mode = symbolMode;
1704         if (configList->rootEntry == &rootmenu)
1705                 configList->updateListAll();
1706         else
1707                 configList->setRootMenu(&rootmenu);
1708         configList->setAllOpen(true);
1709         configApp->processEvents();
1710         menuList->mode = menuMode;
1711         menuList->setRootMenu(&rootmenu);
1712         menuList->setAllOpen(true);
1713         menuView->show();
1714         menuList->setFocus();
1715 }
1716
1717 void ConfigMainWindow::showFullView(void)
1718 {
1719         singleViewAction->setEnabled(true);
1720         singleViewAction->setChecked(false);
1721         splitViewAction->setEnabled(true);
1722         splitViewAction->setChecked(false);
1723         fullViewAction->setEnabled(false);
1724         fullViewAction->setChecked(true);
1725
1726         menuView->hide();
1727         menuList->setRootMenu(0);
1728         configList->mode = fullMode;
1729         if (configList->rootEntry == &rootmenu)
1730                 configList->updateListAll();
1731         else
1732                 configList->setRootMenu(&rootmenu);
1733         configList->setFocus();
1734 }
1735
1736 /*
1737  * ask for saving configuration before quitting
1738  * TODO ask only when something changed
1739  */
1740 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1741 {
1742         if (!conf_get_changed()) {
1743                 e->accept();
1744                 return;
1745         }
1746         QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1747                         QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1748         mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1749         mb.setButtonText(QMessageBox::No, "&Discard Changes");
1750         mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1751         switch (mb.exec()) {
1752         case QMessageBox::Yes:
1753                 if (saveConfig())
1754                         e->accept();
1755                 else
1756                         e->ignore();
1757                 break;
1758         case QMessageBox::No:
1759                 e->accept();
1760                 break;
1761         case QMessageBox::Cancel:
1762                 e->ignore();
1763                 break;
1764         }
1765 }
1766
1767 void ConfigMainWindow::showIntro(void)
1768 {
1769         static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
1770                 "For each option, a blank box indicates the feature is disabled, a check\n"
1771                 "indicates it is enabled, and a dot indicates that it is to be compiled\n"
1772                 "as a module.  Clicking on the box will cycle through the three states.\n\n"
1773                 "If you do not see an option (e.g., a device driver) that you believe\n"
1774                 "should be present, try turning on Show All Options under the Options menu.\n"
1775                 "Although there is no cross reference yet to help you figure out what other\n"
1776                 "options must be enabled to support the option you are interested in, you can\n"
1777                 "still view the help of a grayed-out option.\n\n"
1778                 "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1779                 "which you can then match by examining other options.\n\n";
1780
1781         QMessageBox::information(this, "qconf", str);
1782 }
1783
1784 void ConfigMainWindow::showAbout(void)
1785 {
1786         static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1787                 "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
1788                 "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1789
1790         QMessageBox::information(this, "qconf", str);
1791 }
1792
1793 void ConfigMainWindow::saveSettings(void)
1794 {
1795         configSettings->setValue("/window x", pos().x());
1796         configSettings->setValue("/window y", pos().y());
1797         configSettings->setValue("/window width", size().width());
1798         configSettings->setValue("/window height", size().height());
1799
1800         QString entry;
1801         switch(configList->mode) {
1802         case singleMode :
1803                 entry = "single";
1804                 break;
1805
1806         case symbolMode :
1807                 entry = "split";
1808                 break;
1809
1810         case fullMode :
1811                 entry = "full";
1812                 break;
1813
1814         default:
1815                 break;
1816         }
1817         configSettings->setValue("/listMode", entry);
1818
1819         configSettings->writeSizes("/split1", split1->sizes());
1820         configSettings->writeSizes("/split2", split2->sizes());
1821 }
1822
1823 void ConfigMainWindow::conf_changed(void)
1824 {
1825         if (saveAction)
1826                 saveAction->setEnabled(conf_get_changed());
1827 }
1828
1829 void fixup_rootmenu(struct menu *menu)
1830 {
1831         struct menu *child;
1832         static int menu_cnt = 0;
1833
1834         menu->flags |= MENU_ROOT;
1835         for (child = menu->list; child; child = child->next) {
1836                 if (child->prompt && child->prompt->type == P_MENU) {
1837                         menu_cnt++;
1838                         fixup_rootmenu(child);
1839                         menu_cnt--;
1840                 } else if (!menu_cnt)
1841                         fixup_rootmenu(child);
1842         }
1843 }
1844
1845 static const char *progname;
1846
1847 static void usage(void)
1848 {
1849         printf("%s [-s] <config>\n", progname);
1850         exit(0);
1851 }
1852
1853 int main(int ac, char** av)
1854 {
1855         ConfigMainWindow* v;
1856         const char *name;
1857
1858         progname = av[0];
1859         configApp = new QApplication(ac, av);
1860         if (ac > 1 && av[1][0] == '-') {
1861                 switch (av[1][1]) {
1862                 case 's':
1863                         conf_set_message_callback(NULL);
1864                         break;
1865                 case 'h':
1866                 case '?':
1867                         usage();
1868                 }
1869                 name = av[2];
1870         } else
1871                 name = av[1];
1872         if (!name)
1873                 usage();
1874
1875         conf_parse(name);
1876         fixup_rootmenu(&rootmenu);
1877         conf_read(NULL);
1878         //zconfdump(stdout);
1879
1880         configSettings = new ConfigSettings();
1881         configSettings->beginGroup("/kconfig/qconf");
1882         v = new ConfigMainWindow();
1883
1884         //zconfdump(stdout);
1885         configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1886         configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1887         v->show();
1888         configApp->exec();
1889
1890         configSettings->endGroup();
1891         delete configSettings;
1892         delete v;
1893         delete configApp;
1894
1895         return 0;
1896 }