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