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