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