OK, I've fixed everything you suggested me.

Here's the new version. I've tried to limit the search to QAbstractButton,
QGroupBox and Qlabel but no big improvements. Maybe we should simply add a
delay, so the user can type the whole search query and then start the search,
200-400 ms. Other ideas?

Now when the dialog is hidden the search box is reset.
The "rubber" button will be in the next patch, maybe. :P

Thanks for your comments!
venom00

Index: src/frontends/qt4/GuiDocument.h
===================================================================
--- src/frontends/qt4/GuiDocument.h     (revisione 38474)
+++ src/frontends/qt4/GuiDocument.h     (copia locale)
@@ -77,6 +77,7 @@
        void updateDefaultFormat();
        void updatePagestyle(std::string const &, std::string const &);
        bool isChildIncluded(std::string const &);
+       void hideView();
 
        ///
        BufferParams const & params() const { return bp_; }
Index: src/frontends/qt4/GuiDocument.cpp
===================================================================
--- src/frontends/qt4/GuiDocument.cpp   (revisione 38474)
+++ src/frontends/qt4/GuiDocument.cpp   (copia locale)
@@ -2139,6 +2139,11 @@
                          includeonlys_.end(), child) != includeonlys_.end());
 }
 
+void GuiDocument::hideView() {
+       Dialog::hideView();
+       // Reset the search box
+       this->docPS->resetSearch();
+}
 
 void GuiDocument::applyView()
 {
Index: src/frontends/qt4/GuiPrefs.cpp
===================================================================
--- src/frontends/qt4/GuiPrefs.cpp      (revisione 38474)
+++ src/frontends/qt4/GuiPrefs.cpp      (copia locale)
@@ -3201,6 +3201,11 @@
                modules_[i]->update(rc);
 }
 
+void GuiPreferences::hideView() {
+       Dialog::hideView();
+       // Reset the search box
+       this->prefsPS->resetSearch();
+}
 
 void GuiPreferences::applyView()
 {
Index: src/frontends/qt4/PanelStack.cpp
===================================================================
--- src/frontends/qt4/PanelStack.cpp    (revisione 38474)
+++ src/frontends/qt4/PanelStack.cpp    (copia locale)
@@ -15,12 +15,21 @@
 #include "qt_helpers.h"
 
 #include "support/debug.h"
+#include "support/foreach.h"
 
+#include <QApplication>
+#include <QAbstractButton>
+#include <QComboBox>
 #include <QFontMetrics>
+#include <QGroupBox>
 #include <QHBoxLayout>
 #include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListWidget>
 #include <QStackedWidget>
 #include <QTreeWidget>
+#include <QVBoxLayout>
 
 #include "support/lassert.h"
 
@@ -35,7 +44,9 @@
 {
        list_ = new QTreeWidget(this);
        stack_ = new QStackedWidget(this);
+       search_ = new QLineEdit(this);
 
+       // Configure tree
        list_->setRootIsDecorated(false);
        list_->setColumnCount(1);
        list_->header()->hide();
@@ -43,14 +54,28 @@
        list_->header()->setStretchLastSection(false);
        list_->setMinimumSize(list_->viewport()->size());
 
+
        connect(list_, SIGNAL(currentItemChanged(QTreeWidgetItem*,
QTreeWidgetItem*)),
                this, SLOT(switchPanel(QTreeWidgetItem *, QTreeWidgetItem*)));
        connect(list_, SIGNAL(itemClicked (QTreeWidgetItem*, int)),
                this, SLOT(itemSelected(QTreeWidgetItem *, int)));
 
-       QHBoxLayout * layout = new QHBoxLayout(this);
-       layout->addWidget(list_, 0);
-       layout->addWidget(stack_, 1);
+       // Configure the search box
+#if QT_VERSION >= 0x040700
+       search_->setPlaceholderText(qt_("Search"));
+#endif
+
+       connect(search_, SIGNAL(textChanged(QString)), this,
SLOT(filterChanged(QString)));
+
+       // Create the output layout, horizontal plus a VBox on the left with the
search
+       // box and the tree
+       QVBoxLayout * left_layout = new QVBoxLayout();
+       left_layout->addWidget(search_, 0);
+       left_layout->addWidget(list_, 1);
+
+       QHBoxLayout * main_layout = new QHBoxLayout(this);
+       main_layout->addLayout(left_layout, 0);
+       main_layout->addWidget(stack_, 1);
 }
 
 
@@ -136,11 +161,18 @@
                return;
 
        // if we have a category, expand the tree and go to the
-       // first item
+       // first enabled item
        if (item->childCount() > 0) {
                item->setExpanded(true);
-               if (previous && previous->parent() != item)
-                       switchPanel( item->child(0), previous );
+               if (previous && previous->parent() != item) {
+                       // Looks for a child not disabled
+                       for (int i = 0; i < item->childCount(); ++i) {
+                               if (item->child(i)->flags() & Qt::ItemIsEnabled)
{
+                                       switchPanel(item->child(i), previous);
+                                       break;
+                               }
+                       }
+               }
        }
        else if (QWidget * w = widget_map_.value(item, 0)) {
                stack_->setCurrentWidget(w);
@@ -148,6 +180,106 @@
 }
 
 
+void PanelStack::filterChanged(QString const & search) {
+       bool enable_all = search.length() == 0;
+
+       // If the search string is empty we have to re-enable everything
+       if (enable_all) {
+               foreach (QTreeWidgetItem * tree_item, panel_map_) {
+                       tree_item->setDisabled(false);
+                       tree_item->setTextColor(0,
QApplication::palette().color(QPalette::Active, QPalette::Text));
+               }
+       } else {
+               // Otherwise disable everything and then re-enable panes
matching the
+               // search criteria
+               foreach (QTreeWidgetItem * tree_item, panel_map_) {
+                       tree_item->setDisabled(true);
+                       tree_item->setTextColor(0,
QApplication::palette().color(QPalette::Disabled, QPalette::Text));
+               }
+       }
+
+       foreach (QTreeWidgetItem * tree_item, panel_map_) {
+
+               // Current widget
+               QWidget * pane_widget = this->widget_map_[tree_item];
+
+               // First of all we look in the pane name
+               bool pane_matches = tree_item->text(0).contains(search,
Qt::CaseInsensitive);
+
+               // If the tree item has an associated pane
+               if (pane_widget) {
+
+                       // Collect all the children widgets (recursive)
+                       QList<QWidget *> children =
pane_widget->findChildren<QWidget *>();
+
+                       // Loops on the list of children widgets
+                       size_t last_child = children.size();
+                       for (size_t child_index = 0; child_index != last_child;
++child_index) {
+                               bool widget_matches = false;
+
+                               // Try to cast to the most common widgets and
looks in it's content by each
+                               // It's bad OOP, it would be nice to have a
QWidget::toString() overloaded by
+                               // each widget, but this would require to change
Qt or subclass each widget.
+                               // Note that we have to ignore the amperstand
symbol
+                               if (QAbstractButton * button =
qobject_cast<QAbstractButton *>(children[child_index])) {
+                                       widget_matches =
button->text().replace('&', "")
+                                               .contains(search,
Qt::CaseInsensitive);
+
+                               } else if (QGroupBox * group_box =
qobject_cast<QGroupBox *>(children[child_index])) {
+                                       widget_matches =
group_box->title().replace('&', "")
+                                               .contains(search,
Qt::CaseInsensitive);
+
+                               } else if (QLabel * label = qobject_cast<QLabel
*>(children[child_index])) {
+                                       widget_matches =
label->text().replace('&', "")
+                                               .contains(search,
Qt::CaseInsensitive);
+
+                               } else if (QLineEdit * line_edit =
qobject_cast<QLineEdit *>(children[child_index])) {
+                                       widget_matches =
line_edit->text().replace('&', "")
+                                               .contains(search,
Qt::CaseInsensitive);
+
+                               } else if (QListWidget * list_widget =
qobject_cast<QListWidget *>(children[child_index])) {
+                                       widget_matches =
(list_widget->findItems(search, Qt::MatchContains)).count() != 0;
+
+                               } else if (QTreeWidget * tree_view =
qobject_cast<QTreeWidget *>(children[child_index])) {
+                                       widget_matches =
(tree_view->findItems(search, Qt::MatchContains)).count() != 0;
+
+                               } else if (QComboBox * combo_box =
qobject_cast<QComboBox *>(children[child_index])) {
+                                       widget_matches =
(combo_box->findText(search, Qt::MatchContains)) != -1;
+
+                               } else {
+                                       continue;
+                               }
+
+                               // If this widget meets the search criteria
+                               if (widget_matches && !enable_all) {
+                                       // The pane too meets the search
criteria
+                                       pane_matches = true;
+                                       // Highlight the widget
+                                       QPalette widget_palette =
children[child_index]->palette();
+
widget_palette.setColor(children[child_index]->foregroundRole(), Qt::red);
+
children[child_index]->setPalette(widget_palette);
+                               } else {
+                                       // Reset the color of the widget
+                                       children[child_index]->setPalette(
QApplication::palette( children[child_index] ) );
+                               }
+                       }
+
+                       // If the pane meets the search criteria
+                       if (pane_matches && !enable_all) {
+                               // Expand and enable the pane and his ancestors
(typically just the parent)
+                               QTreeWidgetItem * item = tree_item;
+                               do {
+                                       item->setExpanded(true);
+                                       item->setDisabled(false);
+                                       item->setTextColor(0,
QApplication::palette().color(QPalette::Active, QPalette::Text));
+                                       item = item->parent();
+                               } while (item);
+                       }
+               }
+
+       }
+}
+
 void PanelStack::itemSelected(QTreeWidgetItem * item, int)
 {
        // de-select the category if a child is selected
@@ -162,6 +294,10 @@
                qMax(list_->height(), stack_->height()));
 }
 
+void PanelStack::resetSearch() {
+       this->search_->setText("");
+}
+
 } // namespace frontend
 } // namespace lyx
 
Index: src/frontends/qt4/PanelStack.h
===================================================================
--- src/frontends/qt4/PanelStack.h      (revisione 38474)
+++ src/frontends/qt4/PanelStack.h      (copia locale)
@@ -16,6 +16,7 @@
 #include <QWidget>
 #include <QHash>
 
+class QLineEdit;
 class QTreeWidget;
 class QTreeWidgetItem;
 class QStackedWidget;
@@ -44,8 +45,12 @@
        bool isCurrentPanel(QString const & name) const;
        ///
        QSize sizeHint() const;
+       /// resets the search box
+       void resetSearch();
 
 public Q_SLOTS:
+       /// the option filter changed
+       void filterChanged(QString const & search);
        /// set current panel from an item
        void switchPanel(QTreeWidgetItem * it, QTreeWidgetItem * previous = 0);
        /// click on the tree
@@ -61,6 +66,9 @@
 
        WidgetMap widget_map_;
 
+       /// contains the search box
+       QLineEdit * search_;
+
        /// contains the items
        QTreeWidget * list_;
 
Index: src/frontends/qt4/Dialog.h
===================================================================
--- src/frontends/qt4/Dialog.h  (revisione 38474)
+++ src/frontends/qt4/Dialog.h  (copia locale)
@@ -119,7 +119,7 @@
        virtual void applyView() = 0;
 
        /// Hide the dialog from sight
-       void hideView();
+       virtual void hideView();
 
        /// Prepare dialog and display it.
        void showView();
Index: src/frontends/qt4/GuiPrefs.h
===================================================================
--- src/frontends/qt4/GuiPrefs.h        (revisione 38474)
+++ src/frontends/qt4/GuiPrefs.h        (copia locale)
@@ -67,6 +67,7 @@
 
        void apply(LyXRC & rc) const;
        void updateRc(LyXRC const & rc);
+       void hideView();
 
 public Q_SLOTS:
        void change_adaptor();
Index: src/frontends/qt4/GuiDocument.h
===================================================================
--- src/frontends/qt4/GuiDocument.h	(revisione 38474)
+++ src/frontends/qt4/GuiDocument.h	(copia locale)
@@ -77,6 +77,7 @@
        void updateDefaultFormat();
        void updatePagestyle(std::string const &, std::string const &);
        bool isChildIncluded(std::string const &);
+	void hideView();

        ///
        BufferParams const & params() const { return bp_; }
Index: src/frontends/qt4/GuiDocument.cpp
===================================================================
--- src/frontends/qt4/GuiDocument.cpp	(revisione 38474)
+++ src/frontends/qt4/GuiDocument.cpp	(copia locale)
@@ -2139,6 +2139,11 @@
                          includeonlys_.end(), child) != includeonlys_.end());
 }

+void GuiDocument::hideView() {
+	Dialog::hideView();
+	// Reset the search box
+	this->docPS->resetSearch();
+}

 void GuiDocument::applyView()
 {
Index: src/frontends/qt4/GuiPrefs.cpp
===================================================================
--- src/frontends/qt4/GuiPrefs.cpp	(revisione 38474)
+++ src/frontends/qt4/GuiPrefs.cpp	(copia locale)
@@ -3201,6 +3201,11 @@
                modules_[i]->update(rc);
 }

+void GuiPreferences::hideView() {
+	Dialog::hideView();
+	// Reset the search box
+	this->prefsPS->resetSearch();
+}

 void GuiPreferences::applyView()
 {
Index: src/frontends/qt4/PanelStack.cpp
===================================================================
--- src/frontends/qt4/PanelStack.cpp	(revisione 38474)
+++ src/frontends/qt4/PanelStack.cpp	(copia locale)
@@ -15,12 +15,21 @@
 #include "qt_helpers.h"

 #include "support/debug.h"
+#include "support/foreach.h"

+#include <QApplication>
+#include <QAbstractButton>
+#include <QComboBox>
 #include <QFontMetrics>
+#include <QGroupBox>
 #include <QHBoxLayout>
 #include <QHeaderView>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListWidget>
 #include <QStackedWidget>
 #include <QTreeWidget>
+#include <QVBoxLayout>

 #include "support/lassert.h"

@@ -35,7 +44,9 @@
 {
        list_ = new QTreeWidget(this);
        stack_ = new QStackedWidget(this);
+	search_ = new QLineEdit(this);

+	// Configure tree
        list_->setRootIsDecorated(false);
        list_->setColumnCount(1);
        list_->header()->hide();
@@ -43,14 +54,28 @@
        list_->header()->setStretchLastSection(false);
        list_->setMinimumSize(list_->viewport()->size());

+
        connect(list_, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
                this, SLOT(switchPanel(QTreeWidgetItem *, QTreeWidgetItem*)));
        connect(list_, SIGNAL(itemClicked (QTreeWidgetItem*, int)),
                this, SLOT(itemSelected(QTreeWidgetItem *, int)));

-	QHBoxLayout * layout = new QHBoxLayout(this);
-	layout->addWidget(list_, 0);
-	layout->addWidget(stack_, 1);
+	// Configure the search box
+#if QT_VERSION >= 0x040700
+	search_->setPlaceholderText(qt_("Search"));
+#endif
+
+	connect(search_, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
+
+	// Create the output layout, horizontal plus a VBox on the left with the search
+	// box and the tree
+	QVBoxLayout * left_layout = new QVBoxLayout();
+	left_layout->addWidget(search_, 0);
+	left_layout->addWidget(list_, 1);
+
+	QHBoxLayout * main_layout = new QHBoxLayout(this);
+	main_layout->addLayout(left_layout, 0);
+	main_layout->addWidget(stack_, 1);
 }


@@ -136,11 +161,18 @@
                return;

        // if we have a category, expand the tree and go to the
-	// first item
+	// first enabled item
        if (item->childCount() > 0) {
                item->setExpanded(true);
-		if (previous && previous->parent() != item)
-			switchPanel( item->child(0), previous );
+		if (previous && previous->parent() != item) {
+			// Looks for a child not disabled
+			for (int i = 0; i < item->childCount(); ++i) {
+				if (item->child(i)->flags() & Qt::ItemIsEnabled) {
+					switchPanel(item->child(i), previous);
+					break;
+				}
+			}
+		}
        }
        else if (QWidget * w = widget_map_.value(item, 0)) {
                stack_->setCurrentWidget(w);
@@ -148,6 +180,106 @@
 }


+void PanelStack::filterChanged(QString const & search) {
+	bool enable_all = search.length() == 0;
+
+	// If the search string is empty we have to re-enable everything
+	if (enable_all) {
+		foreach (QTreeWidgetItem * tree_item, panel_map_) {
+			tree_item->setDisabled(false);
+			tree_item->setTextColor(0, QApplication::palette().color(QPalette::Active, QPalette::Text));
+		}
+	} else {
+		// Otherwise disable everything and then re-enable panes matching the
+		// search criteria
+		foreach (QTreeWidgetItem * tree_item, panel_map_) {
+			tree_item->setDisabled(true);
+			tree_item->setTextColor(0, QApplication::palette().color(QPalette::Disabled, QPalette::Text));
+		}
+	}
+
+	foreach (QTreeWidgetItem * tree_item, panel_map_) {
+
+		// Current widget
+		QWidget * pane_widget = this->widget_map_[tree_item];
+
+		// First of all we look in the pane name
+		bool pane_matches = tree_item->text(0).contains(search, Qt::CaseInsensitive);
+
+		// If the tree item has an associated pane
+		if (pane_widget) {
+
+			// Collect all the children widgets (recursive)
+			QList<QWidget *> children = pane_widget->findChildren<QWidget *>();
+
+			// Loops on the list of children widgets
+			size_t last_child = children.size();
+			for (size_t child_index = 0; child_index != last_child; ++child_index) {
+				bool widget_matches = false;
+
+				// Try to cast to the most common widgets and looks in it's content by each
+				// It's bad OOP, it would be nice to have a QWidget::toString() overloaded by
+				// each widget, but this would require to change Qt or subclass each widget.
+				// Note that we have to ignore the amperstand symbol
+				if (QAbstractButton * button = qobject_cast<QAbstractButton *>(children[child_index])) {
+					widget_matches = button->text().replace('&', "")
+						.contains(search, Qt::CaseInsensitive);
+
+				} else if (QGroupBox * group_box = qobject_cast<QGroupBox *>(children[child_index])) {
+					widget_matches = group_box->title().replace('&', "")
+						.contains(search, Qt::CaseInsensitive);
+
+				} else if (QLabel * label = qobject_cast<QLabel *>(children[child_index])) {
+					widget_matches = label->text().replace('&', "")
+						.contains(search, Qt::CaseInsensitive);
+
+				} else if (QLineEdit * line_edit = qobject_cast<QLineEdit *>(children[child_index])) {
+					widget_matches = line_edit->text().replace('&', "")
+						.contains(search, Qt::CaseInsensitive);
+
+				} else if (QListWidget * list_widget = qobject_cast<QListWidget *>(children[child_index])) {
+					widget_matches = (list_widget->findItems(search, Qt::MatchContains)).count() != 0;
+
+				} else if (QTreeWidget * tree_view = qobject_cast<QTreeWidget *>(children[child_index])) {
+					widget_matches = (tree_view->findItems(search, Qt::MatchContains)).count() != 0;
+
+				} else if (QComboBox * combo_box = qobject_cast<QComboBox *>(children[child_index])) {
+					widget_matches = (combo_box->findText(search, Qt::MatchContains)) != -1;
+
+				} else {
+					continue;
+				}
+
+				// If this widget meets the search criteria
+				if (widget_matches && !enable_all) {
+					// The pane too meets the search criteria
+					pane_matches = true;
+					// Highlight the widget
+					QPalette widget_palette = children[child_index]->palette();
+					widget_palette.setColor(children[child_index]->foregroundRole(), Qt::red);
+					children[child_index]->setPalette(widget_palette);
+				} else {
+					// Reset the color of the widget
+					children[child_index]->setPalette( QApplication::palette( children[child_index] ) );
+				}
+			}
+
+			// If the pane meets the search criteria
+			if (pane_matches && !enable_all) {
+				// Expand and enable the pane and his ancestors	(typically just the parent)
+				QTreeWidgetItem * item = tree_item;
+				do {
+					item->setExpanded(true);
+					item->setDisabled(false);
+					item->setTextColor(0, QApplication::palette().color(QPalette::Active, QPalette::Text));
+					item = item->parent();
+				} while (item);
+			}
+		}
+
+	}
+}
+
 void PanelStack::itemSelected(QTreeWidgetItem * item, int)
 {
        // de-select the category if a child is selected
@@ -162,6 +294,10 @@
                qMax(list_->height(), stack_->height()));
 }

+void PanelStack::resetSearch() {
+	this->search_->setText("");
+}
+
 } // namespace frontend
 } // namespace lyx

Index: src/frontends/qt4/PanelStack.h
===================================================================
--- src/frontends/qt4/PanelStack.h	(revisione 38474)
+++ src/frontends/qt4/PanelStack.h	(copia locale)
@@ -16,6 +16,7 @@
 #include <QWidget>
 #include <QHash>

+class QLineEdit;
 class QTreeWidget;
 class QTreeWidgetItem;
 class QStackedWidget;
@@ -44,8 +45,12 @@
        bool isCurrentPanel(QString const & name) const;
        ///
        QSize sizeHint() const;
+	/// resets the search box
+	void resetSearch();

 public Q_SLOTS:
+	/// the option filter changed
+	void filterChanged(QString const & search);
        /// set current panel from an item
        void switchPanel(QTreeWidgetItem * it, QTreeWidgetItem * previous = 0);
        /// click on the tree
@@ -61,6 +66,9 @@

        WidgetMap widget_map_;

+	/// contains the search box
+	QLineEdit * search_;
+
        /// contains the items
        QTreeWidget * list_;

Index: src/frontends/qt4/Dialog.h
===================================================================
--- src/frontends/qt4/Dialog.h	(revisione 38474)
+++ src/frontends/qt4/Dialog.h	(copia locale)
@@ -119,7 +119,7 @@
        virtual void applyView() = 0;

        /// Hide the dialog from sight
-	void hideView();
+	virtual void hideView();

        /// Prepare dialog and display it.
        void showView();
Index: src/frontends/qt4/GuiPrefs.h
===================================================================
--- src/frontends/qt4/GuiPrefs.h	(revisione 38474)
+++ src/frontends/qt4/GuiPrefs.h	(copia locale)
@@ -67,6 +67,7 @@

        void apply(LyXRC & rc) const;
        void updateRc(LyXRC const & rc);
+	void hideView();

 public Q_SLOTS:
        void change_adaptor();

Reply via email to