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