[PATCH] qconf: fix SIGSEGV on empty menu items
[linux-2.6.git] / scripts / kconfig / qconf.cc
index 4590cd3..a8ffc32 100644 (file)
@@ -6,16 +6,20 @@
 #include <qapplication.h>
 #include <qmainwindow.h>
 #include <qtoolbar.h>
+#include <qlayout.h>
 #include <qvbox.h>
 #include <qsplitter.h>
 #include <qlistview.h>
-#include <qtextview.h>
+#include <qtextbrowser.h>
 #include <qlineedit.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
 #include <qmenubar.h>
 #include <qmessagebox.h>
 #include <qaction.h>
 #include <qheader.h>
 #include <qfiledialog.h>
+#include <qdragobject.h>
 #include <qregexp.h>
 
 #include <stdlib.h>
 #endif
 
 static QApplication *configApp;
+static ConfigSettings *configSettings;
+
+QAction *ConfigMainWindow::saveAction;
 
 static inline QString qgettext(const char* str)
 {
-  return QString::fromLocal8Bit(gettext(str));
+       return QString::fromLocal8Bit(gettext(str));
 }
 
 static inline QString qgettext(const QString& str)
 {
-  return QString::fromLocal8Bit(gettext(str.latin1()));
-}
-
-ConfigSettings::ConfigSettings()
-       : showAll(false), showName(false), showRange(false), showData(false)
-{
-}
-
-#if QT_VERSION >= 300
-/**
- * Reads the list column settings from the application settings.
- */
-void ConfigSettings::readListSettings()
-{
-       showAll = readBoolEntry("/kconfig/qconf/showAll", false);
-       showName = readBoolEntry("/kconfig/qconf/showName", false);
-       showRange = readBoolEntry("/kconfig/qconf/showRange", false);
-       showData = readBoolEntry("/kconfig/qconf/showData", false);
+       return QString::fromLocal8Bit(gettext(str.latin1()));
 }
 
 /**
@@ -88,76 +78,7 @@ bool ConfigSettings::writeSizes(const QString& key, const QValueList<int>& value
                stringList.push_back(QString::number(*it));
        return writeEntry(key, stringList);
 }
-#endif
-
-
-/*
- * update all the children of a menu entry
- *   removes/adds the entries from the parent widget as necessary
- *
- * parent: either the menu list widget or a menu entry widget
- * menu: entry to be updated
- */
-template <class P>
-void ConfigList::updateMenuList(P* parent, struct menu* menu)
-{
-       struct menu* child;
-       ConfigItem* item;
-       ConfigItem* last;
-       bool visible;
-       enum prop_type type;
-
-       if (!menu) {
-               while ((item = parent->firstChild()))
-                       delete item;
-               return;
-       }
-
-       last = parent->firstChild();
-       if (last && !last->goParent)
-               last = 0;
-       for (child = menu->list; child; child = child->next) {
-               item = last ? last->nextSibling() : parent->firstChild();
-               type = child->prompt ? child->prompt->type : P_UNKNOWN;
-
-               switch (mode) {
-               case menuMode:
-                       if (!(child->flags & MENU_ROOT))
-                               goto hide;
-                       break;
-               case symbolMode:
-                       if (child->flags & MENU_ROOT)
-                               goto hide;
-                       break;
-               default:
-                       break;
-               }
-
-               visible = menu_is_visible(child);
-               if (showAll || visible) {
-                       if (!item || item->menu != child)
-                               item = new ConfigItem(parent, last, child, visible);
-                       else
-                               item->testUpdateMenu(visible);
 
-                       if (mode == fullMode || mode == menuMode || type != P_MENU)
-                               updateMenuList(item, child);
-                       else
-                               updateMenuList(item, 0);
-                       last = item;
-                       continue;
-               }
-       hide:
-               if (item && item->menu == child) {
-                       last = parent->firstChild();
-                       if (last == item)
-                               last = 0;
-                       else while (last->nextSibling() != item)
-                               last = last->nextSibling();
-                       delete item;
-               }
-       }
-}
 
 #if QT_VERSION >= 300
 /*
@@ -355,6 +276,12 @@ ConfigItem::~ConfigItem(void)
        }
 }
 
+ConfigLineEdit::ConfigLineEdit(ConfigView* parent)
+       : Parent(parent)
+{
+       connect(this, SIGNAL(lostFocus()), SLOT(hide()));
+}
+
 void ConfigLineEdit::show(ConfigItem* i)
 {
        item = i;
@@ -385,14 +312,14 @@ void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
        hide();
 }
 
-ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings)
-       : Parent(p), cview(cv),
+ConfigList::ConfigList(ConfigView* p, const char *name)
+       : Parent(p, name),
          updateAll(false),
          symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no),
          choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no),
          menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void),
          showAll(false), showName(false), showRange(false), showData(false),
-         rootEntry(0)
+         rootEntry(0), headerPopup(0)
 {
        int i;
 
@@ -406,11 +333,14 @@ ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* conf
        connect(this, SIGNAL(selectionChanged(void)),
                SLOT(updateSelection(void)));
 
-       if (configSettings) {
-               showAll = configSettings->showAll;
-               showName = configSettings->showName;
-               showRange = configSettings->showRange;
-               showData = configSettings->showData;
+       if (name) {
+               configSettings->beginGroup(name);
+               showAll = configSettings->readBoolEntry("/showAll", false);
+               showName = configSettings->readBoolEntry("/showName", false);
+               showRange = configSettings->readBoolEntry("/showRange", false);
+               showData = configSettings->readBoolEntry("/showData", false);
+               configSettings->endGroup();
+               connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
        }
 
        for (i = 0; i < colNr; i++)
@@ -441,6 +371,30 @@ void ConfigList::reinit(void)
        updateListAll();
 }
 
+void ConfigList::saveSettings(void)
+{
+       if (name()) {
+               configSettings->beginGroup(name());
+               configSettings->writeEntry("/showName", showName);
+               configSettings->writeEntry("/showRange", showRange);
+               configSettings->writeEntry("/showData", showData);
+               configSettings->writeEntry("/showAll", showAll);
+               configSettings->endGroup();
+       }
+}
+
+ConfigItem* ConfigList::findConfigItem(struct menu *menu)
+{
+       ConfigItem* item = (ConfigItem*)menu->data;
+
+       for (; item; item = item->nextItem) {
+               if (this == item->listView())
+                       break;
+       }
+
+       return item;
+}
+
 void ConfigList::updateSelection(void)
 {
        struct menu *menu;
@@ -450,9 +404,8 @@ void ConfigList::updateSelection(void)
        if (!item)
                return;
 
-       cview->setHelp(item);
-
        menu = item->menu;
+       emit menuChanged(menu);
        if (!menu)
                return;
        type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
@@ -464,8 +417,20 @@ void ConfigList::updateList(ConfigItem* item)
 {
        ConfigItem* last = 0;
 
-       if (!rootEntry)
-               goto update;
+       if (!rootEntry) {
+               if (mode != listMode)
+                       goto update;
+               QListViewItemIterator it(this);
+               ConfigItem* item;
+
+               for (; it.current(); ++it) {
+                       item = (ConfigItem*)it.current();
+                       if (!item->menu)
+                               continue;
+                       item->testUpdateMenu(menu_is_visible(item->menu));
+               }
+               return;
+       }
 
        if (rootEntry != &rootmenu && (mode == singleMode ||
            (mode == symbolMode && rootEntry->parent != &rootmenu))) {
@@ -491,14 +456,6 @@ update:
        triggerUpdate();
 }
 
-void ConfigList::setAllOpen(bool open)
-{
-       QListViewItemIterator it(this);
-
-       for (; it.current(); it++)
-               it.current()->setOpen(open);
-}
-
 void ConfigList::setValue(ConfigItem* item, tristate val)
 {
        struct symbol* sym;
@@ -581,6 +538,7 @@ void ConfigList::setRootMenu(struct menu *menu)
        rootEntry = menu;
        updateListAll();
        setSelected(currentItem(), hasFocus());
+       ensureItemVisible(currentItem());
 }
 
 void ConfigList::setParentMenu(void)
@@ -603,6 +561,74 @@ void ConfigList::setParentMenu(void)
        }
 }
 
+/*
+ * update all the children of a menu entry
+ *   removes/adds the entries from the parent widget as necessary
+ *
+ * parent: either the menu list widget or a menu entry widget
+ * menu: entry to be updated
+ */
+template <class P>
+void ConfigList::updateMenuList(P* parent, struct menu* menu)
+{
+       struct menu* child;
+       ConfigItem* item;
+       ConfigItem* last;
+       bool visible;
+       enum prop_type type;
+
+       if (!menu) {
+               while ((item = parent->firstChild()))
+                       delete item;
+               return;
+       }
+
+       last = parent->firstChild();
+       if (last && !last->goParent)
+               last = 0;
+       for (child = menu->list; child; child = child->next) {
+               item = last ? last->nextSibling() : parent->firstChild();
+               type = child->prompt ? child->prompt->type : P_UNKNOWN;
+
+               switch (mode) {
+               case menuMode:
+                       if (!(child->flags & MENU_ROOT))
+                               goto hide;
+                       break;
+               case symbolMode:
+                       if (child->flags & MENU_ROOT)
+                               goto hide;
+                       break;
+               default:
+                       break;
+               }
+
+               visible = menu_is_visible(child);
+               if (showAll || visible) {
+                       if (!item || item->menu != child)
+                               item = new ConfigItem(parent, last, child, visible);
+                       else
+                               item->testUpdateMenu(visible);
+
+                       if (mode == fullMode || mode == menuMode || type != P_MENU)
+                               updateMenuList(item, child);
+                       else
+                               updateMenuList(item, 0);
+                       last = item;
+                       continue;
+               }
+       hide:
+               if (item && item->menu == child) {
+                       last = parent->firstChild();
+                       if (last == item)
+                               last = 0;
+                       else while (last->nextSibling() != item)
+                               last = last->nextSibling();
+                       delete item;
+               }
+       }
+}
+
 void ConfigList::keyPressEvent(QKeyEvent* ev)
 {
        QListViewItem* i = currentItem();
@@ -610,7 +636,7 @@ void ConfigList::keyPressEvent(QKeyEvent* ev)
        struct menu *menu;
        enum prop_type type;
 
-       if (ev->key() == Key_Escape && mode != fullMode) {
+       if (ev->key() == Key_Escape && mode != fullMode && mode != listMode) {
                emit parentSelected();
                ev->accept();
                return;
@@ -755,23 +781,62 @@ skip:
 
 void ConfigList::focusInEvent(QFocusEvent *e)
 {
+       struct menu *menu = NULL;
+
        Parent::focusInEvent(e);
 
-       QListViewItem* item = currentItem();
-       if (!item)
-               return;
+       ConfigItem* item = (ConfigItem *)currentItem();
+       if (item) {
+               setSelected(item, TRUE);
+               menu = item->menu;
+       }
+       emit gotFocus(menu);
+}
 
-       setSelected(item, TRUE);
-       emit gotFocus();
+void ConfigList::contextMenuEvent(QContextMenuEvent *e)
+{
+       if (e->y() <= header()->geometry().bottom()) {
+               if (!headerPopup) {
+                       QAction *action;
+
+                       headerPopup = new QPopupMenu(this);
+                       action = new QAction(NULL, "Show Name", 0, this);
+                         action->setToggleAction(TRUE);
+                         connect(action, SIGNAL(toggled(bool)),
+                                 parent(), SLOT(setShowName(bool)));
+                         connect(parent(), SIGNAL(showNameChanged(bool)),
+                                 action, SLOT(setOn(bool)));
+                         action->setOn(showName);
+                         action->addTo(headerPopup);
+                       action = new QAction(NULL, "Show Range", 0, this);
+                         action->setToggleAction(TRUE);
+                         connect(action, SIGNAL(toggled(bool)),
+                                 parent(), SLOT(setShowRange(bool)));
+                         connect(parent(), SIGNAL(showRangeChanged(bool)),
+                                 action, SLOT(setOn(bool)));
+                         action->setOn(showRange);
+                         action->addTo(headerPopup);
+                       action = new QAction(NULL, "Show Data", 0, this);
+                         action->setToggleAction(TRUE);
+                         connect(action, SIGNAL(toggled(bool)),
+                                 parent(), SLOT(setShowData(bool)));
+                         connect(parent(), SIGNAL(showDataChanged(bool)),
+                                 action, SLOT(setOn(bool)));
+                         action->setOn(showData);
+                         action->addTo(headerPopup);
+               }
+               headerPopup->exec(e->globalPos());
+               e->accept();
+       } else
+               e->ignore();
 }
 
 ConfigView* ConfigView::viewList;
 
-ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview,
-                      ConfigSettings *configSettings)
-       : Parent(parent)
+ConfigView::ConfigView(QWidget* parent, const char *name)
+       : Parent(parent, name)
 {
-       list = new ConfigList(this, cview, configSettings);
+       list = new ConfigList(this, name);
        lineEdit = new ConfigLineEdit(this);
        lineEdit->hide();
 
@@ -791,6 +856,50 @@ ConfigView::~ConfigView(void)
        }
 }
 
+void ConfigView::setShowAll(bool b)
+{
+       if (list->showAll != b) {
+               list->showAll = b;
+               list->updateListAll();
+               emit showAllChanged(b);
+       }
+}
+
+void ConfigView::setShowName(bool b)
+{
+       if (list->showName != b) {
+               list->showName = b;
+               list->reinit();
+               emit showNameChanged(b);
+       }
+}
+
+void ConfigView::setShowRange(bool b)
+{
+       if (list->showRange != b) {
+               list->showRange = b;
+               list->reinit();
+               emit showRangeChanged(b);
+       }
+}
+
+void ConfigView::setShowData(bool b)
+{
+       if (list->showData != b) {
+               list->showData = b;
+               list->reinit();
+               emit showDataChanged(b);
+       }
+}
+
+void ConfigList::setAllOpen(bool open)
+{
+       QListViewItemIterator it(this);
+
+       for (; it.current(); it++)
+               it.current()->setOpen(open);
+}
+
 void ConfigView::updateList(ConfigItem* item)
 {
        ConfigView* v;
@@ -807,10 +916,360 @@ void ConfigView::updateListAll(void)
                v->list->updateListAll();
 }
 
+ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
+       : Parent(parent, name), menu(0)
+{
+       if (name) {
+               configSettings->beginGroup(name);
+               _showDebug = configSettings->readBoolEntry("/showDebug", false);
+               configSettings->endGroup();
+               connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
+       }
+
+       has_dbg_info = 0;
+}
+
+void ConfigInfoView::saveSettings(void)
+{
+       if (name()) {
+               configSettings->beginGroup(name());
+               configSettings->writeEntry("/showDebug", showDebug());
+               configSettings->endGroup();
+       }
+}
+
+void ConfigInfoView::setShowDebug(bool b)
+{
+       if (_showDebug != b) {
+               _showDebug = b;
+               if (menu)
+                       menuInfo();
+               else if (sym)
+                       symbolInfo();
+               emit showDebugChanged(b);
+       }
+}
+
+void ConfigInfoView::setInfo(struct menu *m)
+{
+       if (menu == m)
+               return;
+       menu = m;
+       if (!menu) {
+               has_dbg_info = 0;
+               clear();
+       } else {
+               has_dbg_info = 1;
+               menuInfo();
+       }
+}
+
+void ConfigInfoView::setSource(const QString& name)
+{
+       const char *p = name.latin1();
+
+       menu = NULL;
+       sym = NULL;
+
+       switch (p[0]) {
+       case 'm':
+               struct menu *m;
+
+               if (sscanf(p, "m%p", &m) == 1 && menu != m) {
+                       menu = m;
+                       menuInfo();
+                       emit menuSelected(menu);
+               }
+               break;
+       case 's':
+               struct symbol *s;
+
+               if (sscanf(p, "s%p", &s) == 1 && sym != s) {
+                       sym = s;
+                       symbolInfo();
+               }
+               break;
+       }
+}
+
+void ConfigInfoView::symbolInfo(void)
+{
+       QString str;
+
+       if (!has_dbg_info)
+               return;
+
+       str += "<big>Symbol: <b>";
+       str += print_filter(sym->name);
+       str += "</b></big><br><br>value: ";
+       str += print_filter(sym_get_string_value(sym));
+       str += "<br>visibility: ";
+       str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
+       str += "<br>";
+       str += debug_info(sym);
+
+       setText(str);
+}
+
+void ConfigInfoView::menuInfo(void)
+{
+       struct symbol* sym;
+       QString head, debug, help;
+
+       sym = menu->sym;
+       if (sym) {
+               if (menu->prompt) {
+                       head += "<big><b>";
+                       head += print_filter(_(menu->prompt->text));
+                       head += "</b></big>";
+                       if (sym->name) {
+                               head += " (";
+                               if (showDebug())
+                                       head += QString().sprintf("<a href=\"s%p\">", sym);
+                               head += print_filter(sym->name);
+                               if (showDebug())
+                                       head += "</a>";
+                               head += ")";
+                       }
+               } else if (sym->name) {
+                       head += "<big><b>";
+                       if (showDebug())
+                               head += QString().sprintf("<a href=\"s%p\">", sym);
+                       head += print_filter(sym->name);
+                       if (showDebug())
+                               head += "</a>";
+                       head += "</b></big>";
+               }
+               head += "<br><br>";
+
+               if (showDebug())
+                       debug = debug_info(sym);
+
+               help = print_filter(_(sym->help));
+       } else if (menu->prompt) {
+               head += "<big><b>";
+               head += print_filter(_(menu->prompt->text));
+               head += "</b></big><br><br>";
+               if (showDebug()) {
+                       if (menu->prompt->visible.expr) {
+                               debug += "&nbsp;&nbsp;dep: ";
+                               expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
+                               debug += "<br><br>";
+                       }
+               }
+       }
+       if (showDebug())
+               debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
+
+       setText(head + debug + help);
+}
+
+QString ConfigInfoView::debug_info(struct symbol *sym)
+{
+       QString debug;
+
+       debug += "type: ";
+       debug += print_filter(sym_type_name(sym->type));
+       if (sym_is_choice(sym))
+               debug += " (choice)";
+       debug += "<br>";
+       if (sym->rev_dep.expr) {
+               debug += "reverse dep: ";
+               expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
+               debug += "<br>";
+       }
+       for (struct property *prop = sym->prop; prop; prop = prop->next) {
+               switch (prop->type) {
+               case P_PROMPT:
+               case P_MENU:
+                       debug += QString().sprintf("prompt: <a href=\"m%p\">", prop->menu);
+                       debug += print_filter(_(prop->text));
+                       debug += "</a><br>";
+                       break;
+               case P_DEFAULT:
+                       debug += "default: ";
+                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+                       debug += "<br>";
+                       break;
+               case P_CHOICE:
+                       if (sym_is_choice(sym)) {
+                               debug += "choice: ";
+                               expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+                               debug += "<br>";
+                       }
+                       break;
+               case P_SELECT:
+                       debug += "select: ";
+                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+                       debug += "<br>";
+                       break;
+               case P_RANGE:
+                       debug += "range: ";
+                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+                       debug += "<br>";
+                       break;
+               default:
+                       debug += "unknown property: ";
+                       debug += prop_get_type_name(prop->type);
+                       debug += "<br>";
+               }
+               if (prop->visible.expr) {
+                       debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
+                       expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
+                       debug += "<br>";
+               }
+       }
+       debug += "<br>";
+
+       return debug;
+}
+
+QString ConfigInfoView::print_filter(const QString &str)
+{
+       QRegExp re("[<>&\"\\n]");
+       QString res = str;
+       for (int i = 0; (i = res.find(re, i)) >= 0;) {
+               switch (res[i].latin1()) {
+               case '<':
+                       res.replace(i, 1, "&lt;");
+                       i += 4;
+                       break;
+               case '>':
+                       res.replace(i, 1, "&gt;");
+                       i += 4;
+                       break;
+               case '&':
+                       res.replace(i, 1, "&amp;");
+                       i += 5;
+                       break;
+               case '"':
+                       res.replace(i, 1, "&quot;");
+                       i += 6;
+                       break;
+               case '\n':
+                       res.replace(i, 1, "<br>");
+                       i += 4;
+                       break;
+               }
+       }
+       return res;
+}
+
+void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
+{
+       QString* text = reinterpret_cast<QString*>(data);
+       QString str2 = print_filter(str);
+
+       if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
+               *text += QString().sprintf("<a href=\"s%p\">", sym);
+               *text += str2;
+               *text += "</a>";
+       } else
+               *text += str2;
+}
+
+QPopupMenu* ConfigInfoView::createPopupMenu(const QPoint& pos)
+{
+       QPopupMenu* popup = Parent::createPopupMenu(pos);
+       QAction* action = new QAction(NULL,"Show Debug Info", 0, popup);
+         action->setToggleAction(TRUE);
+         connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
+         connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool)));
+         action->setOn(showDebug());
+       popup->insertSeparator();
+       action->addTo(popup);
+       return popup;
+}
+
+void ConfigInfoView::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+       Parent::contentsContextMenuEvent(e);
+}
+
+ConfigSearchWindow::ConfigSearchWindow(QWidget* parent, const char *name)
+       : Parent(parent, name), result(NULL)
+{
+       setCaption("Search Config");
+
+       QVBoxLayout* layout1 = new QVBoxLayout(this, 11, 6);
+       QHBoxLayout* layout2 = new QHBoxLayout(0, 0, 6);
+       layout2->addWidget(new QLabel("Find:", this));
+       editField = new QLineEdit(this);
+       connect(editField, SIGNAL(returnPressed()), SLOT(search()));
+       layout2->addWidget(editField);
+       searchButton = new QPushButton("Search", this);
+       searchButton->setAutoDefault(FALSE);
+       connect(searchButton, SIGNAL(clicked()), SLOT(search()));
+       layout2->addWidget(searchButton);
+       layout1->addLayout(layout2);
+
+       split = new QSplitter(this);
+       split->setOrientation(QSplitter::Vertical);
+       list = new ConfigView(split, name);
+       list->list->mode = listMode;
+       info = new ConfigInfoView(split, name);
+       connect(list->list, SIGNAL(menuChanged(struct menu *)),
+               info, SLOT(setInfo(struct menu *)));
+       layout1->addWidget(split);
+
+       if (name) {
+               int x, y, width, height;
+               bool ok;
+
+               configSettings->beginGroup(name);
+               width = configSettings->readNumEntry("/window width", parent->width() / 2);
+               height = configSettings->readNumEntry("/window height", parent->height() / 2);
+               resize(width, height);
+               x = configSettings->readNumEntry("/window x", 0, &ok);
+               if (ok)
+                       y = configSettings->readNumEntry("/window y", 0, &ok);
+               if (ok)
+                       move(x, y);
+               QValueList<int> sizes = configSettings->readSizes("/split", &ok);
+               if (ok)
+                       split->setSizes(sizes);
+               configSettings->endGroup();
+               connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings()));
+       }
+}
+
+void ConfigSearchWindow::saveSettings(void)
+{
+       if (name()) {
+               configSettings->beginGroup(name());
+               configSettings->writeEntry("/window x", pos().x());
+               configSettings->writeEntry("/window y", pos().y());
+               configSettings->writeEntry("/window width", size().width());
+               configSettings->writeEntry("/window height", size().height());
+               configSettings->writeSizes("/split", split->sizes());
+               configSettings->endGroup();
+       }
+}
+
+void ConfigSearchWindow::search(void)
+{
+       struct symbol **p;
+       struct property *prop;
+       ConfigItem *lastItem = NULL;
+
+       free(result);
+       list->list->clear();
+
+       result = sym_re_search(editField->text().latin1());
+       if (!result)
+               return;
+       for (p = result; *p; p++) {
+               for_all_prompts((*p), prop)
+                       lastItem = new ConfigItem(list->list, lastItem, prop->menu,
+                                                 menu_is_visible(prop->menu));
+       }
+}
+
 /*
  * Construct the complete config widget
  */
 ConfigMainWindow::ConfigMainWindow(void)
+       : searchWindow(0)
 {
        QMenuBar* menu;
        bool ok;
@@ -818,42 +1277,30 @@ ConfigMainWindow::ConfigMainWindow(void)
 
        QWidget *d = configApp->desktop();
 
-       ConfigSettings* configSettings = new ConfigSettings();
-#if QT_VERSION >= 300
-       width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64);
-       height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64);
+       width = configSettings->readNumEntry("/window width", d->width() - 64);
+       height = configSettings->readNumEntry("/window height", d->height() - 64);
        resize(width, height);
-       x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok);
+       x = configSettings->readNumEntry("/window x", 0, &ok);
        if (ok)
-               y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok);
+               y = configSettings->readNumEntry("/window y", 0, &ok);
        if (ok)
                move(x, y);
-       showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false);
-
-       // read list settings into configSettings, will be used later for ConfigList setup
-       configSettings->readListSettings();
-#else
-       width = d->width() - 64;
-       height = d->height() - 64;
-       resize(width, height);
-       showDebug = false;
-#endif
 
        split1 = new QSplitter(this);
        split1->setOrientation(QSplitter::Horizontal);
        setCentralWidget(split1);
 
-       menuView = new ConfigView(split1, this, configSettings);
+       menuView = new ConfigView(split1, "menu");
        menuList = menuView->list;
 
        split2 = new QSplitter(split1);
        split2->setOrientation(QSplitter::Vertical);
 
        // create config tree
-       configView = new ConfigView(split2, this, configSettings);
+       configView = new ConfigView(split2, "config");
        configList = configView->list;
 
-       helpText = new QTextView(split2);
+       helpText = new ConfigInfoView(split2, "help");
        helpText->setTextFormat(Qt::RichText);
 
        setTabOrder(configList, helpText);
@@ -869,10 +1316,15 @@ ConfigMainWindow::ConfigMainWindow(void)
          connect(quitAction, SIGNAL(activated()), SLOT(close()));
        QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this);
          connect(loadAction, SIGNAL(activated()), SLOT(loadConfig()));
-       QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
+       saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
          connect(saveAction, SIGNAL(activated()), SLOT(saveConfig()));
+       conf_set_changed_callback(conf_changed);
+       // Set saveAction's initial state
+       conf_changed();
        QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this);
          connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs()));
+       QAction *searchAction = new QAction("Search", "&Search", CTRL+Key_F, this);
+         connect(searchAction, SIGNAL(activated()), SLOT(searchConfig()));
        QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this);
          connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView()));
        QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this);
@@ -882,24 +1334,29 @@ ConfigMainWindow::ConfigMainWindow(void)
 
        QAction *showNameAction = new QAction(NULL, "Show Name", 0, this);
          showNameAction->setToggleAction(TRUE);
-         showNameAction->setOn(configList->showName);
-         connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool)));
+         connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool)));
+         connect(configView, SIGNAL(showNameChanged(bool)), showNameAction, SLOT(setOn(bool)));
+         showNameAction->setOn(configView->showName());
        QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this);
          showRangeAction->setToggleAction(TRUE);
+         connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool)));
+         connect(configView, SIGNAL(showRangeChanged(bool)), showRangeAction, SLOT(setOn(bool)));
          showRangeAction->setOn(configList->showRange);
-         connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool)));
        QAction *showDataAction = new QAction(NULL, "Show Data", 0, this);
          showDataAction->setToggleAction(TRUE);
+         connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool)));
+         connect(configView, SIGNAL(showDataChanged(bool)), showDataAction, SLOT(setOn(bool)));
          showDataAction->setOn(configList->showData);
-         connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool)));
        QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this);
          showAllAction->setToggleAction(TRUE);
+         connect(showAllAction, SIGNAL(toggled(bool)), configView, SLOT(setShowAll(bool)));
+         connect(showAllAction, SIGNAL(toggled(bool)), menuView, SLOT(setShowAll(bool)));
          showAllAction->setOn(configList->showAll);
-         connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool)));
        QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this);
          showDebugAction->setToggleAction(TRUE);
-         showDebugAction->setOn(showDebug);
-         connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
+         connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool)));
+         connect(helpText, SIGNAL(showDebugChanged(bool)), showDebugAction, SLOT(setOn(bool)));
+         showDebugAction->setOn(helpText->showDebug());
 
        QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this);
          connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro()));
@@ -923,6 +1380,8 @@ ConfigMainWindow::ConfigMainWindow(void)
        saveAction->addTo(config);
        saveAsAction->addTo(config);
        config->insertSeparator();
+       searchAction->addTo(config);
+       config->insertSeparator();
        quitAction->addTo(config);
 
        // create options menu
@@ -942,20 +1401,27 @@ ConfigMainWindow::ConfigMainWindow(void)
        showIntroAction->addTo(helpMenu);
        showAboutAction->addTo(helpMenu);
 
+       connect(configList, SIGNAL(menuChanged(struct menu *)),
+               helpText, SLOT(setInfo(struct menu *)));
        connect(configList, SIGNAL(menuSelected(struct menu *)),
                SLOT(changeMenu(struct menu *)));
        connect(configList, SIGNAL(parentSelected()),
                SLOT(goBack()));
+       connect(menuList, SIGNAL(menuChanged(struct menu *)),
+               helpText, SLOT(setInfo(struct menu *)));
        connect(menuList, SIGNAL(menuSelected(struct menu *)),
                SLOT(changeMenu(struct menu *)));
 
-       connect(configList, SIGNAL(gotFocus(void)),
-               SLOT(listFocusChanged(void)));
-       connect(menuList, SIGNAL(gotFocus(void)),
+       connect(configList, SIGNAL(gotFocus(struct menu *)),
+               helpText, SLOT(setInfo(struct menu *)));
+       connect(menuList, SIGNAL(gotFocus(struct menu *)),
+               helpText, SLOT(setInfo(struct menu *)));
+       connect(menuList, SIGNAL(gotFocus(struct menu *)),
                SLOT(listFocusChanged(void)));
+       connect(helpText, SIGNAL(menuSelected(struct menu *)),
+               SLOT(setMenuLink(struct menu *)));
 
-#if QT_VERSION >= 300
-       QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol");
+       QString listMode = configSettings->readEntry("/listMode", "symbol");
        if (listMode == "single")
                showSingleView();
        else if (listMode == "full")
@@ -964,162 +1430,13 @@ ConfigMainWindow::ConfigMainWindow(void)
                showSplitView();
 
        // UI setup done, restore splitter positions
-       QValueList<int> sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok);
+       QValueList<int> sizes = configSettings->readSizes("/split1", &ok);
        if (ok)
                split1->setSizes(sizes);
 
-       sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok);
+       sizes = configSettings->readSizes("/split2", &ok);
        if (ok)
                split2->setSizes(sizes);
-#else
-       showSplitView();
-#endif
-       delete configSettings;
-}
-
-static QString print_filter(const QString &str)
-{
-       QRegExp re("[<>&\"\\n]");
-       QString res = str;
-       for (int i = 0; (i = res.find(re, i)) >= 0;) {
-               switch (res[i].latin1()) {
-               case '<':
-                       res.replace(i, 1, "&lt;");
-                       i += 4;
-                       break;
-               case '>':
-                       res.replace(i, 1, "&gt;");
-                       i += 4;
-                       break;
-               case '&':
-                       res.replace(i, 1, "&amp;");
-                       i += 5;
-                       break;
-               case '"':
-                       res.replace(i, 1, "&quot;");
-                       i += 6;
-                       break;
-               case '\n':
-                       res.replace(i, 1, "<br>");
-                       i += 4;
-                       break;
-               }
-       }
-       return res;
-}
-
-static void expr_print_help(void *data, const char *str)
-{
-       reinterpret_cast<QString*>(data)->append(print_filter(str));
-}
-
-/*
- * display a new help entry as soon as a new menu entry is selected
- */
-void ConfigMainWindow::setHelp(QListViewItem* item)
-{
-       struct symbol* sym;
-       struct menu* menu = 0;
-
-       configList->parent()->lineEdit->hide();
-       if (item)
-               menu = ((ConfigItem*)item)->menu;
-       if (!menu) {
-               helpText->setText(QString::null);
-               return;
-       }
-
-       QString head, debug, help;
-       menu = ((ConfigItem*)item)->menu;
-       sym = menu->sym;
-       if (sym) {
-               if (menu->prompt) {
-                       head += "<big><b>";
-                       head += print_filter(_(menu->prompt->text));
-                       head += "</b></big>";
-                       if (sym->name) {
-                               head += " (";
-                               head += print_filter(_(sym->name));
-                               head += ")";
-                       }
-               } else if (sym->name) {
-                       head += "<big><b>";
-                       head += print_filter(_(sym->name));
-                       head += "</b></big>";
-               }
-               head += "<br><br>";
-
-               if (showDebug) {
-                       debug += "type: ";
-                       debug += print_filter(sym_type_name(sym->type));
-                       if (sym_is_choice(sym))
-                               debug += " (choice)";
-                       debug += "<br>";
-                       if (sym->rev_dep.expr) {
-                               debug += "reverse dep: ";
-                               expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
-                               debug += "<br>";
-                       }
-                       for (struct property *prop = sym->prop; prop; prop = prop->next) {
-                               switch (prop->type) {
-                               case P_PROMPT:
-                               case P_MENU:
-                                       debug += "prompt: ";
-                                       debug += print_filter(_(prop->text));
-                                       debug += "<br>";
-                                       break;
-                               case P_DEFAULT:
-                                       debug += "default: ";
-                                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
-                                       debug += "<br>";
-                                       break;
-                               case P_CHOICE:
-                                       if (sym_is_choice(sym)) {
-                                               debug += "choice: ";
-                                               expr_print(prop->expr, expr_print_help, &debug, E_NONE);
-                                               debug += "<br>";
-                                       }
-                                       break;
-                               case P_SELECT:
-                                       debug += "select: ";
-                                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
-                                       debug += "<br>";
-                                       break;
-                               case P_RANGE:
-                                       debug += "range: ";
-                                       expr_print(prop->expr, expr_print_help, &debug, E_NONE);
-                                       debug += "<br>";
-                                       break;
-                               default:
-                                       debug += "unknown property: ";
-                                       debug += prop_get_type_name(prop->type);
-                                       debug += "<br>";
-                               }
-                               if (prop->visible.expr) {
-                                       debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
-                                       expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
-                                       debug += "<br>";
-                               }
-                       }
-                       debug += "<br>";
-               }
-
-               help = print_filter(_(sym->help));
-       } else if (menu->prompt) {
-               head += "<big><b>";
-               head += print_filter(_(menu->prompt->text));
-               head += "</b></big><br><br>";
-               if (showDebug) {
-                       if (menu->prompt->visible.expr) {
-                               debug += "&nbsp;&nbsp;dep: ";
-                               expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
-                               debug += "<br><br>";
-                       }
-               }
-       }
-       if (showDebug)
-               debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
-       helpText->setText(head + debug + help);
 }
 
 void ConfigMainWindow::loadConfig(void)
@@ -1147,23 +1464,75 @@ void ConfigMainWindow::saveConfigAs(void)
                QMessageBox::information(this, "qconf", "Unable to save configuration!");
 }
 
+void ConfigMainWindow::searchConfig(void)
+{
+       if (!searchWindow)
+               searchWindow = new ConfigSearchWindow(this, "search");
+       searchWindow->show();
+}
+
 void ConfigMainWindow::changeMenu(struct menu *menu)
 {
        configList->setRootMenu(menu);
        backAction->setEnabled(TRUE);
 }
 
-void ConfigMainWindow::listFocusChanged(void)
+void ConfigMainWindow::setMenuLink(struct menu *menu)
 {
-       if (menuList->hasFocus()) {
-               if (menuList->mode == menuMode)
+       struct menu *parent;
+       ConfigList* list = NULL;
+       ConfigItem* item;
+
+       if (!menu_is_visible(menu) && !configView->showAll())
+               return;
+
+       switch (configList->mode) {
+       case singleMode:
+               list = configList;
+               parent = menu_get_parent_menu(menu);
+               if (!parent)
+                       return;
+               list->setRootMenu(parent);
+               break;
+       case symbolMode:
+               if (menu->flags & MENU_ROOT) {
+                       configList->setRootMenu(menu);
                        configList->clearSelection();
-               setHelp(menuList->selectedItem());
-       } else if (configList->hasFocus()) {
-               setHelp(configList->selectedItem());
+                       list = menuList;
+               } else {
+                       list = configList;
+                       parent = menu_get_parent_menu(menu->parent);
+                       if (!parent)
+                               return;
+                       item = menuList->findConfigItem(parent);
+                       if (item) {
+                               menuList->setSelected(item, TRUE);
+                               menuList->ensureItemVisible(item);
+                       }
+                       list->setRootMenu(parent);
+               }
+               break;
+       case fullMode:
+               list = configList;
+               break;
+       }
+
+       if (list) {
+               item = list->findConfigItem(menu);
+               if (item) {
+                       list->setSelected(item, TRUE);
+                       list->ensureItemVisible(item);
+                       list->setFocus();
+               }
        }
 }
 
+void ConfigMainWindow::listFocusChanged(void)
+{
+       if (menuList->mode == menuMode)
+               configList->clearSelection();
+}
+
 void ConfigMainWindow::goBack(void)
 {
        ConfigItem* item;
@@ -1223,60 +1592,13 @@ void ConfigMainWindow::showFullView(void)
        configList->setFocus();
 }
 
-void ConfigMainWindow::setShowAll(bool b)
-{
-       if (configList->showAll == b)
-               return;
-       configList->showAll = b;
-       configList->updateListAll();
-       menuList->showAll = b;
-       menuList->updateListAll();
-}
-
-void ConfigMainWindow::setShowDebug(bool b)
-{
-       if (showDebug == b)
-               return;
-       showDebug = b;
-}
-
-void ConfigMainWindow::setShowName(bool b)
-{
-       if (configList->showName == b)
-               return;
-       configList->showName = b;
-       configList->reinit();
-       menuList->showName = b;
-       menuList->reinit();
-}
-
-void ConfigMainWindow::setShowRange(bool b)
-{
-       if (configList->showRange == b)
-               return;
-       configList->showRange = b;
-       configList->reinit();
-       menuList->showRange = b;
-       menuList->reinit();
-}
-
-void ConfigMainWindow::setShowData(bool b)
-{
-       if (configList->showData == b)
-               return;
-       configList->showData = b;
-       configList->reinit();
-       menuList->showData = b;
-       menuList->reinit();
-}
-
 /*
  * ask for saving configuration before quitting
  * TODO ask only when something changed
  */
 void ConfigMainWindow::closeEvent(QCloseEvent* e)
 {
-       if (!sym_change_count) {
+       if (!conf_get_changed()) {
                e->accept();
                return;
        }
@@ -1324,17 +1646,10 @@ void ConfigMainWindow::showAbout(void)
 
 void ConfigMainWindow::saveSettings(void)
 {
-#if QT_VERSION >= 300
-       ConfigSettings *configSettings = new ConfigSettings;
-       configSettings->writeEntry("/kconfig/qconf/window x", pos().x());
-       configSettings->writeEntry("/kconfig/qconf/window y", pos().y());
-       configSettings->writeEntry("/kconfig/qconf/window width", size().width());
-       configSettings->writeEntry("/kconfig/qconf/window height", size().height());
-       configSettings->writeEntry("/kconfig/qconf/showName", configList->showName);
-       configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange);
-       configSettings->writeEntry("/kconfig/qconf/showData", configList->showData);
-       configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll);
-       configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug);
+       configSettings->writeEntry("/window x", pos().x());
+       configSettings->writeEntry("/window y", pos().y());
+       configSettings->writeEntry("/window width", size().width());
+       configSettings->writeEntry("/window height", size().height());
 
        QString entry;
        switch(configList->mode) {
@@ -1350,13 +1665,16 @@ void ConfigMainWindow::saveSettings(void)
                entry = "full";
                break;
        }
-       configSettings->writeEntry("/kconfig/qconf/listMode", entry);
+       configSettings->writeEntry("/listMode", entry);
 
-       configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes());
-       configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes());
+       configSettings->writeSizes("/split1", split1->sizes());
+       configSettings->writeSizes("/split2", split2->sizes());
+}
 
-       delete configSettings;
-#endif
+void ConfigMainWindow::conf_changed(void)
+{
+       if (saveAction)
+               saveAction->setEnabled(conf_get_changed());
 }
 
 void fixup_rootmenu(struct menu *menu)
@@ -1414,13 +1732,19 @@ int main(int ac, char** av)
        conf_read(NULL);
        //zconfdump(stdout);
 
+       configSettings = new ConfigSettings();
+       configSettings->beginGroup("/kconfig/qconf");
        v = new ConfigMainWindow();
 
        //zconfdump(stdout);
-       v->show();
+       configApp->setMainWidget(v);
        configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
        configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
+       v->show();
        configApp->exec();
 
+       configSettings->endGroup();
+       delete configSettings;
+
        return 0;
 }