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