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