GNU Linux-libre 4.19.286-gnu1
[releases.git] / scripts / kconfig / qconf.cc
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
4  * Released under the terms of the GNU GPL v2.0.
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.c"
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_changable(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         QAction *saveAsAction = new QAction("Save &As...", this);
1397           connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs()));
1398         QAction *searchAction = new QAction("&Find", this);
1399         searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
1400           connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig()));
1401         singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
1402         singleViewAction->setCheckable(true);
1403           connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView()));
1404         splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
1405         splitViewAction->setCheckable(true);
1406           connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView()));
1407         fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
1408         fullViewAction->setCheckable(true);
1409           connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView()));
1410
1411         QAction *showNameAction = new QAction("Show Name", this);
1412           showNameAction->setCheckable(true);
1413           connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
1414           showNameAction->setChecked(configView->showName());
1415         QAction *showRangeAction = new QAction("Show Range", this);
1416           showRangeAction->setCheckable(true);
1417           connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
1418         QAction *showDataAction = new QAction("Show Data", this);
1419           showDataAction->setCheckable(true);
1420           connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
1421
1422         QActionGroup *optGroup = new QActionGroup(this);
1423         optGroup->setExclusive(true);
1424         connect(optGroup, SIGNAL(triggered(QAction*)), configView,
1425                 SLOT(setOptionMode(QAction *)));
1426         connect(optGroup, SIGNAL(triggered(QAction *)), menuView,
1427                 SLOT(setOptionMode(QAction *)));
1428
1429         configView->showNormalAction = new QAction("Show Normal Options", optGroup);
1430         configView->showAllAction = new QAction("Show All Options", optGroup);
1431         configView->showPromptAction = new QAction("Show Prompt Options", optGroup);
1432         configView->showNormalAction->setCheckable(true);
1433         configView->showAllAction->setCheckable(true);
1434         configView->showPromptAction->setCheckable(true);
1435
1436         QAction *showDebugAction = new QAction("Show Debug Info", this);
1437           showDebugAction->setCheckable(true);
1438           connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
1439           showDebugAction->setChecked(helpText->showDebug());
1440
1441         QAction *showIntroAction = new QAction("Introduction", this);
1442           connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro()));
1443         QAction *showAboutAction = new QAction("About", this);
1444           connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout()));
1445
1446         // init tool bar
1447         toolBar->addAction(backAction);
1448         toolBar->addSeparator();
1449         toolBar->addAction(loadAction);
1450         toolBar->addAction(saveAction);
1451         toolBar->addSeparator();
1452         toolBar->addAction(singleViewAction);
1453         toolBar->addAction(splitViewAction);
1454         toolBar->addAction(fullViewAction);
1455
1456         // create config menu
1457         QMenu* config = menu->addMenu("&File");
1458         config->addAction(loadAction);
1459         config->addAction(saveAction);
1460         config->addAction(saveAsAction);
1461         config->addSeparator();
1462         config->addAction(quitAction);
1463
1464         // create edit menu
1465         QMenu* editMenu = menu->addMenu("&Edit");
1466         editMenu->addAction(searchAction);
1467
1468         // create options menu
1469         QMenu* optionMenu = menu->addMenu("&Option");
1470         optionMenu->addAction(showNameAction);
1471         optionMenu->addAction(showRangeAction);
1472         optionMenu->addAction(showDataAction);
1473         optionMenu->addSeparator();
1474         optionMenu->addActions(optGroup->actions());
1475         optionMenu->addSeparator();
1476         optionMenu->addAction(showDebugAction);
1477
1478         // create help menu
1479         menu->addSeparator();
1480         QMenu* helpMenu = menu->addMenu("&Help");
1481         helpMenu->addAction(showIntroAction);
1482         helpMenu->addAction(showAboutAction);
1483
1484         connect(configList, SIGNAL(menuChanged(struct menu *)),
1485                 helpText, SLOT(setInfo(struct menu *)));
1486         connect(configList, SIGNAL(menuSelected(struct menu *)),
1487                 SLOT(changeMenu(struct menu *)));
1488         connect(configList, SIGNAL(parentSelected()),
1489                 SLOT(goBack()));
1490         connect(menuList, SIGNAL(menuChanged(struct menu *)),
1491                 helpText, SLOT(setInfo(struct menu *)));
1492         connect(menuList, SIGNAL(menuSelected(struct menu *)),
1493                 SLOT(changeMenu(struct menu *)));
1494
1495         connect(configList, SIGNAL(gotFocus(struct menu *)),
1496                 helpText, SLOT(setInfo(struct menu *)));
1497         connect(menuList, SIGNAL(gotFocus(struct menu *)),
1498                 helpText, SLOT(setInfo(struct menu *)));
1499         connect(menuList, SIGNAL(gotFocus(struct menu *)),
1500                 SLOT(listFocusChanged(void)));
1501         connect(helpText, SIGNAL(menuSelected(struct menu *)),
1502                 SLOT(setMenuLink(struct menu *)));
1503
1504         QString listMode = configSettings->value("/listMode", "symbol").toString();
1505         if (listMode == "single")
1506                 showSingleView();
1507         else if (listMode == "full")
1508                 showFullView();
1509         else /*if (listMode == "split")*/
1510                 showSplitView();
1511
1512         // UI setup done, restore splitter positions
1513         QList<int> sizes = configSettings->readSizes("/split1", &ok);
1514         if (ok)
1515                 split1->setSizes(sizes);
1516
1517         sizes = configSettings->readSizes("/split2", &ok);
1518         if (ok)
1519                 split2->setSizes(sizes);
1520 }
1521
1522 void ConfigMainWindow::loadConfig(void)
1523 {
1524         QString s = QFileDialog::getOpenFileName(this, "", conf_get_configname());
1525         if (s.isNull())
1526                 return;
1527         if (conf_read(QFile::encodeName(s)))
1528                 QMessageBox::information(this, "qconf", "Unable to load configuration!");
1529         ConfigView::updateListAll();
1530 }
1531
1532 bool ConfigMainWindow::saveConfig(void)
1533 {
1534         if (conf_write(NULL)) {
1535                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1536                 return false;
1537         }
1538         conf_write_autoconf(0);
1539
1540         return true;
1541 }
1542
1543 void ConfigMainWindow::saveConfigAs(void)
1544 {
1545         QString s = QFileDialog::getSaveFileName(this, "", conf_get_configname());
1546         if (s.isNull())
1547                 return;
1548         saveConfig();
1549 }
1550
1551 void ConfigMainWindow::searchConfig(void)
1552 {
1553         if (!searchWindow)
1554                 searchWindow = new ConfigSearchWindow(this, "search");
1555         searchWindow->show();
1556 }
1557
1558 void ConfigMainWindow::changeMenu(struct menu *menu)
1559 {
1560         configList->setRootMenu(menu);
1561         if (configList->rootEntry->parent == &rootmenu)
1562                 backAction->setEnabled(false);
1563         else
1564                 backAction->setEnabled(true);
1565 }
1566
1567 void ConfigMainWindow::setMenuLink(struct menu *menu)
1568 {
1569         struct menu *parent;
1570         ConfigList* list = NULL;
1571         ConfigItem* item;
1572
1573         if (configList->menuSkip(menu))
1574                 return;
1575
1576         switch (configList->mode) {
1577         case singleMode:
1578                 list = configList;
1579                 parent = menu_get_parent_menu(menu);
1580                 if (!parent)
1581                         return;
1582                 list->setRootMenu(parent);
1583                 break;
1584         case symbolMode:
1585                 if (menu->flags & MENU_ROOT) {
1586                         configList->setRootMenu(menu);
1587                         configList->clearSelection();
1588                         list = menuList;
1589                 } else {
1590                         list = configList;
1591                         parent = menu_get_parent_menu(menu->parent);
1592                         if (!parent)
1593                                 return;
1594                         item = menuList->findConfigItem(parent);
1595                         if (item) {
1596                                 item->setSelected(true);
1597                                 menuList->scrollToItem(item);
1598                         }
1599                         list->setRootMenu(parent);
1600                 }
1601                 break;
1602         case fullMode:
1603                 list = configList;
1604                 break;
1605         default:
1606                 break;
1607         }
1608
1609         if (list) {
1610                 item = list->findConfigItem(menu);
1611                 if (item) {
1612                         item->setSelected(true);
1613                         list->scrollToItem(item);
1614                         list->setFocus();
1615                 }
1616         }
1617 }
1618
1619 void ConfigMainWindow::listFocusChanged(void)
1620 {
1621         if (menuList->mode == menuMode)
1622                 configList->clearSelection();
1623 }
1624
1625 void ConfigMainWindow::goBack(void)
1626 {
1627         ConfigItem* item, *oldSelection;
1628
1629         configList->setParentMenu();
1630         if (configList->rootEntry == &rootmenu)
1631                 backAction->setEnabled(false);
1632
1633         if (menuList->selectedItems().count() == 0)
1634                 return;
1635
1636         item = (ConfigItem*)menuList->selectedItems().first();
1637         oldSelection = item;
1638         while (item) {
1639                 if (item->menu == configList->rootEntry) {
1640                         oldSelection->setSelected(false);
1641                         item->setSelected(true);
1642                         break;
1643                 }
1644                 item = (ConfigItem*)item->parent();
1645         }
1646 }
1647
1648 void ConfigMainWindow::showSingleView(void)
1649 {
1650         singleViewAction->setEnabled(false);
1651         singleViewAction->setChecked(true);
1652         splitViewAction->setEnabled(true);
1653         splitViewAction->setChecked(false);
1654         fullViewAction->setEnabled(true);
1655         fullViewAction->setChecked(false);
1656
1657         menuView->hide();
1658         menuList->setRootMenu(0);
1659         configList->mode = singleMode;
1660         if (configList->rootEntry == &rootmenu)
1661                 configList->updateListAll();
1662         else
1663                 configList->setRootMenu(&rootmenu);
1664         configList->setFocus();
1665 }
1666
1667 void ConfigMainWindow::showSplitView(void)
1668 {
1669         singleViewAction->setEnabled(true);
1670         singleViewAction->setChecked(false);
1671         splitViewAction->setEnabled(false);
1672         splitViewAction->setChecked(true);
1673         fullViewAction->setEnabled(true);
1674         fullViewAction->setChecked(false);
1675
1676         configList->mode = symbolMode;
1677         if (configList->rootEntry == &rootmenu)
1678                 configList->updateListAll();
1679         else
1680                 configList->setRootMenu(&rootmenu);
1681         configList->setAllOpen(true);
1682         configApp->processEvents();
1683         menuList->mode = menuMode;
1684         menuList->setRootMenu(&rootmenu);
1685         menuList->setAllOpen(true);
1686         menuView->show();
1687         menuList->setFocus();
1688 }
1689
1690 void ConfigMainWindow::showFullView(void)
1691 {
1692         singleViewAction->setEnabled(true);
1693         singleViewAction->setChecked(false);
1694         splitViewAction->setEnabled(true);
1695         splitViewAction->setChecked(false);
1696         fullViewAction->setEnabled(false);
1697         fullViewAction->setChecked(true);
1698
1699         menuView->hide();
1700         menuList->setRootMenu(0);
1701         configList->mode = fullMode;
1702         if (configList->rootEntry == &rootmenu)
1703                 configList->updateListAll();
1704         else
1705                 configList->setRootMenu(&rootmenu);
1706         configList->setFocus();
1707 }
1708
1709 /*
1710  * ask for saving configuration before quitting
1711  * TODO ask only when something changed
1712  */
1713 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1714 {
1715         if (!conf_get_changed()) {
1716                 e->accept();
1717                 return;
1718         }
1719         QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1720                         QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1721         mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1722         mb.setButtonText(QMessageBox::No, "&Discard Changes");
1723         mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1724         switch (mb.exec()) {
1725         case QMessageBox::Yes:
1726                 if (saveConfig())
1727                         e->accept();
1728                 else
1729                         e->ignore();
1730                 break;
1731         case QMessageBox::No:
1732                 e->accept();
1733                 break;
1734         case QMessageBox::Cancel:
1735                 e->ignore();
1736                 break;
1737         }
1738 }
1739
1740 void ConfigMainWindow::showIntro(void)
1741 {
1742         static const QString str = "Welcome to the qconf graphical configuration tool.\n\n"
1743                 "For each option, a blank box indicates the feature is disabled, a check\n"
1744                 "indicates it is enabled, and a dot indicates that it is to be compiled\n"
1745                 "as a module.  Clicking on the box will cycle through the three states.\n\n"
1746                 "If you do not see an option (e.g., a device driver) that you believe\n"
1747                 "should be present, try turning on Show All Options under the Options menu.\n"
1748                 "Although there is no cross reference yet to help you figure out what other\n"
1749                 "options must be enabled to support the option you are interested in, you can\n"
1750                 "still view the help of a grayed-out option.\n\n"
1751                 "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1752                 "which you can then match by examining other options.\n\n";
1753
1754         QMessageBox::information(this, "qconf", str);
1755 }
1756
1757 void ConfigMainWindow::showAbout(void)
1758 {
1759         static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
1760                 "Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n\n"
1761                 "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1762
1763         QMessageBox::information(this, "qconf", str);
1764 }
1765
1766 void ConfigMainWindow::saveSettings(void)
1767 {
1768         configSettings->setValue("/window x", pos().x());
1769         configSettings->setValue("/window y", pos().y());
1770         configSettings->setValue("/window width", size().width());
1771         configSettings->setValue("/window height", size().height());
1772
1773         QString entry;
1774         switch(configList->mode) {
1775         case singleMode :
1776                 entry = "single";
1777                 break;
1778
1779         case symbolMode :
1780                 entry = "split";
1781                 break;
1782
1783         case fullMode :
1784                 entry = "full";
1785                 break;
1786
1787         default:
1788                 break;
1789         }
1790         configSettings->setValue("/listMode", entry);
1791
1792         configSettings->writeSizes("/split1", split1->sizes());
1793         configSettings->writeSizes("/split2", split2->sizes());
1794 }
1795
1796 void ConfigMainWindow::conf_changed(void)
1797 {
1798         if (saveAction)
1799                 saveAction->setEnabled(conf_get_changed());
1800 }
1801
1802 void fixup_rootmenu(struct menu *menu)
1803 {
1804         struct menu *child;
1805         static int menu_cnt = 0;
1806
1807         menu->flags |= MENU_ROOT;
1808         for (child = menu->list; child; child = child->next) {
1809                 if (child->prompt && child->prompt->type == P_MENU) {
1810                         menu_cnt++;
1811                         fixup_rootmenu(child);
1812                         menu_cnt--;
1813                 } else if (!menu_cnt)
1814                         fixup_rootmenu(child);
1815         }
1816 }
1817
1818 static const char *progname;
1819
1820 static void usage(void)
1821 {
1822         printf("%s [-s] <config>\n", progname);
1823         exit(0);
1824 }
1825
1826 int main(int ac, char** av)
1827 {
1828         ConfigMainWindow* v;
1829         const char *name;
1830
1831         progname = av[0];
1832         configApp = new QApplication(ac, av);
1833         if (ac > 1 && av[1][0] == '-') {
1834                 switch (av[1][1]) {
1835                 case 's':
1836                         conf_set_message_callback(NULL);
1837                         break;
1838                 case 'h':
1839                 case '?':
1840                         usage();
1841                 }
1842                 name = av[2];
1843         } else
1844                 name = av[1];
1845         if (!name)
1846                 usage();
1847
1848         conf_parse(name);
1849         fixup_rootmenu(&rootmenu);
1850         conf_read(NULL);
1851         //zconfdump(stdout);
1852
1853         configSettings = new ConfigSettings();
1854         configSettings->beginGroup("/kconfig/qconf");
1855         v = new ConfigMainWindow();
1856
1857         //zconfdump(stdout);
1858         configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1859         configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1860         v->show();
1861         configApp->exec();
1862
1863         configSettings->endGroup();
1864         delete configSettings;
1865         delete v;
1866         delete configApp;
1867
1868         return 0;
1869 }