ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / scripts / kconfig / qconf.cc
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Released under the terms of the GNU GPL v2.0.
4  */
5
6 #include <qapplication.h>
7 #include <qmainwindow.h>
8 #include <qtoolbar.h>
9 #include <qvbox.h>
10 #include <qsplitter.h>
11 #include <qlistview.h>
12 #include <qtextview.h>
13 #include <qlineedit.h>
14 #include <qmenubar.h>
15 #include <qmessagebox.h>
16 #include <qaction.h>
17 #include <qheader.h>
18 #include <qfiledialog.h>
19 #include <qregexp.h>
20
21 #include <stdlib.h>
22
23 #include "lkc.h"
24 #include "qconf.h"
25
26 #include "qconf.moc"
27 #include "images.c"
28
29 static QApplication *configApp;
30
31 ConfigSettings::ConfigSettings()
32         : showAll(false), showName(false), showRange(false), showData(false)
33 {
34 }
35
36 #if QT_VERSION >= 300
37 /**
38  * Reads the list column settings from the application settings.
39  */
40 void ConfigSettings::readListSettings()
41 {
42         showAll = readBoolEntry("/kconfig/qconf/showAll", false);
43         showName = readBoolEntry("/kconfig/qconf/showName", false);
44         showRange = readBoolEntry("/kconfig/qconf/showRange", false);
45         showData = readBoolEntry("/kconfig/qconf/showData", false);
46 }
47
48 /**
49  * Reads a list of integer values from the application settings.
50  */
51 QValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
52 {
53         QValueList<int> result;
54         QStringList entryList = readListEntry(key, ok);
55         if (ok) {
56                 QStringList::Iterator it;
57                 for (it = entryList.begin(); it != entryList.end(); ++it)
58                         result.push_back((*it).toInt());
59         }
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 QValueList<int>& value)
68 {
69         QStringList stringList;
70         QValueList<int>::ConstIterator it;
71
72         for (it = value.begin(); it != value.end(); ++it)
73                 stringList.push_back(QString::number(*it));
74         return writeEntry(key, stringList);
75 }
76 #endif
77
78
79 /*
80  * update all the children of a menu entry
81  *   removes/adds the entries from the parent widget as necessary
82  *
83  * parent: either the menu list widget or a menu entry widget
84  * menu: entry to be updated
85  */
86 template <class P>
87 void ConfigList::updateMenuList(P* parent, struct menu* menu)
88 {
89         struct menu* child;
90         ConfigItem* item;
91         ConfigItem* last;
92         bool visible;
93         enum prop_type type;
94
95         if (!menu) {
96                 while ((item = parent->firstChild()))
97                         delete item;
98                 return;
99         }
100
101         last = parent->firstChild();
102         if (last && !last->goParent)
103                 last = 0;
104         for (child = menu->list; child; child = child->next) {
105                 item = last ? last->nextSibling() : parent->firstChild();
106                 type = child->prompt ? child->prompt->type : P_UNKNOWN;
107
108                 switch (mode) {
109                 case menuMode:
110                         if (!(child->flags & MENU_ROOT))
111                                 goto hide;
112                         break;
113                 case symbolMode:
114                         if (child->flags & MENU_ROOT)
115                                 goto hide;
116                         break;
117                 default:
118                         break;
119                 }
120
121                 visible = menu_is_visible(child);
122                 if (showAll || visible) {
123                         if (!item || item->menu != child)
124                                 item = new ConfigItem(parent, last, child, visible);
125                         else
126                                 item->testUpdateMenu(visible);
127
128                         if (mode == fullMode || mode == menuMode || type != P_MENU)
129                                 updateMenuList(item, child);
130                         else
131                                 updateMenuList(item, 0);
132                         last = item;
133                         continue;
134                 }
135         hide:
136                 if (item && item->menu == child) {
137                         last = parent->firstChild();
138                         if (last == item)
139                                 last = 0;
140                         else while (last->nextSibling() != item)
141                                 last = last->nextSibling();
142                         delete item;
143                 }
144         }
145 }
146
147 #if QT_VERSION >= 300
148 /*
149  * set the new data
150  * TODO check the value
151  */
152 void ConfigItem::okRename(int col)
153 {
154         Parent::okRename(col);
155         sym_set_string_value(menu->sym, text(dataColIdx).latin1());
156 }
157 #endif
158
159 /*
160  * update the displayed of a menu entry
161  */
162 void ConfigItem::updateMenu(void)
163 {
164         ConfigList* list;
165         struct symbol* sym;
166         struct property *prop;
167         QString prompt;
168         int type;
169         tristate expr;
170
171         list = listView();
172         if (goParent) {
173                 setPixmap(promptColIdx, list->menuBackPix);
174                 prompt = "..";
175                 goto set_prompt;
176         }
177
178         sym = menu->sym;
179         prop = menu->prompt;
180         prompt = menu_get_prompt(menu);
181
182         if (prop) switch (prop->type) {
183         case P_MENU:
184                 if (list->mode == singleMode || list->mode == symbolMode) {
185                         /* a menuconfig entry is displayed differently
186                          * depending whether it's at the view root or a child.
187                          */
188                         if (sym && list->rootEntry == menu)
189                                 break;
190                         setPixmap(promptColIdx, list->menuPix);
191                 } else {
192                         if (sym)
193                                 break;
194                         setPixmap(promptColIdx, 0);
195                 }
196                 goto set_prompt;
197         case P_COMMENT:
198                 setPixmap(promptColIdx, 0);
199                 goto set_prompt;
200         default:
201                 ;
202         }
203         if (!sym)
204                 goto set_prompt;
205
206         setText(nameColIdx, sym->name);
207
208         type = sym_get_type(sym);
209         switch (type) {
210         case S_BOOLEAN:
211         case S_TRISTATE:
212                 char ch;
213
214                 if (!sym_is_changable(sym) && !list->showAll) {
215                         setPixmap(promptColIdx, 0);
216                         setText(noColIdx, 0);
217                         setText(modColIdx, 0);
218                         setText(yesColIdx, 0);
219                         break;
220                 }
221                 expr = sym_get_tristate_value(sym);
222                 switch (expr) {
223                 case yes:
224                         if (sym_is_choice_value(sym) && type == S_BOOLEAN)
225                                 setPixmap(promptColIdx, list->choiceYesPix);
226                         else
227                                 setPixmap(promptColIdx, list->symbolYesPix);
228                         setText(yesColIdx, "Y");
229                         ch = 'Y';
230                         break;
231                 case mod:
232                         setPixmap(promptColIdx, list->symbolModPix);
233                         setText(modColIdx, "M");
234                         ch = 'M';
235                         break;
236                 default:
237                         if (sym_is_choice_value(sym) && type == S_BOOLEAN)
238                                 setPixmap(promptColIdx, list->choiceNoPix);
239                         else
240                                 setPixmap(promptColIdx, list->symbolNoPix);
241                         setText(noColIdx, "N");
242                         ch = 'N';
243                         break;
244                 }
245                 if (expr != no)
246                         setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0);
247                 if (expr != mod)
248                         setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0);
249                 if (expr != yes)
250                         setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0);
251
252                 setText(dataColIdx, QChar(ch));
253                 break;
254         case S_INT:
255         case S_HEX:
256         case S_STRING:
257                 const char* data;
258
259                 data = sym_get_string_value(sym);
260 #if QT_VERSION >= 300
261                 int i = list->mapIdx(dataColIdx);
262                 if (i >= 0)
263                         setRenameEnabled(i, TRUE);
264 #endif
265                 setText(dataColIdx, data);
266                 if (type == S_STRING)
267                         prompt.sprintf("%s: %s", prompt.latin1(), data);
268                 else
269                         prompt.sprintf("(%s) %s", data, prompt.latin1());
270                 break;
271         }
272         if (!sym_has_value(sym) && visible)
273                 prompt += " (NEW)";
274 set_prompt:
275         setText(promptColIdx, prompt);
276 }
277
278 void ConfigItem::testUpdateMenu(bool v)
279 {
280         ConfigItem* i;
281
282         visible = v;
283         if (!menu)
284                 return;
285
286         sym_calc_value(menu->sym);
287         if (menu->flags & MENU_CHANGED) {
288                 /* the menu entry changed, so update all list items */
289                 menu->flags &= ~MENU_CHANGED;
290                 for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
291                         i->updateMenu();
292         } else if (listView()->updateAll)
293                 updateMenu();
294 }
295
296 void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
297 {
298         ConfigList* list = listView();
299
300         if (visible) {
301                 if (isSelected() && !list->hasFocus() && list->mode == menuMode)
302                         Parent::paintCell(p, list->inactivedColorGroup, column, width, align);
303                 else
304                         Parent::paintCell(p, cg, column, width, align);
305         } else
306                 Parent::paintCell(p, list->disabledColorGroup, column, width, align);
307 }
308
309 /*
310  * construct a menu entry
311  */
312 void ConfigItem::init(void)
313 {
314         if (menu) {
315                 ConfigList* list = listView();
316                 nextItem = (ConfigItem*)menu->data;
317                 menu->data = this;
318
319                 if (list->mode != fullMode)
320                         setOpen(TRUE);
321                 sym_calc_value(menu->sym);
322         }
323         updateMenu();
324 }
325
326 /*
327  * destruct a menu entry
328  */
329 ConfigItem::~ConfigItem(void)
330 {
331         if (menu) {
332                 ConfigItem** ip = (ConfigItem**)&menu->data;
333                 for (; *ip; ip = &(*ip)->nextItem) {
334                         if (*ip == this) {
335                                 *ip = nextItem;
336                                 break;
337                         }
338                 }
339         }
340 }
341
342 void ConfigLineEdit::show(ConfigItem* i)
343 {
344         item = i;
345         if (sym_get_string_value(item->menu->sym))
346                 setText(sym_get_string_value(item->menu->sym));
347         else
348                 setText(0);
349         Parent::show();
350         setFocus();
351 }
352
353 void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
354 {
355         switch (e->key()) {
356         case Key_Escape:
357                 break;
358         case Key_Return:
359         case Key_Enter:
360                 sym_set_string_value(item->menu->sym, text().latin1());
361                 parent()->updateList(item);
362                 break;
363         default:
364                 Parent::keyPressEvent(e);
365                 return;
366         }
367         e->accept();
368         parent()->list->setFocus();
369         hide();
370 }
371
372 ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings)
373         : Parent(p), cview(cv),
374           updateAll(false),
375           symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no),
376           choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no),
377           menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void),
378           showAll(false), showName(false), showRange(false), showData(false),
379           rootEntry(0)
380 {
381         int i;
382
383         setSorting(-1);
384         setRootIsDecorated(TRUE);
385         disabledColorGroup = palette().active();
386         disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text());
387         inactivedColorGroup = palette().active();
388         inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight());
389
390         connect(this, SIGNAL(selectionChanged(void)),
391                 SLOT(updateSelection(void)));
392
393         if (configSettings) {
394                 showAll = configSettings->showAll;
395                 showName = configSettings->showName;
396                 showRange = configSettings->showRange;
397                 showData = configSettings->showData;
398         }
399
400         for (i = 0; i < colNr; i++)
401                 colMap[i] = colRevMap[i] = -1;
402         addColumn(promptColIdx, "Option");
403
404         reinit();
405 }
406
407 void ConfigList::reinit(void)
408 {
409         removeColumn(dataColIdx);
410         removeColumn(yesColIdx);
411         removeColumn(modColIdx);
412         removeColumn(noColIdx);
413         removeColumn(nameColIdx);
414
415         if (showName)
416                 addColumn(nameColIdx, "Name");
417         if (showRange) {
418                 addColumn(noColIdx, "N");
419                 addColumn(modColIdx, "M");
420                 addColumn(yesColIdx, "Y");
421         }
422         if (showData)
423                 addColumn(dataColIdx, "Value");
424
425         updateListAll();
426 }
427
428 void ConfigList::updateSelection(void)
429 {
430         struct menu *menu;
431         enum prop_type type;
432
433         ConfigItem* item = (ConfigItem*)selectedItem();
434         if (!item)
435                 return;
436
437         cview->setHelp(item);
438
439         menu = item->menu;
440         if (!menu)
441                 return;
442         type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
443         if (mode == menuMode && type == P_MENU)
444                 emit menuSelected(menu);
445 }
446
447 void ConfigList::updateList(ConfigItem* item)
448 {
449         ConfigItem* last = 0;
450
451         if (!rootEntry)
452                 goto update;
453
454         if (rootEntry != &rootmenu && (mode == singleMode ||
455             (mode == symbolMode && rootEntry->parent != &rootmenu))) {
456                 item = firstChild();
457                 if (!item)
458                         item = new ConfigItem(this, 0, true);
459                 last = item;
460         }
461         if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
462             rootEntry->sym && rootEntry->prompt) {
463                 item = last ? last->nextSibling() : firstChild();
464                 if (!item)
465                         item = new ConfigItem(this, last, rootEntry, true);
466                 else
467                         item->testUpdateMenu(true);
468
469                 updateMenuList(item, rootEntry);
470                 triggerUpdate();
471                 return;
472         }
473 update:
474         updateMenuList(this, rootEntry);
475         triggerUpdate();
476 }
477
478 void ConfigList::setAllOpen(bool open)
479 {
480         QListViewItemIterator it(this);
481
482         for (; it.current(); it++)
483                 it.current()->setOpen(open);
484 }
485
486 void ConfigList::setValue(ConfigItem* item, tristate val)
487 {
488         struct symbol* sym;
489         int type;
490         tristate oldval;
491
492         sym = item->menu ? item->menu->sym : 0;
493         if (!sym)
494                 return;
495
496         type = sym_get_type(sym);
497         switch (type) {
498         case S_BOOLEAN:
499         case S_TRISTATE:
500                 oldval = sym_get_tristate_value(sym);
501
502                 if (!sym_set_tristate_value(sym, val))
503                         return;
504                 if (oldval == no && item->menu->list)
505                         item->setOpen(TRUE);
506                 parent()->updateList(item);
507                 break;
508         }
509 }
510
511 void ConfigList::changeValue(ConfigItem* item)
512 {
513         struct symbol* sym;
514         struct menu* menu;
515         int type, oldexpr, newexpr;
516
517         menu = item->menu;
518         if (!menu)
519                 return;
520         sym = menu->sym;
521         if (!sym) {
522                 if (item->menu->list)
523                         item->setOpen(!item->isOpen());
524                 return;
525         }
526
527         type = sym_get_type(sym);
528         switch (type) {
529         case S_BOOLEAN:
530         case S_TRISTATE:
531                 oldexpr = sym_get_tristate_value(sym);
532                 newexpr = sym_toggle_tristate_value(sym);
533                 if (item->menu->list) {
534                         if (oldexpr == newexpr)
535                                 item->setOpen(!item->isOpen());
536                         else if (oldexpr == no)
537                                 item->setOpen(TRUE);
538                 }
539                 if (oldexpr != newexpr)
540                         parent()->updateList(item);
541                 break;
542         case S_INT:
543         case S_HEX:
544         case S_STRING:
545 #if QT_VERSION >= 300
546                 if (colMap[dataColIdx] >= 0)
547                         item->startRename(colMap[dataColIdx]);
548                 else
549 #endif
550                         parent()->lineEdit->show(item);
551                 break;
552         }
553 }
554
555 void ConfigList::setRootMenu(struct menu *menu)
556 {
557         enum prop_type type;
558
559         if (rootEntry == menu)
560                 return;
561         type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
562         if (type != P_MENU)
563                 return;
564         updateMenuList(this, 0);
565         rootEntry = menu;
566         updateListAll();
567         setSelected(currentItem(), hasFocus());
568 }
569
570 void ConfigList::setParentMenu(void)
571 {
572         ConfigItem* item;
573         struct menu *oldroot;
574
575         oldroot = rootEntry;
576         if (rootEntry == &rootmenu)
577                 return;
578         setRootMenu(menu_get_parent_menu(rootEntry->parent));
579
580         QListViewItemIterator it(this);
581         for (; (item = (ConfigItem*)it.current()); it++) {
582                 if (item->menu == oldroot) {
583                         setCurrentItem(item);
584                         ensureItemVisible(item);
585                         break;
586                 }
587         }
588 }
589
590 void ConfigList::keyPressEvent(QKeyEvent* ev)
591 {
592         QListViewItem* i = currentItem();
593         ConfigItem* item;
594         struct menu *menu;
595         enum prop_type type;
596
597         if (ev->key() == Key_Escape && mode != fullMode) {
598                 emit parentSelected();
599                 ev->accept();
600                 return;
601         }
602
603         if (!i) {
604                 Parent::keyPressEvent(ev);
605                 return;
606         }
607         item = (ConfigItem*)i;
608
609         switch (ev->key()) {
610         case Key_Return:
611         case Key_Enter:
612                 if (item->goParent) {
613                         emit parentSelected();
614                         break;
615                 }
616                 menu = item->menu;
617                 if (!menu)
618                         break;
619                 type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
620                 if (type == P_MENU && rootEntry != menu &&
621                     mode != fullMode && mode != menuMode) {
622                         emit menuSelected(menu);
623                         break;
624                 }
625         case Key_Space:
626                 changeValue(item);
627                 break;
628         case Key_N:
629                 setValue(item, no);
630                 break;
631         case Key_M:
632                 setValue(item, mod);
633                 break;
634         case Key_Y:
635                 setValue(item, yes);
636                 break;
637         default:
638                 Parent::keyPressEvent(ev);
639                 return;
640         }
641         ev->accept();
642 }
643
644 void ConfigList::contentsMousePressEvent(QMouseEvent* e)
645 {
646         //QPoint p(contentsToViewport(e->pos()));
647         //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
648         Parent::contentsMousePressEvent(e);
649 }
650
651 void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e)
652 {
653         QPoint p(contentsToViewport(e->pos()));
654         ConfigItem* item = (ConfigItem*)itemAt(p);
655         struct menu *menu;
656         enum prop_type ptype;
657         const QPixmap* pm;
658         int idx, x;
659
660         if (!item)
661                 goto skip;
662
663         menu = item->menu;
664         x = header()->offset() + p.x();
665         idx = colRevMap[header()->sectionAt(x)];
666         switch (idx) {
667         case promptColIdx:
668                 pm = item->pixmap(promptColIdx);
669                 if (pm) {
670                         int off = header()->sectionPos(0) + itemMargin() +
671                                 treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0));
672                         if (x >= off && x < off + pm->width()) {
673                                 if (item->goParent) {
674                                         emit parentSelected();
675                                         break;
676                                 } else if (!menu)
677                                         break;
678                                 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
679                                 if (ptype == P_MENU && rootEntry != menu &&
680                                     mode != fullMode && mode != menuMode)
681                                         emit menuSelected(menu);
682                                 else
683                                         changeValue(item);
684                         }
685                 }
686                 break;
687         case noColIdx:
688                 setValue(item, no);
689                 break;
690         case modColIdx:
691                 setValue(item, mod);
692                 break;
693         case yesColIdx:
694                 setValue(item, yes);
695                 break;
696         case dataColIdx:
697                 changeValue(item);
698                 break;
699         }
700
701 skip:
702         //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
703         Parent::contentsMouseReleaseEvent(e);
704 }
705
706 void ConfigList::contentsMouseMoveEvent(QMouseEvent* e)
707 {
708         //QPoint p(contentsToViewport(e->pos()));
709         //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
710         Parent::contentsMouseMoveEvent(e);
711 }
712
713 void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e)
714 {
715         QPoint p(contentsToViewport(e->pos()));
716         ConfigItem* item = (ConfigItem*)itemAt(p);
717         struct menu *menu;
718         enum prop_type ptype;
719
720         if (!item)
721                 goto skip;
722         if (item->goParent) {
723                 emit parentSelected();
724                 goto skip;
725         }
726         menu = item->menu;
727         if (!menu)
728                 goto skip;
729         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
730         if (ptype == P_MENU && (mode == singleMode || mode == symbolMode))
731                 emit menuSelected(menu);
732         else if (menu->sym)
733                 changeValue(item);
734
735 skip:
736         //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
737         Parent::contentsMouseDoubleClickEvent(e);
738 }
739
740 void ConfigList::focusInEvent(QFocusEvent *e)
741 {
742         Parent::focusInEvent(e);
743
744         QListViewItem* item = currentItem();
745         if (!item)
746                 return;
747
748         setSelected(item, TRUE);
749         emit gotFocus();
750 }
751
752 ConfigView* ConfigView::viewList;
753
754 ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview,
755                        ConfigSettings *configSettings)
756         : Parent(parent)
757 {
758         list = new ConfigList(this, cview, configSettings);
759         lineEdit = new ConfigLineEdit(this);
760         lineEdit->hide();
761
762         this->nextView = viewList;
763         viewList = this;
764 }
765
766 ConfigView::~ConfigView(void)
767 {
768         ConfigView** vp;
769
770         for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
771                 if (*vp == this) {
772                         *vp = nextView;
773                         break;
774                 }
775         }
776 }
777
778 void ConfigView::updateList(ConfigItem* item)
779 {
780         ConfigView* v;
781
782         for (v = viewList; v; v = v->nextView)
783                 v->list->updateList(item);
784 }
785
786 void ConfigView::updateListAll(void)
787 {
788         ConfigView* v;
789
790         for (v = viewList; v; v = v->nextView)
791                 v->list->updateListAll();
792 }
793
794 /*
795  * Construct the complete config widget
796  */
797 ConfigMainWindow::ConfigMainWindow(void)
798 {
799         QMenuBar* menu;
800         bool ok;
801         int x, y, width, height;
802
803         QWidget *d = configApp->desktop();
804
805         ConfigSettings* configSettings = new ConfigSettings();
806 #if QT_VERSION >= 300
807         width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64);
808         height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64);
809         resize(width, height);
810         x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok);
811         if (ok)
812                 y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok);
813         if (ok)
814                 move(x, y);
815         showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false);
816
817         // read list settings into configSettings, will be used later for ConfigList setup
818         configSettings->readListSettings();
819 #else
820         width = d->width() - 64;
821         height = d->height() - 64;
822         resize(width, height);
823         showDebug = false;
824 #endif
825
826         split1 = new QSplitter(this);
827         split1->setOrientation(QSplitter::Horizontal);
828         setCentralWidget(split1);
829
830         menuView = new ConfigView(split1, this, configSettings);
831         menuList = menuView->list;
832
833         split2 = new QSplitter(split1);
834         split2->setOrientation(QSplitter::Vertical);
835
836         // create config tree
837         configView = new ConfigView(split2, this, configSettings);
838         configList = configView->list;
839
840         helpText = new QTextView(split2);
841         helpText->setTextFormat(Qt::RichText);
842
843         setTabOrder(configList, helpText);
844         configList->setFocus();
845
846         menu = menuBar();
847         toolBar = new QToolBar("Tools", this);
848
849         backAction = new QAction("Back", QPixmap(xpm_back), "Back", 0, this);
850           connect(backAction, SIGNAL(activated()), SLOT(goBack()));
851           backAction->setEnabled(FALSE);
852         QAction *quitAction = new QAction("Quit", "&Quit", CTRL+Key_Q, this);
853           connect(quitAction, SIGNAL(activated()), SLOT(close()));
854         QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this);
855           connect(loadAction, SIGNAL(activated()), SLOT(loadConfig()));
856         QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
857           connect(saveAction, SIGNAL(activated()), SLOT(saveConfig()));
858         QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this);
859           connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs()));
860         QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this);
861           connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView()));
862         QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this);
863           connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView()));
864         QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), "Full View", 0, this);
865           connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView()));
866
867         QAction *showNameAction = new QAction(NULL, "Show Name", 0, this);
868           showNameAction->setToggleAction(TRUE);
869           showNameAction->setOn(configList->showName);
870           connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool)));
871         QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this);
872           showRangeAction->setToggleAction(TRUE);
873           showRangeAction->setOn(configList->showRange);
874           connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool)));
875         QAction *showDataAction = new QAction(NULL, "Show Data", 0, this);
876           showDataAction->setToggleAction(TRUE);
877           showDataAction->setOn(configList->showData);
878           connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool)));
879         QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this);
880           showAllAction->setToggleAction(TRUE);
881           showAllAction->setOn(configList->showAll);
882           connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool)));
883         QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this);
884           showDebugAction->setToggleAction(TRUE);
885           showDebugAction->setOn(showDebug);
886           connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
887
888         QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this);
889           connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro()));
890         QAction *showAboutAction = new QAction(NULL, "About", 0, this);
891           connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout()));
892
893         // init tool bar
894         backAction->addTo(toolBar);
895         toolBar->addSeparator();
896         loadAction->addTo(toolBar);
897         saveAction->addTo(toolBar);
898         toolBar->addSeparator();
899         singleViewAction->addTo(toolBar);
900         splitViewAction->addTo(toolBar);
901         fullViewAction->addTo(toolBar);
902
903         // create config menu
904         QPopupMenu* config = new QPopupMenu(this);
905         menu->insertItem("&File", config);
906         loadAction->addTo(config);
907         saveAction->addTo(config);
908         saveAsAction->addTo(config);
909         config->insertSeparator();
910         quitAction->addTo(config);
911
912         // create options menu
913         QPopupMenu* optionMenu = new QPopupMenu(this);
914         menu->insertItem("&Option", optionMenu);
915         showNameAction->addTo(optionMenu);
916         showRangeAction->addTo(optionMenu);
917         showDataAction->addTo(optionMenu);
918         optionMenu->insertSeparator();
919         showAllAction->addTo(optionMenu);
920         showDebugAction->addTo(optionMenu);
921
922         // create help menu
923         QPopupMenu* helpMenu = new QPopupMenu(this);
924         menu->insertSeparator();
925         menu->insertItem("&Help", helpMenu);
926         showIntroAction->addTo(helpMenu);
927         showAboutAction->addTo(helpMenu);
928
929         connect(configList, SIGNAL(menuSelected(struct menu *)),
930                 SLOT(changeMenu(struct menu *)));
931         connect(configList, SIGNAL(parentSelected()),
932                 SLOT(goBack()));
933         connect(menuList, SIGNAL(menuSelected(struct menu *)),
934                 SLOT(changeMenu(struct menu *)));
935
936         connect(configList, SIGNAL(gotFocus(void)),
937                 SLOT(listFocusChanged(void)));
938         connect(menuList, SIGNAL(gotFocus(void)),
939                 SLOT(listFocusChanged(void)));
940
941 #if QT_VERSION >= 300
942         QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol");
943         if (listMode == "single")
944                 showSingleView();
945         else if (listMode == "full")
946                 showFullView();
947         else /*if (listMode == "split")*/
948                 showSplitView();
949
950         // UI setup done, restore splitter positions
951         QValueList<int> sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok);
952         if (ok)
953                 split1->setSizes(sizes);
954
955         sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok);
956         if (ok)
957                 split2->setSizes(sizes);
958 #else
959         showSplitView();
960 #endif
961         delete configSettings;
962 }
963
964 static QString print_filter(const char *str)
965 {
966         QRegExp re("[<>&\"\\n]");
967         QString res = str;
968         for (int i = 0; (i = res.find(re, i)) >= 0;) {
969                 switch (res[i].latin1()) {
970                 case '<':
971                         res.replace(i, 1, "&lt;");
972                         i += 4;
973                         break;
974                 case '>':
975                         res.replace(i, 1, "&gt;");
976                         i += 4;
977                         break;
978                 case '&':
979                         res.replace(i, 1, "&amp;");
980                         i += 5;
981                         break;
982                 case '"':
983                         res.replace(i, 1, "&quot;");
984                         i += 6;
985                         break;
986                 case '\n':
987                         res.replace(i, 1, "<br>");
988                         i += 4;
989                         break;
990                 }
991         }
992         return res;
993 }
994
995 static void expr_print_help(void *data, const char *str)
996 {
997         ((QString*)data)->append(print_filter(str));
998 }
999
1000 /*
1001  * display a new help entry as soon as a new menu entry is selected
1002  */
1003 void ConfigMainWindow::setHelp(QListViewItem* item)
1004 {
1005         struct symbol* sym;
1006         struct menu* menu = 0;
1007
1008         configList->parent()->lineEdit->hide();
1009         if (item)
1010                 menu = ((ConfigItem*)item)->menu;
1011         if (!menu) {
1012                 helpText->setText(NULL);
1013                 return;
1014         }
1015
1016         QString head, debug, help;
1017         menu = ((ConfigItem*)item)->menu;
1018         sym = menu->sym;
1019         if (sym) {
1020                 if (menu->prompt) {
1021                         head += "<big><b>";
1022                         head += print_filter(menu->prompt->text);
1023                         head += "</b></big>";
1024                         if (sym->name) {
1025                                 head += " (";
1026                                 head += print_filter(sym->name);
1027                                 head += ")";
1028                         }
1029                 } else if (sym->name) {
1030                         head += "<big><b>";
1031                         head += print_filter(sym->name);
1032                         head += "</b></big>";
1033                 }
1034                 head += "<br><br>";
1035
1036                 if (showDebug) {
1037                         debug += "type: ";
1038                         debug += print_filter(sym_type_name(sym->type));
1039                         if (sym_is_choice(sym))
1040                                 debug += " (choice)";
1041                         debug += "<br>";
1042                         if (sym->rev_dep.expr) {
1043                                 debug += "reverse dep: ";
1044                                 expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
1045                                 debug += "<br>";
1046                         }
1047                         for (struct property *prop = sym->prop; prop; prop = prop->next) {
1048                                 switch (prop->type) {
1049                                 case P_PROMPT:
1050                                 case P_MENU:
1051                                         debug += "prompt: ";
1052                                         debug += print_filter(prop->text);
1053                                         debug += "<br>";
1054                                         break;
1055                                 case P_DEFAULT:
1056                                         debug += "default: ";
1057                                         expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1058                                         debug += "<br>";
1059                                         break;
1060                                 case P_CHOICE:
1061                                         if (sym_is_choice(sym)) {
1062                                                 debug += "choice: ";
1063                                                 expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1064                                                 debug += "<br>";
1065                                         }
1066                                         break;
1067                                 case P_SELECT:
1068                                         debug += "select: ";
1069                                         expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1070                                         debug += "<br>";
1071                                         break;
1072                                 case P_RANGE:
1073                                         debug += "range: ";
1074                                         expr_print(prop->expr, expr_print_help, &debug, E_NONE);
1075                                         debug += "<br>";
1076                                         break;
1077                                 default:
1078                                         debug += "unknown property: ";
1079                                         debug += prop_get_type_name(prop->type);
1080                                         debug += "<br>";
1081                                 }
1082                                 if (prop->visible.expr) {
1083                                         debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
1084                                         expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
1085                                         debug += "<br>";
1086                                 }
1087                         }
1088                         debug += "<br>";
1089                 }
1090
1091                 help = print_filter(sym->help);
1092         } else if (menu->prompt) {
1093                 head += "<big><b>";
1094                 head += print_filter(menu->prompt->text);
1095                 head += "</b></big><br><br>";
1096                 if (showDebug) {
1097                         if (menu->prompt->visible.expr) {
1098                                 debug += "&nbsp;&nbsp;dep: ";
1099                                 expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
1100                                 debug += "<br><br>";
1101                         }
1102                 }
1103         }
1104         if (showDebug)
1105                 debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
1106         helpText->setText(head + debug + help);
1107 }
1108
1109 void ConfigMainWindow::loadConfig(void)
1110 {
1111         QString s = QFileDialog::getOpenFileName(".config", NULL, this);
1112         if (s.isNull())
1113                 return;
1114         if (conf_read(s.latin1()))
1115                 QMessageBox::information(this, "qconf", "Unable to load configuration!");
1116         ConfigView::updateListAll();
1117 }
1118
1119 void ConfigMainWindow::saveConfig(void)
1120 {
1121         if (conf_write(NULL))
1122                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1123 }
1124
1125 void ConfigMainWindow::saveConfigAs(void)
1126 {
1127         QString s = QFileDialog::getSaveFileName(".config", NULL, this);
1128         if (s.isNull())
1129                 return;
1130         if (conf_write(s.latin1()))
1131                 QMessageBox::information(this, "qconf", "Unable to save configuration!");
1132 }
1133
1134 void ConfigMainWindow::changeMenu(struct menu *menu)
1135 {
1136         configList->setRootMenu(menu);
1137         backAction->setEnabled(TRUE);
1138 }
1139
1140 void ConfigMainWindow::listFocusChanged(void)
1141 {
1142         if (menuList->hasFocus()) {
1143                 if (menuList->mode == menuMode)
1144                         configList->clearSelection();
1145                 setHelp(menuList->selectedItem());
1146         } else if (configList->hasFocus()) {
1147                 setHelp(configList->selectedItem());
1148         }
1149 }
1150
1151 void ConfigMainWindow::goBack(void)
1152 {
1153         ConfigItem* item;
1154
1155         configList->setParentMenu();
1156         if (configList->rootEntry == &rootmenu)
1157                 backAction->setEnabled(FALSE);
1158         item = (ConfigItem*)menuList->selectedItem();
1159         while (item) {
1160                 if (item->menu == configList->rootEntry) {
1161                         menuList->setSelected(item, TRUE);
1162                         break;
1163                 }
1164                 item = (ConfigItem*)item->parent();
1165         }
1166 }
1167
1168 void ConfigMainWindow::showSingleView(void)
1169 {
1170         menuView->hide();
1171         menuList->setRootMenu(0);
1172         configList->mode = singleMode;
1173         if (configList->rootEntry == &rootmenu)
1174                 configList->updateListAll();
1175         else
1176                 configList->setRootMenu(&rootmenu);
1177         configList->setAllOpen(TRUE);
1178         configList->setFocus();
1179 }
1180
1181 void ConfigMainWindow::showSplitView(void)
1182 {
1183         configList->mode = symbolMode;
1184         if (configList->rootEntry == &rootmenu)
1185                 configList->updateListAll();
1186         else
1187                 configList->setRootMenu(&rootmenu);
1188         configList->setAllOpen(TRUE);
1189         configApp->processEvents();
1190         menuList->mode = menuMode;
1191         menuList->setRootMenu(&rootmenu);
1192         menuList->setAllOpen(TRUE);
1193         menuView->show();
1194         menuList->setFocus();
1195 }
1196
1197 void ConfigMainWindow::showFullView(void)
1198 {
1199         menuView->hide();
1200         menuList->setRootMenu(0);
1201         configList->mode = fullMode;
1202         if (configList->rootEntry == &rootmenu)
1203                 configList->updateListAll();
1204         else
1205                 configList->setRootMenu(&rootmenu);
1206         configList->setAllOpen(FALSE);
1207         configList->setFocus();
1208 }
1209
1210 void ConfigMainWindow::setShowAll(bool b)
1211 {
1212         if (configList->showAll == b)
1213                 return;
1214         configList->showAll = b;
1215         configList->updateListAll();
1216         menuList->showAll = b;
1217         menuList->updateListAll();
1218 }
1219
1220 void ConfigMainWindow::setShowDebug(bool b)
1221 {
1222         if (showDebug == b)
1223                 return;
1224         showDebug = b;
1225 }
1226
1227 void ConfigMainWindow::setShowName(bool b)
1228 {
1229         if (configList->showName == b)
1230                 return;
1231         configList->showName = b;
1232         configList->reinit();
1233         menuList->showName = b;
1234         menuList->reinit();
1235 }
1236
1237 void ConfigMainWindow::setShowRange(bool b)
1238 {
1239         if (configList->showRange == b)
1240                 return;
1241         configList->showRange = b;
1242         configList->reinit();
1243         menuList->showRange = b;
1244         menuList->reinit();
1245 }
1246
1247 void ConfigMainWindow::setShowData(bool b)
1248 {
1249         if (configList->showData == b)
1250                 return;
1251         configList->showData = b;
1252         configList->reinit();
1253         menuList->showData = b;
1254         menuList->reinit();
1255 }
1256
1257 /*
1258  * ask for saving configuration before quitting
1259  * TODO ask only when something changed
1260  */
1261 void ConfigMainWindow::closeEvent(QCloseEvent* e)
1262 {
1263         if (!sym_change_count) {
1264                 e->accept();
1265                 return;
1266         }
1267         QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
1268                         QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
1269         mb.setButtonText(QMessageBox::Yes, "&Save Changes");
1270         mb.setButtonText(QMessageBox::No, "&Discard Changes");
1271         mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
1272         switch (mb.exec()) {
1273         case QMessageBox::Yes:
1274                 conf_write(NULL);
1275         case QMessageBox::No:
1276                 e->accept();
1277                 break;
1278         case QMessageBox::Cancel:
1279                 e->ignore();
1280                 break;
1281         }
1282 }
1283
1284 void ConfigMainWindow::showIntro(void)
1285 {
1286         static char str[] = "Welcome to the qconf graphical kernel configuration tool for Linux.\n\n"
1287                 "For each option, a blank box indicates the feature is disabled, a check\n"
1288                 "indicates it is enabled, and a dot indicates that it is to be compiled\n"
1289                 "as a module.  Clicking on the box will cycle through the three states.\n\n"
1290                 "If you do not see an option (e.g., a device driver) that you believe\n"
1291                 "should be present, try turning on Show All Options under the Options menu.\n"
1292                 "Although there is no cross reference yet to help you figure out what other\n"
1293                 "options must be enabled to support the option you are interested in, you can\n"
1294                 "still view the help of a grayed-out option.\n\n"
1295                 "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
1296                 "which you can then match by examining other options.\n\n";
1297
1298         QMessageBox::information(this, "qconf", str);
1299 }
1300
1301 void ConfigMainWindow::showAbout(void)
1302 {
1303         static char str[] = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n\n"
1304                 "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n";
1305
1306         QMessageBox::information(this, "qconf", str);
1307 }
1308
1309 void ConfigMainWindow::saveSettings(void)
1310 {
1311 #if QT_VERSION >= 300
1312         ConfigSettings *configSettings = new ConfigSettings;
1313         configSettings->writeEntry("/kconfig/qconf/window x", pos().x());
1314         configSettings->writeEntry("/kconfig/qconf/window y", pos().y());
1315         configSettings->writeEntry("/kconfig/qconf/window width", size().width());
1316         configSettings->writeEntry("/kconfig/qconf/window height", size().height());
1317         configSettings->writeEntry("/kconfig/qconf/showName", configList->showName);
1318         configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange);
1319         configSettings->writeEntry("/kconfig/qconf/showData", configList->showData);
1320         configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll);
1321         configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug);
1322
1323         QString entry;
1324         switch(configList->mode) {
1325         case singleMode :
1326                 entry = "single";
1327                 break;
1328
1329         case symbolMode :
1330                 entry = "split";
1331                 break;
1332
1333         case fullMode :
1334                 entry = "full";
1335                 break;
1336         }
1337         configSettings->writeEntry("/kconfig/qconf/listMode", entry);
1338
1339         configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes());
1340         configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes());
1341
1342         delete configSettings;
1343 #endif
1344 }
1345
1346 void fixup_rootmenu(struct menu *menu)
1347 {
1348         struct menu *child;
1349         static int menu_cnt = 0;
1350
1351         menu->flags |= MENU_ROOT;
1352         for (child = menu->list; child; child = child->next) {
1353                 if (child->prompt && child->prompt->type == P_MENU) {
1354                         menu_cnt++;
1355                         fixup_rootmenu(child);
1356                         menu_cnt--;
1357                 } else if (!menu_cnt)
1358                         fixup_rootmenu(child);
1359         }
1360 }
1361
1362 static const char *progname;
1363
1364 static void usage(void)
1365 {
1366         printf("%s <config>\n", progname);
1367         exit(0);
1368 }
1369
1370 int main(int ac, char** av)
1371 {
1372         ConfigMainWindow* v;
1373         const char *name;
1374
1375 #ifndef LKC_DIRECT_LINK
1376         kconfig_load();
1377 #endif
1378
1379         progname = av[0];
1380         configApp = new QApplication(ac, av);
1381         if (ac > 1 && av[1][0] == '-') {
1382                 switch (av[1][1]) {
1383                 case 'h':
1384                 case '?':
1385                         usage();
1386                 }
1387                 name = av[2];
1388         } else
1389                 name = av[1];
1390         if (!name)
1391                 usage();
1392
1393         conf_parse(name);
1394         fixup_rootmenu(&rootmenu);
1395         conf_read(NULL);
1396         //zconfdump(stdout);
1397
1398         v = new ConfigMainWindow();
1399
1400         //zconfdump(stdout);
1401         v->show();
1402         configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
1403         configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
1404         configApp->exec();
1405
1406         return 0;
1407 }